Files
BlueArchiveMiniGame/Assets/Scripts/Test/Light/MeshShadowProjector.cs
2025-10-15 20:13:48 +08:00

336 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using System.Collections.Generic;
public class MeshShadowProjector : MonoBehaviour
{
[Header("投影设置")]
public Light shadowLight;
public Transform shadowWall;
public string shadowLayerName = "Shadow";
[Header("Mesh设置")]
public bool keepOriginalMesh = true; // 是否保留原始mesh
public float shadowIntensity = 0.5f; // 阴影强度
private List<GameObject> modifiedObjects = new List<GameObject>();
private Vector3 wallNormal;
private Vector3 wallPosition;
void Start()
{
if (shadowLight == null) shadowLight = FindObjectOfType<Light>();
if (shadowWall == null) shadowWall = FindMainWall();
CalculateWallPlane();
GenerateAllShadowMeshes();
}
[ContextMenu("生成所有阴影Mesh")]
public void GenerateAllShadowMeshes()
{
ClearAllShadowMeshes();
var shadowObjects = FindObjectsInLayer(shadowLayerName);
foreach (GameObject obj in shadowObjects)
{
GenerateShadowMesh(obj);
}
Debug.Log($"为 {modifiedObjects.Count} 个物体生成了阴影Mesh");
}
void GenerateShadowMesh(GameObject shadowCaster)
{
MeshFilter meshFilter = shadowCaster.GetComponent<MeshFilter>();
if (meshFilter == null) return;
// 获取原始mesh
Mesh originalMesh = meshFilter.mesh;
// 创建新的mesh组合原始mesh + 投影mesh
Mesh combinedMesh = CreateCombinedMesh(originalMesh, shadowCaster.transform);
// 应用新mesh
if (keepOriginalMesh)
{
// 创建新物体来存放阴影mesh
CreateShadowObject(shadowCaster, combinedMesh);
}
else
{
// 直接修改原物体
meshFilter.mesh = combinedMesh;
UpdateMeshCollider(shadowCaster, combinedMesh);
modifiedObjects.Add(shadowCaster);
}
}
Mesh CreateCombinedMesh(Mesh originalMesh, Transform objectTransform)
{
// 创建新mesh
Mesh newMesh = new Mesh();
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
List<Vector3> normals = new List<Vector3>();
List<Vector2> uv = new List<Vector2>();
// 1. 添加原始mesh的顶点和三角形
vertices.AddRange(originalMesh.vertices);
triangles.AddRange(originalMesh.triangles);
// 复制原始法线和UV
if (originalMesh.normals.Length == originalMesh.vertices.Length)
normals.AddRange(originalMesh.normals);
else
for (int i = 0; i < originalMesh.vertices.Length; i++)
normals.Add(Vector3.up);
if (originalMesh.uv.Length == originalMesh.vertices.Length)
uv.AddRange(originalMesh.uv);
else
for (int i = 0; i < originalMesh.vertices.Length; i++)
uv.Add(Vector2.zero);
// 2. 添加投影到墙面的顶点
int originalVertexCount = vertices.Count;
List<Vector3> projectedVertices = new List<Vector3>();
List<int> projectedTriangles = new List<int>();
// 投影每个顶点到墙面
foreach (Vector3 localVertex in originalMesh.vertices)
{
Vector3 worldVertex = objectTransform.TransformPoint(localVertex);
Vector3 projectedWorld = ProjectPointToWall(worldVertex);
Vector3 projectedLocal = objectTransform.InverseTransformPoint(projectedWorld);
projectedVertices.Add(projectedLocal);
}
// 添加投影顶点
vertices.AddRange(projectedVertices);
// 为投影顶点添加法线和UV
for (int i = 0; i < projectedVertices.Count; i++)
{
normals.Add(-objectTransform.forward); // 面向墙面
uv.Add(new Vector2(1, 1)); // 使用不同的UV区域
}
// 3. 创建连接原始mesh和投影mesh的侧面三角形
for (int i = 0; i < originalMesh.triangles.Length; i += 3)
{
int v1 = originalMesh.triangles[i];
int v2 = originalMesh.triangles[i + 1];
int v3 = originalMesh.triangles[i + 2];
// 对应的投影顶点索引
int p1 = v1 + originalVertexCount;
int p2 = v2 + originalVertexCount;
int p3 = v3 + originalVertexCount;
// 创建侧面四边形(两个三角形)
// 三角形1
projectedTriangles.Add(v1);
projectedTriangles.Add(p1);
projectedTriangles.Add(v2);
projectedTriangles.Add(v2);
projectedTriangles.Add(p1);
projectedTriangles.Add(p2);
// 三角形2
projectedTriangles.Add(v2);
projectedTriangles.Add(p2);
projectedTriangles.Add(v3);
projectedTriangles.Add(v3);
projectedTriangles.Add(p2);
projectedTriangles.Add(p3);
// 三角形3
projectedTriangles.Add(v3);
projectedTriangles.Add(p3);
projectedTriangles.Add(v1);
projectedTriangles.Add(v1);
projectedTriangles.Add(p3);
projectedTriangles.Add(p1);
}
// 4. 添加投影面的三角形(反转原始三角形)
int[] originalTriangles = originalMesh.triangles;
for (int i = 0; i < originalTriangles.Length; i += 3)
{
// 反转 winding order
projectedTriangles.Add(originalTriangles[i + 2] + originalVertexCount);
projectedTriangles.Add(originalTriangles[i + 1] + originalVertexCount);
projectedTriangles.Add(originalTriangles[i] + originalVertexCount);
}
// 5. 合并所有三角形
triangles.AddRange(projectedTriangles);
// 6. 应用到新mesh
newMesh.vertices = vertices.ToArray();
newMesh.triangles = triangles.ToArray();
newMesh.normals = normals.ToArray();
newMesh.uv = uv.ToArray();
newMesh.RecalculateBounds();
newMesh.RecalculateTangents();
return newMesh;
}
void CreateShadowObject(GameObject original, Mesh shadowMesh)
{
GameObject shadowObject = new GameObject($"{original.name}_ShadowMesh");
shadowObject.transform.SetParent(original.transform);
shadowObject.transform.localPosition = Vector3.zero;
shadowObject.transform.localRotation = Quaternion.identity;
shadowObject.transform.localScale = Vector3.one;
// 添加Mesh Filter
MeshFilter meshFilter = shadowObject.AddComponent<MeshFilter>();
meshFilter.mesh = shadowMesh;
// 添加Mesh Collider
MeshCollider meshCollider = shadowObject.AddComponent<MeshCollider>();
meshCollider.sharedMesh = shadowMesh;
meshCollider.convex = false; // 对于复杂mesh使用非凸体
// 可选:添加材质用于可视化
MeshRenderer renderer = shadowObject.AddComponent<MeshRenderer>();
renderer.material = original.GetComponent<MeshRenderer>().material;
modifiedObjects.Add(shadowObject);
}
void UpdateMeshCollider(GameObject obj, Mesh mesh)
{
MeshCollider meshCollider = obj.GetComponent<MeshCollider>();
if (meshCollider == null)
meshCollider = obj.AddComponent<MeshCollider>();
meshCollider.sharedMesh = mesh;
meshCollider.convex = false;
}
Material CreateShadowMaterial()
{
Material material = new Material(Shader.Find("Standard"));
material.color = new Color(0, 0, 0, shadowIntensity);
material.SetFloat("_Mode", 2); // Fade mode
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
material.SetInt("_ZWrite", 0);
material.DisableKeyword("_ALPHATEST_ON");
material.EnableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = 3000;
return material;
}
Vector3 ProjectPointToWall(Vector3 worldPoint)
{
Vector3 lightDirection = GetLightDirection(worldPoint);
return IntersectWithWall(worldPoint, lightDirection);
}
Vector3 GetLightDirection(Vector3 targetPoint)
{
if (shadowLight.type == LightType.Directional)
{
return -shadowLight.transform.forward;
}
else
{
return (targetPoint - shadowLight.transform.position).normalized;
}
}
Vector3 IntersectWithWall(Vector3 point, Vector3 direction)
{
float denominator = Vector3.Dot(direction, wallNormal);
if (Mathf.Abs(denominator) < 0.0001f)
return point;
float t = Vector3.Dot(wallPosition - point, wallNormal) / denominator;
return point + direction * t;
}
void CalculateWallPlane()
{
if (shadowWall != null)
{
wallPosition = shadowWall.position;
wallNormal = -shadowWall.forward;
}
}
Transform FindMainWall()
{
BoxCollider[] colliders = FindObjectsOfType<BoxCollider>();
return colliders.Length > 0 ? colliders[0].transform : transform;
}
List<GameObject> FindObjectsInLayer(string layerName)
{
int layer = LayerMask.NameToLayer(layerName);
List<GameObject> result = new List<GameObject>();
foreach (GameObject obj in FindObjectsOfType<GameObject>())
{
if (obj.activeInHierarchy && obj.layer == layer)
{
result.Add(obj);
}
}
return result;
}
[ContextMenu("清空所有阴影Mesh")]
void ClearAllShadowMeshes()
{
foreach (GameObject obj in modifiedObjects)
{
if (obj != null)
{
if (obj.name.EndsWith("_ShadowMesh"))
DestroyImmediate(obj);
else
ResetOriginalMesh(obj);
}
}
modifiedObjects.Clear();
}
void ResetOriginalMesh(GameObject obj)
{
MeshFilter meshFilter = obj.GetComponent<MeshFilter>();
if (meshFilter != null && meshFilter.mesh != null)
{
// 重新加载原始mesh
Mesh originalMesh = Instantiate(Resources.Load<Mesh>(obj.name)) ?? CreateSimpleMesh();
meshFilter.mesh = originalMesh;
MeshCollider collider = obj.GetComponent<MeshCollider>();
if (collider != null)
collider.sharedMesh = originalMesh;
}
}
Mesh CreateSimpleMesh()
{
// 创建默认的立方体mesh
return GameObject.CreatePrimitive(PrimitiveType.Cube).GetComponent<MeshFilter>().sharedMesh;
}
void OnDestroy()
{
ClearAllShadowMeshes();
}
}