IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]详解MVP矩阵之齐次坐标和ModelMatrix

    qp120291570发表于 2016-03-27 21:07:44
    love 0

    齐次坐标(Homogeneous Coordinates)

    其次坐标这个概念在第一次看real-time rendering 这本书的时候就有提起到,但当时看的一头雾水,只知道其次坐标在某些计算中比较方便,而事实上齐次坐标有着非常重要的意义和作用,主要是在处理三维透视方面,常用的几个地方,比如texture mapping 透视矫正,Projection Matrix的计算等等。

    在笛卡尔坐标系中,两条平行线是永不相交的,而在现实中,两条平行线是可能相交于一点的,比如说下面的铁轨。



    图一 . 铁路越来越窄,最后汇集到一点


    在这种透视空间中,笛卡尔坐标就无法描述了。


    解决方案就是齐次坐标,一句话解释,就是用N+1个数来表示N维空间中的点。

    比如一个二维坐标(X,Y),它的齐次坐标就是(x, y, w),  关系如下

    X = x/w 
    Y = y/w 

    所以在知道齐次坐标的情况下,可以很方便地得到其笛卡尔坐标。之所以称之为其次坐标,是因为奇次坐标具有缩放不变性,比如(1,2,3),(2,4,6), (1a,2a,3a) 所表达的笛卡尔坐标是一样的!

    有了其次坐标我们就可以证明两条平行线相交了。

    有两条直线

    Ax + By + C = 0

    Ax + By + D = 0

    很明显他们在平面内是平行线,永不相交(如果C==D的话,两条直线就重合了)

    化为其次坐标,

    A(x/w) + B(y/w) +  C = 0;

    A(x/w) + B(y/w) +  D = 0;

    同乘w,得到

    Ax + By + Cw = 0

    Ax + By + Dw = 0

    则有解 (x,y,0),所以两条平行线相交在(x,y,0),也就是相交在无穷远的点。


    ModelMatrix

    在Unity中,每一个GameObject上面都会有一个Transform,用来记录这个Object的position,rotation和scale.


    图二 . Unity中的Transform Compoent


    这三项都可以用矩阵来表示,位移矩阵是



    旋转矩阵由Quatenion转过来 

    R = (  
            1.0f - 2.0f*y*y - 2.0f*z*z, 2.0f*x*y - 2.0f*z*w, 2.0f*x*z + 2.0f*y*w, 0.0f,  
            2.0f*x*y + 2.0f*z*w, 1.0f - 2.0f*x*x - 2.0f*z*z, 2.0f*y*z - 2.0f*x*w, 0.0f,  
            2.0f*x*z - 2.0f*y*w, 2.0f*y*z + 2.0f*x*w, 1.0f - 2.0f*x*x - 2.0f*y*y, 0.0f,  
            0.0f, 0.0f, 0.0f, 1.0f  
            )  

    缩放矩阵


    具体的矩阵推导可以参考我之前写的Real-Time Rendering (2) - 变换和矩阵(Transforms and Matrics)。


    所谓的ModelMatrix,就是将模型坐标变换到WorldMatrix的Matrix,


    WorldMatrix = Mt * Mr * Ms

    Vw = WorldMatrix * Vm


    注意这里的Matrix都是列主序,  Vm是Model坐标系下的坐标,Vw是世界坐标系下的坐标,矩阵是右乘Vm, Vm用的齐次坐标,w = 1.

    顺序一定是T*R*S。


    Transform Class

    这里写一个简单的类来处理GameObejct的位置,旋转和缩放.

    Transform.h

    #pragma once
    #include "Quaternion.h"
    #include "RiCore/RiDevice.h"
    
    class Transform
    {
    public:
    
    	Transform();
    	Transform(const Vector3& position, const Quaternion& rotation, const Vector3& scale);
    	~Transform();
    
    	Matrix4x4 GetLocalToWorldMatrix();
    	bool isDirty;
    	Vector3 position;
    	Quaternion rotation;
    	Vector3 scale;
    
    	void Translate(const Vector3 &delta);
    	void Rotate(float xRot, float yRot, float zRot);
    	void Scale(const Vector3 &scale);
    
    private:
    	Matrix4x4 localToWorldMatrix;
    	Matrix4x4 worldToLocalMatrix;
    };

    Transform.cpp

    #include "stableheader.h" 
    #include "Transform.h"
    #include "Log/Log.h"
    
    Transform::Transform()
    {
    
    }
    
    Transform::Transform(const Vector3& _position, const Quaternion& _rotation, const Vector3& _scale)
    {
    	position = _position;
    	rotation = _rotation;
    	scale = _scale;
    	isDirty = true;
    }
    
    Transform::~Transform()
    {
    
    }
    
    Matrix4x4 Transform::GetLocalToWorldMatrix()
    {
    	if (isDirty)
    	{
    		Matrix4x4 transMatrix(1, 0, 0, 0,
    			0, 1, 0, 0,
    			0, 0, 1, 0,
    			position.x, position.y, position.z, 1);
    
    		Matrix4x4 scaleMatrix(scale.x, 0, 0, 0,
    			0, scale.y, 0, 0,
    			0, 0, scale.z, 0,
    			0, 0, 0, 1);
    
    		localToWorldMatrix = transMatrix * rotation.GetRotMatrix() * scaleMatrix;
    	}
    	return localToWorldMatrix;
    }
    
    void Transform::Translate(const Vector3 &delta)
    {
    	position.x += delta.x;
    	position.y += delta.y;
    	position.z += delta.z;
    
    	isDirty = true;
    
    }
    
    
    void Transform::Rotate(float xRot, float yRot, float zRot)
    {
    	rotation = rotation * Quaternion::Euler(xRot, yRot, zRot);
    	isDirty = true;
    }
    
    Matrix4x4 Transform :: Scale(const Vector3& _scale)
    {
    	scale = _scale;
    	isDirty = true;
    }
    
    

    用了一个标志位来记录position,rotation,scale是否发生改变,如果发生改变就将isDirty至为true,下次获取WorldMatrix的时候就重新计算。


    测试


    //Unity
    transform.position = new Vector3(1, 2, 3);
    transform.rotation = Quaternion.Euler(30, 45, 60);
    transform.localScale = new Vector3(3, 2, 1);
    Debug.Log(transform.localToWorldMatrix);


    图三 . Unity中的运行结果


    //C++
    renderObj = new RenderObject();
    renderObj->transform->Translate(Vector3(1, 2, 3));
    renderObj->transform->Rotate(30, 45, 60);
    renderObj->transform->Scale(Vector3(3, 2, 1));
    qDebug() << renderObj->transform->GetLocalToWorldMatrix();



    图四 .C++运行结果


    参考

    Tutorial 3 : Matrices - http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/

    OpenGL Transformation - http://www.songho.ca/opengl/gl_transform.html#modelview

    Homogeneous Coordinates - http://www.songho.ca/math/homogeneous/homogeneous.html

    The Truth Behind Homogeneous Coordinates - http://deltaorange.com/2012/03/08/the-truth-behind-homogenous-coordinates/

    Real Time Rendering 3rd





沪ICP备19023445号-2号
友情链接