頂點從世界空間轉換到NDC空間:
需要先通過視角矩陣轉換到攝像機空間,然后再通過透視矩陣轉換到齊次裁剪空間,最后做透視除法(齊次裁剪空間的坐標除以自己的w分量),公式:
\(V_{proj} = M_{proj}M_{view}V_{world} \tag{1.1}\)
\(V_{ndc} = V_{proj} / V_{proj}.w \tag{1.2}\)
根據深度值反推世界坐標
當我們知道一個坐標的深度值,並且知道當前的屏幕空間坐標,那么就可以通過屏幕空間坐標和深度值得到NDC坐標
\(V_{ndc} = (V_{screen}.x * 2.0 - 1.0, V_{screen}.y * 2.0 - 1.0, depth, 1.0) \tag{2.1}\)
當我們知道了NDC坐標之后我們可能會就想可以根據上面的世界空間轉換到NDC空間的步驟的逆直接得到世界坐標了,這個步驟會是這樣:
\(V_{proj} = (M_{proj}M_{view})^{-1}V_{ndc}) * V_{proj}.w \tag{2.2}\)
是的,這時候你就會發現你並不知道\(V_{proj}.w\)的值是什么,因為這個值在做透視除法之后已經丟失了。
但是,可以世界坐標的w值時已知的且\(V_{world}.w = 1.0\),所以有:
\(V_{world}.w = ((M_{proj}M_{view})^{-1}V_{ndc}).w * V_{proj}.w = 1.0 \tag{2.3}\)
所以有:
\(V_{proj}.w = 1.0 / ((M_{proj}M_{view})^{-1}V_{ndc}).w \tag{2.4}\)
把上式帶入\(\left( 2.2 \right)\),得:
\(V_{proj} = (M_{proj}M_{view})^{-1}V_{ndc}) / ((M_{proj}M_{view})^{-1}V_{ndc}).w \tag{2.5}\)
所以,世界坐標就為NDC坐標右乘\(M_{proj}M_{view})^{-1}\),再除以自身的w。
在Unity中的實例:
//main problem encountered is camera.projectionMatrix = ??????? worked but further from camera became more inaccurate
//had to use GL.GetGPUProjectionMatrix( ) seems to stay pretty exact now
//在C#中傳遞變換矩陣進shader:
Matrix4x4 viewMat = camera.worldToCameraMatrix;
Matrix4x4 projMat = GL.GetGPUProjectionMatrix( camera.projectionMatrix, false );
Matrix4x4 viewProjMat = (projMat * viewMat);
Shader.SetGlobalMatrix("_ViewProjInv", viewProjMat.inverse);
//in fragment shader:
uniform float4x4 _ViewProjInv;
float4 GetWorldPositionFromDepth( float2 uv_depth )
{
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv_depth);
float4 H = float4(uv_depth.x*2.0-1.0, (uv_depth.y)*2.0-1.0, depth, 1.0);
float4 D = mul(_ViewProjInv,H);
return D/D.w;
}