1. 程式人生 > >Directx11教程(8) 一個新的camera類

Directx11教程(8) 一個新的camera類

scl ima walk 是把 允許 視圖矩陣 概念 正交 兩種

原文:Directx11教程(8) 一個新的camera類

     本章我們將替換掉CameraClass類,實現一個稍微靠譜點的攝像機類。並通過Q,W,E,A,S,D,Z,X,C等按鍵實現攝像機的控制。

     該類的主要功能就是根據指定的攝像機位置,up方向以及lookat方向,得到最終的視圖矩陣,所謂視圖矩陣就是把世界坐標系的頂點位置轉化到視點(或者說攝像機)空間的矩陣。該類可以實現兩種模式的攝像機操作,一類是AIRCRAFT攝像機,允許攝像機在空間自由運動,具有6個自由度。另一種是LANDOBJECT攝像機,只允許沿著某些特定的軸運動。

    

     下面簡單看下計算ViewMatrix的原理:

      

技術分享圖片

     假設攝像機的局部坐標系為(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、把視點移回原點,則轉化矩陣為:

技術分享圖片

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

     則轉為矩陣為:

技術分享圖片

則世界坐標系到視覺坐標系的轉化矩陣為上面兩個矩陣相乘,最終的矩陣為:

技術分享圖片

      通常在攝像機類中,都是給定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);

程序執行結果如下圖,圖中的立方體一直處於旋轉狀態(其實我們是在旋轉攝像機)

技術分享圖片

完整的代碼請參考:

工程文件myTutorialD3D11_7

代碼下載:

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

Directx11教程(8) 一個新的camera類