OpenGL 模型视图投影矩阵 仿射矩阵
矩阵基础知识
要对矩阵进行运算,必须先要了解矩阵的计算公式,这个知识的内容涉及到了线性代数。
我们知道在Cocos2dx中,有关于平移,旋转,缩放等等操作,都必须要进行矩阵的乘法。
只需要一张图就能理解怎么计算矩阵的乘法了。

Cocos2dx中的代码实现
先来看cocos2dx中的代码实现:
.h文件的代码
#include <stdio.h>
#include "CCMath.h"
#include "CCVector2.h"
#include "CCVector3.h"
#include "CCVector4.h"
#include "CCPoint.h"
#include "CCMatrix.h"
//仿射矩阵
//单位矩阵
/**@{
Affine transform
a b 0
c d 0
tx ty 1
Identity
1 0 0
0 1 0
0 0 1
*/
struct AffineTransform {
float a, b, c, d;
float tx, ty;
static const AffineTransform IDENTITY;
};
#endif /* CCAffineTransform_hpp */
// 创建仿射矩阵
AffineTransform AffineTransformMake(float a, float b, float c, float d, float tx, float ty);
// 节点乘以仿射矩阵
Vector2 PointApplyAffineTransform(const Vector2& point, const AffineTransform& t);
// size乘以仿射矩阵
CCSize SizeApplyAffineTransform(const CCSize& size, const AffineTransform& t);
// rect乘以仿射矩阵
CCRect RectApplyAffineTransform(const CCRect& rect, const AffineTransform& anAffineTransform);
// rect乘以矩阵
CCRect RectApplyTransform(const CCRect& rect, const Matrix& transform);
// 节点乘以矩阵
Vector2 PointApplyTransform(const Vector2& point, const Matrix& transform);
/**
Translation, equals
1 0 1
0 1 0 * affine transform
tx ty 1
*/
// 平移
AffineTransform AffineTransformTranslate(const AffineTransform& t, float tx, float ty);
/**
Rotation, equals
cos(angle) sin(angle) 0
-sin(angle) cos(angle) 0 * AffineTransform
0 0 1
*/
// 旋转
AffineTransform AffineTransformRotate(const AffineTransform& aTransform, float anAngle);
/**
Scale, equals
sx 0 0
0 sy 0 * affineTransform
0 0 1
*/
// 缩放
AffineTransform AffineTransformScale(const AffineTransform& t, float sx, float sy);
/**Concat two affine transform, t1 * t2*/
AffineTransform AffineTransformConcat(const AffineTransform& t1, const AffineTransform& t2);
/**Compare affine transform.*/
// 仿射矩阵是否和矩阵相等
bool AffineTransformEqualToTransform(const AffineTransform& t1, const AffineTransform& t2);
/**Get the inverse of affine transform.*/
// 获取仿射矩阵的逆矩阵
AffineTransform AffineTransformInvert(const AffineTransform& t);
/**Concat Mat4, return t1 * t2.*/
// 矩阵相乘
Matrix TransformConcat(const Matrix& t1, const Matrix& t2);
// 单位矩阵
extern const AffineTransform AffineTransformIdentity;
.cpp的代码:
#include "CCAffineTransform.h"
#include <algorithm>
#include <math.h>
using namespace std;
// 构造函数
AffineTransform AffineTransformMake(float a, float b, float c, float d, float tx, float ty)
{
AffineTransform t;
t.a = a; t.b = b; t.c = c; t.d = d; t.tx = tx; t.ty = ty;
return t;
}
Vector2 PointApplyAffineTransform(const Vector2& point, const AffineTransform& t)
{
Vector2 p;
p.x = (float)((double)t.a * point.x + (double)t.c * point.y + t.tx);
p.y = (float)((double)t.b * point.x + (double)t.d * point.y + t.ty);
return p;
}
Vector2 PointApplyTransform(const Vector2& point, const Matrix& transform)
{
Vector3 vec(point.x, point.y, 0);
transform.transformPoint(&vec);
return Vector2(vec.x, vec.y);
}
CCSize SizeApplyAffineTransform(const CCSize& size, const AffineTransform& t)
{
CCSize s;
s.width = (float)((double)t.a * size.width + (double)t.c * size.height);
s.height = (float)((double)t.b * size.width + (double)t.d * size.height);
return s;
}
// 单位矩阵
AffineTransform AffineTransformMakeIdentity()
{
return AffineTransformMake(1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
}
extern const AffineTransform AffineTransformIdentity = AffineTransformMakeIdentity();
const AffineTransform AffineTransform::IDENTITY = AffineTransformMakeIdentity();
CCRect RectApplyAffineTransform(const CCRect& rect, const AffineTransform& anAffineTransform)
{
float top = rect.getMinY();
float left = rect.getMinX();
float right = rect.getMaxX();
float bottom = rect.getMaxY();
Vector2 topLeft = PointApplyAffineTransform(Vector2(left, top), anAffineTransform);
Vector2 topRight = PointApplyAffineTransform(Vector2(right, top), anAffineTransform);
Vector2 bottomLeft = PointApplyAffineTransform(Vector2(left, bottom), anAffineTransform);
Vector2 bottomRight = PointApplyAffineTransform(Vector2(right, bottom), anAffineTransform);
float minX = min(min(topLeft.x, topRight.x), min(bottomLeft.x, bottomRight.x));
float maxX = max(max(topLeft.x, topRight.x), max(bottomLeft.x, bottomRight.x));
float minY = min(min(topLeft.y, topRight.y), min(bottomLeft.y, bottomRight.y));
float maxY = max(max(topLeft.y, topRight.y), max(bottomLeft.y, bottomRight.y));
return CCRect(minX, minY, (maxX - minX), (maxY - minY));
}
CCRect RectApplyTransform(const CCRect& rect, const Matrix& transform)
{
float top = rect.getMinY();
float left = rect.getMinX();
float right = rect.getMaxX();
float bottom = rect.getMaxY();
Vector3 topLeft(left, top, 0);
Vector3 topRight(right, top, 0);
Vector3 bottomLeft(left, bottom, 0);
Vector3 bottomRight(right, bottom, 0);
transform.transformPoint(&topLeft);
transform.transformPoint(&topRight);
transform.transformPoint(&bottomLeft);
transform.transformPoint(&bottomRight);
float minX = min(min(topLeft.x, topRight.x), min(bottomLeft.x, bottomRight.x));
float maxX = max(max(topLeft.x, topRight.x), max(bottomLeft.x, bottomRight.x));
float minY = min(min(topLeft.y, topRight.y), min(bottomLeft.y, bottomRight.y));
float maxY = max(max(topLeft.y, topRight.y), max(bottomLeft.y, bottomRight.y));
return CCRect(minX, minY, (maxX - minX), (maxY - minY));
}
AffineTransform AffineTransformTranslate(const AffineTransform& t, float tx, float ty)
{
return AffineTransformMake(t.a, t.b, t.c, t.d, t.tx + t.a * tx + t.c * ty, t.ty + t.b * tx + t.d * ty);
}
AffineTransform AffineTransformScale(const AffineTransform& t, float sx, float sy)
{
return AffineTransformMake(t.a * sx, t.b * sx, t.c * sy, t.d * sy, t.tx, t.ty);
}
AffineTransform AffineTransformRotate(const AffineTransform& t, float anAngle)
{
float sine = sinf(anAngle);
float cosine = cosf(anAngle);
return AffineTransformMake( t.a * cosine + t.c * sine,
t.b * cosine + t.d * sine,
t.c * cosine - t.a * sine,
t.d * cosine - t.b * sine,
t.tx,
t.ty);
}
/* Concatenate `t2' to `t1' and return the result:
t' = t1 * t2 */
AffineTransform AffineTransformConcat(const AffineTransform& t1, const AffineTransform& t2)
{
return AffineTransformMake( t1.a * t2.a + t1.b * t2.c, t1.a * t2.b + t1.b * t2.d, //a,b
t1.c * t2.a + t1.d * t2.c, t1.c * t2.b + t1.d * t2.d, //c,d
t1.tx * t2.a + t1.ty * t2.c + t2.tx, //tx
t1.tx * t2.b + t1.ty * t2.d + t2.ty); //ty
}
Matrix TransformConcat(const Matrix& t1, const Matrix& t2)
{
return t1 * t2;
}
/* Return true if `t1' and `t2' are equal, false otherwise. */
bool AffineTransformEqualToTransform(const AffineTransform& t1, const AffineTransform& t2)
{
return (t1.a == t2.a && t1.b == t2.b && t1.c == t2.c && t1.d == t2.d && t1.tx == t2.tx && t1.ty == t2.ty);
}
AffineTransform AffineTransformInvert(const AffineTransform& t)
{
float determinant = 1 / (t.a * t.d - t.b * t.c);
return AffineTransformMake(determinant * t.d, -determinant * t.b, -determinant * t.c, determinant * t.a,
determinant * (t.c * t.ty - t.d * t.tx), determinant * (t.b * t.tx - t.a * t.ty) );
}
讲解
看讲解:
1. CCAffineTransform
首先要知道定义了这么一个CCAffineTransform专门用来表示变换矩阵。
struct CCAffineTransform {
float a, b, c, d;
float tx, ty;
};
在代码中AffineTransformMake用来构造这个变换矩阵。
用图表示则是:

2. 单位矩阵
很明显,就是这个函数AffineTransformMakeIdentity。
3. 平移
AffineTransformTranslate,用公式来看就是:

4. 缩放
AffineTransformScale,用公式来看就是:

5. 旋转
AffineTransformRotate,用公式来看就是:
先要根据旋转角度去求出对应的值,再将矩阵右乘以变换矩阵

6. 连接
TransformConcat,直接相乘,用公式来看就是:

6. 逆
AffineTransformInvert,求矩阵的逆矩阵,通过公示可以计算得出:

这些是实现cocos2dx矩阵变换的基础,掌握这个很有必要。这里
参考
http://www.cnblogs.com/logicbaby/p/4282003.html
http://www.360doc.com/content/13/1223/22/110467_339624338.shtml
http://blog.csdn.net/runaying/article/details/13094553
其它
和矩阵的乘积,得到的结果就是经过变换后的矩阵。注意,顺序应该是缩放,渲染,平移,只要更改顺序得到的结果就会很不一样。

这就是几个坐标系在整个渲染过程的变换,经历三次矩阵变换,这就是著名的MVP矩阵的由来
本地坐标空间(local space):在建模软件中的坐标空间,物体本身的坐标空间。
世界坐标空间(world space):将所有模型摆放到一个世界空间中,这个模型在空间中的坐标。
观察坐标空间(view space):即摄像机范围空间
裁剪坐标空间(clip space):将不在摄像机范围内的模型裁剪出去后的空间
屏幕空间坐标(screen space):最终显示在屏幕上的坐标空间。
坐标系
GL坐标系:Cocos2D以OpenglES为图形库,所以它使用OpenglES坐标系。GL坐标系原点在屏幕左下角,x轴向右,y轴向上。
屏幕坐标系:苹果的Quarze2D使用的是不同的坐标系统,原点在屏幕左上角,x轴向右,y轴向下。ios的屏幕触摸事件CCTouch传入的位置信息使用的是该坐标系。因此在cocos2d中对触摸事件做出响应前需要首先把触摸点转化到GL坐标系。可以使用CCDirector的convertToGL来完成这一转化。
世界坐标系:世界坐标系也叫做绝对坐标系,是游戏开发中的概念,它建立了描述其他坐标系所需要的参考框架。我们能够用世界坐标系来描述其他坐标系的位置,而不能用更大的,外部的坐标系来描述世界坐标系。cocos2d中的元素是有父子关系的层级结构,我们通过CCNode的position设定元素的位置使用的是相对与其父节点的本地坐标系而非世界坐标系。最后在绘制屏幕的时候cocos2d会把这些元素的本地坐标映射成世界坐标系坐标。世界坐标系和GL坐标系一致,原点在屏幕左下角,x轴向右,y轴向上。
本地坐标系:本地坐标系也叫做物体坐标系,是和特定物体相关联的坐标系。每个物体都有它们独立的坐标系,当物体移动或改变方向时,和该物体关联的坐标系将随之移动或改变方向。例如坐出租车的时候对驾驶员说“向左转”,我们使用的是车的物体坐标系,“前”、“后”、“左”、“右”只有在物体坐标系中才有意义。但如果我们说“向东开”,我们使用的就是世界坐标系了,无论是车内还是车外的人都知道应该向什么方向开。CCNode的position使用的就是父节点的本地坐标系,它和GL坐标系也是一致的,x轴向右,y轴向上,原点在父节点的左下角。如果父节点是场景树中的顶层节点,那么它使用的本地坐标系就和世界坐标系重合了。在CCNode对象中有几个方便的函数可以做坐标转换:convertToWorldSpace方法可以把基于当前节点的本地坐标系下的坐标转换到世界坐标系中。convertToNodeSpace方法可以把世界坐标转换到当前节点的本地坐标系中。注意这些方法转换的是基于当前节点的坐标,而一个节点的position所使用的坐标是基于它父节点的本地坐标,因此我们要把node的位置转换到世界坐标系中应该调用父节点的convertToWorldSpace函数 [node.parent convertToWorldSpace:[node position]]。几乎所有的游戏引擎都会使用本地坐标系而非世界坐标系来指定元素的位置,这样做的好处是当计算物体运动的时候使用同一本地坐标系的元素可以作为一个子系统独立计算,最后再加上坐标系的运动即可,这是物理研究中常用的思路。例如一个在行驶的车厢内上下跳动的人,我们只需要在每帧绘制的时候计算他在车厢坐标系中的位置,然后加上车的位置就可以计算出人在世界坐标系中的位置,如果使用单一的世界坐标系,人的运动轨迹就变复杂了。
OpenGL 模型视图投影矩阵 仿射矩阵的更多相关文章
- WEBGL学习【八】模型视图投影矩阵
<!--探讨WEBGL中不同图形的绘制方法:[待测试2017.11.6]--> <!DOCTYPE HTML> <html lang="en"> ...
- OpenGL模型视图变换、投影变换、视口变换的理解
OpenGL中不设置模型,投影,视口,所绘制的几何图形的坐标只能是-1到1(X轴向右,Y轴向上,Z轴垂直屏幕向外). 产生目标场景的过程类似于用照相机进行拍照: (1)把照相机固定在三角架上,并让他对 ...
- 简单理解OpenGL模型视图变换
前几天学习了OpenGL的绘图原理(其实就是坐标的不停变换变换),看到网上有个比较好的例程,于是学习了下,并在自己感兴趣的部分做了注释. 首先通过glMatrixMode(GL_MODELVIEW)设 ...
- WEBGL学习【四】模型视图矩阵
<html lang="zh-CN"> <!--服务器运行地址:http://127.0.0.1:8080/webgl/LearnNeHeWebGL/NeHeWe ...
- WebGL或OpenGL关于模型视图投影变换的设置技巧
目录 1. 具体实例 2. 解决方案 1) Cube.html 2) Cube.js 3) 运行结果 3. 详细讲解 1) 模型变换 2) 视图变换 3) 投影变换 4) 模型视图投影矩阵 4. 存在 ...
- three.js中的矩阵变换(模型视图投影变换)
目录 1. 概述 2. 基本变换 2.1. 矩阵运算 2.2. 模型变换矩阵 2.2.1. 平移矩阵 2.2.2. 旋转矩阵 2.2.2.1. 绕X轴旋转矩阵 2.2.2.2. 绕Y轴旋转矩阵 2.2 ...
- OpenGL(五) 三维变换之模型视图矩阵
计算机三维图形学中,一个基本的任务是如何描述三维空间中一个物体位置的变化,也就是如何 描述物体的运动.通常情况下,物体位置的变化包含三个基本的变化:平移.旋转和缩放,物体的运动也可以用这三个基本的运动 ...
- OpenGL中glRotatef()函数究竟对矩阵做了什么
OpenGL中glRotatef()函数究竟对矩阵做了什么 我们知道OpenGL中维持着两套矩阵,一个是模型视图矩阵(model view matrix),另一个是投影矩阵(projection ma ...
- Android OpenGL ES 开发(五): OpenGL ES 使用投影和相机视图
OpenGL ES环境允许你以更接近于你眼睛看到的物理对象的方式来显示你绘制的对象.物理查看的模拟是通过对你所绘制的对象的坐标进行数学变换完成的: Projection - 这个变换是基于他们所显示的 ...
随机推荐
- 【codeforces】【比赛题解】#937 CF Round #467 (Div. 2)
没有参加,但是之后几天打了哦,第三场AK的CF比赛. CF大扫荡计划正在稳步进行. [A]Olympiad 题意: 给\(n\)个人颁奖,要满足: 至少有一个人拿奖. 如果得分为\(x\)的有奖,那么 ...
- wiki confluence安装
注意:安装前请先确认内存 至少2G 1.上传 atlassian-confluence-5.9.3-x64.bin 文件,修改权限 chmod 777 atlassian-confluence-5.9 ...
- Fiddler 4 抓包(APP HTTPS )
一.手机连接Fiddler 1.配置fiddler 1.安装fiddler,基本下一步下一步即可: 2.打开fiddler,点击顶部栏Tools——>Options 3.在HTTPS页签勾选“D ...
- python基础学习之路No.3 控制流if,while,for
在学习编程语言的过程中,有一个很重要的东西,它就是判断,也可以称为控制流. 一般有if.while.for三种 ⭐if语句 if语句可以有一个通俗的解释,如果.假如 如果条件1满足,则…… 如果条件2 ...
- AdvStringGrid 复选框、goRowSelect
var I: Integer; begin do begin AdvStringGrid1.AddCheckBox(, I, True, True); AdvStringGrid1.Cells[,I] ...
- java 局部内部类
可以在代码块里创建内部类,典型的方法是在一个方法体的里面创建,局部内部类不能有访问说明符,因为它不是外围类的一部分,但是可以访问当前代码块的常量,以及此外围类的所有成员,下面分别对局部内部类和匿名内部 ...
- Linux学习笔记:mv移动或文件重命名
mv命令是move的缩写,可以用来移动文件或者将文件改名(move (rename) files),是Linux系统下常用的命令,经常用来备份文件或者目录. 语法:mv 源文件 目标文件 视mv命令中 ...
- ASP.NET MVC之验证终结者篇
有时候我觉得,很多人将一个具体的技术细节写的那么复杂,我觉得没有必要,搞得很多人一头雾水的,你能教会别人用就成了,具体的细节可以去查MSDN什么的,套用爱因斯坦的名言:能在网上查到的就不要去记,用的时 ...
- NPOI 读取单元格的格式
最近做项目需要导入一部分数据, 导入的数据的中, 有部分的百分比数据使用的是excel 的百分比, 有部分的数据使用的是字符串形式的格式,(数据来源于不同的人统计), 格式略微有点乱, 要求导入系统的 ...
- 使用linux mysql客户端建立表时遇到格式解析的问题
发现在notepad++写好的建表脚本,粘贴到linux客户端后,执行时总是报我的脚本有问题. 我看了又看,发现建表脚本本身是没有问题,问题出在"Tab"键上和注释上边了. 解决办 ...