深度和法線紋理

書中提及到「觀察空間下的法線信息會被編碼進紋理的R和G通道,而深度信息會被編碼進B和A通道」。那麼,法線佔兩個通道,剩下的軸可以用叉積計算,這個很好理解。但是為甚麼深度信息需要佔兩個通道呢?

另外,Unity也提供了內置的Shader計算深度法線紋理。在builtin_shaders-xxx/DefaultResources/Camera-DepthNormalTexture.shader文件中能找到用於渲染的Pass。

Unity獲取深度和法線紋理

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}} $$