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

发表于2017-09-08
评论0 223浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏程序行业精英群

711501594

开发时对于矩阵的使用还是比较多的,为此,下面就给大家分别介绍下MVP矩阵中的齐次坐标和ModelMatrix(模型矩阵)。

齐次坐标(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转过来 copy

  1. R = (    
  2.         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,    
  3.         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,    
  4.         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,    
  5.         0.0f, 0.0f, 0.0f, 1.0f    
  6.         )    

缩放矩阵


具体的矩阵推导可以参考我之前写的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

  1. #pragma once  
  2. #include "Quaternion.h"  
  3. #include "RiCore/RiDevice.h"  
  4.   
  5. class Transform  
  6. {  
  7. public:  
  8.   
  9.     Transform();  
  10.     Transform(const Vector3& position, const Quaternion& rotation, const Vector3& scale);  
  11.     ~Transform();  
  12.   
  13.     Matrix4x4 GetLocalToWorldMatrix();  
  14.     bool isDirty;  
  15.     Vector3 position;  
  16.     Quaternion rotation;  
  17.     Vector3 scale;  
  18.   
  19.     void Translate(const Vector3 &delta);  
  20.     void Rotate(float xRot, float yRot, float zRot);  
  21.     void Scale(<span style=< span="">"font-family: Arial, Helvetica, sans-serif;">const Vector3 &scale);  </span style=<>
  22.   
  23. private:  
  24.     Matrix4x4 localToWorldMatrix;  
  25.     Matrix4x4 worldToLocalMatrix;  
  26. };  

Transform.cpp

  1. #include "stableheader.h"   
  2. #include "Transform.h"  
  3. #include "Log/Log.h"  
  4.   
  5. Transform::Transform()  
  6. {  
  7.   
  8. }  
  9.   
  10. Transform::Transform(const Vector3& _position, const Quaternion& _rotation, const Vector3& _scale)  
  11. {  
  12.     position = _position;  
  13.     rotation = _rotation;  
  14.     scale = _scale;  
  15.     isDirty = true;  
  16. }  
  17.   
  18. Transform::~Transform()  
  19. {  
  20.   
  21. }  
  22.   
  23. Matrix4x4 Transform::GetLocalToWorldMatrix()  
  24. {  
  25.     if (isDirty)  
  26.     {  
  27.         Matrix4x4 transMatrix(1, 0, 0, 0,  
  28.             0, 1, 0, 0,  
  29.             0, 0, 1, 0,  
  30.             position.x, position.y, position.z, 1);  
  31.   
  32.         Matrix4x4 scaleMatrix(scale.x, 0, 0, 0,  
  33.             0, scale.y, 0, 0,  
  34.             0, 0, scale.z, 0,  
  35.             0, 0, 0, 1);  
  36.   
  37.         localToWorldMatrix = transMatrix * rotation.GetRotMatrix() * scaleMatrix;  
  38.     }  
  39.     return localToWorldMatrix;  
  40. }  
  41.   
  42. void Transform::Translate(const Vector3 &delta)  
  43. {  
  44.     position.x  = delta.x;  
  45.     position.y  = delta.y;  
  46.     position.z  = delta.z;  
  47.   
  48.     isDirty = true;  
  49.   
  50. }  
  51.   
  52.   
  53. void Transform::Rotate(float xRot, float yRot, float zRot)  
  54. {  
  55.     rotation = rotation * Quaternion::Euler(xRot, yRot, zRot);  
  56.     isDirty = true;  
  57. }  
  58.   
  59. Matrix4x4 Transform :: Scale(const Vector3& _scale)  
  60. {  
  61.     scale = _scale;  
  62.     <span style="font-family: Arial, Helvetica, sans-serif;"</span style=>isDirty = true;  
  63. }  

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


测试

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


图三 . Unity中的运行结果


  1. //C   
  2. renderObj = new RenderObject();  
  3. renderObj->transform->Translate(Vector3(1, 2, 3));  
  4. renderObj->transform->Rotate(30, 45, 60);  
  5. renderObj->transform->Scale(Vector3(3, 2, 1));  
  6. 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

原文链接

著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台