OpenGL ES 3.0(四)矩陣變換

1、概述

前面幾篇關于OpenGLES的文章:

OpenGL ES 2.0 顯示圖形(上)

OpenGL ES 2.0 顯示圖形(下)

OpenGL ES 3.0(一)綜述

OpenGL ES 3.0(二)GLSL與著色器

OpenGL ES 3.0(三)紋理

有討論到關于圖像的變換和移動,前面這些變換是圖像基于每一幀改變物體的頂點并且重配置緩沖區從而使它們移動,但這太繁瑣了,而且會消耗很多的處理時間。現在有一個更好的解決方案,使用(多個)矩陣(Matrix)對象可以更好的變換(Transform)一個物體。

矩陣是一種非常有用的數學工具,如果上過線性代數這門課的話應該都會有所了解。這篇主要討論一下在圖像處理中一些最簡單的矩陣變換,并且在OpenGL ES中進行實踐。

2、向量的乘

要討論矩陣,一定繞不開向量。向量可以說是最簡單的矩陣,可以把它理解為1n的矩陣或者是n1的矩陣,關于向量的加減運算和長度計算等實在太過基礎這邊不再討論。這邊關于向量的運算主要討論其乘法相關的運算。

2.1 點乘

向量的點乘一般用 v · k 這樣表示,兩個向量的點乘結果等于它們的數乘結果再乘兩個向量之間夾角的余弦值。公式如下:

點乘公式

等式右邊||v|| 和 ||k|| 分別代表兩個向量的長度。通過上面的公式也可以反推出兩個向量之間的夾角。而對于具體的兩個向量,其點乘就是對應位置上的數字相乘再進行累加。

向量點乘

2.2 叉乘

叉乘只在3D空間中有定義,它需要兩個不平行向量作為輸入,生成一個正交于兩個輸入向量的第三個向量。如果輸入的兩個向量也是正交的,那么叉乘之后將會產生3個互相正交的向量。下圖展示了3D空間中叉乘的樣子:

3D空間中的叉乘

叉乘公式如下:

向量叉乘

3、矩陣

上面討論的向量其實也是矩陣的一種,矩陣就是數字、符號或表達式數組。矩陣中每一項叫做矩陣的元素(Element)。下面是一個2×3矩陣的例子:

2*3矩陣

矩陣可以通過(i, j)進行索引,i是行,j是列,這就是上面的矩陣叫做2×3矩陣的原因。獲取上面4的索引是(2, 1)(第二行,第一列)(注:如果是圖像索引應該是(1, 2),先算列,再算行)。

下面討論一下一些矩陣的基本運算。

3.1 矩陣加減

矩陣的加減是矩陣最簡單的操作,矩陣和標量進行加減其標量值要加減到矩陣每一個值當中。矩陣與矩陣之間的加減就是兩個矩陣對應元素的加減運算,不過在相同索引下的元素才能進行運算。這也就是說加法和減法只對同維度的矩陣才是有定義的。

矩陣與標量加法
矩陣之間減法

3.2 矩陣數乘

矩陣與標量相乘和矩陣與標量的加減一樣,會對每個數據進行乘。所以一般也用矩陣的數乘來縮放矩陣。

矩陣數乘

3.3 矩陣之間相乘

矩陣之間相乘會復雜許多,并且會有一些限制:

**① **只有當左側矩陣的列數與右側矩陣的行數相等,兩個矩陣才能相乘。

**② **矩陣相乘不遵守交換律,也就是說A ·B 和 B·A 并不相等。

矩陣相乘時是將左邊矩陣的i行和右邊矩陣的j列分別對應相乘并相加得到新矩陣i行j列位置上的值。結果矩陣的維度是(n, m),n等于左側矩陣的行數,m等于右側矩陣的列數。

矩陣之間相乘

4、矩陣變換

前面已經討論了矩陣和向量的一些基本操作,接下來討論通過這些操作來對矩陣進行一些常見的變換。

4.1 縮放

對一個向量進行縮放(Scaling)就是對向量的長度進行縮放,而保持它的方向不變。由于進行的是2維或3維操作,可以分別定義一個有2或3個縮放變量的向量,每個變量縮放一個軸(x、y或z)。

先來嘗試縮放向量v = (3,2)。可以把向量沿著x軸縮放0.5,使它的寬度縮小為原來的二分之一;將沿著y軸把向量的高度縮放為原來的兩倍。最終可得到如下向量s = (1.5,4):

向量縮放

