三维视图变换与相机模型

先来看一下游戏中常用的两种相机模型:欧拉相机模型和UVN相机模型。

 

1.欧拉相机模型

 

欧拉角(Euler Angles)是用来描述三维欧几里德(Euclidean)空间中的刚体方向的一种方法,即通过俯仰角(Pitch)、偏转角(Yaw)、滚动角(Roll)描述物体的方向。

注意的问题:

1)  Undo

假定施加于物体上的角度变换序列为PYR,若要恢复物体原来的方向,则需要施加的变换序列为:(-R)(-Y)(-P)

 2) 万向节锁(Gimbal Lock

在两个旋转轴重合时,就出现了万向节,此时需要删掉一个。一旦先择±90度为pitch角,就被限定在只能绕竖轴旋转。这种现象,角度为±90度的第二次旋转使得第一次和第三次旋转轴相同,成为万向锁。为了消除限制欧拉角的这种别名现象,规定万向锁情况下只能由yaw完成绕竖轴的旋转。即若pitch = ±90度,则roll = 0

                 

 

2. UVN相机模型

与欧拉相机模型不同,UVN相机模型采用向量来描述相机的朝向,其中n表示相机的朝向,v为上向量,u为右向量。

注意的问题:

因为uvn是线性无关的,所以当改变n后,需要重新计算vn的值:

v = n × u

u=n×v

 3.小结

 

   从两种相机模型的数据描述可以看出,两者都是以相机本身为中心,均绕相机旋转。因而适应于第一人称场景的相机控制。这两种模型的代码实现可以参考OGRECamera类。

 

    而很多软件需要绕观察的物体旋转,如CAD软件中的绕物体的视图旋转操作。什么样的相机模型适合这种应用呢?

 

 ArcBall是一种绕模型为中心旋转的相机模型。其原理相当简单:将模型假想成一个球,相机在该球上移动。


若用鼠标来控制相机的旋转,就需要将屏幕坐标(x,y)映射到球面上(x1,y1,z1)。为了简化运算,可以假定球的半径为1,球的中心点为屏幕的中心点(暂且只考虑x.y轴),
假定覆盖屏幕最大圆的半径为R,因此不难将(x,y)转换为(x1,y1):


 x1 = x/R * 2 - 1;
 y1 = 1 - y/R*2


又因球的半径为1,所以有:
 z1*z1 = 1 - x1*x1 - y1*y1。

若鼠标移动起始坐标为(X1,Y1), 终止坐标为(X2,Y2),映射到球面上可得到两个向量V1,V2,由V1,V2便可以计算出旋转轴和旋转角度。

因此整个算法的关键思想上将屏幕上的点转换到假象的球面上。

以下为基于Direct3D数学库实现的ArcBall:


class AArcBall
{
public:
   AArcBall(){}
   ~AArcBall(){}

   void SetRect(RECT rect)
   {
      m_Rect = rect;
   }

   void Start(int x, int y)
   {
      MapToSphere(x, y, &m_vStart);
   }

   void RotateTo(int x, int y)
   {
      MapToSphere(x, y, &m_vEnd);

      // compute the angle
      float fDot = D3DXVec3Dot(&m_vStart, &m_vEnd);
      float angle = acosf(fDot);
      // axis
      D3DXVECTOR3 vAxis;
      D3DXVec3Cross(&vAxis, &m_vStart, &m_vEnd);

      m_qCurrent = D3DXQUATERNION(vAxis.x, vAxis.y, vAxis.z, angle);
   }

   void GetRotationMatrix(D3DXMATRIX* pMatRot)
   {
      D3DXMatrixRotationQuaternion(pMatRot, &m_qCurrent);
   };

private:
   void MapToSphere(int x, int y, D3DXVECTOR3* pVec)
   {
      int nRadius = (m_Rect.Width() > m_Rect.Height())? m_Rect.Width() : m_Rect.Height();
      // translate to [0...2]
      float ptx = float(x) / nRadius*2;
      float pty = float(y) / nRadius*2;
      // move [0,0] to the center
      ptx = ptx - 1;
      pty = 1 - pty;

      // get z
      float z2 = 1 - ptx*ptx - pty*pty;
      float ptz = z2 > 0? sqrt(z2) : 0;

     
      *pVec = D3DXVECTOR3(ptx, pty, ptz);
      D3DXVec3Normalize(pVec, pVec);
   }

private:
   CRect       m_Rect;

   D3DXVECTOR3 m_vStart;
   D3DXVECTOR3 m_vEnd;

   D3DXQUATERNION m_qCurrent;    // current one

};

posted on 2008-08-29 14:24  张大大123  阅读(303)  评论(0编辑  收藏  举报

导航