iOS开发学习OpenGL ES系列 -- 变换矩阵

从数学上说,变换就是在两个坐标系之间转换顶点坐标。每个坐标系都是相对于其他的参照坐标系定义的。对于OpenGL ES来说,最终的参照坐标系是在一个像素颜色渲染缓存中的像素位置的2D数组。

OpenGL ES中的四种基本变换:平移(translation)、旋转(rotation)。基本变换的联合决定了在一个新的坐标系中的每一个顶点位置是怎么转换为参考坐标系中的一个位置的。四个基本变换足以产生无穷的坐标系。

每个基本变换对应于矩阵的一个简单变化。定义一个与参考坐标系相同的坐标系的矩阵叫做单位矩阵。任意两个矩阵可以在一个联级(又叫矩阵乘法)操作中结合起来以产生一个新矩阵,这个新矩阵包含了两个矩阵的所有变换。实际上,每个基本变换会产生一个简单的矩阵,然后把这个简单的矩阵与当前变换矩阵连接起来以产生一个新的当前矩阵。

定义全局

平移
通过相对于参考坐标系的原点移动新坐标系的原点、平移定义了一个新的坐标系。平移不会影响坐标轴的单位长度,平移不会改变坐标轴相对于参考坐标系的方向。
GLKit提供了GLKMatrix4MakeTranslation(float x,float y,float z)函数,这个函数通过平移一个单位矩阵来返回一个定义了坐标系的新矩阵。x、y、z参数指定了新坐标原点沿着当前参考坐标系的每个轴移动的单位数。

修改代码,看一下GLKit提供的GLKMatrix4MakeTranslation函数实现平移效果。

在顶点着色器中新增了一个uniform mat4 transform。 mat4这个类型前文有提到过,4X4的矩阵。它是Shader内置的类型,支持直接加减乘等操作。使用矩阵会产生更少的运算指令,GPU可以更好的优化运算过程。

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

因为我们的数据更新在update里,而赋值绘制在glkView:(GLKView *)view drawInRect:(CGRect)rect中,所以需要定义为全局:

GLKMatrix4 transformMatrix;

// 初始化为单位矩阵,不对图形产生任何变换
transformMatrix = GLKMatrix4Identity;

这里的GLKMatrix4Identity相当于下面的矩阵:

接下来在应用中创建一个平移变换矩阵:

// 定义一个在值域-1和1之间的局部变量
GLfloat elValue = sinf(changeValue);

// 创建平移变换矩阵,这里只改变了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)经过(-1,0,0)的平移之后为(-1,0.5,0),我们看一下矩阵计算:

如果平移经过(1,0,0)的平移之后就为:(1 0.5 0 1),这就是我们上面看到的三角形的上顶点在屏幕之间移动了。

通过上面的矩阵计算可以发现,平移矩阵就是在一个4x4的变换矩阵中填充第4行前面三个位置:

缩放
缩放是通过相对于参考坐标系的坐标轴的单位长度改变新坐标系的坐标轴的单位长度来定义一个新坐标系。缩放坐标系与参考坐标系使用同一个原点。坐标轴的方向通常不会改变。不过,通过一个负值所做的缩放就会翻转坐标轴的方向。

GLKit提供了GLKMatrix4MakeScale(float x,float y,float z)函数,这个函数会通过扩大或者缩小一个单位矩阵的任意坐标轴的单位长度来返回一个定义了坐标系的矩阵。x、y和z参数指定了用来扩大或者缩小每个轴的单位长度的因素。GLKMatrix4Scale(float x,float y,float z)函数通过按指定的因数缩放作为参数传入矩阵来返回一个定义了坐标系的新矩阵。

GLKMatrix4 scaleMatrix = GLKMatrix4MakeScale(elValue, elValue, 1.0);
// 很明显这个矩阵会改变顶点坐标的x、y值

看一下最终效果:

简单分析:elValue为正弦函数的值,所以在-1和1之间,在elValue为1是,不会对原数据有影响,当为-1时,就会翻转坐标轴的方向。所以最终效果就是上图那样,从原图缩小到0,然后负数增大,就是倒过来了。

假定还是点(0.0,0.5,0.0)的x和y值在缩小0.5之后为(0.0,0.25,0.0),看下矩阵计算:

旋转
旋转是通过相对于参考坐标系坐标轴的方向旋转新坐标系的坐标轴来定义一个新坐标系。旋转的坐标系会与参考坐标系使用同一个原点。旋转不会影响坐标轴的单位长度,只有坐标轴的方向会发生变化。

GLKit提供了GLKMatrix4MakeRotation(float angleRedians, float x, float y, float z)函数,这个函数通过旋转一个单位矩阵来返回一个定义了坐标系的新矩阵。angleRedians参数指定了要旋转的弧度数。使用GLKMathDegreesToRadians(float degrees)函数可以把角度转换成弧度。x、y和z参数用于指定当前坐标系的哪一个轴作为旋转的轮毂。

例如:GLKMatrix4MakeRotation(GLKMathDegreesToRadians(60.0) , 1.0, 0.0, 0.0)会沿着一个特定的坐标系的X轴旋转60度来产生一个新的坐标系。

相对于平移和缩放,旋转要复杂一些。旋转包含两个重要元素,旋转的角度,绕什么轴旋转。

GLKMatrix4 rotateMatrix = GLKMatrix4MakeRotation(elValue , 0.0, 0.0, 1.0);
// 绕Z轴旋转

看下效果图:

除了利用GLKit提供的GLKMatrix4MakeRotation函数直接生成,我们还可以自己定义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)
如果这个顺序反过来,就完全不同了。从线性代数角度来讲,就是矩阵A乘以矩阵B不等于矩阵B乘以矩阵A。
*/
transformMatrix = GLKMatrix4Multiply(translateMatrix, rotateMatrix);
transformMatrix = GLKMatrix4Multiply(transformMatrix, scaleMatrix);

看下最终包含了平移、缩放、旋转的效果:

关于OpenGL ES的三种基本变换就说到这里,其实还有两个重要的投影变换 -- 透视投影和正交投影。就不在本篇继续说了,尽量控制一下篇幅,就在下一篇讲吧!

本例代码:LearningOpenGL ES GitHub

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容

  • 1 前言 OpenGL渲染3D模型离不开空间几何的数学理论知识,而本篇文章的目的就是对空间几何进行简单的介绍,并对...
    RichardJieChen阅读 6,991评论 1 11
  • 深度渲染缓存 深度缓存是一个可选的输出缓存,并且与像素颜色渲染缓存相似,几乎所有的OpenGL ES都使用深度缓存...
    McDan阅读 1,618评论 0 0
  • 目录 一、分析拉伸的原因 二、准备知识,三维变换 三、OpenGL 下的三维变换 四、修复拉伸问题 一、分析拉伸的...
    半纸渊阅读 2,205评论 3 0
  • 1. 深度渲染缓存(Depth Render Buffer) 三角形、线段和点是按它们被GPU处理的顺序被渲染的。...
    碧玉小瑕阅读 395评论 0 0
  • >*很不幸,没人能告诉你母体是什么,你只能自己体会* --骇客帝国 在第四章“可视效果”中,我们研究了一些增强图层...
    夜空下最亮的亮点阅读 1,658评论 0 2