zoukankan      html  css  js  c++  java
  • 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矩阵的由来

    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]]。几乎所有的游戏引擎都会使用本地坐标系而非世界坐标系来指定元素的位置,这样做的好处是当计算物体运动的时候使用同一本地坐标系的元素可以作为一个子系统独立计算,最后再加上坐标系的运动即可,这是物理研究中常用的思路。例如一个在行驶的车厢内上下跳动的人,我们只需要在每帧绘制的时候计算他在车厢坐标系中的位置,然后加上车的位置就可以计算出人在世界坐标系中的位置,如果使用单一的世界坐标系,人的运动轨迹就变复杂了。

  • 相关阅读:
    Netty之Channel*
    Netty之ByteBuf
    Spark On Yarn的各种Bug
    外网无法访问hdfs文件系统
    报错:Unsupported field: HourOfDay
    启动spark-shell --master yarn的bug
    KMP算法思路
    单例模式的正确实现
    redis 持久化
    记一次MacOs降级过程
  • 原文地址:https://www.cnblogs.com/George1994/p/6364879.html
Copyright © 2011-2022 走看看