矩阵基础知识

要对矩阵进行运算,必须先要了解矩阵的计算公式,这个知识的内容涉及到了线性代数。

我们知道在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矩阵的由来

  1. 本地坐标空间(local space):在建模软件中的坐标空间,物体本身的坐标空间。

  2. 世界坐标空间(world space):将所有模型摆放到一个世界空间中,这个模型在空间中的坐标。

  3. 观察坐标空间(view space):即摄像机范围空间

  4. 裁剪坐标空间(clip space):将不在摄像机范围内的模型裁剪出去后的空间

  5. 屏幕空间坐标(screen space):最终显示在屏幕上的坐标空间。

坐标系

  1. GL坐标系:Cocos2D以OpenglES为图形库,所以它使用OpenglES坐标系。GL坐标系原点在屏幕左下角,x轴向右,y轴向上。

  2. 屏幕坐标系:苹果的Quarze2D使用的是不同的坐标系统,原点在屏幕左上角,x轴向右,y轴向下。ios的屏幕触摸事件CCTouch传入的位置信息使用的是该坐标系。因此在cocos2d中对触摸事件做出响应前需要首先把触摸点转化到GL坐标系。可以使用CCDirector的convertToGL来完成这一转化。

  3. 世界坐标系:世界坐标系也叫做绝对坐标系,是游戏开发中的概念,它建立了描述其他坐标系所需要的参考框架。我们能够用世界坐标系来描述其他坐标系的位置,而不能用更大的,外部的坐标系来描述世界坐标系。cocos2d中的元素是有父子关系的层级结构,我们通过CCNode的position设定元素的位置使用的是相对与其父节点的本地坐标系而非世界坐标系。最后在绘制屏幕的时候cocos2d会把这些元素的本地坐标映射成世界坐标系坐标。世界坐标系和GL坐标系一致,原点在屏幕左下角,x轴向右,y轴向上。

  4. 本地坐标系:本地坐标系也叫做物体坐标系,是和特定物体相关联的坐标系。每个物体都有它们独立的坐标系,当物体移动或改变方向时,和该物体关联的坐标系将随之移动或改变方向。例如坐出租车的时候对驾驶员说“向左转”,我们使用的是车的物体坐标系,“前”、“后”、“左”、“右”只有在物体坐标系中才有意义。但如果我们说“向东开”,我们使用的就是世界坐标系了,无论是车内还是车外的人都知道应该向什么方向开。CCNode的position使用的就是父节点的本地坐标系,它和GL坐标系也是一致的,x轴向右,y轴向上,原点在父节点的左下角。如果父节点是场景树中的顶层节点,那么它使用的本地坐标系就和世界坐标系重合了。在CCNode对象中有几个方便的函数可以做坐标转换:convertToWorldSpace方法可以把基于当前节点的本地坐标系下的坐标转换到世界坐标系中。convertToNodeSpace方法可以把世界坐标转换到当前节点的本地坐标系中。注意这些方法转换的是基于当前节点的坐标,而一个节点的position所使用的坐标是基于它父节点的本地坐标,因此我们要把node的位置转换到世界坐标系中应该调用父节点的convertToWorldSpace函数 [node.parent convertToWorldSpace:[node position]]。几乎所有的游戏引擎都会使用本地坐标系而非世界坐标系来指定元素的位置,这样做的好处是当计算物体运动的时候使用同一本地坐标系的元素可以作为一个子系统独立计算,最后再加上坐标系的运动即可,这是物理研究中常用的思路。例如一个在行驶的车厢内上下跳动的人,我们只需要在每帧绘制的时候计算他在车厢坐标系中的位置,然后加上车的位置就可以计算出人在世界坐标系中的位置,如果使用单一的世界坐标系,人的运动轨迹就变复杂了。

