zoukankan      html  css  js  c++  java
  • 三维视图变换与相机模型

    先来看一下游戏中常用的两种相机模型:欧拉相机模型和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

    };

  • 相关阅读:
    jdbc操作数据库并自动获取字段类型
    oracle sqlplus登陆命令
    2014木瓜移动校园招聘笔试题
    线段树总结
    JAVA的Split小技巧
    百度地图2.2框架黑屏和只有网格处理方法
    Zend Studio 实用快捷键大全
    Ext4中获取下拉框的值
    Java如何显示不同语言的时间?
    Java如何以不同国家的格式显示时间?
  • 原文地址:https://www.cnblogs.com/aiwz/p/6333138.html
Copyright © 2011-2022 走看看