矩阵基础知识

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

我们知道在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. MVC中检测到有潜在危险的 Request.Form 值

    在做mvc项目时,当使用xhedit or.ueditor编辑器时,点击提交时,编辑器中的内容会带有html标签提交给服务器,这时就是会报错,出现如下内容: “/”应用程序中的服务器错误. 从客户端( ...

  2. linux音频alsa-uda134x驱动文档阅读之一转自http://blog.csdn.net/wantianpei/article/details/7817293

    前言 目前,linux系统常用的音频驱动有两种形式:alsa oss alsa:现在是linux下音频驱动的主要形式,与简单的oss兼容.oss:过去的形式而我们板子上的uda1341用的就是alsa ...

  3. SQL 根据关联表更新主表中字段数据

    今天遇到一个客户的数据更新问题,两个相关联的表,一个主表用于保存单据主要信息,一个副表用于保存单据的明细信息:现在要把主表的其中一个字段的数据更新到副表的一个字段中保存.精通的SQL语法的,当然是很简 ...

  4. 简易博客[ html + css ] 练习

    1. 前言 通过使用 html + css 编写一个简易的博客作为入门练习 2. 代码及实现 2.1 目录结构 2.2 代码部分 <!DOCTYPE html> <html lang ...

  5. 工具类DateHandler

    package com.ctid.rachel.core.util; import java.math.BigDecimal;import java.util.Calendar;import java ...

  6. django(1)安装及配置

    1.版本选择 Django 1.5.x 支持 Python 2.6.5 Python 2.7, Python 3.2 和 3.3. Django 1.6.x 支持 Python 2.6.X, 2.7. ...

  7. SonarQube的安装、配置与使用(windows)

    onarQube是管理代码质量一个开放平台,可以快速的定位代码中潜在的或者明显的错误,下面将会介绍一下这个工具的安装.配置以及使用. 准备工作: 1.jdk(不再介绍) 2.sonarqube:htt ...

  8. nsis安装包_示例脚本语法解析

    以下是代码及解析,其中有底色的部分为脚本内容. 注释.!define.变量.!include.常量 ; Script generated by the HM NIS Edit Script Wizar ...

  9. Winfom 插件式(Plugins)/模块化开发框架-动态加载DLL窗体-Devexpress

    插件式(AddIn)架构,不是一个新名词,应用程序采用插件式拼合,可以更好的支持扩展.很多著名的软件都采用了插件式的架构,如常见的IDE:Eclipse,Visual Studio,SharpDeve ...

  10. js 参数传递

    最近在读<javascript高级程序设计>时碰到了js传递方式的问题,花费了些时间,不过总算明白了. 数据类型 在 javascript 中数据类型可以分为两类: 基本类型值 primi ...