OpenGL ES通常是在3D空間進行操作的,對于2D的情況可以把z軸縮放1倍,就相當于不縮放z軸。上圖的縮放操作是不均勻縮放,因為每個軸的縮放因子都不一樣。如果每個軸的縮放因子都一樣那么就叫均勻縮放。

下面構造一個變換矩陣來提供縮放功能。從單位矩陣(矩陣正斜對角線為1,其余為0的矩陣),每個對角線元素會分別與向量的對應元素相乘,而不會干擾其他維度上的向量值。對于需要將任意向量(x,y,x)分別縮放(S1,S2,S3)倍時,可以用這樣的特性來構造縮放矩陣。如下:

縮放矩陣

第四個縮放向量仍然是1,因為在3D空間中縮放w分量是無意義的。w分量另有其他用途,會在后面討論。

4.2 位移

位移是在原始向量的基礎上加上另一個向量從而獲得一個在不同位置的新向量的過程,從而在位移向量基礎上移動了原始向量。和縮放矩陣一樣,在4×4矩陣上有幾個特別的位置用來執行特定的操作,對于位移來說它們是第四列最上面的3個值。如果我們把位移向量表示為(Tx,Ty,Tz),就能把位移矩陣定義為:

位移矩陣

因為所有的位移值都要乘以向量的w行,所以位移值會加到向量的原始值上。而如果用3x3矩陣位移值就沒地方放也沒地方乘了,所以是不行的。這也是為什么3維的向量要用四維變換矩陣進行。

這個向量的w分量也叫齊次坐標分量。想要從齊次向量得到3維向量,可以把x、y和z坐標分別除以w坐標。通常不會注意這個問題,因為w分量通常是1.0。使用齊次坐標的好處在于它允許在3D向量上進行位移(如果沒有w分量是不能位移向量的)。如果一個向量的齊次坐標分量值是0,這個坐標就是方向向量,因為w坐標是0,這個向量就不能位移。有了位移矩陣就可以在3個方向(x、y、z)上移動物體。

4.3 旋轉

上面幾個的變換內容相對容易理解,在二維或三維空間中也容易表示出來,但旋轉稍復雜些。

首先來定義一個向量的旋轉到底是什么。二維或三維空間中的旋轉用角來表示。角可以是角度制或弧度制的,周角是360角度或2 PI弧度。下圖中展示的二維向量v是由k向右旋轉72度所得的:

二維向量的旋轉

在三維空間中旋轉需要定義一個角和一個旋轉軸。物體會沿著給定的旋轉軸旋轉特定角度。當二維向量在三維空間中旋轉時,一般把旋轉軸設為z軸。

給定一個角度,可以把一個向量變換為一個經過旋轉的新向量。這通常是使用一系列正弦和余弦函數的各種巧妙的組合得到的。旋轉矩陣在三維空間中每個單位軸都有不同定義,旋轉角度用θ表示:

沿x軸旋轉:

沿x軸的旋轉矩陣

沿y軸旋轉:

沿y軸的旋轉矩陣

沿z軸旋轉:

沿z軸的旋轉矩陣

利用旋轉矩陣可以把任意位置向量沿一個單位旋轉軸進行旋轉。也可以將多個矩陣復合,比如先沿著x軸旋轉再沿著y軸旋轉。但是這會很快導致一個問題——萬向節死鎖。對于三維空間中的旋轉,一個更好的模型是沿著任意的一個軸旋轉。這樣的一個旋轉矩陣是存在的,但其非常復雜這邊不進行展開討論。

4.4 組合變換

使用矩陣進行變換的真正厲害之處在于,根據矩陣之間的乘法,可以把多個變換組合到一個矩陣中。假設有一個頂點(x, y, z),希望將其縮放2倍,然后位移(1, 2, 3)個單位。需要一個位移和縮放矩陣來完成這些變換。其最終的組合變換矩陣如下:

組合變換矩陣

當矩陣相乘時先寫位移再寫縮放變換。矩陣乘法是不遵守交換律的,這意味著它們的順序很重要。當矩陣相乘時,在最右邊的矩陣是第一個與向量相乘的,所以應該從右向左讀這個乘法。建議在設計組合矩陣時,先進行縮放操作,然后是旋轉,最后才是位移,否則它們會互相影響。比如,如果先位移再縮放,位移的向量也會同樣被縮放。

用最終的變換矩陣左乘我們的向量會得到以下結果:

組合變換矩陣效果

如上所示,其目標向量確實放大來兩倍并進行了位移。

