獲取示例代碼
在介紹本文的代碼之前,先要了解一個(gè)概念:矩陣。學(xué)過(guò)線性代數(shù)的朋友應(yīng)該都知道矩陣相當(dāng)于是一個(gè)二維數(shù)組,有自己的運(yùn)算規(guī)則接奈。下面就通過(guò)幾個(gè)例子簡(jiǎn)單了解一下矩陣的特性歼捐。
3X3矩陣的加法
從圖中可以看出3X3矩陣就像是一個(gè)3X3的表格喂很,每個(gè)單元格中填寫(xiě)一個(gè)數(shù)廷没。它的加法就是把兩個(gè)矩陣對(duì)應(yīng)位置的元素加起來(lái)放在結(jié)果矩陣對(duì)應(yīng)的位置上。那么如果相加的兩個(gè)矩陣尺寸不一樣怎么辦魔市?答案是無(wú)法運(yùn)算主届。矩陣的加減要求兩邊的矩陣必須尺寸相等。
下面是減法的運(yùn)算待德,和加法相似君丁,很好理解。
接下來(lái)是乘法磅网。
乘法稍微有些復(fù)雜谈截,所以我用符號(hào)來(lái)代替數(shù)值,方便觀察規(guī)律涧偷。我們看結(jié)果的第一行第一列aj + bm + cp
簸喂,它就是左邊的矩陣第一行和右邊的矩陣第一列逐個(gè)相乘再相加的結(jié)果。
再看結(jié)果的第二行第一列
aj + bm + cp
燎潮,它就是左邊的矩陣第二行和右邊的矩陣第一列逐個(gè)相乘再相加的結(jié)果喻鳄。
差不多已經(jīng)可以總結(jié)出規(guī)律了,結(jié)果矩陣的第n行第m列的結(jié)果就是左邊的矩陣第n行和右邊的矩陣第m列逐個(gè)相乘再相加的結(jié)果确封。讀者可以看看其他的值是不是這樣計(jì)算出來(lái)的除呵。
最后說(shuō)一下除法,矩陣的除法有些特殊爪喘,比如說(shuō)B/A,可以換算成B*inv(A)颜曾。inv(A)是A的逆矩陣,因?yàn)椴皇撬芯仃嚩加心婢仃嚤#猿ㄔ诰仃囉?jì)算中并不總是可用泛豪。逆矩陣的求解比較復(fù)雜,這里暫時(shí)就不解釋了侦鹏。本文目前也沒(méi)有用到求逆矩陣的地方诡曙。如果讀者感興趣的話,可以自行百度或者翻一翻以前的線性代數(shù)課本略水。
變換矩陣
說(shuō)完了矩陣价卤,那么什么是變換矩陣呢?在圖形繪制過(guò)程中渊涝,有三種變換慎璧,分別是平移,縮放驶赏,旋轉(zhuǎn)炸卑。如果我們想要用代碼表示一個(gè)3D環(huán)境中的變換需要幾個(gè)變量呢,首先要有平移tx, ty, tz
煤傍,然后是縮放sx, sy, sz
盖文,最后是旋轉(zhuǎn)rx, ry, rz
。在渲染的時(shí)候把這些變量附加到原始的位置數(shù)據(jù)上就可以實(shí)現(xiàn)變換了蚯姆。這種方式雖然可行但不夠好五续,尤其是在GPU上這種方式產(chǎn)生的運(yùn)算負(fù)擔(dān)遠(yuǎn)大于使用矩陣洒敏。
attribute vec4 position;
attribute vec4 color;
uniform float elapsedTime;
uniform mat4 transform;
varying vec4 fragColor;
void main(void) {
fragColor = color;
gl_Position = transform * position;
}
這是本文代碼例子中的Vertex Shader,新增了一個(gè)uniform
uniform mat4 transform;
疙驾,mat4
這個(gè)類(lèi)型前文有提到過(guò)凶伙,4X4的矩陣。它是Shader內(nèi)置的類(lèi)型它碎,支持直接加減乘等操作函荣。使用矩陣會(huì)產(chǎn)生更少的運(yùn)算指令,GPU可以更好的優(yōu)化運(yùn)算過(guò)程扳肛。那么應(yīng)該怎么使用呢傻挂?接下來(lái)我就一一介紹每一種變換矩陣。
平移矩陣
假設(shè)有一個(gè)點(diǎn)(1, 2, 3)
,經(jīng)過(guò)大小為(1, 2, 3)
的平移挖息,最終必定會(huì)平移到(1+1, 2+2, 3+3)
的位置金拒。使用矩陣計(jì)算如下。
這里補(bǔ)充一點(diǎn)套腹,如果左邊的矩陣的列數(shù)等于右邊的矩陣的行數(shù)绪抛,它們就可以相乘,結(jié)果矩陣的行數(shù)等于左邊矩陣的行數(shù)电禀,列數(shù)等于右邊矩陣的列數(shù)幢码。
平移矩陣就是一個(gè)4X4的單位矩陣的第4行的前三個(gè)元素用tx,ty尖飞,tz填充之后的矩陣蛤育。下面就是一個(gè)單位矩陣。
使用
GLKMatrix4 translateMatrix = GLKMatrix4MakeTranslation(tx, ty, tz);
可以得到一個(gè)平移矩陣葫松。GLKMatrix4MakeTranslation
位于GLKit
中。
縮放矩陣
縮放矩陣的三個(gè)縮放元素sx底洗,sy腋么,sz,分布在從左到右的對(duì)角線上亥揖,矩陣相乘后位置的x珊擂,y,z分別乘以了sx费变,sy摧扇,sz,從而實(shí)現(xiàn)了縮放挚歧。
代碼實(shí)現(xiàn)如下扛稽。
GLKMatrix4 scaleMatrix = GLKMatrix4MakeScale(sx, sy, sz);
旋轉(zhuǎn)矩陣
旋轉(zhuǎn)矩陣相比于上面兩個(gè)略微有些復(fù)雜,旋轉(zhuǎn)包含兩個(gè)重要元素滑负,旋轉(zhuǎn)的角度在张,繞什么軸旋轉(zhuǎn)用含。具體原理可以參考三維空間中的旋轉(zhuǎn):旋轉(zhuǎn)矩陣、歐拉角帮匾。代碼實(shí)現(xiàn)如下啄骇。
GLKMatrix4 rotateMatrix = GLKMatrix4MakeRotation(M_PI/2 , 0.0, 0.0, 1.0);
M_PI/2是弧度,0.0瘟斜,0.0缸夹,1.0是旋轉(zhuǎn)軸的向量。
綜合三個(gè)矩陣
現(xiàn)在我們得到了三個(gè)矩陣螺句,接下來(lái)就是把它們相乘虽惭。
self.transformMatrix = GLKMatrix4Multiply(translateMatrix, rotateMatrix);
self.transformMatrix = GLKMatrix4Multiply(self.transformMatrix, scaleMatrix);
注意相乘的順序translateMatrix * rotateMatrix * scaleMatrix
,這樣可以保證先縮放再旋轉(zhuǎn)壹蔓,最后再平移趟妥。如果先平移再縮放,點(diǎn)的位置已經(jīng)改變佣蓉,縮放出來(lái)的結(jié)果自然就不對(duì)了披摄。
代碼實(shí)現(xiàn)
最后回到本文的代碼實(shí)現(xiàn)中來(lái),我把chapter4的代碼整理了一下勇凭,公用的東西移到了基類(lèi)GLBaseViewController
里疚膊,這樣可以更加專(zhuān)注于要重點(diǎn)介紹的知識(shí)點(diǎn)。目前ViewController.m
中代碼如下虾标。
//
// ViewController.m
// OpenGLESDemo
//
// Created by wangyang on 15/8/28.
// Copyright (c) 2015年 wangyang. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@property (assign, nonatomic) GLKMatrix4 transformMatrix;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.transformMatrix = GLKMatrix4Identity;
}
#pragma mark - Update Delegate
- (void)update {
[super update];
float varyingFactor = sin(self.elapsedTime);
GLKMatrix4 scaleMatrix = GLKMatrix4MakeScale(varyingFactor, varyingFactor, 1.0);
GLKMatrix4 rotateMatrix = GLKMatrix4MakeRotation(varyingFactor , 0.0, 0.0, 1.0);
GLKMatrix4 translateMatrix = GLKMatrix4MakeTranslation(varyingFactor, 0.0, 0.0);
// transformMatrix = translateMatrix * rotateMatrix * scaleMatrix
// 矩陣會(huì)按照從右到左的順序應(yīng)用到position上寓盗。也就是先縮放(scale),再旋轉(zhuǎn)(rotate),最后平移(translate)
// 如果這個(gè)順序反過(guò)來(lái),就完全不同了璧函。從線性代數(shù)角度來(lái)講傀蚌,就是矩陣A乘以矩陣B不等于矩陣B乘以矩陣A。
self.transformMatrix = GLKMatrix4Multiply(translateMatrix, rotateMatrix);
self.transformMatrix = GLKMatrix4Multiply(self.transformMatrix, scaleMatrix);
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
[super glkView:view drawInRect:rect];
GLuint transformUniformLocation = glGetUniformLocation(self.shaderProgram, "transform");
glUniformMatrix4fv(transformUniformLocation, 1, 0, self.transformMatrix.m);
[self drawTriangle];
}
#pragma mark - Draw Many Things
- (void)drawTriangle {
static GLfloat triangleData[36] = {
0, 0.5f, 0, 1, 0, 0, // x, y, z, r, g, b,每一行存儲(chǔ)一個(gè)點(diǎn)的信息蘸吓,位置和顏色
-0.5f, 0.0f, 0, 0, 1, 0,
0.5f, 0.0f, 0, 0, 0, 1,
0, -0.5f, 0, 1, 0, 0,
-0.5f, 0.0f, 0, 0, 1, 0,
0.5f, 0.0f, 0, 0, 0, 1,
};
[self bindAttribs:triangleData];
glDrawArrays(GL_TRIANGLES, 0, 6);
}
@end
Vertex Shader
attribute vec4 position;
attribute vec4 color;
uniform float elapsedTime;
uniform mat4 transform;
varying vec4 fragColor;
void main(void) {
fragColor = color;
gl_Position = transform * position;
}
代碼中每一次update計(jì)算新的變換矩陣善炫,渲染時(shí)把值傳遞給Vertex Shader的uniform mat4 transform
,Vertex Shader把原始位置和transform相乘库继,得出新的位置箩艺。注意,因?yàn)閠ransform是mat4宪萄,所以給uniform賦值時(shí)使用的是glUniformMatrix4fv
艺谆。代碼效果如下。
本篇主要介紹了什么是變換矩陣拜英,如何使用變換矩陣以及怎樣和Vertex Shader配合静汤。下一篇就要開(kāi)始介紹3D渲染最基本的技術(shù),透視投影矩陣。