GameTech/URP 渲染实战

案例 2.7第一人称手持物:清不清深度入门

目标:用 Overlay 相机渲染一个“拿在手里”的物体,对比 Clear Depth 关闭和开启的效果。

Assets/Cookbook/Ch02/Editor/Ch02Shots.cs(节选:stack)
s.Cam.cullingMask = ~(1 << overlayLayer);          // Base 相机不画“手持物”层
if (s.Panel == 0) return;

// 叠加相机:只画手持物这一层,挂到 Base 相机的 Stack 里
var go = new GameObject("Overlay Camera");
go.transform.SetPositionAndRotation(s.Cam.transform.position, s.Cam.transform.rotation);
var cam = go.AddComponent<Camera>();
cam.fieldOfView = s.Cam.fieldOfView;
cam.nearClipPlane = 0.05f;
cam.aspect = 640f / 420f;
cam.cullingMask = 1 << overlayLayer;
var data = go.AddComponent<UniversalAdditionalCameraData>();
data.renderType = CameraRenderType.Overlay;
// clearDepth 在 URP 14 里只有 getter,Inspector 上的 Clear Depth 勾选框对应序列化字段 m_ClearDepth
var so = new SerializedObject(data);
so.FindProperty("m_ClearDepth").boolValue = s.Panel == 2;   // 面板 1 不清深度,面板 2 清深度
so.ApplyModifiedPropertiesWithoutUndo();
s.CamData.cameraStack.Add(cam);

var item = s.Knot(s.Lit(new Color(1f, 0.6f, 0.15f), 1f, 0.8f), Vector3.zero, 0.3f);
item.layer = overlayLayer;
item.transform.SetParent(s.Cam.transform, false);
item.transform.localPosition = new Vector3(0.36f, -0.2f, 1.25f);
item.transform.localRotation = Quaternion.Euler(20, 40, 0);
图 2-15左:只有 Base 相机;中:加 Overlay 相机,不清深度(手持物插进了近处的柱子里);右:Overlay 相机清除深度(手持物总在最前面)

中图里,Overlay 相机沿用了 Base 相机的深度缓冲,于是手持物和场景做了正常的深度测试,右下部分被离相机很近的柱子挡住——第一人称游戏里“枪管插进墙里”就是这个样子。勾上 Clear Depth 后,Overlay 相机开始渲染前把深度清空,手持物永远画在场景之上。代码里通过 SerializedObject 修改 m_ClearDepth,是因为 URP 14 的 UniversalAdditionalCameraData.clearDepth 只有 getter;在编辑器里直接勾选 Overlay 相机 Rendering 面板中的 Clear Depth 即可。

提示手持物常用比场景更小的 FOV 来避免透视畸变。不同 FOV 的两台相机,深度值的含义不同,这时混用深度缓冲(不清深度)就失去了物理意义——再一次说明手持物的 Overlay 相机通常应该清深度。