深度緩沖中的深度值計算及可視化

概述

渲染管線中的頂點變換中,介紹了頂點在各個坐標(biāo)空間的變換。變換到最后,是屏幕坐標(biāo)空間。在OpenGL中,屏幕空間坐標(biāo)的Z值即是深度緩沖中的深度值。深度緩沖包含了一個介于0.0和1.0之間的深度值,它將會與觀察者視角所看見的場景中所有物體的z值進行比較。本文將介紹深度值的計算,以及從深度值反向計算出相機空間中的頂點的Z值。

深度值計算

渲染管線中的頂點變換中,計算得到了透視投影矩陣:
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}
同時,也得到了視口變換矩陣:
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}
首先,根據(jù)透視矩陣,計算NDC空間的Z值。這里,相機空間中的坐標(biāo)經(jīng)過透視矩陣變換后,還要進行齊次除法,才能得到NDC空間中的坐標(biāo)。
\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}

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

由此,可以得出:
\begin{aligned} z_{ndc} &= \frac{\frac{f+n}{f-n}z_{eye}+\frac{-2nf}{f-n}}{z_{eye}} \\ &=\frac{f+n}{f-n}+\frac{-2nf}{z_{eye}(f-n)} \end{aligned} \tag{1}
根據(jù)上述公式,可以得出:
z_{eye} = \frac{2nf}{(f+n)-z_{ndc}(f-n)} \tag{2}
根據(jù)視口變換矩陣,可以得出:
z_{win} = \frac{1}{2}z_{ndc}+\frac{1}{2} \tag{3}

\left(1\right)帶入\left(3\right),可以得到:
\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}

即:
z_{win} = \frac{\frac{1}{n}-\frac{1}{z_{eye}}}{\frac{1}{n}-\frac{1}{f}} \tag{4}

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

Learn OpenGL CN學(xué)習(xí)過的,可能對深度測試這一節(jié)的內(nèi)容有些印象。它得到的深度值的公式是:
F_{depth} = \frac{1/z - 1/near}{1/far - 1/near}
\left(4\right)式對比,發(fā)現(xiàn)有些不一樣,這是怎么回事呢?

這里要注意,本文定義的nfz_{eye}是實際的坐標(biāo)值,是負的。而深度測試文中,定義的near、far代表了近平面和遠平面,而z代表了近、遠平面之間的值,它們都是正的。將n=-near、f=-farz_{eye}=-z代入\left(4\right)式,可得:
\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}

深度值的線性可視化

經(jīng)過上面的推導(dǎo),我們得出了深度值的計算公式。

現(xiàn)在,反過來,我們知道了屏幕空間中的深度值,怎么求出相機空間中的深度值呢?

首先,根據(jù)\left(3\right),可以推導(dǎo)出:
z_{ndc} = 2z_{win}-1
對于公式2,得出的是實際坐標(biāo)的Z值。為了和OpenGL中的定義統(tǒng)一,也將near、farz代入公式\left(2\right),可以得到:
\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}
深度測試這一節(jié)中,得出的公式是:
float \quad linearDepth = (2.0 * near * far) / (far + near - z * (far - near));
對比發(fā)現(xiàn),跟公式\left(5\right)有些不一樣。這是因為,linearDepth求出的是頂點距離相機的距離,是正值。而z_{eye}是頂點的實際坐標(biāo),是負值,將z_{eye}取反,即可得到linearDepth。
\begin{aligned} linearDepth &= -z_{eye} \\ &= \frac{2nearfar}{(far+near)-z_{ndc}(far-near)} \end{aligned}
至此,推導(dǎo)完成。

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容