書中提及到「觀察空間下的法線信息會被編碼進紋理的R和G通道,而深度信息會被編碼進B和A通道」。那麼,法線佔兩個通道,剩下的軸可以用叉積計算,這個很好理解。但是為甚麼深度信息需要佔兩個通道呢?
另外,Unity也提供了內置的Shader計算深度法線紋理。在builtin_shaders-xxx/DefaultResources/Camera-DepthNormalTexture.shader
文件中能找到用於渲染的Pass。
camera.depthTextureMode = DepthTextureMode.Depth;
camera.depthTextureMode = DepthTextureMode.DepthNormals;
sampler2D _CameraDepthTexture;
sampler2D _CameraDepthNormalsTexture;
// 組合
camera.depthTextureMode |= DepthTextureMode.Depth;
camera.depthTextureMode |= DepthTextureMode.DepthNormals;
即使在2021版Unity中,也不能直接通過Inspector
設置Camera
組件的depthTextureMode
。
在Unity Shader中取樣深度紋理時,需要注意平台的差異化。不過,可以使用Unity提供的SAMPLE_DEPTH_TEXTURE
宏對深度紋理進行采樣
float d = SAMPLE_DEPTH_TEXTURE (_CameraDepthTexture, i.uv);
<aside> <img src="/icons/drafts_gray.svg" alt="/icons/drafts_gray.svg" width="40px" /> 推導過程可以在第四章中找到
</aside>
$$ z_{clip} = -z_{view} \frac{Far + Near}{Far - Near} - \frac{2NearFar}{Far - Near} \\[5px] w_{clip} = -z_{view} $$
通過齊次除法,就能得到NDC下的z分量
$$ z_{ndc} = \frac{z_{clip}}{w_{clip}} = \frac{Far + Near}{Far - Near} + \frac{2NearFar}{(Far - Near)z_{view}} $$
然後因為Unity使用的是OpenGL的機制,所以z分量映射到[-1, 1]的範圍。為了存儲到紋理中,需要映射到[0, 1],所以得
$$ d = 0.5 z_{ndc} + 0.5 $$
根據上面的式子,可以反推導出$z_{view}$。有
$$ z_{view} = \frac{1}{\frac{Far - Near}{FarNear}d - \frac{1}{Near}} $$