iOS開發(fā)學習OpenGL ES系列 -- 變換矩陣

從數(shù)學上說,變換就是在兩個坐標系之間轉換頂點坐標。每個坐標系都是相對于其他的參照坐標系定義的。對于OpenGL ES來說,最終的參照坐標系是在一個像素顏色渲染緩存中的像素位置的2D數(shù)組。

OpenGL ES中的四種基本變換:平移(translation)、旋轉(rotation)?;咀儞Q的聯(lián)合決定了在一個新的坐標系中的每一個頂點位置是怎么轉換為參考坐標系中的一個位置的。四個基本變換足以產(chǎn)生無窮的坐標系。

每個基本變換對應于矩陣的一個簡單變化。定義一個與參考坐標系相同的坐標系的矩陣叫做單位矩陣。任意兩個矩陣可以在一個聯(lián)級(又叫矩陣乘法)操作中結合起來以產(chǎn)生一個新矩陣,這個新矩陣包含了兩個矩陣的所有變換。實際上,每個基本變換會產(chǎn)生一個簡單的矩陣,然后把這個簡單的矩陣與當前變換矩陣連接起來以產(chǎn)生一個新的當前矩陣。

定義全局

平移
通過相對于參考坐標系的原點移動新坐標系的原點、平移定義了一個新的坐標系。平移不會影響坐標軸的單位長度,平移不會改變坐標軸相對于參考坐標系的方向。
GLKit提供了GLKMatrix4MakeTranslation(float x,float y,float z)函數(shù),這個函數(shù)通過平移一個單位矩陣來返回一個定義了坐標系的新矩陣。x、y、z參數(shù)指定了新坐標原點沿著當前參考坐標系的每個軸移動的單位數(shù)。

修改代碼,看一下GLKit提供的GLKMatrix4MakeTranslation函數(shù)實現(xiàn)平移效果。

在頂點著色器中新增了一個uniform mat4 transform。 mat4這個類型前文有提到過,4X4的矩陣。它是Shader內置的類型,支持直接加減乘等操作。使用矩陣會產(chǎn)生更少的運算指令,GPU可以更好的優(yōu)化運算過程。

attribute vec4 position;
attribute vec4 color;
varying vec4 fColor;
uniform mat4 transform;
void main(void) {
    fColor = color;
    gl_Position = transform * position;
}

因為我們的數(shù)據(jù)更新在update里,而賦值繪制在glkView:(GLKView *)view drawInRect:(CGRect)rect中,所以需要定義為全局:

GLKMatrix4 transformMatrix;

// 初始化為單位矩陣,不對圖形產(chǎn)生任何變換
transformMatrix = GLKMatrix4Identity;

這里的GLKMatrix4Identity相當于下面的矩陣:

接下來在應用中創(chuàng)建一個平移變換矩陣:

// 定義一個在值域-1和1之間的局部變量
GLfloat elValue = sinf(changeValue);

// 創(chuàng)建平移變換矩陣,這里只改變了X坐標
GLKMatrix4 translateMatrix = 
GLKMatrix4MakeTranslation(elValue, 0.0, 0.0);

把這個變換矩陣賦值到uniform mat4 transform上:

GLuint transformUniformLocation = glGetUniformLocation(program, "transform");
glUniformMatrix4fv(transformUniformLocation, 1, 0, transformMatrix.m);

繪制之后看下效果:

因為我們只是平移了坐標X值,Y和Z值沒有變,所以三角形只是沿著X軸平行移動。

上面的elValue值域為[-1,1],先看一下為-1的時候,我們取其中一個頂點(0,0.5,0)經(jīng)過(-1,0,0)的平移之后為(-1,0.5,0),我們看一下矩陣計算:

如果平移經(jīng)過(1,0,0)的平移之后就為:(1 0.5 0 1),這就是我們上面看到的三角形的上頂點在屏幕之間移動了。

通過上面的矩陣計算可以發(fā)現(xiàn),平移矩陣就是在一個4x4的變換矩陣中填充第4行前面三個位置:

縮放
縮放是通過相對于參考坐標系的坐標軸的單位長度改變新坐標系的坐標軸的單位長度來定義一個新坐標系??s放坐標系與參考坐標系使用同一個原點。坐標軸的方向通常不會改變。不過,通過一個負值所做的縮放就會翻轉坐標軸的方向。

GLKit提供了GLKMatrix4MakeScale(float x,float y,float z)函數(shù),這個函數(shù)會通過擴大或者縮小一個單位矩陣的任意坐標軸的單位長度來返回一個定義了坐標系的矩陣。x、y和z參數(shù)指定了用來擴大或者縮小每個軸的單位長度的因素。GLKMatrix4Scale(float x,float y,float z)函數(shù)通過按指定的因數(shù)縮放作為參數(shù)傳入矩陣來返回一個定義了坐標系的新矩陣。

GLKMatrix4 scaleMatrix = GLKMatrix4MakeScale(elValue, elValue, 1.0);
// 很明顯這個矩陣會改變頂點坐標的x、y值

看一下最終效果:

簡單分析:elValue為正弦函數(shù)的值,所以在-1和1之間,在elValue為1是,不會對原數(shù)據(jù)有影響,當為-1時,就會翻轉坐標軸的方向。所以最終效果就是上圖那樣,從原圖縮小到0,然后負數(shù)增大,就是倒過來了。

