zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Best Time to Buy and Sell Stock
    Remove Nth Node From End of List
    Unique Paths
    Swap Nodes in Pairs
    Convert Sorted Array to Binary Search Tree
    Populating Next Right Pointers in Each Node
    Maximum Subarray
    Climbing Stairs
    Unique Binary Search Trees
    Remove Duplicates from Sorted Array
  • 原文地址:https://www.cnblogs.com/mikewolf2002/p/2390669.html
Copyright © 2011-2022 走看看