1 起因
因为最近在尝试做游戏手机端的景深效果,所以需要从场景的深度图(z-buffer)中还原场景在观察空间下的深度值。这需要理解图形学中坐标系的变化原理。所以本篇博客主要关注的是投影矩阵(projection matrix)。
2 复习
首先复习一下在图形学中的5个坐标系。分别为
- 局部空间(local space)
- 世界空间(world space)
- 观察空间(view space)
- 裁剪空间(clip space)
- 屏幕空间(screen space)
以及在相邻的坐标系中需要用一个矩阵进行坐标系的转换
- 局部空间 -> 世界空间 : model matrix
- 世界空间 -> 观察空间 : view matrix
- 观察空间 -> 裁剪空间 :
- 观察空间 -> 4D裁剪空间坐标 : projection matrix
- 4D裁剪空间坐标 -> 3D标准化设备坐标(NDC) : 透视除法(Perspective Division)
- 透视除法 : 将x, y, z, w坐标除以w
- 也有的地方把裁剪空间分为裁剪空间和NDC空间 NDC vs clip
- 裁剪空间 -> 屏幕空间 : 根据设定的窗口大小等信息,将NDC转为屏幕像素坐标,在opengl中式gl_viewport函数
3 投影矩阵 projection matrix
这篇博客主要讲关于projection matrix,因为从z-buffer中还原到projection matrix需要用到的就是projection matrix。
3.1 z-buffer
z-buffer是用于做深度检测的(depth test),用于判断场景中物体的前后关系,达到正确的渲染效果。z-buffer是在3D标准化设备坐标中z的大小(即透视除法之后)。
3.2 推导
设在观察空间中的坐标为$(x_e, y_e, z_e, w_e)$, 在4D裁剪空间的坐标为$(x_c, y_c, z_c, w_c)$, 3D标准化设备NDC坐标为$(x_n, y_n, z_n, w_n)$
我们需要求得一个projection矩阵将$(x_e, y_e, z_e, w_e)$映射到$(x_c, y_c, z_c, w_c)$(这正是我们对projection矩阵的定义)
3.2.1 思路
我们首先引入$(x_p, y_p, z_p, w_p)$(p是观察空间的点投影映射到一个二维平面后的坐标,所以我们不关心$z_p$与$w_p$),然后建立$(x_p, y_p, z_p, w_p)$与$(x_n, y_n, z_n, w_n)$的映射关系(因为p与n的点的定义范围是明确的,比如x_p是在[l, r]之间,x_n是在[-1, 1]之间,二者的点可以容易建立方程),最后经过简单的处理就可以得到投影矩阵。
3.2.2 推导
假设相机位置到透视投影的近平面距离为$-n$
$\frac{x_p}{x_e} = \frac{-n}{z_e}$
$\frac{y_p}{y_e} = \frac{-n}{y_e}$
我们知道裁剪空间的坐标需要除以$w_c$的值获得其NDC的值。可以看到$x_p$与$y_p$都除以了$-z_e$。于是可以获得$w_c$的等式(投影矩阵的第4行):$w_c = -z_e$(从后面的推导可以看出这样的设置是合理的)
$x_p$从$[l, r]$映射到$x_n[-1, 1]$
$y_p$从$[b, t]$映射到$y_n[-1, 1]$
$z_p$从$[-n, -f]$映射到$z_n[-1, 1]$
$x_n = \frac{1-(-1)}{r-l}x_p + d$
带入$(l, -1)$得到$-1=\frac{2l}{r-l} + d$
$d=\frac{-r-l}{r-l}$
$x_n = \frac{2}{r-l}x_p - \frac{r+l}{r-l}$
$=\frac{2}{r-l}\frac{-nx_e}{z_e} - \frac{r+l}{r-l}$
$=\frac{2nx_e + (r+l)z_e}{-z_e(r-l)}$
这就得到了$x_n$与$x_e$的关系,将等式两边同时乘以$w_c$就得到了$x_c$的等式(投影矩阵的第1行): $x_c =\frac{2nx_e + (r+l)z_e}{r-l}$
同理
$y_n =\frac{2ny_e + (t+b)z_e}{-z_e(t-b)}$
(投影矩阵的第2行): $y_c =\frac{2ny_e + (t+b)z_e}{t-b}$
$z_c$与$z_e$的推导方式与上面的有所不同:
$z_n=\frac{z_c}{w_c}=\frac{Az_e + Bw_e}{w_c}, w_e = 1, w_c=-z_e$
=>代入$(-n, 1) (-f, -1)$得
$1=\frac{-An + B}{-n}$
$-1=\frac{-Af + B}{-f}$
=>解得$A=-\frac{f+n}{f-n}$
$B=-\frac{2fn}{f-n}$
即$z_n=\frac{-\frac{f+n}{f-n}z_e - \frac{2fn}{f-n}w_e}{-z_e}$,等式两边同时乘以$-z_e$也就得到了$z_c$的等式(投影矩阵的第3行):$z_c=-\frac{f+n}{f-n}z_e - \frac{2fn}{f-n}w_e$
3.2.4 综上
4 z-buffer中获得观察空间的z值
之前在投影矩阵中我们获得了从观察空间到z-buffer的映射(投影矩阵的第3行等式两边同时除以$w_c$),因此我们可以构造逆映射获得从z-buffer到观察空间z值的映射。
$z_n=\frac{f+n}{f-n} + \frac{2fn}{z_e(f-n)}$
于是
$z_e = \frac{2fn}{(f-n)(z_n - \frac{f+n}{f-n})} = \frac{2fn}{(f-n)z_n - (f+n)}$
注意当z-buffer的存储范围为[0, 1]时,先将$z_n = z_n * 2 - 1$将$z_n$映射到[-1, 1]再套用上面的公式求$z_e$