代码改变世界

Direct3D轮回:构建基于Direct3D的通用摄影机类

2011-06-23 20:30  独孤残云  阅读(2953)  评论(27编辑  收藏  举报

Direct3D渲染管线主要完成了三次矩阵变换:

1.世界变换——局部坐标到全局坐标的变换;

2.摄影变换——全局坐标到摄影坐标的变换;

3.投影变换——摄影坐标到投影坐标的变换。

其中的摄影变换我们大都通过封装一个称之为“摄影机”的对象加以实现。

如下即为一个基于Direct3D机制的通用摄影机实现:

/*-------------------------------------

代码清单:D3DCamera.h
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/


#include 
"D3DInit.h"

#pragma once

class CD3DCamera
{
public:
    CD3DCamera(
void);
    
~CD3DCamera(void);
public:
    D3DXVECTOR3 GetCameraPos()                      {
return m_cameraPosition;}             //---获得摄影机位置
    void        SetCameraPos(D3DXVECTOR3 cameraPos) {m_cameraPosition = cameraPos;}        //---设置摄影机位置
    D3DXVECTOR3 GetCameraTar()                      {return m_cameraTarget;}               //---获得摄影机目标
    void        SetCameraTar(D3DXVECTOR3 cameraTar) {m_cameraTarget = cameraTar;}          //---设置摄影机目标
    D3DXVECTOR3 GetCameraUp()                       {return m_cameraUp;}                   //---获得摄影机Y方向
    void        SetCameraUp(D3DXVECTOR3 cameraUp)   {m_cameraUp = cameraUp;}               //---设置摄影机Y方向
    D3DMATRIX   GetViewMatrix()                     {return m_viewMatrix;}                 //---获得摄影矩阵
public:
    
void        Update();           //---更新摄影机
    void        Release();          //---释放摄影机
private:
    
void        UpdateCamera();     //---更新摄影机(私有)
    void        RotateManager();    //---处理旋转(私有)
    void        MoveManager();      //---处理平移(私有)
private:
    D3DXVECTOR3 m_cameraPosition;   
//---摄影机位置
    D3DXVECTOR3 m_cameraTarget;     //---摄影机目标
    D3DXVECTOR3 m_cameraUp;         //---摄影机Y方向

    D3DXMATRIX   m_viewMatrix;       
//---摄影机摄影矩阵

    
float m_rotateSpeed;            //---旋转速度
    float m_moveSpeed;              //---平移速度
    float m_rotateRight;            //---水平方向旋转(以右为正)
    float m_rotateUp;               //---垂直方向旋转(以上为正)
    float m_moveForward;            //---前后方向移动(以前为正)
    float m_moveRight;              //---左右方向移动(以右为正)
    float m_moveUp;                 //---上下方向移动(以上为正)
    POINT m_nowMousePos;            //---当前鼠标位置
    POINT m_originMousePos;         //---前次鼠标位置
};

 

/*-------------------------------------

代码清单:D3DCamera.cpp
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
"StdAfx.h"
#include 
"D3DCamera.h"
#include 
"D3DGame.h"

extern IDirect3DDevice9 *g_pD3DDevice;
extern CMouseInput      *g_pMouseInput;
extern CKeyboardInput   *g_pKeyboardInput;

CD3DCamera::CD3DCamera(
void) :  m_cameraPosition(D3DXVECTOR3_ZERO),
                                m_cameraTarget(
0.0f,0.0f,100.0f),
                                m_cameraUp(D3DXVECTOR3_UP),
                                m_rotateSpeed(
0.005f),
                                m_moveSpeed(
0.1f),
                                m_rotateRight(
0.0f),
                                m_rotateUp(
0.0f),          
                                m_moveForward(
0.0f),         
                                m_moveRight(
0.0f),   
                                m_moveUp(
0.0f
{
    g_pMouseInput
->GetPosition(m_originMousePos);
}

CD3DCamera::
~CD3DCamera(void)
{

}

void CD3DCamera::Update()
{
    
//---处理旋转
    RotateManager();
    
//---处理平移
    MoveManager();
    
//---最后更新相机
    UpdateCamera();
}

void CD3DCamera::Release()
{

}

void CD3DCamera::RotateManager()
{
    
// 获得鼠标当前位置
    g_pMouseInput->GetPosition(m_nowMousePos);
    
// 如果鼠标右键按下
    if(g_pMouseInput->RightButton() == BUTTONSTATE_PRESSED)
    {
        
// X方向旋转量
        float Xdif = m_nowMousePos.x-m_originMousePos.x;
        
// Y方向旋转量
        float Ydif = m_nowMousePos.y-m_originMousePos.y;
        
// 分别累积到水平与垂直方向旋转
        m_rotateRight += m_rotateSpeed * Xdif;
        m_rotateUp 
+= m_rotateSpeed * Ydif;
    }
    
// 更新前次鼠标位置
    m_originMousePos = m_nowMousePos;
}

void CD3DCamera::MoveManager()
{
    
// 前移
    if (g_pKeyboardInput->IsKeyDown(DIK_W) || g_pKeyboardInput->IsKeyDown(DIK_UP))
        m_moveForward 
= m_moveSpeed;
    
// 后移
    if (g_pKeyboardInput->IsKeyDown(DIK_S) || g_pKeyboardInput->IsKeyDown(DIK_DOWN))
        m_moveForward 
= -m_moveSpeed;
    
// 左移
    if (g_pKeyboardInput->IsKeyDown(DIK_D) || g_pKeyboardInput->IsKeyDown(DIK_RIGHT))
        m_moveRight 
= +m_moveSpeed;
    
// 右移
    if (g_pKeyboardInput->IsKeyDown(DIK_A) || g_pKeyboardInput->IsKeyDown(DIK_LEFT))
        m_moveRight 
= -m_moveSpeed;
}


void CD3DCamera::UpdateCamera()
{
    
//---先累计旋转量
    D3DXMATRIX diffx;
    D3DXMatrixRotationX(
&diffx,m_rotateUp);
    D3DXMATRIX diffy;
    D3DXMatrixRotationY(
&diffy,m_rotateRight);

    D3DXMATRIX diff 
= diffx * diffy;

    D3DXVECTOR3 Adiff;
    D3DXVec3TransformCoord(
&Adiff,&D3DXVECTOR3_ZERO,&diff);
    D3DXVECTOR3 Sdiff;
    D3DXVec3TransformCoord(
&Sdiff,&(D3DXVECTOR3_FORWARD*100.0f),&diff);

    
//---而后在旋转基础上累计平移量
    D3DXVECTOR3 Xdiff;
    D3DXVec3TransformCoord(
&Xdiff,&D3DXVECTOR3(m_moveRight,0.0f,0.0f),&diff);
    D3DXVECTOR3 Zdiff;
    D3DXVec3TransformCoord(
&Zdiff,&D3DXVECTOR3(0.0f,0.0f,m_moveForward),&diff);

    
//---根据平移计算Position
    m_cameraPosition = m_cameraPosition + Xdiff + Zdiff;
    
//---在已获得平移的基础上,根据旋转计算Target
    m_cameraTarget = m_cameraPosition + Adiff + Sdiff;

    
//---计算得最终的摄影矩阵

    D3DXMatrixLookAtLH(
&m_viewMatrix,&(m_cameraPosition + Adiff),&m_cameraTarget,&m_cameraUp);
    m_moveForward 
= 0;
    m_moveRight 
= 0;
}

代码比较基础,给出了相对完整的注释~

矩阵运算的相关API说明,大家可以参看我在小组中发的这篇帖子:http://space.cnblogs.com/group/topic/32546/

需要说明一点问题:乘法不满足交换律。是以UpdateCamera()函数中关于旋转量与平移量的累计次序不可颠倒~

摄影机构建完毕之后,我们可以再简单构建一个参照系对象,来验证摄影机工作是否正常:

/*-------------------------------------

代码清单:CoordCross.h
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
"D3DInit.h"

#pragma once

class CCoordCross
{
public:
    CCoordCross(
void);
    
~CCoordCross(void);
public:
    
void Draw();
    
void Release();
private:
    
void InitVertices();
private:
    IDirect3DVertexBuffer9
* m_pVB;
};
/*-------------------------------------

代码清单:CoordCross.吹泡泡~ 囧~
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
"StdAfx.h"
#include 
"CoordCross.h"
#include 
"D3DGame.h"

extern IDirect3DDevice9 *g_pD3DDevice;

struct VertexPositionColor{
    VertexPositionColor(){}
    VertexPositionColor(
float x, float y, float z, D3DCOLOR color){
        _x 
= x; _y = y; _z = z;
        _color 
= color;
    }
    
float _x, _y, _z;
    D3DCOLOR _color;
    
static const DWORD FVF;
};
const DWORD VertexPositionColor::FVF = (D3DFVF_XYZ | D3DFVF_DIFFUSE);

CCoordCross::CCoordCross(
void):m_pVB(0)
{
    InitVertices();
}

CCoordCross::
~CCoordCross(void)
{

}

void CCoordCross::InitVertices()
{
    g_pD3DDevice
->CreateVertexBuffer(
        
18 * sizeof(VertexPositionColor),
        D3DUSAGE_WRITEONLY,
        VertexPositionColor::FVF,
        D3DPOOL_MANAGED,
        
&m_pVB,
        
0);

    VertexPositionColor
* pVertices;
    m_pVB
->Lock(0,0,(void**)&pVertices,0);

    pVertices[
0]  = VertexPositionColor(0.0f,0.0f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
1]  = VertexPositionColor(5.0f,0.0f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
2]  = VertexPositionColor(5.0f,0.0f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
3]  = VertexPositionColor(4.5f,0.5f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
4]  = VertexPositionColor(5.0f,0.0f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
5]  = VertexPositionColor(4.5f,-0.5f,0.0f,D3DXCOLOR_WHITE);

    pVertices[
6]  = VertexPositionColor(0.0f,0.0f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
7]  = VertexPositionColor(0.0f,5.0f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
8]  = VertexPositionColor(0.0f,5.0f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
9]  = VertexPositionColor(0.5f,4.5f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
10= VertexPositionColor(0.0f,5.0f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
11= VertexPositionColor(-0.5f,4.5f,0.0f,D3DXCOLOR_WHITE);

    pVertices[
12= VertexPositionColor(0.0f,0.0f,0.0f,D3DXCOLOR_WHITE);
    pVertices[
13= VertexPositionColor(0.0f,0.0f,5.0f,D3DXCOLOR_WHITE);
    pVertices[
14= VertexPositionColor(0.0f,0.0f,5.0f,D3DXCOLOR_WHITE);
    pVertices[
15= VertexPositionColor(0.0f,0.5f,4.5f,D3DXCOLOR_WHITE);
    pVertices[
16= VertexPositionColor(0.0f,0.0f,5.0f,D3DXCOLOR_WHITE);
    pVertices[
17= VertexPositionColor(0.0f,-0.5f,4.5f,D3DXCOLOR_WHITE);

    m_pVB
->Unlock();

}

void CCoordCross::Draw()
{
    g_pD3DDevice
->SetStreamSource(0,m_pVB,0,sizeof(VertexPositionColor));
    g_pD3DDevice
->SetFVF(VertexPositionColor::FVF);
    g_pD3DDevice
->DrawPrimitive(D3DPT_LINELIST,0,9);
}

void CCoordCross::Release()
{
    ReleaseCOM(m_pVB);
}

《龙书》中最为基本的基于顶点缓冲区的绘制方法~

如果您有不明白的地方可以翻看《龙书》,或者发表评论,我会尽量给出详尽的解答 ^ ^

最后是我们的主体代码部分:

D3DGame.cpp
/*-------------------------------------

代码清单:D3DGame.cpp
来自:
http://www.cnblogs.com/kenkao

-------------------------------------
*/

#include 
"StdAfx.h"
#include 
"D3DGame.h"
#include 
"D3DCamera.h"
#include 
"CoordCross.h"
#include 
<stdio.h>

HINSTANCE g_hInst;
HWND g_hWnd;
IDirect3D9       
*g_pD3D        = NULL;
IDirect3DDevice9 
*g_pD3DDevice  = NULL;
CMouseInput      
*g_pMouseInput = NULL;
CKeyboardInput   
*g_pKeyboardInput = NULL;
CD3DCamera       
*g_pD3DCamera  = NULL;
CCoordCross      
*g_pCoordCross = NULL;

// 鼠标输入单元测试函数
void TestMouseInput();
void TestKeyboardInput();

void Initialize(HINSTANCE hInst, HWND hWnd)
{
    g_hInst 
= hInst;
    g_hWnd  
= hWnd;
    InitD3D(
&g_pD3D, &g_pD3DDevice, hWnd);
    g_pMouseInput 
= new CMouseInput;
    g_pMouseInput
->Initialize(hInst,hWnd);
    g_pKeyboardInput 
= new CKeyboardInput;
    g_pKeyboardInput
->Initialize(hInst,hWnd);
    g_pD3DCamera 
= new CD3DCamera;
}

void LoadContent()
{
    g_pCoordCross 
= new CCoordCross;
    g_pD3DCamera
->SetCameraPos(D3DXVECTOR3(3.0f,2.0f,-8.0f));
}

void Update()
{
    g_pMouseInput
->GetState();
    g_pKeyboardInput
->GetState();
    g_pD3DCamera
->Update();
}

void Draw()
{
    g_pD3DDevice
->SetTransform(D3DTS_VIEW,&g_pD3DCamera->GetViewMatrix());
    g_pD3DDevice
->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(100,149,237,255), 1.0f0);
    
if(SUCCEEDED(g_pD3DDevice->BeginScene())) 
    {
        g_pCoordCross
->Draw();
        g_pD3DDevice
->EndScene();
    }
    g_pD3DDevice
->Present(NULL, NULL, NULL, NULL);
}

