深度缓冲中的深度值计算及可视化

概述

渲染管线中的顶点变换中,介绍了顶点在各个坐标空间的变换。 变换到最后,是屏幕坐标空间。在OpenGL中,屏幕空间坐标的Z值即是深度缓冲中的深度值。深度缓冲包含了一个介于0.0和1.0之间的深度值,它将会与观察者视角所看见的场景中所有物体的z值进行比较。本文将介绍深度值的计算,以及从深度值反向计算出相机空间中的顶点的Z值。

深度值计算

渲染管线中的顶点变换中,计算得到了透视投影矩阵:
M p e r s p = [ 2 n r − l 0 l + r l − r 0 0 2 n t − b b + t b − t 0 0 0 f + n f − n 2 n f n − f 0 0 1 0 ] M_{persp} = \begin{bmatrix} \frac{2n}{r-l} & 0 & \frac{l+r}{l-r} & 0 \\ 0 & \frac{2n}{t-b} & \frac{b+t}{b-t} & 0 \\ 0 & 0 & \frac{f+n}{f-n} & \frac{2nf}{n-f} \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} Mpersp=rl2n0000tb2n00lrl+rbtb+tfnf+n100nf2nf0
同时,也得到了视口变换矩阵:
M v i e w p o r t = [ w 2 0 0 w 2 0 h 2 0 h 2 0 0 1 2 1 2 0 0 0 1 ] M_{viewport} = \begin{bmatrix} \frac{w}{2} & 0 & 0 & \frac{w}{2} \\ 0 & \frac{h}{2} & 0 & \frac{h}{2} \\ 0 & 0 & \frac{1}{2} & \frac{1}{2} \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} Mviewport=2w00002h00002102w2h211
首先,根据透视矩阵,计算NDC空间的Z值。这里,相机空间中的坐标经过透视矩阵变换后,还要进行齐次除法,才能得到NDC空间中的坐标。
( x c l i p y c l i p z c l i p w c l i p ) = M p e r s p ( x e y e y e y e z e y e w e y e ) \begin{pmatrix} x_{clip} \\ y_{clip} \\ z_{clip} \\ w_{clip} \\ \end{pmatrix} = M_{persp} \begin{pmatrix} x_{eye} \\ y_{eye} \\ z_{eye} \\ w_{eye} \\ \end{pmatrix} xclipyclipzclipwclip=Mperspxeyeyeyezeyeweye

( x n d c y n d c z n d c ) = ( x c l i p w c l i p y c l i p w c l i p z c l i p w c l i p ) \begin{pmatrix} x_{ndc} \\ y_{ndc} \\ z_{ndc} \\ \end{pmatrix} = \begin{pmatrix} \frac{x_{clip}}{w_{clip}} \\ \frac{y_{clip}}{w_{clip}} \\ \frac{z_{clip}}{w_{clip}} \\ \end{pmatrix} xndcyndczndc=wclipxclipwclipyclipwclipzclip