假定還是點(0.0,0.5,0.0)的x和y值在縮小0.5之后為(0.0,0.25,0.0),看下矩陣計算:

旋轉
旋轉是通過相對于參考坐標系坐標軸的方向旋轉新坐標系的坐標軸來定義一個新坐標系。旋轉的坐標系會與參考坐標系使用同一個原點。旋轉不會影響坐標軸的單位長度,只有坐標軸的方向會發(fā)生變化。

GLKit提供了GLKMatrix4MakeRotation(float angleRedians, float x, float y, float z)函數(shù),這個函數(shù)通過旋轉一個單位矩陣來返回一個定義了坐標系的新矩陣。angleRedians參數(shù)指定了要旋轉的弧度數(shù)。使用GLKMathDegreesToRadians(float degrees)函數(shù)可以把角度轉換成弧度。x、y和z參數(shù)用于指定當前坐標系的哪一個軸作為旋轉的輪轂。

例如:GLKMatrix4MakeRotation(GLKMathDegreesToRadians(60.0) , 1.0, 0.0, 0.0)會沿著一個特定的坐標系的X軸旋轉60度來產(chǎn)生一個新的坐標系。

相對于平移和縮放,旋轉要復雜一些。旋轉包含兩個重要元素,旋轉的角度,繞什么軸旋轉。

GLKMatrix4 rotateMatrix = GLKMatrix4MakeRotation(elValue , 0.0, 0.0, 1.0);
// 繞Z軸旋轉

看下效果圖:

除了利用GLKit提供的GLKMatrix4MakeRotation函數(shù)直接生成,我們還可以自己定義4x4的旋轉矩陣:

// 繞x軸
    transformMatrix = GLKMatrix4Make(1.0, 0.0,           0.0,          0.0,
                                     0.0, cos(elValue), -sin(elValue), 0.0,
                                     0.0, sin(elValue),  cos(elValue), 0.0,
                                     0.0, 0.0,           0.0,          1.0);

    // 繞y軸
    transformMatrix = GLKMatrix4Make(cos(elValue),0.0, sin(elValue), 0.0,
                                     0.0,         1.0, 0.0,          0.0,
                                    -sin(elValue),0.0, cos(elValue), 0.0,
                                     0.0,         0.0, 0.0,          1.0);
    
    // 繞z軸
    transformMatrix = GLKMatrix4Make(cos(elValue),-sin(elValue), 0.0, 0.0,
                                     sin(elValue), cos(elValue), 0.0, 0.0,
                                     0.0,           0.0,         1.0, 0.0,
                                     0.0,           0.0,         0.0, 1.0);

關于旋轉矩陣可以參考這篇三維空間中的旋轉:旋轉矩陣、歐拉角

下面結合三種變換,直接相乘就可以,但是要注意順序:平移旋轉縮放。這樣可以保證先縮放,再旋轉,最后平移。

// 縮放
GLKMatrix4 scaleMatrix = GLKMatrix4MakeScale(elValue, elValue, 1.0);
    
// 旋轉
GLKMatrix4 rotateMatrix = GLKMatrix4MakeRotation(elValue , 0.0, 0.0, 1.0);
    
// 平移
GLKMatrix4 translateMatrix = GLKMatrix4MakeTranslation(elValue, elValue, 0.0);

/*
transformMatrix = translateMatrix * rotateMatrix * scaleMatrix
矩陣會按照從右到左的順序應用到position上。也就是先縮放(scale),再旋轉(rotate),最后平移(translate)
如果這個順序反過來,就完全不同了。從線性代數(shù)角度來講,就是矩陣A乘以矩陣B不等于矩陣B乘以矩陣A。
*/
transformMatrix = GLKMatrix4Multiply(translateMatrix, rotateMatrix);
transformMatrix = GLKMatrix4Multiply(transformMatrix, scaleMatrix);

看下最終包含了平移、縮放、旋轉的效果:

關于OpenGL ES的三種基本變換就說到這里,其實還有兩個重要的投影變換 -- 透視投影和正交投影。就不在本篇繼續(xù)說了,盡量控制一下篇幅,就在下一篇講吧!

本例代碼:LearningOpenGL ES GitHub

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1 前言 OpenGL渲染3D模型離不開空間幾何的數(shù)學理論知識,而本篇文章的目的就是對空間幾何進行簡單的介紹,并對...
    RichardJieChen閱讀 7,520評論 1 11
  • 深度渲染緩存 深度緩存是一個可選的輸出緩存,并且與像素顏色渲染緩存相似,幾乎所有的OpenGL ES都使用深度緩存...
    McDan閱讀 1,753評論 0 0
  • 目錄 一、分析拉伸的原因 二、準備知識,三維變換 三、OpenGL 下的三維變換 四、修復拉伸問題 一、分析拉伸的...
    半紙淵閱讀 2,418評論 3 0
  • 1. 深度渲染緩存(Depth Render Buffer) 三角形、線段和點是按它們被GPU處理的順序被渲染的。...
    碧玉小瑕閱讀 460評論 0 0
  • >*很不幸,沒人能告訴你母體是什么,你只能自己體會* --駭客帝國 在第四章“可視效果”中,我們研究了一些增強圖層...
    夜空下最亮的亮點閱讀 1,721評論 0 2

友情鏈接更多精彩內容