void UnloadContent()
{
    ReleaseCOM(g_pCoordCross);
}

void Dispose()
{
    ReleaseCOM(g_pD3DCamera);
    ReleaseCOM(g_pKeyboardInput);
    ReleaseCOM(g_pMouseInput);
    ReleaseCOM(g_pD3DDevice);
    ReleaseCOM(g_pD3D);
}

void TestMouseInput()
{
    POINT point;
    g_pMouseInput
->GetState();
    g_pMouseInput
->GetPosition(point);
    TCHAR tmpText[
50];
    
if(g_pMouseInput->LeftButton()==BUTTONSTATE_PRESSED)
    {
        sprintf(tmpText,
"鼠标左键已按下,X-Y坐标为(%d,%d)",point.x,point.y);
        MessageBox(NULL,tmpText,
"提示",MB_OK|MB_ICONINFORMATION);
    }
    
else if(g_pMouseInput->MiddleButton()==BUTTONSTATE_PRESSED)
    {
        sprintf(tmpText,
"鼠标滚轮已按下,X-Y坐标为(%d,%d)",point.x,point.y);
        MessageBox(NULL,tmpText,
"提示",MB_OK|MB_ICONINFORMATION);
    }
    
else if(g_pMouseInput->RightButton()==BUTTONSTATE_PRESSED)
    {
        sprintf(tmpText,
"鼠标右键已按下,X-Y坐标为(%d,%d)",point.x,point.y);
        MessageBox(NULL,tmpText,
"提示",MB_OK|MB_ICONINFORMATION);
    }
}

void TestKeyboardInput()
{
    TCHAR tmpText[
50];
    
// 获得键盘输入设备状态
    g_pKeyboardInput->GetState();
    
// 单键检测
    if(g_pKeyboardInput->IsKeyDown(DIK_D))
    {
        sprintf(tmpText,
"D键被按下");
        MessageBox(NULL,tmpText,
"提示",MB_OK|MB_ICONINFORMATION);
    }
    
// 组合键检测
    else if(g_pKeyboardInput->IsKeyDown(DIK_A)&g_pKeyboardInput->IsKeyDown(DIK_S))
    {
        sprintf(tmpText,
"A&S组合键被按下");
        MessageBox(NULL,tmpText,
"提示",MB_OK|MB_ICONINFORMATION);
    }
}

保留了两个测试函数,本例中没有用到~

最后是效果图: