溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

發(fā)布時間:2020-07-14 01:51:20 來源:網(wǎng)絡(luò) 閱讀:407 作者:拳四郎 欄目:開發(fā)技術(shù)

提要

       在圖形的計(jì)算中,比如旋轉(zhuǎn)、縮放、平移、投影等操作,矩陣都扮演著極其重要的角色,它是操作圖元的基本工具。雖然很多的圖形API已經(jīng)封裝好了這些矩陣操作,但是理解這些矩陣操作的原理會非常非常有幫助,比如說我們可以通過一些矩陣的快捷計(jì)算來加速你的代碼。

      如果你有一些線性代數(shù)的基礎(chǔ),看下面的內(nèi)容的時候也不會很輕松,因?yàn)橛悬c(diǎn)難且比較沒意思,如果沒有修過這門課,最好把線性代數(shù)這本書拿來看看,因?yàn)檫@些東西真是基礎(chǔ)中的基礎(chǔ),而且非常的重要。


齊次記法(Homogeneous Notation)

       空間一個點(diǎn)對應(yīng)的是一個空間的位置,一個向量對應(yīng)一個方向,兩者都可以用一個三維向量 V = (Vx, Vy, Vz)來表示.

       這兩者如果對于變換(比如旋轉(zhuǎn),縮放),用一個 3*3 矩陣就可以搞定,但對于平移變換就不適用了,因?yàn)槲恢米儞Q對于向量是沒有意義的,而對于點(diǎn)才是有意義的。

       齊次記法就是用來解決這個問題的。

       所謂齊次記法就是用n+1維矢量表示n維矢量。

       在齊次記法下,空間點(diǎn)記為 P = (Px, Py, Pz, Pw), 其中Pw = 1。

       空間向量記為 V = (Vx, Vy, Vz, Vw),其中Vw = 0.

       當(dāng)出現(xiàn)Pw!=0 且Pw != 1時,就需要將坐標(biāo)齊次化了,做法是同除以Pw,記為(Px/Pw, Py/Pw, Pz/Pw, 1).

       齊次記法下的變換矩陣如下所示:

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

給定一個移動變換矩陣

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

對于一個向量  = (Vx, Vy, Vz, Vw)和 T 相乘之后各值不變。

對于一個點(diǎn)   = (Px, Py, Pz, Pw)和 T 相乘之后結(jié)果變?yōu)?(Px+tx, Py+ty, Pz+tz, 1).

       齊次坐標(biāo)帶來的便利:提供了用矩陣運(yùn)算把二維、三維甚至高維空間中的一個點(diǎn)集從一個坐標(biāo)系變化到另一個坐標(biāo)系的有效方法。




基礎(chǔ)變換

基礎(chǔ)的變換包括平移,旋轉(zhuǎn),縮放,切變,反射,投影等,下面一個個來看。


平移變換

上面已經(jīng)提到了,平移矩陣用T來表示:

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

tx,ty和tz分別表示向x,y,z方向移動的距離,如圖

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

注意這個仿射矩陣(Offine Tranform Matrics)對于空間向量是沒有作用的。

其逆矩    Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics),表示向相反的方向移動。


 旋轉(zhuǎn)矩陣 Rotating

旋轉(zhuǎn)變幻是指繞著一個軸旋轉(zhuǎn)一定的角度,繞x,y,z旋轉(zhuǎn)的旋轉(zhuǎn)矩陣可以記為:

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

逆陣   Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics), 表示繞同一個軸按相反的方向旋轉(zhuǎn)相同的角度。

旋轉(zhuǎn)矩陣的行列式都為1,因?yàn)樗钦痪仃嚒?/p>


關(guān)于圖形(或物體)繞自身的某點(diǎn)旋轉(zhuǎn),其真實(shí)的過程是先將物體移動到旋轉(zhuǎn)點(diǎn)與坐標(biāo)原點(diǎn)相重合的位置,再將圖形繞原點(diǎn)旋轉(zhuǎn),然后再進(jìn)行平移變換,平移到原先的位置。

整個矩陣計(jì)算過程為   Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)


縮放變換 Scaling

縮放就是放大和縮小,其矩陣表示為

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

如果 Sx = Sy = Sz,則稱為等比變換(uniform),否則就不是(nonuniform)。

        其逆陣   Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics),表示按相反的方式進(jìn)行縮放。

        Sx,Sy,Sz中有一個為負(fù)數(shù),則改矩陣就是反射矩陣,如果剛好有兩個因子為 -1, 則圖形旋轉(zhuǎn) Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics) 。反射矩陣通常需要特殊對待,比如,對于一個三角形,經(jīng)過反射變換,頂點(diǎn)的順序就可能會改變,這就會影響到面的法線,光照和背面消隱等算法就會受影響??梢酝ㄟ^計(jì)算左上角 3*3 矩陣的行列式的值來進(jìn)行判斷,若行列式的值為負(fù),則是反射矩陣。


切變變換 Shearing

切變變換可以用于游戲中,制作出爆炸的時候畫面抖動的效果,一共有六種:

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