由此,可以得出:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ \begin{aligned…
根据上述公式,可以得出:
z e y e = 2 n f ( f + n ) − z n d c ( f − n ) (2) z_{eye} = \frac{2nf}{(f+n)-z_{ndc}(f-n)} \tag{2} zeye=(f+n)zndc(fn)2nf(2)
根据视口变换矩阵,可以得出:
z w i n = 1 2 z n d c + 1 2 (3) z_{win} = \frac{1}{2}z_{ndc}+\frac{1}{2} \tag{3} zwin=21zndc+21(3)

( 1 ) \left(1\right) (1)带入 ( 3 ) \left(3\right) (3),可以得到:
z w i n = 1 2 ( z n d c + 1 ) = 1 2 ( f + n f − n + − 2 n f z e y e ( f − n ) + 1 ) = f − n f z e y e f − n = 1 n − 1 z e y e 1 n − 1 f \begin{aligned} z_{win} &= \frac{1}{2}(z_{ndc}+1) \\ &=\frac{1}{2}(\frac{f+n}{f-n}+\frac{-2nf}{z_{eye}(f-n)} + 1) \\ &=\frac{f-\frac{nf}{z_{eye}}}{f-n} \\ &= \frac{\frac{1}{n}-\frac{1}{z_{eye}}}{\frac{1}{n}-\frac{1}{f}} \end{aligned} zwin=21(zndc+1)=21(fnf+n+zeye(fn)2nf+1)=fnfzeyenf=n1f1n1zeye1

即:
z w i n = 1 n − 1 z e y e 1 n − 1 f (4) z_{win} = \frac{\frac{1}{n}-\frac{1}{z_{eye}}}{\frac{1}{n}-\frac{1}{f}} \tag{4} zwin=n1f1n1zeye1(4)

到这一步,即可以求得屏幕空间中的深度。

Learn OpenGL CN学习过的,可能对深度测试这一节的内容有些印象。它得到的深度值的公式是:
F d e p t h = 1 / z − 1 / n e a r 1 / f a r − 1 / n e a r F_{depth} = \frac{1/z - 1/near}{1/far - 1/near} Fdepth=1/far1/near1/z1/near
( 4 ) \left(4\right) (4)式对比,发现有些不一样,这是怎么回事呢?

这里要注意,本文定义的 n n n f f f z e y e z_{eye} zeye是实际的坐标值,是负的。而深度测试文中,定义的 n e a r near near f a r far far代表了近平面和远平面,而 z z z代表了近、远平面之间的值,它们都是正的。将 n = − n e a r n=-near n=near f = − f a r f=-far f=far z e y e = − z z_{eye}=-z zeye=z代入 ( 4 ) \left(4\right) (4)式,可得:
F d e p t h = z w i n = 1 n − 1 z e y e 1 n − 1 f = 1 − n e a r − 1 − z 1 − n e a r − 1 − f a r = 1 z − 1 n e a r 1 f a r − 1 n e a r \begin{aligned} F_{depth} &= z_{win} \\ &= \frac{\frac{1}{n}-\frac{1}{z_{eye}}}{\frac{1}{n}-\frac{1}{f}} \\ &= \frac{\frac{1}{-near}-\frac{1}{-z}}{\frac{1}{-near}-\frac{1}{-far}} \\ &= \frac{\frac{1}{z}-\frac{1}{near}}{\frac{1}{far}-\frac{1}{near}} \end{aligned} Fdepth=zwin=n1f1n1zeye1=near1far1near1z1=far1near1z1near1

深度值的线性可视化

经过上面的推导,我们得出了深度值的计算公式。

现在,反过来,我们知道了屏幕空间中的深度值,怎么求出相机空间中的深度值呢?

首先,根据 ( 3 ) \left(3\right) (3),可以推导出:
z n d c = 2 z w i n − 1 z_{ndc} = 2z_{win}-1 zndc=2zwin1
对于公式2,得出的是实际坐标的 Z Z Z值。为了和OpenGL中的定义统一,也将 n e a r near near f a r far far z z z代入公式 ( 2 ) \left(2\right) (2),可以得到:
z e y e = 2 ( − n e a r ) ( − f a r ) ( ( − f a r ) + ( − n e a r ) ) − z n d c ( ( − f a r ) − ( − n e a r ) ) = 2 n e a r f a r − ( f a r + n e a r ) − z n d c ( n e a r − f a r ) (5) \begin{aligned} z_{eye} &= \frac{2(-near)(-far)}{((-far)+(-near))-z_{ndc}((-far)-(-near))} \\ &= \frac{2nearfar}{-(far+near)-z_{ndc}(near-far)} \\ \end{aligned} \tag{5} zeye=((far)+(near))zndc((far)(near))2(near)(far)=(far+near)zndc(nearfar)2nearfar(5)
深度测试这一节中,得出的公式是:
f l o a t l i n e a r D e p t h = ( 2.0 ∗ n e a r ∗ f a r ) / ( f a r + n e a r − z ∗ ( f a r − n e a r ) ) ; float \quad linearDepth = (2.0 * near * far) / (far + near - z * (far - near)); floatlinearDepth=(2.0nearfar)/(far+nearz(farnear));
对比发现,跟公式 ( 5 ) \left(5\right) (5)有些不一样。这是因为, l i n e a r D e p t h linearDepth linearDepth求出的是顶点距离相机的距离,是正值。而 z e y e z_{eye} zeye是顶点的实际坐标,是负值,将 z e y e z_{eye} zeye取反,即可得到 l i n e a r D e p t h linearDepth linearDepth
l i n e a r D e p t h = − z e y e = 2 n e a r f a r ( f a r + n e a r ) − z n d c ( f a r − n e a r ) \begin{aligned} linearDepth &= -z_{eye} \\ &= \frac{2nearfar}{(far+near)-z_{ndc}(far-near)} \end{aligned} linearDepth=zeye=(far+near)zndc(farnear)2nearfar
至此,推导完成。

参考

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值