您好,登錄后才能下訂單哦!
在圖形的計算中,比如旋轉、縮放、平移、投影等操作,矩陣都扮演著極其重要的角色,它是操作圖元的基本工具。雖然很多的圖形API已經封裝好了這些矩陣操作,但是理解這些矩陣操作的原理會非常非常有幫助,比如說我們可以通過一些矩陣的快捷計算來加速你的代碼。
如果你有一些線性代數的基礎,看下面的內容的時候也不會很輕松,因為有點難且比較沒意思,如果沒有修過這門課,最好把線性代數這本書拿來看看,因為這些東西真是基礎中的基礎,而且非常的重要。
空間一個點對應的是一個空間的位置,一個向量對應一個方向,兩者都可以用一個三維向量 V = (Vx, Vy, Vz)來表示.
這兩者如果對于變換(比如旋轉,縮放),用一個 3*3 矩陣就可以搞定,但對于平移變換就不適用了,因為位置變換對于向量是沒有意義的,而對于點才是有意義的。
齊次記法就是用來解決這個問題的。
所謂齊次記法就是用n+1維矢量表示n維矢量。
在齊次記法下,空間點記為 P = (Px, Py, Pz, Pw), 其中Pw = 1。
空間向量記為 V = (Vx, Vy, Vz, Vw),其中Vw = 0.
當出現Pw!=0 且Pw != 1時,就需要將坐標齊次化了,做法是同除以Pw,記為(Px/Pw, Py/Pw, Pz/Pw, 1).
齊次記法下的變換矩陣如下所示:
給定一個移動變換矩陣
對于一個向量 V = (Vx, Vy, Vz, Vw)和 T 相乘之后各值不變。
對于一個點 P = (Px, Py, Pz, Pw)和 T 相乘之后結果變為 (Px+tx, Py+ty, Pz+tz, 1).
齊次坐標帶來的便利:提供了用矩陣運算把二維、三維甚至高維空間中的一個點集從一個坐標系變化到另一個坐標系的有效方法。
基礎的變換包括平移,旋轉,縮放,切變,反射,投影等,下面一個個來看。
上面已經提到了,平移矩陣用T來表示:
tx,ty和tz分別表示向x,y,z方向移動的距離,如圖
注意這個仿射矩陣(Offine Tranform Matrics)對于空間向量是沒有作用的。
其逆矩 ,表示向相反的方向移動。
旋轉變幻是指繞著一個軸旋轉一定的角度,繞x,y,z旋轉的旋轉矩陣可以記為:
逆陣 , 表示繞同一個軸按相反的方向旋轉相同的角度。
旋轉矩陣的行列式都為1,因為它是正交矩陣。
關于圖形(或物體)繞自身的某點旋轉,其真實的過程是先將物體移動到旋轉點與坐標原點相重合的位置,再將圖形繞原點旋轉,然后再進行平移變換,平移到原先的位置。
整個矩陣計算過程為
縮放就是放大和縮小,其矩陣表示為
如果 Sx = Sy = Sz,則稱為等比變換(uniform),否則就不是(nonuniform)。
其逆陣 ,表示按相反的方式進行縮放。
Sx,Sy,Sz中有一個為負數,則改矩陣就是反射矩陣,如果剛好有兩個因子為 -1, 則圖形旋轉 。反射矩陣通常需要特殊對待,比如,對于一個三角形,經過反射變換,頂點的順序就可能會改變,這就會影響到面的法線,光照和背面消隱等算法就會受影響。可以通過計算左上角 3*3 矩陣的行列式的值來進行判斷,若行列式的值為負,則是反射矩陣。
切變變換可以用于游戲中,制作出爆炸的時候畫面抖動的效果,一共有六種:
第一個下標表示要改變的坐標軸,第二個下標表示沿著那個坐標軸變換。相關的矩陣也可以由此得出:第一個下標決定行,第二個決定列,則有:
效果如下:
其逆陣:
由于矩陣乘法是沒有交換率的,所以矩陣相乘的順序非常重要,比如 S(2, 0.5, 1)和 , 根據它們執行的順序不同,得到的結果也會不一樣。
將多個矩陣整合到一起的另一個好處是提高了效率,一般的順序時 TRS。
歐拉變換可以將物體旋轉到任意的方向,一個歐拉變換可以分為三個分量 h(ead), p(ich), r(oll),記為E(h,p,r)。
其實就是三個旋轉矩陣的級聯矩陣:,由于都為對稱陣,其逆陣 = 。
使用歐拉變換的時候會出現一個很蛋疼的問題-gimbal lock,可以看看這個視頻- youtube video explaining gimbal lock
還會出現的一個問題就是兩個歐拉角之間的插值問題。
為了避免萬圣節鎖,一個方法是設定好旋轉軸的旋轉順序。
另一中方法是使用四元組。
模型是由一系列的頂點構成的,頂點的坐標是相對于模型的中心來定義的,如果某個頂點的坐標值是(0,0,0),就意味著這個頂點在模型的正中間。
現在假設世界坐標在,模型的左邊,則模型左邊對應于世界坐標需要乘以一個平移矩陣,這個矩陣就是model matrix(模型矩陣)。
人們在操作這個模型的時候,需要對其進行一些變換,就需要將其每個定點移動到原點。
通過下圖中黑色的箭頭,就是將模型移動到原點。
這個變換矩陣就是model matrix(模型矩陣)。
這個過程可以描述為:
接下來是View Matrix(視口矩陣)。
當你站在一座山的前面,想從各個角度來觀察這座山的時候,你可以選擇跑到不同的位置去看,也可以選擇...移動整座山。這在現實生活中看似不行,但在圖形學中,這一切都是可行的。
現在在整個世界中只有一個model,當需要觀察這個物體的時候,需要一個攝像機來進行觀察,假設攝像機初始化在原點,經過一個平移矩陣移動,
glm::mat4 ViewMatrix = glm::translate(Tx, Ty ,Tz);
這里提一下glm中的一個神奇的lookat函數~超強的生成 View Matrics
glm::mat4 CameraMatrix = glm::LookAt( cameraPosition, // the position of your camera, in world space cameraTarget, // where you want to look at, in world space upVector // probably glm::vec3(0,1,0), but (0,-1,0) would make you looking upside-down, which can be great too );
接下來是Projection matrices(投影矩陣)
經過前面的Model Matriix 和 View Matrix的變換,現在處在的就是攝像機空間,也就意味著(0,0)上的點就會出現在屏幕的最中央,但是并不是兩個坐標就可以決定頂點是否顯示,我們不能忽略 Z 坐標,也就是頂點距離攝像機的位置。
在透視投影中,根據頂點的坐標值,當Vx,Vy的值相同的時候,Vz的值越大,頂點就越在中間,可以參考下圖。
一個4*4的矩陣可以用來描述投影:
glm::mat4 projectionMatrix = glm::perspective( FoV, // The horizontal Field of View, in degrees : the amount of "zoom". Think "camera lens". Usually between 90° (extra wide) and 30° (quite zoomed in) 4.0f / 3.0f, // Aspect Ratio. Depends on the size of your window. Notice that 4/3 == 800/600 == 1280/960, sounds familiar ? 0.1f, // Near clipping plane. Keep as big as possible, or you'll get precision issues. 100.0f // Far clipping plane. Keep as little as possible. );
我們知道,OpenGL中自帶了一些接口函數,可以很方便的定義視口,投影矩陣等,但如果使用GLSL的話,所有頂點的位置都是由 *.vert 中的代碼來確定,下面我們就來實踐一下剛才學習的Model,View,Projection。
首先是兩個簡單的Shader:
basic.vert
#version 400 layout(location = 0) in vec3 vertexPosition_modelspace; // Values that stay constant for the whole mesh. uniform mat4 MVP; void main(){ // Output position of the vertex, in clip space : MVP * position gl_Position = MVP * vec4(vertexPosition_modelspace,1); }
basic.frag
#version 400 // Ouput data out vec3 color; void main() { // Output color = red color = vec3(1,0,0); }
void CGL::compileShader() { static const GLfloat g_vertex_buffer_data[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; static const GLushort g_element_buffer_data[] = { 0, 1, 2 }; GLuint vertexbuffer; glGenBuffers(1, &vertexbuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glVertexAttribPointer( 0, // attribute. No particular reason for 0, but must match the layout in the shader. 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); if( ! prog.compileShaderFromFile("shader/basic.vert",GLSLShader::VERTEX) ) { printf("Vertex shader failed to compile!\n%s", prog.log().c_str()); exit(1); } if( ! prog.compileShaderFromFile("shader/basic.frag",GLSLShader::FRAGMENT)) { printf("Fragment shader failed to compile!\n%s", prog.log().c_str()); exit(1); } if( ! prog.link() ) { printf("Shader program failed to link!\n%s", prog.log().c_str()); exit(1); } if( ! prog.validate() ) { printf("Program failed to validate!\n%s", prog.log().c_str()); exit(1); } prog.use(); }
void CGL::setUniform() { // Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f); // Camera matrix glm::mat4 View = glm::lookAt( glm::vec3(3,3,3), // Camera is at (4,3,3), in World Space glm::vec3(0,0,0), // and looks at the origin glm::vec3(0,1,0) // Head is up (set to 0,-1,0 to look upside-down) ); // Model matrix : an identity matrix (model will be at the origin) glm::mat4 Model = glm::mat4(1.0f); // Our ModelViewProjection : multiplication of our 3 matrices glm::mat4 MVP = Projection * View * Model; // Remember, matrix multiplication is the other way around prog.setUniform("MVP",MVP); prog.setUniform("modelMatrics",Model); }
wiki.變換矩陣 - http://zh.wikipedia.org/wiki/%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5#.E4.BB.BF.E5.B0.84.E5.8F.98.E6.8D.A2
The Matrix and Quaternions FAQ - http://www.cs.princeton.edu/~gewang/projects/darth/stuff/quat_faq.html
Matrices - http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
Real-Time Rendering 3rd
Fundamentals of Computer Graphics 2rd
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。