第一個下標(biāo)表示要改變的坐標(biāo)軸,第二個下標(biāo)表示沿著那個坐標(biāo)軸變換。相關(guān)的矩陣也可以由此得出:第一個下標(biāo)決定行,第二個決定列,則有:

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

效果如下:

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

其逆陣:

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)


級聯(lián)變換 Concatenation of Transforms

        由于矩陣乘法是沒有交換率的,所以矩陣相乘的順序非常重要,比如 S(2, 0.5, 1)和 Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics), 根據(jù)它們執(zhí)行的順序不同,得到的結(jié)果也會不一樣。

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)


       將多個矩陣整合到一起的另一個好處是提高了效率,一般的順序時 TRS。


歐拉變換 Euler TransForm

       歐拉變換可以將物體旋轉(zhuǎn)到任意的方向,一個歐拉變換可以分為三個分量 h(ead), p(ich), r(oll),記為E(h,p,r)。

       其實(shí)就是三個旋轉(zhuǎn)矩陣的級聯(lián)矩陣:Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics),由于都為對稱陣,其逆陣  Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics) =  Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)。

      使用歐拉變換的時候會出現(xiàn)一個很蛋疼的問題-gimbal lock,可以看看這個視頻- youtube video explaining gimbal lock

      還會出現(xiàn)的一個問題就是兩個歐拉角之間的插值問題。

      為了避免萬圣節(jié)鎖,一個方法是設(shè)定好旋轉(zhuǎn)軸的旋轉(zhuǎn)順序。

      另一中方法是使用四元組。

      

模型矩陣,視口矩陣和投影矩陣  The Model, View and Projection Matrices

        模型是由一系列的頂點(diǎn)構(gòu)成的,頂點(diǎn)的坐標(biāo)是相對于模型的中心來定義的,如果某個頂點(diǎn)的坐標(biāo)值是(0,0,0),就意味著這個頂點(diǎn)在模型的正中間。

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)


現(xiàn)在假設(shè)世界坐標(biāo)在,模型的左邊,則模型左邊對應(yīng)于世界坐標(biāo)需要乘以一個平移矩陣,這個矩陣就是model matrix(模型矩陣)。

人們在操作這個模型的時候,需要對其進(jìn)行一些變換,就需要將其每個定點(diǎn)移動到原點(diǎn)。

通過下圖中黑色的箭頭,就是將模型移動到原點(diǎn)。

這個變換矩陣就是model matrix(模型矩陣)。

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)

這個過程可以描述為:


Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)


         接下來是View Matrix(視口矩陣)。

        當(dāng)你站在一座山的前面,想從各個角度來觀察這座山的時候,你可以選擇跑到不同的位置去看,也可以選擇...移動整座山。這在現(xiàn)實(shí)生活中看似不行,但在圖形學(xué)中,這一切都是可行的。

        現(xiàn)在在整個世界中只有一個model,當(dāng)需要觀察這個物體的時候,需要一個攝像機(jī)來進(jìn)行觀察,假設(shè)攝像機(jī)初始化在原點(diǎn),經(jīng)過一個平移矩陣移動,

glm::mat4 ViewMatrix = glm::translate(Tx, Ty ,Tz);

這個矩陣就是View Matrix(視口矩陣),對應(yīng)的就是世界坐標(biāo)遠(yuǎn)點(diǎn)到攝像機(jī)的變換矩陣,過程可以由下圖描述。


Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)


這里提一下glm中的一個神奇的lookat函數(shù)~超強(qiáng)的生成 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 );

整個階段的描述如下:

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)


接下來是Projection matrices(投影矩陣)

        經(jīng)過前面的Model Matriix 和 View Matrix的變換,現(xiàn)在處在的就是攝像機(jī)空間,也就意味著(0,0)上的點(diǎn)就會出現(xiàn)在屏幕的最中央,但是并不是兩個坐標(biāo)就可以決定頂點(diǎn)是否顯示,我們不能忽略 Z 坐標(biāo),也就是頂點(diǎn)距離攝像機(jī)的位置。

       在透視投影中,根據(jù)頂點(diǎn)的坐標(biāo)值,當(dāng)Vx,Vy的值相同的時候,Vz的值越大,頂點(diǎn)就越在中間,可以參考下圖。

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)


一個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. );

通過投影矩陣變換,模型從照相機(jī)坐標(biāo)變?yōu)榱她R次坐標(biāo),過程描述如下:

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)



GLSL實(shí)戰(zhàn)MVP

        我們知道,OpenGL中自帶了一些接口函數(shù),可以很方便的定義視口,投影矩陣等,但如果使用GLSL的話,所有頂點(diǎn)的位置都是由 *.vert  中的代碼來確定,下面我們就來實(shí)踐一下剛才學(xué)習(xí)的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); } 

就是將模型坐標(biāo)與MVP矩陣相乘。


basic.frag

#version 400  // Ouput data out vec3 color;  void main() { 	// Output color = red  	color = vec3(1,0,0); }

接著時初始化shader,vao

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(); } 

然后是初始化Uniform變量:

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); } 

渲染一下:

Real-Rime Rendering (2) - 變換和矩陣(Transforms and Matrics)



參考

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


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI