1. 程式人生 > >Opengl es2.0 學習筆記(六)矩陣變化

Opengl es2.0 學習筆記(六)矩陣變化


一.座標系變化

openGL使用右手座標
從左到右,x遞增
從下到上,y遞增
從遠到近,z遞增

MVP
模型矩陣(Model):將區域性座標轉換為世界座標
觀察矩陣(View):
投影矩陣(Projection):正交還是透視投影

1.概述

為了將座標從一個座標系變換到另一個座標系,我們需要用到幾個變換矩陣,最重要的幾個分別是模型(Model)、觀察(View)、投影(Projection)三個矩陣。我們的頂點座標起始於區域性空間(Local Space),在這裡它稱為區域性座標(Local Coordinate),它在之後會變為世界座標(World Coordinate),觀察座標(View Coordinate),裁剪座標(Clip Coordinate),並最後以螢幕座標(Screen Coordinate)的形式結束。下面的這張圖展示了整個流程以及各個變換過程做了什麼:
在這裡插入圖片描述

  1. 區域性座標是物件相對於區域性原點的座標,也是物體起始的座標。
  2. 下一步是將區域性座標變換為世界空間座標,世界空間座標是處於一個更大的空間範圍的。這些座標相對於世界的全域性原點,它們會和其它物體一起相對於世界的原點進行擺放。
  3. 接下來我們將世界座標變換為觀察空間座標,使得每個座標都是從攝像機或者說觀察者的角度進行觀察的。
  4. 座標到達觀察空間之後,我們需要將其投影到裁剪座標。裁剪座標會被處理至-1.0到1.0的範圍內,並判斷哪些頂點將會出現在螢幕上。
  5. 最後,我們將裁剪座標變換為螢幕座標,我們將使用一個叫做視口變換(Viewport Transform)的過程。視口變換將位於-1.0到1.0範圍的座標變換到由glViewport函式所定義的座標範圍內。最後變換出來的座標將會送到光柵器,將其轉化為片段。

你可能已經大致瞭解了每個座標空間的作用。我們之所以將頂點變換到各個不同的空間的原因是有些操作在特定的座標系統中才有意義且更方便。例如,當需要對物體進行修改的時候,在區域性空間中來操作會更說得通;如果要對一個物體做出一個相對於其它物體位置的操作時,在世界座標系中來做這個才更說得通,等等。如果我們願意,我們也可以定義一個直接從區域性空間變換到裁剪空間的變換矩陣,但那樣會失去很多靈活性。

接下來我們將要更仔細地討論各個座標系統。

區域性空間

區域性空間是指物體所在的座標空間,即物件最開始所在的地方。想象你在一個建模軟體(比如說Blender)中建立了一個立方體。你建立的立方體的原點有可能位於(0, 0, 0),即便它有可能最後在程式中處於完全不同的位置。甚至有可能你建立的所有模型都以(0, 0, 0)為初始位置(譯註:然而它們會最終出現在世界的不同位置)。所以,你的模型的所有頂點都是在區域性

空間中:它們相對於你的物體來說都是區域性的。

我們一直使用的那個箱子的頂點是被設定在-0.5到0.5的座標範圍中,(0, 0)是它的原點。這些都是區域性座標。

世界空間

如果我們將我們所有的物體匯入到程式當中,它們有可能會全擠在世界的原點(0, 0, 0)上,這並不是我們想要的結果。我們想為每一個物體定義一個位置,從而能在更大的世界當中放置它們。世界空間中的座標正如其名:是指頂點相對於(遊戲)世界的座標。如果你希望將物體分散在世界上擺放(特別是非常真實的那樣),這就是你希望物體變換到的空間。物體的座標將會從區域性變換到世界空間;該變換是由模型矩陣(Model Matrix)實現的。

模型矩陣是一種變換矩陣,它能通過對物體進行位移、縮放、旋轉來將它置於它本應該在的位置或朝向。你可以將它想像為變換一個房子,你需要先將它縮小(它在區域性空間中太大了),並將其位移至郊區的一個小鎮,然後在y軸上往左旋轉一點以搭配附近的房子。你也可以把上一節將箱子到處擺放在場景中用的那個矩陣大致看作一個模型矩陣;我們將箱子的區域性座標變換到場景/世界中的不同位置。

觀察空間

觀察空間經常被人們稱之OpenGL的攝像機(Camera)(所以有時也稱為攝像機空間(Camera Space)或視覺空間(Eye Space))。觀察空間是將世界空間座標轉化為使用者視野前方的座標而產生的結果。因此觀察空間就是從攝像機的視角所觀察到的空間。而這通常是由一系列的位移和旋轉的組合來完成,平移/旋轉場景從而使得特定的物件被變換到攝像機的前方。這些組合在一起的變換通常儲存在一個觀察矩陣(View Matrix)裡,它被用來將世界座標變換到觀察空間。在下一節中我們將深入討論如何建立一個這樣的觀察矩陣來模擬一個攝像機。