OpenGL 模型视图投影矩阵 仿射矩阵的更多相关文章

  1. WEBGL学习【八】模型视图投影矩阵

    <!--探讨WEBGL中不同图形的绘制方法:[待测试2017.11.6]--> <!DOCTYPE HTML> <html lang="en"> ...

  2. OpenGL模型视图变换、投影变换、视口变换的理解

    OpenGL中不设置模型,投影,视口,所绘制的几何图形的坐标只能是-1到1(X轴向右,Y轴向上,Z轴垂直屏幕向外). 产生目标场景的过程类似于用照相机进行拍照: (1)把照相机固定在三角架上,并让他对 ...

  3. 简单理解OpenGL模型视图变换

    前几天学习了OpenGL的绘图原理(其实就是坐标的不停变换变换),看到网上有个比较好的例程,于是学习了下,并在自己感兴趣的部分做了注释. 首先通过glMatrixMode(GL_MODELVIEW)设 ...

  4. WEBGL学习【四】模型视图矩阵

    <html lang="zh-CN"> <!--服务器运行地址:http://127.0.0.1:8080/webgl/LearnNeHeWebGL/NeHeWe ...

  5. WebGL或OpenGL关于模型视图投影变换的设置技巧

    目录 1. 具体实例 2. 解决方案 1) Cube.html 2) Cube.js 3) 运行结果 3. 详细讲解 1) 模型变换 2) 视图变换 3) 投影变换 4) 模型视图投影矩阵 4. 存在 ...

  6. 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 ...

  7. OpenGL(五) 三维变换之模型视图矩阵

    计算机三维图形学中,一个基本的任务是如何描述三维空间中一个物体位置的变化,也就是如何 描述物体的运动.通常情况下,物体位置的变化包含三个基本的变化:平移.旋转和缩放,物体的运动也可以用这三个基本的运动 ...

  8. OpenGL中glRotatef()函数究竟对矩阵做了什么

    OpenGL中glRotatef()函数究竟对矩阵做了什么 我们知道OpenGL中维持着两套矩阵,一个是模型视图矩阵(model view matrix),另一个是投影矩阵(projection ma ...

  9. Android OpenGL ES 开发(五): OpenGL ES 使用投影和相机视图

    OpenGL ES环境允许你以更接近于你眼睛看到的物理对象的方式来显示你绘制的对象.物理查看的模拟是通过对你所绘制的对象的坐标进行数学变换完成的: Projection - 这个变换是基于他们所显示的 ...

随机推荐

  1. 使用纯注解与配置类开发springMVC项目,去掉xml配置

    最近拜读了杨开振老师的书,深入浅出springBoot2.x,挖掘了很多以前被忽略的知识, 开发一年多,工作中一直用传统springmvc的开发,基本都还是用的传统的xml配置开发, 看到书里有提到, ...

  2. 遍历 USB devcie,读取设备描述符 device descriptor【转】

    转自:http://blog.csdn.net/flyyyri/article/details/5480347 理论:    对于USB接口的设备,现在越来越多了.本篇我们就通过获取一个USB扫描仪设 ...

  3. linux系统iostat命令详解

    iostat  -k 3 5  (以KB为单位,每3秒统计一次,共统计5次) • avg-cpu: 总体cpu使用情况统计信息,对于多核cpu,这里为所有cpu的平均值    %user    用户空 ...

  4. linux文件管理 -> 系统文件属性

    -rw-------. 1 root root 4434 May 30 13:58 ks.cfg -rw-------. ①:文件类型与权限 ②:硬链接次数 root ③:所属用户 root ④:所属 ...

  5. Gitlab权限管理

    使用管理员登陆gitlab(版本为8.9)创建一个组 给用户授权 创建新用户 再创建两个dev1和dev2 然后再到项目界面授权给pm授权master 创建库(事先先建一个java组) 设置权限 创建 ...

  6. 【小程序开发总结】微信小程序开发常用技术方法总结

    1.获取input的值 <input bindinput="bindKeyInput" placeholder="输入同步到view中"/>   b ...

  7. python网络编程-socketserver

    一:socketserver简化了网络服务器的编写. 它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer. 这4个类是同步进行处 ...

  8. centos7 安装java和tomcat9

    centos7 安装java 下载好java安装包后,首先是解压,然后配置环境变量. 在usr下新建Java文件夹,把java解压到Java文件夹中 新建文件夹 # mkdir /usr/Java 键 ...

  9. cuowu

    ngFor不能用于Object rowspan colspan不能绑定变量,要用attr.colspan https://stackoverflow.com/questions/35615751/wh ...

  10. Hive与HBase区别 大墨垂杨

    大墨垂杨 http://www.cnblogs.com/quchunhui/p/5340989.html