投影矩陣與Reversed Z

投影矩陣與Reversed Z

導(dǎo)言

投影矩陣是圖形學(xué)中實(shí)現(xiàn)物件渲染顯示的關(guān)鍵要素,許多的效果實(shí)現(xiàn)都繞不開這一項(xiàng)撮躁。而由于其推導(dǎo)過程雖然簡單,但也不是一目了然,因此在使用的時(shí)候經(jīng)常會忘記其具體過程與結(jié)果马昨,而需要反復(fù)查閱資料,費(fèi)時(shí)費(fèi)力扛施;再加上DX API跟OPEN GL API的區(qū)別以及UE中慣用的Reversed Z手段鸿捧,使得這個(gè)問題就更為復(fù)雜,往往上一次搞清楚之后疙渣,下次再碰到類似問題又要重頭推導(dǎo)一遍匙奴,為了提高使用效率,特此將平時(shí)工作中的心得與經(jīng)驗(yàn)整理出來妄荔。

推導(dǎo)

假設(shè)投影覆蓋范圍上下左右前后分別用t, b, l, r, n, f來表示:

對于DX而言泼菌,經(jīng)過投影矩陣轉(zhuǎn)化后得到的上下左右(Y, X)的范圍為[-1, 1],而前后(Z)的范圍為[0,1];

對于GL而言啦租,經(jīng)過投影矩陣轉(zhuǎn)化后得到的上下左右前后(Y, X)的范圍都為[-1, 1];

DX API 正交投影矩陣推導(dǎo)

以X軸方向(左右)為例哗伯,為了滿足條件,就要保證l投影后對應(yīng)的是-1刷钢,而r對應(yīng)的是1笋颤,且這個(gè)變換應(yīng)該是線性的,那么給出的方程式應(yīng)該為:

x^{'} = \frac{x-l}{r-l} * 2 - 1 = \frac{2x}{r-l} - \frac{r+l}{r-l}

也就是說投影矩陣M的第一個(gè)元素M[0][0] = \frac{2}{r-l} 内地,第四個(gè)元素M[0][3] = \frac{r+l}{r-l} 伴澄,剩下兩個(gè)元素為0;

同理阱缓,對于Y軸方向也有類似結(jié)論非凌。

而對于Z軸方向,n對應(yīng)的是0荆针,而f對應(yīng)的是1敞嗡,因此有:

綜上,可以給出正交投影矩陣的結(jié)果如下:

DX API 透視投影矩陣推導(dǎo)

透視矩陣相對于正交矩陣而言航背,要稍微復(fù)雜一點(diǎn)喉悴,這是因?yàn)橥敢暰仃嚻渥儞Q關(guān)系不再是線性的,通過數(shù)學(xué)公式描述會比較抽象玖媚,為了方便敘述箕肃,下面給出一個(gè)示意圖:

如圖,依然以X軸為例今魔,注意勺像,此時(shí)的u, b, l, r對應(yīng)的是近平面上的上下左右數(shù)值障贸。

圖中n為近平面裁剪距離,xs為采樣點(diǎn)處的x坐標(biāo)吟宦,與視點(diǎn)的連線與近平面的交點(diǎn)的x坐標(biāo)為xn篮洁,根據(jù)三角形相似:

從而可以得出:

注意,這里不能通過三角形近似求得xs處的left&right殃姓,之后再仿照公式1求得當(dāng)前點(diǎn)的x值袁波,這是因?yàn)樯蠄D中的水平線條指的是z軸,即(0, 0, z)蜗侈,而非((r+l)/2, (t+b)/2, z)軸锋叨,因此三角形近似求得的結(jié)果會存在問題。

令xn = x宛篇,將公式5代入到公式1娃磺,得到:

同理:

可以看到叫倍,公式6&7中都帶有一個(gè)分母z吆倦,為了得到這個(gè)分母听诸,就需要將變換后的w分量變成z晌梨,通過歸一化的方式來處理,這個(gè)修正方法會對z變換產(chǎn)生影響须妻,為了保證變換后的z范圍依然對應(yīng)于[0,1]仔蝌,就有如下方程:

求得:

因此瞧挤,整個(gè)投影矩陣可以寫成:

假設(shè)n為z軸(深度方向)上的距離,r跟l分別是此深度值下左右邊界,那么這個(gè)矩陣的[0][0]與[1][1]就分別表征的是水平方向與垂直方向上半個(gè)FOV角對應(yīng)的正切函數(shù)的倒數(shù):
P[0][0] = 1 / (\frac{\frac{r-l}{2}}{n}) = 1/tan(\frac{FOV_{hor}}{2}) \\ P[1][1] = 1 / (\frac{\frac{t-b}{2}}{n}) = 1/tan(\frac{FOV_{ver}}{2})

在這個(gè)投影矩陣下,Device Depth與Linear Depth(Z)之間的變換關(guān)系可以表示為:

d = \frac{f}{f-n} - \frac{fn} {z (f - n)}

Make ~ A = \frac{1} {f} - \frac{1}{n} = - \frac{f - n}{fn}, ~~ B = \frac{1}{n} \\ d = -\frac{B}{A} + \frac{1}{z \cdot A} \\ z = \frac{1}{A d+B}

OpenGL 投影矩陣

OpenGL與DX的區(qū)別有以下兩點(diǎn):

  1. OpenGL采用的是右手坐標(biāo)系妒穴,簡單來說,相對于DX而言摊崭,Z軸方向相反

  2. Z軸投影后的范圍也有所不同讼油,DX為[0, 1],而OpenGL為[-1, 1]

按照上述區(qū)別來看呢簸,如果取[n, f]均為正值矮台,那么對于OpenGL而言,就需要將[-n, -f]會映射到[-1, 1]根时,即[n,f]映射到[1, -1]瘦赫,因此,仿照剛才的推導(dǎo)過程蛤迎,對于OpenGL的正交投影矩陣确虱,其XY軸不變,Z軸數(shù)值首先變成相反數(shù)(Z軸方向調(diào)轉(zhuǎn)了)替裆,其次由于范圍的變化校辩,Z軸變換的兩個(gè)關(guān)鍵參數(shù),其形式將與XY軸保持一致辆童,最終結(jié)果為:

對于透視投影矩陣宜咒,首先對Z軸(第三列)進(jìn)行翻轉(zhuǎn),其次參考前面求取DX透視矩陣a,b的兩個(gè)方式把鉴,可以求得第三行的兩個(gè)關(guān)鍵參數(shù)故黑,其最終結(jié)果為:

Reversed Z

在前面推導(dǎo)的透視矩陣中,最終計(jì)算得到的深度值d(指的是Device Depth)跟z(通常說的Linear Depth)之間的關(guān)系為:

這里的A對應(yīng)的是矩陣第三行的第四個(gè)元素M[2][3]庭砍,而B對應(yīng)的則是M[3][3]场晶。在這種公式下得到的深度跟z的倒數(shù)呈線性關(guān)系,實(shí)際上怠缸,深度只是為了表征物件距離相機(jī)的遠(yuǎn)近峰搪,可以采用任何的公式實(shí)現(xiàn)d與z之間的映射,那么公式13有什么優(yōu)越之處呢凯旭?取n = 5, f = 1005概耻,那么對于DX來說,A = -5*1005/1000 = -5.025; B = 1005/1000 = 1.005;繪制d-z曲線如圖所示:

總的來說罐呼,這個(gè)公式 有以下幾個(gè)優(yōu)點(diǎn):

  1. 這個(gè)公式可以很好的契合透視投影近大遠(yuǎn)小的特征鞠柄,從上圖中可以看到d在z較小的時(shí)候增長很快,到后期之后基本平穩(wěn)嫉柴,而對于渲染結(jié)果而言厌杜,則是近景處深度精度高,遠(yuǎn)景處深度精度低,符合實(shí)際需要

  2. 可以通過齊次矩陣借助硬件實(shí)現(xiàn)倒數(shù)計(jì)算

  3. 采用這個(gè)公式計(jì)算得到的深度d在屏幕空間是線性的夯尽,因此在光柵化的時(shí)候可以通過線性運(yùn)算實(shí)現(xiàn)面片之間的深度數(shù)據(jù)插值計(jì)算瞧壮。

  4. 采用這個(gè)公式,即使far值取無窮大匙握,對于深度精度的影響也基本可忽略(當(dāng)然咆槽,這里需要將裁剪與far值進(jìn)行分開處理)

