圖片來(lái)源:Joachim Baecker.
術(shù)語(yǔ)
在大多數(shù)3D工作中针贬,我們參照的依據(jù)是歐幾里得幾何學(xué)中的三維空間(X, Y, Z)赴背。但在某些情況下,參照投影幾何更適用嫉称,除了 X, Y, Z 分量外,增加一個(gè) W 分量灵疮,這個(gè)四維空間叫做“投影空間”织阅,在四維空間中的坐標(biāo)叫“齊次坐標(biāo)”。
為了達(dá)到3D軟件的目的始藕,“投影的” 和 “齊次的” 可以理解為 "4D"蒲稳。
不是四元數(shù)
雖然四元數(shù)跟齊次坐標(biāo)長(zhǎng)得很像氮趋,都是 4D 矢量伍派,通常用(X, Y, Z, W)來(lái)表示江耀。但是,四元數(shù)和齊次坐標(biāo)是不同的概念诉植,適用的領(lǐng)域也不同祥国。
這篇文章跟“四元數(shù)”沒(méi)有一毛錢(qián)關(guān)系。
類(lèi)比2D
了解3D之前晾腔,我們先看看2D的投影是怎么回事兒舌稀。
想象投影儀在一個(gè)屏幕上投影一張2D圖片,很容易就可以得到投影圖片的 X, Y 分量灼擂。
現(xiàn)在壁查,看投影儀和屏幕之間,你就可以發(fā)現(xiàn) W 分量了剔应。
W 分量是投影儀到屏幕的距離
那么 W 分量的作用是什么呢睡腿?想象一下,如果我們移動(dòng)投影儀的位置峻贮,來(lái)增加或減少 W分量的值席怪,那么投影出的2D圖片會(huì)發(fā)生什么?如果將投影儀靠近屏幕纤控,2D圖片縮小挂捻,如果投影儀遠(yuǎn)離屏幕,2D圖片放大船万。沒(méi)錯(cuò)刻撒,這就是 W 分量的作用。
W 分量的值耿导,影響了投影出 2D 圖片的大小
應(yīng)用到3D
到目前為止声怔,還沒(méi)有一個(gè)3D投影儀,很難想象3D中的投影幾何碎节,但是 3D 下的 W 分量與 2D 的作用相同捧搞。當(dāng) W 增大,坐標(biāo)被拉伸狮荔;W 縮小胎撇,坐標(biāo)被壓縮, W 對(duì)3D坐標(biāo)做縮放變換殖氏。
W = 1時(shí)
通常晚树,給3D編程初學(xué)者的建議是,無(wú)論什么時(shí)候?qū)?3D 坐標(biāo)轉(zhuǎn)換為 4D 坐標(biāo)時(shí)雅采,讓 W=1爵憎。原因是慨亲,當(dāng)縮放坐標(biāo)的 W 為1時(shí),坐標(biāo)不會(huì)增大或縮小宝鼓,保持原有的大小刑棵。所以,當(dāng) W=1愚铡,不會(huì)影響到 X, Y, Z 分量的值蛉签。
因此,當(dāng)談?wù)摰?3D 計(jì)算機(jī)圖形學(xué)時(shí)沥寥,當(dāng)坐標(biāo)中 W=1時(shí)被稱作“正確”碍舍。如果渲染使用 W>1 的坐標(biāo),每一個(gè)3D物體看起來(lái)都會(huì)變大邑雅,反之片橡,W<1 的坐標(biāo)中,3D物體會(huì)變谢匆啊捧书;如果渲染時(shí)試圖讓 W=0,那么你的程序會(huì)奔潰录煤,當(dāng)做透視除法的時(shí)候除數(shù)為0鳄厌;如果 W<0,每一個(gè)物體都會(huì)上下翻轉(zhuǎn)妈踊,水平翻轉(zhuǎn)了嚎。
在數(shù)學(xué)中,沒(méi)有所謂的“不正確”的齊次坐標(biāo)廊营,使用齊次坐標(biāo)時(shí)讓 W=1 僅僅是用于計(jì)算機(jī)圖形學(xué)中的投影轉(zhuǎn)換歪泳。
數(shù)學(xué)原理
現(xiàn)在,讓我們來(lái)看一些例子露筒,了解數(shù)學(xué)原理
在距屏幕3米遠(yuǎn)的位置放一個(gè)投影儀呐伞,投影出一個(gè)點(diǎn)(15, 21)在 2D 圖像中,相應(yīng)的投影坐標(biāo)中的向量為 (X, Y, W)=(15, 21, 3)慎式。
現(xiàn)在伶氢,想象推動(dòng)投影儀向屏幕靠近,直到距離1米瘪吏,越靠近屏幕投影癣防,投影出的圖像越小。投影儀靠近了3倍掌眠,因此圖像縮小了3倍蕾盯。如果我們將原向量的 X, Y, W 分量都除以 3,我們得到一個(gè)新向量 W=1:
投影出的點(diǎn)在坐標(biāo)中的新位置(5, 7)
這就是怎樣將一個(gè)“不正確”齊次坐標(biāo)轉(zhuǎn)換到一個(gè)“正確”坐標(biāo)的方法:所有分量除以 W蓝丙,這個(gè)過(guò)程對(duì) 2D 和 3D 同樣適用级遭。
通過(guò)給向量乘以 W 的倒數(shù)望拖,來(lái)實(shí)現(xiàn)向量的所有分量除以 W,下面是一個(gè)4D的例子:
用 GLM 庫(kù)編寫(xiě)挫鸽,類(lèi)似如下實(shí)現(xiàn):
glm::vec4 coordinate(10, 20, 30, 5);
glm::vec4 correctCoordinate = (1.0/coordinate.w) * coordinate;
//now, correctCoordinate == (2,4,6,1)
在計(jì)算機(jī)圖形學(xué)中使用齊次坐標(biāo)
就像開(kāi)始提到的说敏,針對(duì)3D 計(jì)算機(jī)圖形學(xué)中,有些情況下使用齊次坐標(biāo)很有用掠兄,下面我們來(lái)看看這些情況:
3D 坐標(biāo)中的轉(zhuǎn)換矩陣
旋轉(zhuǎn)和縮放的轉(zhuǎn)換矩陣只需要3列像云,但是為了處理平移锌雀,至少需要4列矩陣蚂夕,這就是為什么矩陣變換通常用4×4的矩陣。然而腋逆,4列矩陣不能與3維向量相乘婿牍,只能與4維向量相乘,這就是為什么我們使用齊次的4維向量取代3維向量惩歉。
4列矩陣只能與4維向量相乘等脂,這就是為什么我們常常使用齊次的4維向量取代3維向量。
通過(guò)齊次坐標(biāo)處理矩陣變化撑蚌,第4維 W 分量通常不用改變上遥。從3D轉(zhuǎn)換到4D,只需將 W 分量設(shè)置為1争涌,并且經(jīng)過(guò)變換矩陣處理后粉楚,W 分量的值任為1,這意味著我們忽略 W 分量即可轉(zhuǎn)換回 3D坐標(biāo)亮垫。這個(gè)對(duì)目前為止大多數(shù)的矩陣變化都適用模软,如平移、旋轉(zhuǎn)饮潦、縮放燃异。需要注意的例外是投影矩陣會(huì)影響 W 分量。
透視變換
在 3D 世界继蜡,物體離相機(jī)越遠(yuǎn)看起來(lái)越小回俐,這個(gè)現(xiàn)象叫做透視。在鏡頭中稀并,如果貓離相機(jī)足夠近仅颇,遠(yuǎn)處的大山會(huì)比貓看上去小。
在3D計(jì)算機(jī)圖形學(xué)中灵莲,透視是通過(guò)變換矩陣改變向量的 W 分量來(lái)實(shí)現(xiàn)的。在變換到相機(jī)空間后(對(duì)向量應(yīng)用了相機(jī)矩陣)殴俱,還沒(méi)有進(jìn)行投影變換(還沒(méi)有對(duì)向量應(yīng)用投影矩陣)政冻,每個(gè)向量的 Z 分量表示了距離相機(jī)的距離枚抵。因此,Z 分量越大明场,矢量應(yīng)該越小汽摹。W 分量影響這個(gè)縮放,所以投影矩陣用 Z 分量的值改變 W 分量的值苦锨。
在3D計(jì)算機(jī)圖形學(xué)中逼泣,透視是通過(guò)投影矩陣變換,改變每一個(gè)向量中 W 分量的值來(lái)實(shí)現(xiàn)透視的
下面看一個(gè)透視例子舟舒,通過(guò)投影矩陣變換到齊次坐標(biāo)拉庶。
注意投影矩陣是怎樣用 Z 分量改變 W 分量的。
經(jīng)過(guò)投影矩陣透視變換后秃励,每一個(gè)向量即經(jīng)過(guò)了“透視除法”氏仗。
透視除法只是將齊次坐標(biāo)中的 W 分量轉(zhuǎn)換為1的專(zhuān)用名詞
繼續(xù)上面的例子,透視除法這步如下:
完成透視除法后夺鲜,W分量就沒(méi)用了皆尔,我們就得到了一個(gè)完全符合3D透視投影規(guī)則的3D坐標(biāo)。
在GLM中币励,透視投影矩陣可以通過(guò)使用 glm::perspective
或 glm::frustum
方法來(lái)創(chuàng)建慷蠕。在老式的 OpenGL 中,通常使用gluPerspective
或 gluFrustum
方法食呻。在 OpenGL 中流炕,在頂點(diǎn) shader 作用了每一個(gè)頂點(diǎn)后,自動(dòng)進(jìn)行透視除法搁进。這就是頂點(diǎn) shader 中 main 方法輸出的 gl_position 變量浪感,是4維向量,而不是3維向量饼问。
設(shè)置平行光
齊次坐標(biāo)中的一個(gè)屬性影兽,是允許有一個(gè)無(wú)限遠(yuǎn)的點(diǎn)(或無(wú)限長(zhǎng)的向量),在3D坐標(biāo)中這個(gè)是不允許的莱革。當(dāng) W=0 時(shí)峻堰,這點(diǎn)表示無(wú)限遠(yuǎn)的一個(gè)點(diǎn)。如果你嘗試將一個(gè) W=0 的齊次坐標(biāo)轉(zhuǎn)換為一個(gè)普通的 W=1的齊次坐標(biāo)盅视,這會(huì)導(dǎo)致4次除0操作:
這意味著捐名,不能將 W=0 的齊次坐標(biāo)轉(zhuǎn)換到 3D 坐標(biāo)。
那這有什么用呢闹击?用處說(shuō)來(lái)就來(lái)了镶蹋,平行光可以認(rèn)為是一個(gè)無(wú)限遠(yuǎn)處的點(diǎn)光源。當(dāng)一個(gè)點(diǎn)光源在無(wú)限遠(yuǎn)的位置,光線就會(huì)變成平行的贺归,并且所有光線都在同一方向淆两,這就是平行光的基本定義。想想太陽(yáng)吧拂酣。
所以在傳統(tǒng)的3D圖形中秋冰,平行光可以通過(guò)改變點(diǎn)光源位置向量中的 W 分量來(lái)表示,當(dāng) W= 1時(shí)婶熬,是一個(gè)點(diǎn)光源剑勾;當(dāng) W= 0 時(shí),是一個(gè)平行光赵颅。
在實(shí)現(xiàn)光照代碼時(shí)虽另,這更多的是一種傳統(tǒng)的約定,但不是一種有用的方法性含。因?yàn)槠叫泄夂忘c(diǎn)光源的行為不同洲赵,通常分開(kāi)實(shí)現(xiàn)。一個(gè)經(jīng)典的光照 shader 實(shí)現(xiàn)如下:
if (lightPosition.w == 0.0) {
//directional light code here
} else {
//point light code here
}
總結(jié)
齊次坐標(biāo)有一個(gè)額外的維度叫 W 分量商蕴,用來(lái)縮放X, Y, Z三個(gè)分量的值。平移和透視投影的矩陣變換只能在齊次坐標(biāo)中使用芝发,所以在3D計(jì)算機(jī)圖形學(xué)中绪商,當(dāng) W=1時(shí),X, Y, Z 分量被稱為“正確的”辅鲸。任何齊次坐標(biāo)格郁,只要 W 不為0,都可以通過(guò)將每一個(gè)分量除以 W 來(lái)轉(zhuǎn)換到 W=1的向量独悴。當(dāng) W=0 時(shí)例书,這個(gè)坐標(biāo)表示無(wú)窮遠(yuǎn)的一個(gè)點(diǎn)(或者表示無(wú)限長(zhǎng)的一個(gè)向量),通常用于表示平行光的方向刻炒。