5、OpenGLES中的矩陣

前面已經討論完了矩陣的基本知識及操作,接下來討論下關于OpenGLES 中矩陣的應用。OpenGLES中有一個類是專門用來進行矩陣處理的android.opengl.Matrix。這個類里面包括了對于矩陣的各種前面提到的處理變換操作。

**① **Matrix.setIdentityM() :用來創建一個單位矩陣。其中第一個參數是創建出來的單位矩陣存儲的地方,是一個float類型的一維數組。第二個參數是存儲的數據位置的偏移量,也就是說從哪里開始存儲。生成的結果先按照列優先存儲的,也就是說先存放第一列的數據,再存放第二列的數據,以此類推。前面理論部分已經提到,所有變換都是基于單位矩陣的基礎上進行的,所以第一步創建單位矩陣是必須的。

**② **Matrix.rotateM() :用來進行旋轉變換的。第一個參數是需要變換的矩陣;第二參數是偏移量;第三個參數是旋轉角度,這邊是以角度制,也就是說是0-360這個范圍;第四、五、六個參數分別代表旋轉軸向量的x,y,z值。如果x=0,y=0,z = 1 就相當于以z軸為旋轉軸進行旋轉,其他類似。

旋轉90度效果

**③ **Matrix.translateM() :用來進行圖像的位移,第一個參數是需要變換的矩陣;第二個參數是偏移量;第三、四、五個參數分別對應x,y,z 方向的位移量。其以圖像自身x,y,z方向為單位,也就是說當x方向位移量為0.5時,相當于向右移動0.5個身位,其他類似。

偏移x=0.5 y=0.5效果

**④ **Matrix.scaleM():用來進行圖像的縮放,第一個參數是需要變換的矩陣;第三、四、五個參數分別對應x,y,z 方向的縮放比例,當x方向縮放為0.5時,相當于向x方向縮放為原來的0.5倍,其他類似。

縮放x=2 y=0.5 效果

前面說過這些變換是可以進行組合運算的,其組合代碼如下:


Triangle.kt

init{

    ...

    Matrix.setIdentityM(mTransMatrix, 0)

    Matrix.scaleM(mTransMatrix,0,2f,0.5f,1f)

    Matrix.rotateM(mTransMatrix, 0, mAngle, 0f, 0f, 1.0f)

    Matrix.translateM(mTransMatrix, 0, 0.5f, 0.5f, 0f)

    ...

}

之間用上面的操作相當于生成了一個組合矩陣,其效果如下圖:

組合矩陣效果

光生成了變換矩陣還不能完成上述操作,還需要將變換矩陣數據傳入到頂點著色器上:


// Triangle.kt

private val vertexShaderCode =

                    ...

                    "uniform mat4 transform;" +

                    "void main() {" +

                    " gl_Position = transform * vec4(aPos, 1.0);" +

                    ...

                    "}"

fun draw() {

...

        val transformLoc = GLES30.glGetUniformLocation(mProgram, "transform")

        GLES30.glUniformMatrix4fv(transformLoc, 1, false, mTransMatrix, 0)

...

}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,362評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,013評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,346評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,421評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,146評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,534評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,585評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,767評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,318評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,074評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,258評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,828評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,486評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,916評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,156評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,993評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,234評論 2 375

推薦閱讀更多精彩內容

  • 版本記錄 前言 OpenGL 圖形庫項目中一直也沒用過,最近也想學著使用這個圖形庫,感覺還是很有意思,也就自然想著...
    刀客傳奇閱讀 5,232評論 0 3
  • 1 前言 OpenGL渲染3D模型離不開空間幾何的數學理論知識,而本篇文章的目的就是對空間幾何進行簡單的介紹,并對...
    RichardJieChen閱讀 7,069評論 1 11
  • 變換(Transformations) 我們可以嘗試著在每一幀改變物體的頂點并且重設緩沖區從而使他們移動,但這太繁...
    IceMJ閱讀 4,162評論 0 1
  • “靜”應該是冬季獨有的特點!站在空曠的山間,遠遠的眺望著山的那端,朦朧中連綿起伏,一切都靜的出奇。山靜靜地...
    偷閑躲靜閱讀 498評論 0 2
  • 清邁這幾日 總在暴曬的大熱大悶後 再下場無所顧及的雨 所以白天我基本窩在旅館擼擼貓 寫寫資料 出門 是需要勇氣加持...
    嗚嚶閱讀 268評論 0 0