根據(jù)浮點(diǎn)數(shù)的表示規(guī)則,我們知道圈纺,浮點(diǎn)數(shù)在0附近的數(shù)值精度會遠(yuǎn)遠(yuǎn)高于其他位置的數(shù)值精度秦忿,且距離0越遠(yuǎn),精度越低蛾娶。浮點(diǎn)數(shù)的精度規(guī)則對應(yīng)到深度計(jì)算上來灯谣,就會發(fā)現(xiàn),在近平面處的浮點(diǎn)精度最高蛔琅,如下圖所示胎许,在靠近0處占據(jù)的有效數(shù)據(jù)位數(shù)就越多,而遠(yuǎn)平面處的浮點(diǎn)精度最低罗售,占據(jù)的有效數(shù)據(jù)位數(shù)就越少呐萨,乍聽起來似乎還不錯(cuò),不過由于通常情況下近平面處是沒有任何物件的莽囤,距離相機(jī)最近的物件谬擦,通常距離近平面還有一段距離,這就使得近平面處的浮點(diǎn)數(shù)精度被浪費(fèi)了朽缎,是否有辦法將這些浪費(fèi)的精度用起來呢惨远?

有人提出了Reversed Z的深度表示方法,關(guān)于Reversed Z的歷史话肖,最早可以追溯到99年的一篇SIGGRAPH文章北秽,之后在 Matt PettineoBrano Kemen的博客中也有提到,最近的一次是雪崩工作室Emily Persson在SIGGRAPH 2012上的演講中:Creating Vast Game Worlds 最筒。

Reversed Z的基本思路就是對原有的d進(jìn)行一次翻轉(zhuǎn)操作:

上圖是將之前繪制結(jié)果直接01翻轉(zhuǎn)得到的結(jié)果贺氓,而下圖則是采用reversed z的表現(xiàn),可以看到兩者的區(qū)別在于浮點(diǎn)數(shù)的有效位數(shù)在reversed z的時(shí)候分布比較均勻床蜘。

對于DX而言辙培,reversed z的作用就是將此前[n, f]到[0, 1]的映射改為[1, 0],因此邢锯,其透視投影矩陣將變成如下形式:

而如果取r = -l = w扬蕊, t = -b = h的話,這個(gè)矩陣可以簡化為:

對于OpenGL而言丹擎,其深度d隨著z變化的曲線示意圖給出如下:

同樣尾抑,如果將浮點(diǎn)精度有效刻度添加上去的話歇父,結(jié)果如下圖所示:

可以看到,浮點(diǎn)數(shù)有效范圍也基本上集中在近平面處再愈,而雖然最終存儲到深度貼圖中的數(shù)據(jù)會被轉(zhuǎn)換到[0, 1]范圍內(nèi)榜苫,但是在此前一步通過投影矩陣得到的落在[-1, 1]范圍的結(jié)果其實(shí)已經(jīng)導(dǎo)致精度減半了,要想保留精度翎冲,就需要在投影矩陣階段進(jìn)行處理垂睬,那么,對于這種[-1, 1]的情況府适,要怎么進(jìn)行修正呢?

我們嘗試將[-n, -f]的映射范圍從[-1,1]更改為[0, -2]肺樟,之后應(yīng)用reversed z檐春,得到結(jié)果如上圖,可以看到有效刻度聚集的情況依然沒有好轉(zhuǎn)么伯∨迸看起來對于OpenGL這種[-1, 1]的映射實(shí)現(xiàn)無法享受reversed z的優(yōu)勢了?在繼續(xù)思考的同時(shí)田柔,我們再來看下虛幻的深度計(jì)算公式是怎么樣的俐巴。

