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

来源:互联网 时间:1970-01-01

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()
{
    // 注意:垂直方向绕X轴转动;水平方向绕Y轴转动。这个不要弄反^ ^
    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);

    //---根据旋转处理Target
    m_cameraTarget = m_cameraPosition + Adiff + Sdiff;

    //---m_cameraUp暂且不做处理,Hero站在地面上头顶永远向上,囧~
    //---如何支持飞行态大家自行推导即可。

    //---根据平移处理Position
    D3DXVECTOR3 Xdiff;
    D3DXVec3TransformCoord(&Xdiff,&D3DXVECTOR3(m_moveRight,0.0f,0.0f),&diff);
    D3DXVECTOR3 Zdiff;
    D3DXVec3TransformCoord(&Zdiff,&D3DXVECTOR3(0.0f,0.0f,m_moveForward),&diff);

    // 计算得摄影变换矩阵,这也是摄影机所有运算的最终结果
    m_cameraPosition = m_cameraPosition + Xdiff + Zdiff;
    D3DXMatrixLookAtLH(&m_viewMatrix,&(m_cameraPosition + Adiff),&m_cameraTarget,&m_cameraUp);
    m_moveForward = 0;
    m_moveRight = 0;
}

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

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

需要说明一点问题:矩阵乘法不满足交换律。是以旋转处理及平移处理两方法调用的先后次序不可颠倒~

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

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

代码清单: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.0f, 0);
    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);
    }
}

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

最后是效果图:

 


相关阅读:
Top