裁剪空間

在一個頂點著色器執行的最後,OpenGL期望所有的座標都能落在一個特定的範圍內,且任何在這個範圍之外的點都應該被裁剪掉(Clipped)。被裁剪掉的座標就會被忽略,所以剩下的座標就將變為螢幕上可見的片段。這也就是裁剪空間(Clip Space)名字的由來。

因為將所有可見的座標都指定在-1.0到1.0的範圍內不是很直觀,所以我們會指定自己的座標集(Coordinate Set)並將它變換回標準化裝置座標系,就像OpenGL期望的那樣。

為了將頂點座標從觀察變換到裁剪空間,我們需要定義一個投影矩陣(Projection Matrix),它指定了一個範圍的座標,比如在每個維度上的-1000到1000。投影矩陣接著會將在這個指定的範圍內的座標變換為標準化裝置座標的範圍(-1.0, 1.0)。所有在範圍外的座標不會被對映到在-1.0到1.0的範圍之間,所以會被裁剪掉。在上面這個投影矩陣所指定的範圍內,座標(1250, 500, 750)將是不可見的,這是由於它的x座標超出了範圍,它被轉化為一個大於1.0的標準化裝置座標,所以被裁剪掉了。

如果只是圖元(Primitive),例如三角形,的一部分超出了裁剪體積(Clipping Volume),則OpenGL會重新構建這個三角形為一個或多個三角形讓其能夠適合這個裁剪範圍。

由投影矩陣建立的觀察箱(Viewing Box)被稱為平截頭體(Frustum),每個出現在平截頭體範圍內的座標都會最終出現在使用者的螢幕上。將特定範圍內的座標轉化到標準化裝置座標系的過程(而且它很容易被對映到2D觀察空間座標)被稱之為投影(Projection),因為使用投影矩陣能將3D座標投影(Project)到很容易對映到2D的標準化裝置座標系中。

一旦所有頂點被變換到裁剪空間,最終的操作——透視除法(Perspective Division)將會執行,在這個過程中我們將位置向量的x,y,z分量分別除以向量的齊次w分量;透視除法是將4D裁剪空間座標變換為3D標準化裝置座標的過程。這一步會在每一個頂點著色器執行的最後被自動執行。

在這一階段之後,最終的座標將會被對映到螢幕空間中(使用glViewport中的設定),並被變換成片段。

將觀察座標變換為裁剪座標的投影矩陣可以為兩種不同的形式,每種形式都定義了不同的平截頭體。我們可以選擇建立一個正射投影矩陣(Orthographic Projection Matrix)或一個透視投影矩陣(Perspective Projection Matrix)。

正射投影

正射投影矩陣定義了一個類似立方體的平截頭箱,它定義了一個裁剪空間,在這空間之外的頂點都會被裁剪掉。建立一個正射投影矩陣需要指定可見平截頭體的寬、高和長度。在使用正射投影矩陣變換至裁剪空間之後處於這個平截頭體內的所有座標將不會被裁剪掉。它的平截頭體看起來像一個容器:

orthographic projection frustum

上面的平截頭體定義了可見的座標,它由由寬、高、近(Near)平面和遠(Far)平面所指定。任何出現在近平面之前或遠平面之後的座標都會被裁剪掉。正射平截頭體直接將平截頭體內部的所有座標對映為標準化裝置座標,因為每個向量的w分量都沒有進行改變;如果w分量等於1.0,透視除法則不會改變這個座標。

要建立一個正射投影矩陣,我們可以使用GLM的內建函式

~opengles 裡面貌似沒有,需要自己實現

//glm::ortho:
glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

前兩個引數指定了平截頭體的左右座標,第三和第四引數指定了平截頭體的底部和頂部。通過這四個引數我們定義了近平面和遠平面的大小,然後第五和第六個引數則定義了近平面和遠平面的距離。這個投影矩陣會將處於這些x,y,z值範圍內的座標變換為標準化裝置座標。

正射投影矩陣直接將座標對映到2D平面中,即你的螢幕,但實際上一個直接的投影矩陣會產生不真實的結果,因為這個投影沒有將透視(Perspective)考慮進去。所以我們需要透視投影矩陣來解決這個問題。

透視投影

如果你曾經體驗過實際生活給你帶來的景象,你就會注意到離你越遠的東西看起來更小。這個奇怪的效果稱之為透視(Perspective)。透視的效果在我們看一條無限長的高速公路或鐵路時尤其明顯,正如下面圖片顯示的那樣:

