版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2017.09.04 |
前言
OpenGL 圖形庫項目中一直也沒用過,最近也想學著使用這個圖形庫臭增,感覺還是很有意思懂酱,也就自然想著好好的總結(jié)一下,希望對大家能有所幫助誊抛。
1. OpenGL 圖形庫使用(一) —— 概念基礎
2. OpenGL 圖形庫使用(二) —— 渲染模式列牺、對象、擴展和狀態(tài)機
3. OpenGL 圖形庫使用(三) —— 著色器拗窃、數(shù)據(jù)類型與輸入輸出
4. OpenGL 圖形庫使用(四) —— Uniform及更多屬性
5. OpenGL 圖形庫使用(五) —— 紋理
6. OpenGL 圖形庫使用(六) —— 變換
概述
OpenGL
希望在每次頂點著色器運行后瞎领,我們可見的所有頂點都為標準化設備坐標(Normalized Device Coordinate, NDC)
泌辫。也就是說,每個頂點的x九默,y震放,z坐標都應該在-1.0到1.0之間,超出這個坐標范圍的頂點都將不可見驼修。我們通常會自己設定一個坐標的范圍殿遂,之后再在頂點著色器中將這些坐標變換為標準化設備坐標。然后將這些標準化設備坐標傳入光柵器(Rasterizer
)乙各,將它們變換為屏幕上的二維坐標或像素墨礁。
將坐標變換為標準化設備坐標,接著再轉(zhuǎn)化為屏幕坐標的過程通常是分步進行的觅丰,也就是類似于流水線那樣子饵溅。在流水線中,物體的頂點在最終轉(zhuǎn)化為屏幕坐標之前還會被變換到多個坐標系統(tǒng)(Coordinate System)
妇萄。將物體的坐標變換到幾個過渡坐標系(Intermediate Coordinate System)
的優(yōu)點在于蜕企,在這些特定的坐標系統(tǒng)中,一些操作或運算更加方便和容易冠句,這一點很快就會變得很明顯轻掩。對我們來說比較重要的總共有5個不同的坐標系統(tǒng):
- 局部空間(
Local Space
,或者稱為物體空間(Object Space)) - 世界空間(
World Space
) - 觀察空間(
View Space
懦底,或者稱為視覺空間(Eye Space)) - 裁剪空間(
Clip Space
) - 屏幕空間(
Screen Space
)
這就是一個頂點在最終被轉(zhuǎn)化為片段之前需要經(jīng)歷的所有不同狀態(tài)唇牧。
為了將坐標從一個坐標系變換到另一個坐標系,我們需要用到幾個變換矩陣聚唐,最重要的幾個分別是模型(Model)
丐重、觀察(View)
、投影(Projection)
三個矩陣杆查。我們的頂點坐標起始于局部空間(Local Space)
扮惦,在這里它稱為局部坐標(Local Coordinate)
,它在之后會變?yōu)槭澜缱鴺?code>(World Coordinate)亲桦,觀察坐標(View Coordinate)
崖蜜,裁剪坐標(Clip Coordinate)
,并最后以屏幕坐標(Screen Coordinate)
的形式結(jié)束客峭。下面的這張圖展示了整個流程以及各個變換過程做了什么:
- 局部坐標是對象相對于局部原點的坐標豫领,也是物體起始的坐標。
- 下一步是將局部坐標變換為世界空間坐標舔琅,世界空間坐標是處于一個更大的空間范圍的等恐。這些坐標相對于世界的全局原點,它們會和其它物體一起相對于世界的原點進行擺放。
- 接下來我們將世界坐標變換為觀察空間坐標鼠锈,使得每個坐標都是從攝像機或者說觀察者的角度進行觀察的闪檬。
- 坐標到達觀察空間之后,我們需要將其投影到裁剪坐標购笆。裁剪坐標會被處理至-1.0到1.0的范圍內(nèi)粗悯,并判斷哪些頂點將會出現(xiàn)在屏幕上。
- 最后同欠,我們將裁剪坐標變換為屏幕坐標暇咆,我們將使用一個叫做視口變換
(Viewport Transform)
的過程衡查。視口變換將位于-1.0到1.0范圍的坐標變換到由glViewport
函數(shù)所定義的坐標范圍內(nèi)。最后變換出來的坐標將會送到光柵器,將其轉(zhuǎn)化為片段闪萄。
你可能已經(jīng)大致了解了每個坐標空間的作用扣草。我們之所以將頂點變換到各個不同的空間的原因是有些操作在特定的坐標系統(tǒng)中才有意義且更方便蒲列。例如娄柳,當需要對物體進行修改的時候,在局部空間中來操作會更說得通粮坞;如果要對一個物體做出一個相對于其它物體位置的操作時蚊荣,在世界坐標系中來做這個才更說得通,等等莫杈。
局部空間
局部空間是指物體所在的坐標空間互例,即對象最開始所在的地方。想象你在一個建模軟件(比如說Blender)中創(chuàng)建了一個立方體筝闹。你創(chuàng)建的立方體的原點有可能位于(0, 0, 0)媳叨,即便它有可能最后在程序中處于完全不同的位置。甚至有可能你創(chuàng)建的所有模型都以(0, 0, 0)為初始位置(譯注:然而它們會最終出現(xiàn)在世界的不同位置)关顷。所以糊秆,你的模型的所有頂點都是在局部空間中:它們相對于你的物體來說都是局部的。
世界空間
如果我們將我們所有的物體導入到程序當中议双,它們有可能會全擠在世界的原點(0, 0, 0)上痘番,這并不是我們想要的結(jié)果。我們想為每一個物體定義一個位置聋伦,從而能在更大的世界當中放置它們。世界空間中的坐標正如其名:是指頂點相對于(游戲)世界的坐標界睁。如果你希望將物體分散在世界上擺放(特別是非常真實的那樣)觉增,這就是你希望物體變換到的空間。物體的坐標將會從局部變換到世界空間翻斟;該變換是由模型矩陣(Model Matrix)
實現(xiàn)的逾礁。
模型矩陣是一種變換矩陣,它能通過對物體進行位移、縮放嘹履、旋轉(zhuǎn)來將它置于它本應該在的位置或朝向腻扇。
觀察空間
觀察空間經(jīng)常被人們稱之OpenGL的攝像機(Camera)(所以有時也稱為攝像機空間(Camera Space)或視覺空間(Eye Space))。觀察空間是將世界空間坐標轉(zhuǎn)化為用戶視野前方的坐標而產(chǎn)生的結(jié)果砾嫉。因此觀察空間就是從攝像機的視角所觀察到的空間幼苛。而這通常是由一系列的位移和旋轉(zhuǎn)的組合來完成,平移/旋轉(zhuǎn)場景從而使得特定的對象被變換到攝像機的前方焕刮。這些組合在一起的變換通常存儲在一個觀察矩陣(View Matrix)
里舶沿,它被用來將世界坐標變換到觀察空間。
裁剪空間
在一個頂點著色器運行的最后配并,OpenGL期望所有的坐標都能落在一個特定的范圍內(nèi)括荡,且任何在這個范圍之外的點都應該被裁剪掉(Clipped)。被裁剪掉的坐標就會被忽略溉旋,所以剩下的坐標就將變?yōu)槠聊簧峡梢姷钠位濉_@也就是裁剪空間(Clip Space)
名字的由來。
因為將所有可見的坐標都指定在-1.0到1.0的范圍內(nèi)不是很直觀观腊,所以我們會指定自己的坐標集(Coordinate Set)
并將它變換回標準化設備坐標系邑闲,就像OpenGL期望的那樣。
為了將頂點坐標從觀察變換到裁剪空間恕沫,我們需要定義一個投影矩陣(Projection Matrix)
监憎,它指定了一個范圍的坐標,比如在每個維度上的-1000到1000婶溯。投影矩陣接著會將在這個指定的范圍內(nèi)的坐標變換為標準化設備坐標的范圍(-1.0, 1.0)鲸阔。所有在范圍外的坐標不會被映射到在-1.0到1.0的范圍之間,所以會被裁剪掉迄委。在上面這個投影矩陣所指定的范圍內(nèi)褐筛,坐標(1250, 500, 750)將是不可見的,這是由于它的x坐標超出了范圍叙身,它被轉(zhuǎn)化為一個大于1.0的標準化設備坐標渔扎,所以被裁剪掉了。
如果只是圖元(Primitive)
信轿,例如三角形晃痴,的一部分超出了裁剪體積(Clipping Volume)
,則OpenGL會重新構(gòu)建這個三角形為一個或多個三角形讓其能夠適合這個裁剪范圍财忽。
由投影矩陣創(chuàng)建的觀察箱(Viewing Box)
被稱為平截頭體(Frustum)
倘核,每個出現(xiàn)在平截頭體范圍內(nèi)的坐標都會最終出現(xiàn)在用戶的屏幕上。將特定范圍內(nèi)的坐標轉(zhuǎn)化到標準化設備坐標系的過程(而且它很容易被映射到2D觀察空間坐標)被稱之為投影(Projection)
即彪,因為使用投影矩陣能將3D坐標投影(Project)到很容易映射到2D的標準化設備坐標系中紧唱。
一旦所有頂點被變換到裁剪空間,最終的操作——透視除法(Perspective Division)
將會執(zhí)行,在這個過程中我們將位置向量的x漏益,y蛹锰,z分量分別除以向量的齊次w分量;透視除法是將4D裁剪空間坐標變換為3D標準化設備坐標的過程绰疤。這一步會在每一個頂點著色器運行的最后被自動執(zhí)行铜犬。
在這一階段之后,最終的坐標將會被映射到屏幕空間中(使用glViewport
中的設定)峦睡,并被變換成片段翎苫。
將觀察坐標變換為裁剪坐標的投影矩陣可以為兩種不同的形式,每種形式都定義了不同的平截頭體榨了。我們可以選擇創(chuàng)建一個正射投影矩陣(Orthographic Projection Matrix)
或一個透視投影矩陣(Perspective Projection Matrix)
煎谍。
1. 正射投影
正射投影矩陣定義了一個類似立方體的平截頭箱,它定義了一個裁剪空間龙屉,在這空間之外的頂點都會被裁剪掉呐粘。創(chuàng)建一個正射投影矩陣需要指定可見平截頭體的寬、高和長度转捕。在使用正射投影矩陣變換至裁剪空間之后處于這個平截頭體內(nèi)的所有坐標將不會被裁剪掉作岖。它的平截頭體看起來像一個容器:
上面的平截頭體定義了可見的坐標,它由由寬五芝、高痘儡、近(Near)平面和遠(Far)平面所指定。任何出現(xiàn)在近平面之前或遠平面之后的坐標都會被裁剪掉枢步。正射平截頭體直接將平截頭體內(nèi)部的所有坐標映射為標準化設備坐標沉删,因為每個向量的w分量都沒有進行改變;如果w分量等于1.0醉途,透視除法則不會改變這個坐標矾瑰。
要創(chuàng)建一個正射投影矩陣,我們可以使用GLM
的內(nèi)置函數(shù)glm::ortho
隘擎。
glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);
前兩個參數(shù)指定了平截頭體的左右坐標殴穴,第三和第四參數(shù)指定了平截頭體的底部和頂部。通過這四個參數(shù)我們定義了近平面和遠平面的大小货葬,然后第五和第六個參數(shù)則定義了近平面和遠平面的距離采幌。這個投影矩陣會將處于這些x,y震桶,z值范圍內(nèi)的坐標變換為標準化設備坐標休傍。
正射投影矩陣直接將坐標映射到2D平面中,即你的屏幕尼夺,但實際上一個直接的投影矩陣會產(chǎn)生不真實的結(jié)果尊残,因為這個投影沒有將透視(Perspective)
考慮進去。所以我們需要透視投影矩陣來解決這個問題淤堵。
2. 透視投影
如果你曾經(jīng)體驗過實際生活給你帶來的景象寝衫,你就會注意到離你越遠的東西看起來更小。這個奇怪的效果稱之為透視(Perspective)拐邪。透視的效果在我們看一條無限長的高速公路或鐵路時尤其明顯慰毅,正如下面圖片顯示的那樣:
正如你看到的那樣,由于透視扎阶,這兩條線在很遠的地方看起來會相交汹胃。這正是透視投影想要模仿的效果,它是使用透視投影矩陣來完成的东臀。這個投影矩陣將給定的平截頭體范圍映射到裁剪空間着饥,除此之外還修改了每個頂點坐標的w值,從而使得離觀察者越遠的頂點坐標w分量越大惰赋。被變換到裁剪空間的坐標都會在-w到w的范圍之間(任何大于這個范圍的坐標都會被裁剪掉)宰掉。OpenGL要求所有可見的坐標都落在-1.0到1.0范圍內(nèi),作為頂點著色器最后的輸出赁濒,因此轨奄,一旦坐標在裁剪空間內(nèi)之后,透視除法就會被應用到裁剪空間坐標上:
頂點坐標的每個分量都會除以它的w分量拒炎,距離觀察者越遠頂點坐標就會越小挪拟。這是也是w分量非常重要的另一個原因,它能夠幫助我們進行透視投影击你。最后的結(jié)果坐標就是處于標準化設備空間中的玉组。
在GLM中可以這樣創(chuàng)建一個透視投影矩陣:
glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
同樣,glm::perspective
所做的其實就是創(chuàng)建了一個定義了可視空間的大平截頭體果漾,任何在這個平截頭體以外的東西最后都不會出現(xiàn)在裁剪空間體積內(nèi)球切,并且將會受到裁剪。一個透視平截頭體可以被看作一個不均勻形狀的箱子绒障,在這個箱子內(nèi)部的每個坐標都會被映射到裁剪空間上的一個點吨凑。下面是一張透視平截頭體的圖片:
它的第一個參數(shù)定義了fov的值,它表示的是視野(Field of View)
户辱,并且設置了觀察空間的大小鸵钝。如果想要一個真實的觀察效果,它的值通常設置為45.0f庐镐,但想要一個末日風格的結(jié)果你可以將其設置一個更大的值恩商。第二個參數(shù)設置了寬高比,由視口的寬除以高所得必逆。第三和第四個參數(shù)設置了平截頭體的近和遠平面怠堪。我們通常設置近距離為0.1f揽乱,而遠距離設為100.0f。所有在近平面和遠平面內(nèi)且處于平截頭體內(nèi)的頂點都會被渲染粟矿。
當你把透視矩陣的 near 值設置太大時(如10.0f)凰棉,OpenGL會將靠近攝像機的坐標(在0.0f和10.0f之間)都裁剪掉,這會導致一個你在游戲中很熟悉的視覺效果:在太過靠近一個物體的時候你的視線會直接穿過去陌粹。
當使用正射投影時撒犀,每一個頂點坐標都會直接映射到裁剪空間中而不經(jīng)過任何精細的透視除法(它仍然會進行透視除法,只是w分量沒有被改變(它保持為1)掏秩,因此沒有起作用)或舞。因為正射投影沒有使用透視,遠處的物體不會顯得更小蒙幻,所以產(chǎn)生奇怪的視覺效果映凳。由于這個原因,正射投影主要用于二維渲染以及一些建筑或工程的程序邮破,在這些場景中我們更希望頂點不會被透視所干擾魏宽。某些如 Blender 等進行三維建模的軟件有時在建模時也會使用正射投影,因為它在各個維度下都更準確地描繪了每個物體决乎。下面你能夠看到在Blender
里面使用兩種投影方式的對比:
你可以看到队询,使用透視投影的話,遠處的頂點看起來比較小构诚,而在正射投影中每個頂點距離觀察者的距離都是一樣的蚌斩。
后記
未完,待續(xù)~~