Files
BlueArchiveMiniGame/Assets/Scripts/Test/Light/MeshShadowProjector.cs

336 lines
11 KiB
C#
Raw Normal View History

2025-10-15 20:13:48 +08:00
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();
}
}