• Directx11教程(8) 一个新的camera类


         本章我们将替换掉CameraClass类,实现一个稍微靠谱点的摄像机类。并通过Q,W,E,A,S,D,Z,X,C等按键实现摄像机的控制。

         该类的主要功能就是根据指定的摄像机位置,up方向以及lookat方向,得到最终的视图矩阵,所谓视图矩阵就是把世界坐标系的顶点位置转化到视点(或者说摄像机)空间的矩阵。该类可以实现两种模式的摄像机操作,一类是AIRCRAFT摄像机,允许摄像机在空间自由运动,具有6个自由度。另一种是LANDOBJECT摄像机,只允许沿着某些特定的轴运动。

        

         下面简单看下计算ViewMatrix的原理:

          

    image

         假设摄像机的局部坐标系为(S,W,Q),S, W, Q它们都是归一化向量, S是局部坐标的右(right)向轴(类似世界坐标系的x轴),W是局部坐标系的上(up)向轴(类似世界坐标系y轴),Q是前(lookat)向轴(类似于世界坐标系的z轴),S向量表示为(Sx, Sy, Sz), up 向量表示为(Wx, Wy, Wz), Q向量表示为(Qx, Qy, Qz),摄像机的位置为(Px, Py, Pz)。

        我们现在要把世界坐标系中的顶点A(x, y, z),如果要转化到视觉坐标系(right,up, lookat)则需要做下面两步:

    1、把视点移回原点,则转化矩阵为:

    image

    2.我们要把一个世界坐标系点K(Kx, Ky, Kz),表示成(S,W,Q)坐标系的点(假设此时,已经经过平移操作,摄像机在世界坐标系的原点),则其公式为:

    Lx = Kx * Sx + Ky * Sy + Kz * Sz;

    Ly = Kx * Wx + Ky * Wy + Kz * Wz;

    Lz = Kx * Qx + Ky * Qy + Kz * Qz

         则转为矩阵为:

    image

    则世界坐标系到视觉坐标系的转化矩阵为上面两个矩阵相乘,最终的矩阵为:

    image

          通常在摄像机类中,都是给定up, lookat,或者lookat, right之后,通过差积的方式求得right或者up,然后再差积,最后求得正交归一化的的坐标系(S, W, Q)。

       在本Camera类中,就是通过这种方式:

    //根据lookup, right求得正交的up

    D3DXVec3Cross(&_up, &_look, &_right);
    //up归一化

    D3DXVec3Normalize(&_up, &_up);

    //根据up和lookup,求得正交的right

    D3DXVec3Cross(&_right, &_up, &_look);

    //righ再归一化
    D3DXVec3Normalize(&_right, &_right);

     

    CameraClass.h的代码如下:

    #pragma once

    #include <d3dx10math.h>

    class CameraClass
        {
        //支持两种摄像机模型 AIRCRAFT 允许在空间自由运动,具有6个自由度
        // LANDOBJECT 沿某些特定轴进行移动
        public:
            enum CameraType { LANDOBJECT, AIRCRAFT };

        public:
            CameraClass(void);
            CameraClass(const CameraClass&);
            ~CameraClass(void);

            void strafe(float units); // 左右
            void fly(float units);   // 上下
            void walk(float units);   // 前后

            void pitch(float angle); // 旋转view坐标系right向量
            void yaw(float angle);  // 旋转up向量
            void roll(float angle); // 旋转look向量

            void getViewMatrix(D3DXMATRIX* V);
            void setCameraType(CameraType cameraType);
            void getPosition(D3DXVECTOR3* pos);
            void setPosition(D3DXVECTOR3* pos);

            void getRight(D3DXVECTOR3* right);
            void getUp(D3DXVECTOR3* up);
            void getLook(D3DXVECTOR3* look);
        private:
            CameraType  _cameraType;
            D3DXVECTOR3 _right;
            D3DXVECTOR3 _up;
            D3DXVECTOR3 _look;
            D3DXVECTOR3 _pos;

        };

    CameraClass.cpp代码如下:

    #include "CameraClass.h"


    CameraClass::CameraClass(void)
        {
        _cameraType = AIRCRAFT;

        _pos   = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
        _right = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
        _up    = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
        _look  = D3DXVECTOR3(0.0f, 0.0f, 1.0f);

        }

    CameraClass::CameraClass(const CameraClass& other)
        {
        }

    CameraClass::~CameraClass(void)
        {
        }

    void CameraClass::getPosition(D3DXVECTOR3* pos)
        {
        *pos = _pos;
        }

    void CameraClass::setPosition(D3DXVECTOR3* pos)
        {
        _pos = *pos;
        }

    void CameraClass::getRight(D3DXVECTOR3* right)
        {
        *right = _right;
        }

    void CameraClass::getUp(D3DXVECTOR3* up)
        {
        *up = _up;
        }

    void CameraClass::getLook(D3DXVECTOR3* look)
        {
        *look = _look;
        }

    //行走,沿着摄像机观察方向的移动
    void CameraClass::walk(float units)
        {
        // 仅在x,z平面移动
        if( _cameraType == LANDOBJECT )
            _pos += D3DXVECTOR3(_look.x, 0.0f, _look.z) * units;

        if( _cameraType == AIRCRAFT )
            _pos += _look * units;
        }

    //扫视,是指保持观察方向不变,沿向量right方向从一边平移到另一边
    void CameraClass::strafe(float units)
        {
        // 仅在x,z平面移动
        if( _cameraType == LANDOBJECT )
            _pos += D3DXVECTOR3(_right.x, 0.0f, _right.z) * units;

        if( _cameraType == AIRCRAFT )
            _pos += _right * units;
        }
    //飞行模式,升降,指沿着向量up方向的移动
    void CameraClass::fly(float units)
        {
        // 仅在y轴移动
        if( _cameraType == LANDOBJECT )
            _pos.y += units;

        if( _cameraType == AIRCRAFT )
            _pos += _up * units;
        }

    //pitch, yaw, roll的概念 http://www.cnblogs.com/mikewolf2002/p/5151606.html

    void CameraClass::pitch(float angle)
        {
        D3DXMATRIX T;
        D3DXMatrixRotationAxis(&T, &_right, angle);

       // 绕着right向量,旋转up和look
        D3DXVec3TransformCoord(&_up,&_up, &T);
        D3DXVec3TransformCoord(&_look,&_look, &T);
        }

    void CameraClass::yaw(float angle)
        {
        D3DXMATRIX T;

        //对LANDOBJECT,总是绕着(0,1,0)旋转。
        if( _cameraType == LANDOBJECT )
            D3DXMatrixRotationY(&T, angle);

        //对于aircraft,绕着up向量旋转
        if( _cameraType == AIRCRAFT )
            D3DXMatrixRotationAxis(&T, &_up, angle);

       // 绕着up或者y轴,旋转right和look
        D3DXVec3TransformCoord(&_right,&_right, &T);
        D3DXVec3TransformCoord(&_look,&_look, &T);
        }

    void CameraClass::roll(float angle)
        {
        //只对aircraft模式才左roll旋转
        if( _cameraType == AIRCRAFT )
            {
            D3DXMATRIX T;
            D3DXMatrixRotationAxis(&T, &_look, angle);

            // 绕着look向量,旋转up和right
            D3DXVec3TransformCoord(&_right,&_right, &T);
            D3DXVec3TransformCoord(&_up,&_up, &T);
            }
        }

    void CameraClass::getViewMatrix(D3DXMATRIX* V)
        {
        // 保持view局部坐标系,各轴的彼此正交
        D3DXVec3Normalize(&_look, &_look);
        // look X right
        D3DXVec3Cross(&_up, &_look, &_right);
        D3DXVec3Normalize(&_up, &_up);

        D3DXVec3Cross(&_right, &_up, &_look);
        D3DXVec3Normalize(&_right, &_right);

       // 生成view矩阵:
        float x = -D3DXVec3Dot(&_right, &_pos);
        float y = -D3DXVec3Dot(&_up, &_pos);
        float z = -D3DXVec3Dot(&_look, &_pos);

        (*V)(0,0) = _right.x; (*V)(0, 1) = _up.x; (*V)(0, 2) = _look.x; (*V)(0, 3) = 0.0f;
        (*V)(1,0) = _right.y; (*V)(1, 1) = _up.y; (*V)(1, 2) = _look.y; (*V)(1, 3) = 0.0f;
        (*V)(2,0) = _right.z; (*V)(2, 1) = _up.z; (*V)(2, 2) = _look.z; (*V)(2, 3) = 0.0f;
        (*V)(3,0) = x;        (*V)(3, 1) = y;     (*V)(3, 2) = z;       (*V)(3, 3) = 1.0f;
        }

    void CameraClass::setCameraType(CameraType cameraType)
        {
        _cameraType = cameraType;
        }

    修改GraphicsClass.h, 设置成员变量m_Camera为public,以便我们在GraphicsClass类中对齐进行操作。

    class GraphicsClass
        { 
    … 
            bool Frame();
            CameraClass* m_Camera; //设为public,便于在SystemClass中控制
        private:

        };

    SystemClass.cpp代码的Frame函数更新如下:


    bool SystemClass::Frame()
        {
        bool result;

        //检测用户是否按下ESC键,如果按下,退出程序.
        if(m_Input->IsKeyDown(VK_ESCAPE))
            {
            return false;
            }

        //如果A,S,D,W,Q,E,Z,X,C键按下,移动摄像机
        if(GetAsyncKeyState('W') & 0x8000)    //前后
            m_Graphics->m_Camera->walk(-0.1);
        if(GetAsyncKeyState('S') & 0x8000)   
            m_Graphics->m_Camera->walk(0.1);
        if(GetAsyncKeyState('A') & 0x8000)    //左右
            m_Graphics->m_Camera->strafe(-0.1);
        if(GetAsyncKeyState('D') & 0x8000)   
            m_Graphics->m_Camera->strafe(0.1);
        if(GetAsyncKeyState('Q') & 0x8000)    //上下
            m_Graphics->m_Camera->fly(-0.1);
        if(GetAsyncKeyState('E') & 0x8000)   
            m_Graphics->m_Camera->fly(0.1);
        if(GetAsyncKeyState('Z') & 0x8000)   
            m_Graphics->m_Camera->pitch(PI/180);
        if(GetAsyncKeyState('X') & 0x8000)   
            m_Graphics->m_Camera->yaw(PI/180);
        if(GetAsyncKeyState('C') & 0x8000)   
            m_Graphics->m_Camera->roll(PI/180);

       
      //动画,旋转摄像机
        m_Graphics->m_Camera->roll(PI/180);

        // 执行帧渲染函数.
        result = m_Graphics->Frame();
        if(!result)
            {
            return false;
            }
        return true;
        }

    注意:我们在SystemClass.cpp中增加了摄像机旋转的代码,所以这个立方体就在哪儿不停转啊转,永不休止……

        m_Graphics->m_Camera->roll(PI/180);

    程序执行结果如下图,图中的立方体一直处于旋转状态(其实我们是在旋转摄像机)

    image

    完整的代码请参考:

    工程文件myTutorialD3D11_7

    代码下载:

    https://files.cnblogs.com/mikewolf2002/myTutorialD3D11.zip

  • 相关阅读:
    华为云确定性运维,为政务云平台稳定可靠运行保驾护航
    实践GoF的设计模式:代理模式
    KubeEdge 1.12版本发布,稳定性、安全性、可扩展性均带来大幅提升
    使用Mask RCNN模型实现人体关键节点标注
    Vue 中为什么要有nextTick?
    后CNN探索,如何用RNN进行图像分类
    带你认识JDK8中超nice的Native Memory Tracking
    实例分析Scheduled Thread Pool Executor与Timer的区别
    最后 3 天|报名参加 OpenYurt+EdgeX 挑战赛 ,冲击最高 5 万元奖励!
    定时任务报警通知解决方案详解
  • 原文地址:https://www.cnblogs.com/mikewolf2002/p/2390669.html
Copyright © 2020-2023  润新知