Unreal Engine Depth Equation

先給出Unreal這邊的Reversed Z的投影矩陣,其實(shí)現(xiàn)有多個(gè)重載版本硬爆,這里取其中一個(gè)有代表性的來舉例(為了方便對比欣舵,下面給出的矩陣相對于UE源碼中的矩陣是轉(zhuǎn)置過的,這也說明缀磕,這里給出的矩陣實(shí)際上是OpenGL的右手坐標(biāo)系的):

可以看到缘圈,UE矩陣相對于前面推導(dǎo)的DX Reversed Z的矩陣的區(qū)別就在于多了一個(gè)f是否等于n的判斷,且在代碼中追蹤可以發(fā)現(xiàn)大部分情況下袜蚕,傳入的f都是跟n相同的糟把,那么這種做法有什么意義呢?

可以得知牲剃,這種時(shí)候得到的d跟z之間的關(guān)系將變成如下所示:

非常的簡潔遣疯,在z=n的時(shí)候,深度為1凿傅,而只有當(dāng)z趨近于無窮大的時(shí)候缠犀,深度才為0。也就是說聪舒,這種表達(dá)方式夭坪,放棄了z = f的時(shí)候的下邊界,而是取無窮遠(yuǎn)作為遠(yuǎn)平面过椎。雖然有輕微的基本可以忽略的精度損失室梅,但是可以避免距離過遠(yuǎn)導(dǎo)致的場景物件的裁剪(當(dāng)然,不可能把相機(jī)可見的所有物件都納入渲染的范圍,UE還有其他手段對物件進(jìn)行剔除亡鼠,比如說ScreenSize等)赏殃,使得渲染的結(jié)果更加真實(shí)。

另外间涵,這里也可以看到仁热,實(shí)際上UE并沒有使用OpenGL的[-1,1]的投影范圍,而是使用了DX的Reversed Z的投影方式勾哩,這樣做的目的顯然是為了最大程度的保留深度貼圖的有效精度范圍抗蠢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市思劳,隨后出現(xiàn)的幾起案子迅矛,更是在濱河造成了極大的恐慌,老刑警劉巖潜叛,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秽褒,死亡現(xiàn)場離奇詭異,居然都是意外死亡威兜,警方通過查閱死者的電腦和手機(jī)销斟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來椒舵,“玉大人蚂踊,你說我怎么就攤上這事”仕蓿” “怎么了悴势?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長措伐。 經(jīng)常有香客問我特纤,道長,這世上最難降的妖魔是什么侥加? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任捧存,我火速辦了婚禮,結(jié)果婚禮上担败,老公的妹妹穿的比我還像新娘昔穴。我一直安慰自己,他們只是感情好提前,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布吗货。 她就那樣靜靜地躺著,像睡著了一般狈网。 火紅的嫁衣襯著肌膚如雪宙搬。 梳的紋絲不亂的頭發(fā)上笨腥,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機(jī)與錄音勇垛,去河邊找鬼脖母。 笑死,一個(gè)胖子當(dāng)著我的面吹牛闲孤,可吹牛的內(nèi)容都是我干的谆级。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼讼积,長吁一口氣:“原來是場噩夢啊……” “哼肥照!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起勤众,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤舆绎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后决摧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亿蒸,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凑兰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年掌桩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姑食。...
    茶點(diǎn)故事閱讀 39,773評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡波岛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出音半,到底是詐尸還是另有隱情则拷,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布曹鸠,位于F島的核電站煌茬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏彻桃。R本人自食惡果不足惜坛善,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望邻眷。 院中可真熱鬧眠屎,春花似錦、人聲如沸肆饶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驯镊。三九已至葫督,卻和暖如春竭鞍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背候衍。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工笼蛛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛉鹿。 一個(gè)月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓滨砍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親妖异。 傳聞我的和親對象是個(gè)殘疾皇子惋戏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容