235 lines
8.7 KiB
C#

namespace UnityEngine.Rendering.Universal
{
public class DebugCascadeShadow : MonoBehaviour
{
public Camera targetCamera;
public bool showCullingSpheres = true;
public bool showCameraFrustums = true;
public bool showCascadeFrustums = true;
[Range(0f, 4f)]
public int showMaxCascade = 4;
public Color[] cascadeColors = new Color[]
{
new Color(1, 0, 0, 0.5f), // 红
new Color(0, 1, 0, 0.5f), // 绿
new Color(0, 0.5f, 1, 0.5f), // 蓝
new Color(1, 1, 0, 0.5f) // 黄
};
public static Vector3 cascadeSplits;
public static Vector4[] cullingSpheres = new Vector4[4];
public static Matrix4x4[] cascadeVps = new Matrix4x4[4];
Plane[][] planes = new Plane[][]
{
new Plane[] { new(),new(),new(),new(),new(),new() },
new Plane[] { new(),new(),new(),new(),new(),new() },
new Plane[] { new(),new(),new(),new(),new(),new() },
new Plane[] { new(),new(),new(),new(),new(),new() },
};
void ExtractFrustumPlanes(in Matrix4x4 VP, in Plane[] planes)
{
// Left clipping plane
planes[0] = new Plane(
new Vector3(VP.m30 + VP.m00, VP.m31 + VP.m01, VP.m32 + VP.m02),
VP.m33 + VP.m03);
// Right clipping plane
planes[1] = new Plane(
new Vector3(VP.m30 - VP.m00, VP.m31 - VP.m01, VP.m32 - VP.m02),
VP.m33 - VP.m03);
// Top clipping plane
planes[2] = new Plane(
new Vector3(VP.m30 - VP.m10, VP.m31 - VP.m11, VP.m32 - VP.m12),
VP.m33 - VP.m13);
// Bottom clipping plane
planes[3] = new Plane(
new Vector3(VP.m30 + VP.m10, VP.m31 + VP.m11, VP.m32 + VP.m12),
VP.m33 + VP.m13);
// Near clipping plane
planes[4] = new Plane(
new Vector3(VP.m20, VP.m21, VP.m22),
VP.m23);
// Far clipping plane
planes[5] = new Plane(
new Vector3(VP.m30 - VP.m20, VP.m31 - VP.m21, VP.m32 - VP.m22),
VP.m33 - VP.m23);
}
protected static void ExtractFrustumPlanesStereo(in Matrix4x4 leftVP, in Matrix4x4 rightVP, in Plane[] planes)
{
// Left clipping plane
planes[0] = new Plane(
new Vector3(leftVP.m30 + leftVP.m00, leftVP.m31 + leftVP.m01, leftVP.m32 + leftVP.m02),
leftVP.m33 + leftVP.m03);
// Right clipping plane
planes[1] = new Plane(
new Vector3(rightVP.m30 - rightVP.m00, rightVP.m31 - rightVP.m01, rightVP.m32 - rightVP.m02),
rightVP.m33 - rightVP.m03);
// Top clipping plane
planes[2] = new Plane(
new Vector3(leftVP.m30 - leftVP.m10, leftVP.m31 - leftVP.m11, leftVP.m32 - leftVP.m12),
leftVP.m33 - leftVP.m13);
// Bottom clipping plane
planes[3] = new Plane(
new Vector3(leftVP.m30 + leftVP.m10, leftVP.m31 + leftVP.m11, leftVP.m32 + leftVP.m12),
leftVP.m33 + leftVP.m13);
// Near clipping plane
planes[4] = new Plane(
new Vector3(leftVP.m20, leftVP.m21, leftVP.m22),
leftVP.m23);
// Far clipping plane
planes[5] = new Plane(
new Vector3(leftVP.m30 - leftVP.m20, leftVP.m31 - leftVP.m21, leftVP.m32 - leftVP.m22),
leftVP.m33 - leftVP.m23);
}
protected void DrawFrustum(in Matrix4x4 vp)
{
Matrix4x4 clip2world = vp.inverse;
Vector4[] corners =
{
new(-1, -1, -1, 1),
new(1, -1, -1, 1),
new(1, 1, -1, 1),
new(-1, 1, -1, 1),
new(-1, -1, 1, 1),
new(1, -1, 1, 1),
new(1, 1, 1, 1),
new(-1, 1, 1, 1),
};
for (int i = 0; i < corners.Length; i++)
{
corners[i] = clip2world * corners[i];
corners[i] /= corners[i].w;
}
Gizmos.DrawLine(corners[0], corners[1]);
Gizmos.DrawLine(corners[1], corners[2]);
Gizmos.DrawLine(corners[2], corners[3]);
Gizmos.DrawLine(corners[3], corners[0]);
Gizmos.DrawLine(corners[4], corners[5]);
Gizmos.DrawLine(corners[5], corners[6]);
Gizmos.DrawLine(corners[6], corners[7]);
Gizmos.DrawLine(corners[7], corners[4]);
Gizmos.DrawLine(corners[0], corners[4]);
Gizmos.DrawLine(corners[1], corners[5]);
Gizmos.DrawLine(corners[2], corners[6]);
Gizmos.DrawLine(corners[3], corners[7]);
}
void OnDrawGizmos()
{
if (targetCamera == null) return;
if (showCullingSpheres)
{
for (int i = 0; i < cullingSpheres.Length; i++)
{
if (i > showMaxCascade - 1)
{
break;
}
if (cullingSpheres[i].w <= 0) continue;
Gizmos.color = cascadeColors[i % cascadeColors.Length];
Vector3 sphereCenter = cullingSpheres[i];
float sphereRadius = cullingSpheres[i].w;
Gizmos.DrawWireSphere(sphereCenter, sphereRadius);
}
}
if (showCascadeFrustums)
{
for (int i = 0; i < cascadeVps.Length; i++)
{
if (i > showMaxCascade - 1)
{
break;
}
var vp = cascadeVps[i];
Gizmos.color = cascadeColors[i % cascadeVps.Length];
DrawFrustum(vp);
ExtractFrustumPlanes(vp, planes[i]);
}
}
if (showCameraFrustums)
{
float lastSplit = targetCamera.nearClipPlane;
for (int i = 0; i < 3; i++)
{
if (i > showMaxCascade - 1)
{
break;
}
if (cascadeSplits[i] <= 0) break;
Gizmos.color = cascadeColors[i % cascadeColors.Length];
DrawCascadeFrustum(targetCamera, lastSplit, cascadeSplits[i]);
lastSplit = cascadeSplits[i];
}
}
}
private void DrawCascadeFrustum(Camera cam, float startDist, float endDist)
{
// 计算视锥体角点
Vector3[] nearCorners = GetFrustumCorners(cam, startDist);
Vector3[] farCorners = GetFrustumCorners(cam, endDist);
// 绘制视锥体线框
Gizmos.DrawLine(nearCorners[0], farCorners[0]);
Gizmos.DrawLine(nearCorners[1], farCorners[1]);
Gizmos.DrawLine(nearCorners[2], farCorners[2]);
Gizmos.DrawLine(nearCorners[3], farCorners[3]);
// 绘制近/远平面
DrawFrustumPlane(nearCorners);
DrawFrustumPlane(farCorners);
}
private Vector3[] GetFrustumCorners(Camera cam, float distance)
{
Vector3[] corners = new Vector3[4];
Transform camTransform = cam.transform;
float halfFOV = cam.fieldOfView * 0.5f * Mathf.Deg2Rad;
float aspect = cam.aspect;
float height = distance * Mathf.Tan(halfFOV);
float width = height * aspect;
// 计算相对于相机的角点
corners[0] = camTransform.forward * distance + camTransform.up * height - camTransform.right * width; // 左下
corners[1] = camTransform.forward * distance + camTransform.up * height + camTransform.right * width; // 右下
corners[2] = camTransform.forward * distance - camTransform.up * height + camTransform.right * width; // 右上
corners[3] = camTransform.forward * distance - camTransform.up * height - camTransform.right * width; // 左上
// 转换到世界坐标
for (int i = 0; i < 4; i++)
{
corners[i] = camTransform.position + corners[i];
}
return corners;
}
private void DrawFrustumPlane(Vector3[] corners)
{
Gizmos.DrawLine(corners[0], corners[1]);
Gizmos.DrawLine(corners[1], corners[2]);
Gizmos.DrawLine(corners[2], corners[3]);
Gizmos.DrawLine(corners[3], corners[0]);
}
}
}