perspective

正如你看到的那樣,由於透視,這兩條線在很遠的地方看起來會相交。這正是透視投影想要模仿的效果,它是使用透視投影矩陣來完成的。這個投影矩陣將給定的平截頭體範圍對映到裁剪空間,除此之外還修改了每個頂點座標的w值,從而使得離觀察者越遠的頂點座標w分量越大。被變換到裁剪空間的座標都會在-w到w的範圍之間(任何大於這個範圍的座標都會被裁剪掉)。OpenGL要求所有可見的座標都落在-1.0到1.0範圍內,作為頂點著色器最後的輸出,因此,一旦座標在裁剪空間內之後,透視除法就會被應用到裁剪空間座標上:

在這裡插入圖片描述

頂點座標的每個分量都會除以它的w分量,距離觀察者越遠頂點座標就會越小。這是也是w分量非常重要的另一個原因,它能夠幫助我們進行透視投影。最後的結果座標就是處於標準化裝置空間中的。如果你對正射投影矩陣和透視投影矩陣是如何計算的很感興趣(且不會對數學感到恐懼的話)我推薦這篇由Songho寫的文章

在GLM中可以這樣建立一個透視投影矩陣:

glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);

同樣,glm::perspective所做的其實就是建立了一個定義了可視空間的大平截頭體,任何在這個平截頭體以外的東西最後都不會出現在裁剪空間體積內,並且將會受到裁剪。一個透視平截頭體可以被看作一個不均勻形狀的箱子,在這個箱子內部的每個座標都會被對映到裁剪空間上的一個點。下面是一張透視平截頭體的圖片:

 perspective_frustum

它的第一個引數定義了fov的值,它表示的是視野(Field of View),並且設定了觀察空間的大小。如果想要一個真實的觀察效果,它的值通常設定為45.0f,但想要一個末日風格的結果你可以將其設定一個更大的值。第二個引數設定了寬高比,由視口的寬除以高所得。第三和第四個引數設定了平截頭體的平面。我們通常設定近距離為0.1f,而遠距離設為100.0f。所有在近平面和遠平面內且處於平截頭體內的頂點都會被渲染。

當你把透視矩陣的 near 值設定太大時(如10.0f),OpenGL會將靠近攝像機的座標(在0.0f和10.0f之間)都裁剪掉,這會導致一個你在遊戲中很熟悉的視覺效果:在太過靠近一個物體的時候你的視線會直接穿過去。

當使用正射投影時,每一個頂點座標都會直接對映到裁剪空間中而不經過任何精細的透視除法(它仍然會進行透視除法,只是w分量沒有被改變(它保持為1),因此沒有起作用)。因為正射投影沒有使用透視,遠處的物體不會顯得更小,所以產生奇怪的視覺效果。由於這個原因,正射投影主要用於二維渲染以及一些建築或工程的程式,在這些場景中我們更希望頂點不會被透視所幹擾。某些如 Blender 等進行三維建模的軟體有時在建模時也會使用正射投影,因為它在各個維度下都更準確地描繪了每個物體。下面你能夠看到在Blender裡面使用兩種投影方式的對比:

perspective_orthographic

你可以看到,使用透視投影的話,遠處的頂點看起來比較小,而在正射投影中每個頂點距離觀察者的距離都是一樣的。

把它們都組合到一起

我們為上述的每一個步驟都建立了一個變換矩陣:模型矩陣、觀察矩陣和投影矩陣。一個頂點座標將會根據以下過程被變換到裁剪座標:

在這裡插入圖片描述

注意矩陣運算的順序是相反的(記住我們需要從右往左閱讀矩陣的乘法)。最後的頂點應該被賦值到頂點著色器中的gl_Position,OpenGL將會自動進行透視除法和裁剪。

然後呢?

頂點著色器的輸出要求所有的頂點都在裁剪空間內,這正是我們剛才使用變換矩陣所做的。OpenGL然後對裁剪座標執行透視除法從而將它們變換到標準化裝置座標。OpenGL會使用glViewPort內部的引數來將標準化裝置座標對映到螢幕座標,每個座標都關聯了一個螢幕上的點(在我們的例子中是一個800x600的螢幕)。這個過程稱為視口變換。

這一章的主題可能會比較難理解,如果你仍然不確定每個空間的作用的話,你也不必太擔心。接下來你會看到我們是怎樣運用這些座標空間的,而且之後也會有足夠多的例子

摘自:

2.常用矩陣

opengl採用列優先矩陣
平移矩陣
translate(x,y,z)
在這裡插入圖片描述
縮放矩陣
scale(x,y,z)
在這裡插入圖片描述
翻轉矩陣
rotate(angle,x,y,z)
在這裡插入圖片描述

3.模板code: