线性深度信息推导

非线性的深度信息

深度信息在透视除法之后,是非线性的

image-20220311174119143

Unity中的做法

查阅 Unity Linear01Depth 函数发现

1
2
3
4
5
6
7
8
9
10
// Z buffer to linear 0..1 depth (0 at eye, 1 at far plane)
inline float Linear01Depth( float z )
{
return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);
}
// Z buffer to linear depth
inline float LinearEyeDepth( float z )
{
return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w);
}

Unity 相关说明文档

1
2
3
4
5
6
_ZBufferParams, float4, Used to linearize Z buffer values.

x is (1-far/near)
y is (far/near)
z is (x/far)
w is (y/far)

_ZBufferParams 带入 Linear01Depth 公式为:

_ZBufferParams带入 LinearEyeDepth 公式为:

正交投影矩阵

透视矩阵

投影矩阵

投影矩阵摘自虎书章节 Chapter: Viewing

简化矩阵


OpenGL 的投影矩阵

(观察空间是右手坐标系, 看向-z 方向; 但是 NDC 是左右坐标系, 看向+z 方向, 所以 OpenGL 中构建 glFrustum() 所构建的 GL_PROJECTION 矩阵, 需要将参数 near,far 要求是正数, 需要对两个参数进行取反操作)

摘自: songho.ca

翻译: 翻译

为了将深度信息记录到纹理上

需要将-1~1 转成 0~1

将 z'带入, 并且计算出 View 空间中的 z

对反算出来的 z 进行取反

在右手空间坐标系下, View 视图中, 摄像机看向的是-z 方向, 所有 z 都是负值, 为了变成线性空间下 0~1 之间, 需要对 z 进行取反

此时 z 是 view 视图下的, 在视锥下, z 的取值范围是[near, far], 所以将 z 变成 0-1 的线性空间只需要/far 即可

可以发现和 Linear01Depth 公式一致

image-20220311174841057

将公式带入之后会发现还原后成功转为0~1之间的线性空间

LinearEyeDepth 公式为:

开始化简

得出结果和上方计算原始深度 z 值的公式一致

image-20220311175008036

将公式带入之后会发现还原后成功转为y=x的线性空间,将非线性深度信息还原成view空间下的深度信息

结论

Linear01Depth 是将深度信息中的信息还原成 0~1 之间线性的值

LinearEyeDepth 是将深度信息中的信息还原成 view 空间中的 z