如果想看视频的话,可以观看我在B站翻译的教程:BV1vt4y1U7HV
实现扫描效果不可能对每个着色器都去增加一段shader代码来实现扫描效果,这是不合理的,不利于项目扩展。所以正确的做法是对于屏幕中看到的像素去做处理,也就是像后处理一样,把rendertexture进行处理后在渲染在屏幕上。
获取深度缓冲
在获取当前渲染的摄像机组件后,对其成员depthTextureMode 赋值就能获得深度贴图。
GetComponent<Camera>().depthTextureMode = DepthTextureMode.Depth;
于是shader中就能够通过声明sampler2D _CameraDepthTexture来获取深度贴图。
亲自尝试了发现:
如果场景中有灯光能投射阴影,即使不去设置摄像机的深度贴图模式也能获取到深度贴图。
重构空间坐标
因为要实现扫描效果,我们需要计算像素点离扫描点的距离。但如何获得空间顶点坐标呢?因为是后处理,所以在shader中处理的顶点其实只有屏幕四个角落的点。所以想要获取当前像素在世界空间的坐标,我们需要使用深度贴图来重构世界空间坐标。
重构空间坐标有两种方法:
- 构建出当前像素的NDC坐标,再通过当前摄像机的视角*投影矩阵的逆矩阵来得到世界空间下的像素坐标。(但这需要在片元着色器中进行矩阵乘法操作,会影响游戏性能)
- 对图像空间下的视锥体射线(从摄像机触发,指向图像上的某点的射线)进行插值,这条射线存储了该像素在世界空间下到摄像机的方向信息。然后,我们把该射线和线性化后的视角空间下的深度值相乘,再加上摄像机的世界位置,就可以得到该像素在世界空间下的位置。
计算视锥体射线
float camFar = Camera.main.farClipPlane;
float camFov = Camera.main.fieldOfView;
float camAspect = Camera.main.aspect;
float fovWHalf = camFov * 0.5f;
Vector3 toRight = Camera.main.transform.right * Mathf.Tan(fovWHalf * Mathf.Deg2Rad) * camAspect;
Vector3 toTop = Camera.main.transform.up * Mathf.Tan(fovWHalf * Mathf.Deg2Rad);
Vector3 topLeft = (Camera.main.transform.forward - toRight + toTop);
float camScale = topLeft.magnitude * camFar;
topLeft.Normalize();
topLeft *= camScale;
Vector3 topRight = (Camera.main.transform.forward + toRigh