从深度图中获取世界空间的坐标
为什么要从深度图重建世界坐标 一个很大的应用情景是在后处理的阶段,或是计算一些屏幕空间的效果(如SSR、SSAO等),只能获取到一张深度贴图,而不是每一个几何体的顶点数据,很多的计算中却又需要用到世界坐标或者是视空间的坐标,这时我们就需要通过深度图来重建世界空间的坐标。 重建世界坐标的流程 首先要获取屏幕空间的UV,这里记为positionSS,范围是(0, 1)(0, 1)。 使用UV采样深度贴图,获取到当前的深度值。 使用UV和深度值,得到标准化设备坐标,这里记为positionNDC。 使用裁剪空间到视空间的变换矩阵乘以positionNDC,除以W分量,得到视空间坐标,这里记为positionVS。 使用视空间到世界空间的变换矩阵乘以positionVS,得到世界空间坐标,这里记为positionWS。 这里使用DepthToPositionShader.shader,假装是屏幕后处理的shader,来演示一下重建世界坐标的流程,这样比直接写屏幕后处理的shader能够更好的去了解Unity的空间变换的方式。 这个shader有以下几个需要注意的点: 为了使用_CameraDepthTexture这张深度贴图,需要在srp的设置中开启Depth Texture这个选项。这样子在渲染的时候会在DepthPrePass用shader中的Depth Only这个pass去先渲染出深度贴图。我们就能够在渲染物体的时候直接拿到包含当前物体的深度贴图了。 顶点着色器和片元着色器中的SV_POSITION并不完全相同。对于顶点着色器来说,SV_POSITION就是之前所说的\((\frac X {\tan {\frac {fovy} 2} \cdot \frac x y }, -\frac Y {\tan {\frac {fovy} 2}}, \frac {Zn} {f - n} + \frac {fn} {f - n}, -Z)\);但是在片元着色器中,SV_POSITION的XY分量会乘上屏幕的宽高,Z分量则是已经除以W之后的深度值。屏幕的宽高信息保存在_ScreenParams这个内置的变量中,它的前两位是屏幕的宽高像素数,后两位是宽高的像素数的倒数加一。 要针对DX11和OpenGL不同的透视变换矩阵来调整UV的Y分量的数值,也就是要注意UNITY_UV_STARTS_AT_TOP这个宏的使用。出现获得的坐标跟随着摄像机的移动发生奇怪的倾斜的时候,往往都是忘记对Y分量的平台差异进行处理。 最后得到的视空间和世界空间的坐标值,要记得除以这个坐标值的W分量,相当于是做了一次归一化,才能得到正确的坐标。 DepthToPositionShader.shader Shader "zznewclear13/DepthToPositionShader" { Properties { [Toggle(REQUIRE_POSITION_VS)] _Require_Position_VS("Require Position VS", float) = 0 } HLSLINCLUDE #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/core.hlsl" #pragma multi_compile _ REQUIRE_POSITION_VS sampler2D _CameraDepthTexture; struct Attributes { float4 positionOS : POSITION; float2 texcoord : TEXCOORD0; }; struct Varyings { float4 positionCS : SV_POSITION; float2 texcoord : TEXCOORD0; }; Varyings Vert(Attributes input) { Varyings output = (Varyings)0; VertexPositionInputs vertexPositionInputs = GetVertexPositionInputs(input....