這章主要探討矩陣,這些矩陣表明了應用在咱們場景上的變換,容許咱們移動物體。然而在webGL api中並無一個專門的camera對象,只有矩陣。好消息是使用矩陣來取代相機對象能讓webgl在不少複雜動畫中擁有更高的靈活性。html
第四章中主要內容:html5
一、瞭解場景從3d世界到二維屏幕所經歷的變換web
二、學習仿射變換算法
三、將矩陣匹配到ESSL uniforms變量中編程
四、瞭解Model-View矩陣和透視投影矩陣canvas
五、構造法線變換矩陣api
六、建立一個相機對象使用它來旋轉3d場景數組
WebGL中並無一個能夠操控的相機對象。然而咱們能夠假設咱們渲染在canvas上的場景就是咱們的相機捕獲的。這章中咱們將學習用4x4矩陣來表達一個相機對象。app
每次咱們移動咱們的相機時,都須要根據相機的新位置跟新相機中的對象。爲了達到目的,咱們須要系統的處理每一個頂點來將變換應用上去,以便產生新的視點座標位置。相似的,咱們須要保證對象的發現和光照方向在相機移動後仍能保持一致。因此咱們須要分析兩種變換:頂點和法線編程語言
webgl場景中的物體在咱們的屏幕看到他們以前會經歷多種不一樣的變換。每個變換都用4x4的矩陣來表達。如何將只有xyz屬性的頂點與4x4矩陣相乘?簡要回答是將咱們的元組增長一個維度,每個頂點如今擁有4個維度了,這種座標表達方式成爲齊次座標。
Homogeneous coordinates
齊次座標系在任何計算機圖形程序中都是關鍵的一部分。它使得咱們可以用4x4矩陣來表達仿射變換(旋轉、縮放、切變、平移)和投影變換。在齊次座標系中,頂點擁有四個部分:x、y、z、w。前三個部分是頂點在歐幾里得空間中的座標。第四個部分是透視部分。因此四元組(x, y, z, w)將咱們帶到了一個新的空間:投影空間。
齊次座標系使得咱們能夠在一中特殊的方程組中求出解,這個方程組中每個方程都表示一個與系統中其餘直線平行的直線。咱們知道在歐幾里得空間中,對這種方程組是無解的,由於他們沒有交點。然而在投影空間中,這種方程序有解,這些直線將會在無窮遠處相交。這實際上表明瞭他們的透視部分的值是0.關於這種觀點的一個很好的物理類比就是想象一下全部的平行鐵路都會來視線的終點處相交。
從齊次座標系變換到歐幾里得座標系是很容易的。
反過來,從歐幾里得空間到投影空間,咱們只須要加上第四維並將它設置爲1.
從齊次座標系到歐幾里得座標系,咱們除以w便可;從歐幾里得座標系到齊次座標系咱們將第四維設置爲1.w=0的其次座標表明一個在無窮遠處的點。
另外一個關於齊次座標系須要知道的事情是,頂點有一個w=1的齊次座標,向量的齊次座標w=0.因此在Phong頂點着色器中,法線向量的處理以下:
vNormal = vec3(uNMatrix * vec4(aVertexNormal, 0.0));
頂點的處理以下:
//Transformed vertex position
vec4 vertex = uMVMatrix * vec4(aVertexPosition, 1.0);
下面來看一下咱們的頂點在呈如今屏幕前須要經歷的一系列變換:
Model transform
咱們從對象座標系來開始咱們的分析。頂點座標在這個空間中被指定(相似矢量切片)。而後若是咱們想移動或者旋轉對象,咱們會使用一個矩陣來編碼這些變換。這個矩陣被稱爲 模型矩陣 。一旦咱們用模型矩陣來乘以咱們對象中的頂點,咱們將會得到新的頂點座標。這些新的頂點決定物體在咱們的3d世界中的位置。
在對象座標系中,每一個物體均可以自由定義它的原點而後指定它的頂點相對於原點的位置;在世界座標系中,原點被全部的物體所共享。世界座標系容許咱們知道物體相對於其餘物體的位置。經過模型變換咱們能夠決定對象在3d世界中的位置。
View transform
接下來的變換是視點變換,將座標系的原點轉換到視點。視點是咱們的眼睛或者相機相對於世界原點的位置。換句話說,視圖變換是經過視點座標來切換世界座標。這個變換能夠用視點矩陣來編碼。咱們經過模型變換後的頂點座標來乘以這個矩陣。這個操做的結果是獲得原點是視點的一堆頂點座標。咱們將在這個座標系中來操做咱們的相機。
Projection transform
下面的操做被稱爲投影變換。這個操做決定了多大的視點空間將被渲染和它將如何被匹配到計算機屏幕上。這個區域被稱爲視錐體,它由六個平面來定義(近平面、遠平面、上底面、下底面、左平面、右平面),以下所示:
這六個平面被編碼在透視矩陣中。任何坐落於視錐體外面的頂點在應用投影變換後將被裁剪,並在後續的處理中被拋棄。通過投影變換後的空間成爲裁切空間。(透視矩陣由視錐體產生,通過透視矩陣變換後的空間稱爲裁切空間,頂點的w座標可能不在是1)
視錐體的形狀和範圍決定了從3d視點空間到2d屏幕的投影類型。若是遠平面和近平面擁有相同的維度,視錐體將產生一個正射投影。不然將產生透視投影,以下圖所示:
目前爲止咱們仍然是在齊次座標系中工做,因此裁切座標有四個部分:x、y、z、w。裁剪是經過比較x、y、z與齊次座標w。若是其中任何一個大於+w或者小於-w,那麼這個頂點位於視錐體外並被拋棄。
Perspective division
一旦決定了多大的視域空間將被渲染,視錐體將被匹配到近平面來爲了產生一張2d圖片。近景面的內容是將被渲染到咱們計算機屏幕的內容。
不一樣的操做系統和顯示設備會有不一樣的機制來渲染2d信息到屏幕上。爲了保證全部狀況下的健壯性,webgl提供一個獨立於任何硬件的中介座標系統。這個空間被稱爲歸一化設備座標(NDC)。
歸一化設備座標經過將得到的裁剪座標除以w份量得到。這也就是爲何這一步叫作透視除法。另外要記住當你除以齊次座標後,咱們從投影空間轉到了歐幾里得空間,因此NDC座標只有三個份量。在NDC座標中,x y座標表明了你的頂點在歸一化2d屏幕上的位置,而z座標編碼了深度信息,即物體相對於近景面和遠景面的位置。儘管在這裏咱們工做在2d屏幕上,咱們仍然保持着深度信息。這容許webgl後面基於物體到近景面的距離來決定如何顯示物體的疊蓋關係。 當使用歸一化設備座標後,深度信息被編碼在了z份量上。
透視除法將視錐體變換精一個立方體中,原點在立方體的中心,最小座標是(-1, -1, -1)最大座標是(1, 1, 1).並且z軸的方向是相反的。以下所示:
Viewport transform
最後NDC被匹配到視口座標。這一步將這些座標映射到屏幕上的可視區域。在webgl中,這個區域由html5的canvas提供,以下圖所示:
與以前的步驟不一樣,視口變換不是由矩陣變換產生的。在這裏咱們使用webgl的viewport函數。
Normal transformations法線變換
當頂點被變換後,法線向量也須要被變換,以便他們可以指向正確的方向。咱們能夠考慮用模型視圖矩陣來作這件事,可是存在一個問題:模型視圖矩陣並不必定可以保持發現的垂直性。
這個問題發生在單軸縮放或者切變的矩陣中。在咱們的例子中,咱們有一個通過延y軸縮放的三角形。咱們可以看到在應用完模型視圖矩陣後,法線N'不在是該表面的法線。因此咱們要從新計算法線矩陣。
Calculating the Normal matrix
若是兩個向量垂直,他們的點乘積是0.
N*S = 0
S是經過表面兩個不一樣的頂點構造的一個向量。
M做爲模型視圖矩陣。咱們可使用M來變換S:
S' = MS
咱們想要找到一個矩陣K來容許咱們作相似的變換。對於法線N:
N' = KN
變換後仍然會保持N'與S'的垂直性:
N'*S' = 0
而後將N'和S'替換:
(KN)*(MS) = 0
因爲向量能夠表示成1x3的矩陣,因此點乘能夠表示爲對一個向量矩陣的轉置與第二個向量相乘:
(KN)T(MS) = 0
矩陣相乘的轉置等於矩陣的轉置逆序相乘:
NTKTMS = 0
矩陣相乘知足結合律,因此:
NT(KTM)S = 0
由於N*S=0,因此NTS = 0.因此這表明(KTM)=I,由於只有標準單位對角矩陣才使得NI=N;
綜上結論:
WebGL implementation WebGL的實現方式
如今咱們來看一下咱們是如何在webgl中實現頂點和法線的變換。下面的圖展現了咱們在理論上所學到的和理論與webgl實踐的關係:
在webgl中,咱們應用於物體上座標來獲得視口座標的五個變換被組織在三個矩陣和一個webgl方法中:
一、模型視圖矩陣將模型變換和視點變換組織在一個矩陣中。當咱們經過這個矩陣來乘以咱們的頂點,咱們就獲得的視點座標
二、法線矩陣經過將模型視圖矩陣求逆並轉置獲得。這個矩陣被用來乘以法線向量以便用來計算光照
三、透視矩陣用來組織投影變換和透視除法,而後咱們就獲得了NDC設備歸一化座標。
最後咱們使用gl.viewport來說NDC座標匹配到視口座標。
gl.viewport(minX, minY, width, height);
視口座標的原點在canvas的左上角。
JavaScript matrices
glmatrixwebgl自己沒有提供方法來實現矩陣操做。webgl作的全部工做就是提供一種將矩陣傳遞給着色器的方式。因此咱們須要使用一個JavaScript類庫來讓咱們擁有操做矩陣的能力。這裏使用這個類庫。
Mapping JavaScript matrices to ESSL uniforms
因爲模型視圖矩陣和透視矩陣在一次渲染過程當中不會改變,因此咱們將他們作uniforms類型傳遞給着色程序。好比,若是咱們要講場景中的一個物體作平移操做,咱們須要用平移操做後的新座標來繪製全部物體。將全部的物體畫在新位置是在一個渲染步驟中完成的。
然而,在渲染被(drawArrays或drawElements)調用以前,咱們須要確保着色器擁有一個更新後的矩陣版本。具體步驟以下:
var reference= getUniformLocation(Object program, String uniformName)
gl.uniformMatrix4fv(WebGLUniformLocation reference, bool transpose,float[] matrix);其中matrix是一個JavaScript變量
uniformMatrix[2 3 4]fv(reference, transpose, matrix)可以經過reference來加載 2x2, 3x3, 4x4的浮點數據矩陣到uniform型着色器變量中。reference是WebGLUniformLocation型的變量。爲了實際操做的簡便,它一般是int數字。按照規範,transpose的值必須是false。matrix老是浮點類型。matrix一般是4,9,16個元素的按照列向量形式的數組。其中matrix參數也能夠是Float32Array類型。這是一種JavaScript類型數組,使用二進制來存儲數據,可以提高性能。
Working with matrices in ESSL
在以下着色器代碼中:
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
將一個計算值傳遞給了以前定義的gl_Position變量。gl_Position將包含着色器最近處理的頂點的裁剪座標。這裏應該記住着色過程是並行的,每個頂點將會被一個頂點着色器的實例所處理。
爲了得到給定頂點的裁切座標,咱們須要先乘以模型視圖矩陣而後是投影矩陣。爲了達到這個目的咱們由左到右來計算(矩陣乘法不知足交換律)。
另外,注意咱們須要包含齊次座標來加強aVertexPosition屬性。由於咱們一般在歐幾里得空間中來定義咱們的圖形。幸運的是ESSL容許咱們經過增長確實部分來建立一個四維向量vec4。由於模型視圖矩陣和透視矩陣都是在齊次座標系中被描述的。
The Model-View matrix
模型視圖矩陣容許在咱們的場景中實現仿射變換。仿射是一個數學名詞來描述一種對物體應用變換後不會改變物體結構的變換。
其中:
m13 m14 m15表明平移
m4 m8 m12老是0,m16老是1;
The Camera matrix
webgl中沒有專門的相機對象,咱們假想的相機對象由一個4x4矩陣所表達。
假定咱們的相機坐落於世界的原點,朝向Z軸的負方向。關於相機的運動問題咱們分紅兩個步驟來看: 相機的平移和相機的旋轉 。
(本書中使用的是列向量來作講解,而glmatrix中使用的是行向量)
The Camera matrix is the inverse of the Model-View matrix
相機矩陣跟模型視圖矩陣爲互逆關係。
根據程序想要達到的效果不一樣,相機類型也分爲兩種類型:軌道相機和跟蹤相機。
軌道相機達到的目的是以一個世界中心店來作旋轉、平移;好比當一個地球所有展示在你面前時,你對地球的旋轉、拉近地球距離,這時候就是軌道相機。
跟蹤相機的原點在相機位置;以相機爲原點進行旋轉前進後退等;cs遊戲就是典型的跟蹤相機模式。
這兩種類型的相機在實現起來的差異就是對相機的模型矩陣的平移和旋轉的前後順序不一樣;軌道相機是先旋轉相機的模型矩陣再平移;跟蹤相機是先平移相機的模型矩陣,在對平移後的矩陣作旋轉。
最終獲得一個相機的模型矩陣,對這個相機的模型矩陣求逆就是以相機爲原點的模型視圖矩陣。
(跟蹤相機和軌道相機是能夠相互轉化的,咱們只須要把平移跟旋轉的順序變更一下便可)
Translating the camera in the line of sight
在視域中移動相機;對於軌道相機因爲老是指向世界的中心點,因此咱們一般使用世界座標的z軸來改變物體與世界的遠近。
而對於跟蹤相機,因爲他可能指向世界的任何地方,因此咱們須要知道在世界座標系中相機的朝向。
對於行向量來講先旋轉後平移,數學上的矩陣操做順序應該爲:vRT;而對於列向量應該是TRv;這是由矩陣的運算法則所決定的。
而在實際編程語言中,一般使用一維數組來存儲4x4矩陣的16個元素;因此有行存儲和列存儲的說法。所謂的行存儲和列存儲的區分就在於數組的前四個元素存儲的是矩陣的第一列仍是第一行;表示列的稱爲列存儲,表示行的成爲行存儲。以下圖數組的前四個元素對應矩陣中的第一列,因此是列存儲。
當須要對物體或相機作一系列變換時,咱們首先要肯定矩陣的變換順序,好比對webgl這種列主序的,先旋轉後平移的矩陣操做是:TRv。 肯定矩陣順序後,接下來肯定矩陣類庫的api調用順序 。向glmatrix這種類庫提供的api,對先旋轉後平移這種矩陣操做的實現方式是:
若是初學者要肯定類庫api的調用順序,最好看一下類庫矩陣運算部分的源碼,不然容易繞暈。其實無論類庫的api如何設計若是調用,只要保證計算的結果符合數學公式便可。
最後獲得一個要傳給着色器的最終矩陣,要保證這個一維數組是以列主序的方式存儲。
綜上來看,一點要保證獲得的一維數組的元素在數學上是按照正確的矩陣運算順序獲得的結果。第二要保證傳給着色器的一維數組是按照列主序的方式來存儲的。
Camera model
相機矩陣中編碼了關於相機軸的指向信息。如圖所示,左上角的3x3矩陣表明了相機的三個軸:
因爲實際中相機矩陣是模型視圖矩陣的逆矩陣,包含在相機矩陣中左上角的3x3的旋轉矩陣,能告訴咱們在世界空間中相機的三個軸的方向。
The Perspective matrix
透視矩陣包含投影變換和透視除法。這兩步合起來將一個3d場景轉換成一個立方體(後面經過視口變換匹配到2d畫布上)。
實際上,透視矩陣決定了相機捕獲到的圖像中幾何物體。在真是世界中,相機鏡頭會決定最終的圖像是如何變形扭曲的。在webgl世界中,咱們使用透視矩陣來模擬這個過程。另外在webgl世界中咱們能夠有另外一種非透視的表達:平行投影。
透視矩陣決定了相機的視野,也就是多少3d空間會被相機捕獲。視野使用角度做爲單位,這個術語一般與視角這個術語交叉使用。
Perspective or orthogonal projection
透視投影可以實現近大遠小的效果,它更接近咱們的眼睛觀察到的真實世界,由於它給咱們的大腦一些深度提示信息,因此咱們能感到距離感。
相反,平行投影使用平行線,沒有近大遠小的感受。因此在平行投影中深度信息被丟失了。
使用glMatrix,咱們經過調用mat4.persective或者mat4.ortho來設置透視投影或平行投影矩陣。
Structure of the WebGL examples
webgl app的生命週期:
矩陣操做函數: