DirectX 3D_实践之在DirectX3D中实现摄像机方位的动态变换

来源:互联网 发布:北京师范学校网络教育 编辑:程序博客网 时间:2024/05/25 19:56

                程序员的身体,众所周知,程序员需要大量的动脑,所以需要大量的供血,进而需要强大的心脏能力。这些无不是一颗定时炸弹,时刻威胁在程序的身体。所以,作为程序员,我们应该珍惜自己的身体。千万记住,你的公司,不会爱惜你的身体的,它只会从你身上进行剥削,榨取剩余价值。所以,我们应该掌握一个度,快到这个度时,忙中偷闲,喝杯茶去。BUG是解不完的,而生命只有一次。为了自己,为了家人,我们应该珍惜,时刻保持警惕。

 

 

              到目前为止,我们已经学会了如果使用D3DXMatrixLookAtLH函数计算出观察矩阵(即取景变换矩阵)。当在某一个固定地点固定摄像机方位时,该函数十分有用,但其对用户接口输入做出响应的移动摄像机来说,就显得力不从心了。本章中,我们实现了一个Camera类,来实现摄像机方位的动态变化,绕3 个坐标轴进行旋转变换,进行平移,扫视,升降。

 

我们先来看camera.h的代码:

#ifndef __cameraH__#define __cameraH__#include <d3dx9.h>class Camera{public:enum CameraType { LANDOBJECT, AIRCRAFT };Camera();Camera(CameraType cameraType);~Camera();void strafe(float units); // left/rightvoid fly(float units);    // up/downvoid walk(float units);   // forward/backwardvoid pitch(float angle); // rotate on right vectorvoid yaw(float angle);   // rotate on up vectorvoid roll(float angle);  // rotate on look vectorvoid 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;};#endif // __cameraH__


再来看DirectX3D.h的代码:

#ifndef __DirectX3DH__#define __DirectX3DH__#include <d3dx9.h>#include <string>#include <limits>namespace d3d{bool InitD3D(HINSTANCE hInstance,       // [in] Application instance.int width, int height,     // [in] Backbuffer dimensions.bool windowed,             // [in] Windowed (true)or full screen (false).D3DDEVTYPE deviceType,     // [in] HAL or REFIDirect3DDevice9** device);// [out]The created device.int EnterMsgLoop( bool (*ptr_display)(float timeDelta));LRESULT CALLBACK WndProc(HWND hwnd,UINT msg, WPARAM wParam,LPARAM lParam);template<class T> void Release(T t){if( t ){t->Release();t = 0;}}template<class T> void Delete(T t){if( t ){delete t;t = 0;}}const D3DXCOLOR      WHITE( D3DCOLOR_XRGB(255, 255, 255) );const D3DXCOLOR      BLACK( D3DCOLOR_XRGB(  0,   0,   0) );const D3DXCOLOR        RED( D3DCOLOR_XRGB(255,   0,   0) );const D3DXCOLOR      GREEN( D3DCOLOR_XRGB(  0, 255,   0) );const D3DXCOLOR       BLUE( D3DCOLOR_XRGB(  0,   0, 255) );const D3DXCOLOR     YELLOW( D3DCOLOR_XRGB(255, 255,   0) );const D3DXCOLOR       CYAN( D3DCOLOR_XRGB(  0, 255, 255) );const D3DXCOLOR    MAGENTA( D3DCOLOR_XRGB(255,   0, 255) );//// Lights//D3DLIGHT9 InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color);D3DLIGHT9 InitPointLight(D3DXVECTOR3* position, D3DXCOLOR* color);D3DLIGHT9 InitSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color);//// Materials//D3DMATERIAL9 InitMtrl(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p);const D3DMATERIAL9 WHITE_MTRL  = InitMtrl(WHITE, WHITE, WHITE, BLACK, 2.0f);const D3DMATERIAL9 RED_MTRL    = InitMtrl(RED, RED, RED, BLACK, 2.0f);const D3DMATERIAL9 GREEN_MTRL  = InitMtrl(GREEN, GREEN, GREEN, BLACK, 2.0f);const D3DMATERIAL9 BLUE_MTRL   = InitMtrl(BLUE, BLUE, BLUE, BLACK, 2.0f);const D3DMATERIAL9 YELLOW_MTRL = InitMtrl(YELLOW, YELLOW, YELLOW, BLACK, 2.0f);//// 外接体对象//struct BoundingBox{BoundingBox();bool isPointInside(D3DXVECTOR3& p);D3DXVECTOR3 _min;D3DXVECTOR3 _max;};struct BoundingSphere{BoundingSphere();D3DXVECTOR3 _center;float       _radius;};//// Constants//const float INFINITY = FLT_MAX;const float EPSILON  = 0.001f;// Function references "desert.bmp" internally.  This file must// be in the working directory.bool DrawBasicScene(IDirect3DDevice9* device,// Pass in 0 for cleanup.float scale);            // uniform scale struct Vertex{Vertex(){}Vertex(float x, float y, float z, float nx, float ny, float nz, float u, float v){_x = x;   _y = y;   _z = z;_nx = nx; _ny = ny; _nz = nz;_u = u;   _v = v;}float _x, _y, _z, _nx, _ny, _nz, _u, _v;static const DWORD FVF;};}#endif 


再来看camera.cpp的代码:

//////////////////////////////////////////////////////////////////////////////////////////////////// // File: camera.cpp// // Author: Frank Luna (C) All Rights Reserved//// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 //// Desc: Defines a camera's position and orientation.//         //////////////////////////////////////////////////////////////////////////////////////////////////#include "camera.h"/*这里我们增加了一个Camera类的设计,首先使用了4个摄像机向量,右向量,上向量,观察向量以及位置向量来定义摄像机相对于世界坐标系的位置和朝向。这些向量实质上为相对世界坐标系描述的摄像机定义了一个局部坐标系。由于右向量,上向量和观察向量定义了摄像机在世界坐标系中的朝向,有时我们也将这三个向量统称为方向向量。方向向量必须是标准正交的。如果一个向量集中的向量都彼此正交,并且均为1,则称该向量是标准正交的。前面,我们提到,标准正交矩阵的一个重要性质是其逆矩阵与其转置矩阵相等。用上述4个向量来描述摄像机,我们可对摄像机实施6种变换:  绕向量right的旋转(俯仰,pitch)  绕向量up的旋转(偏航,yaw)  绕向量look的旋转(滚动,roll)  沿向量right方向的扫视(strafe)  沿向量up方向的升降(fly)  沿向量look的平动。*//*平移的实现:将摄像机的位置向量平移到原点可通过将其与向量-p做向量加法轻松实现,因为p-p=0.旋转:要想使摄像机各向量与世界坐标系各轴重合的工作量大一些,我们需要一个3x3的旋转矩阵A以使向量right,up和look分别与世界坐标系的x,y,z轴重合。绕任意轴的旋转:实现摄像机的旋转方法时,我们应使能够绕任意轴进行旋转,D3DX库提供了如下函数恰好具备该功能。  D3DXMATRIX *D3DXMatrixRotationAxis( D3DXMATRIX *pOut, CONST D3DXVECTOR *pV, FLOAT Angle );  例如,若我们想绕由向量(0.707f,0.707f,0.0f)所确定的轴旋转π/2,可这样做:  D3DXMATRIX R;  D3DXVECTOR3 axis(0.707f,0.707f,0.0f)D3DXMatrixRotationAxis(&R,&axis,D3DX_PI/2);*/Camera::Camera(){_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);}Camera::Camera(CameraType cameraType){_cameraType = cameraType;_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);}Camera::~Camera(){}void Camera::getPosition(D3DXVECTOR3* pos){*pos = _pos;}void Camera::setPosition(D3DXVECTOR3* pos){_pos = *pos;}void Camera::getRight(D3DXVECTOR3* right){*right = _right;}void Camera::getUp(D3DXVECTOR3* up){*up = _up;}void Camera::getLook(D3DXVECTOR3* look){*look = _look;}void Camera::walk(float units){// move only on xz plane for land objectif( _cameraType == LANDOBJECT )_pos += D3DXVECTOR3(_look.x, 0.0f, _look.z) * units;if( _cameraType == AIRCRAFT )_pos += _look * units;}void Camera::strafe(float units){// move only on xz plane for land objectif( _cameraType == LANDOBJECT )_pos += D3DXVECTOR3(_right.x, 0.0f, _right.z) * units;if( _cameraType == AIRCRAFT )_pos += _right * units;}void Camera::fly(float units){// move only on y-axis for land objectif( _cameraType == LANDOBJECT )_pos.y += units;if( _cameraType == AIRCRAFT )_pos += _up * units;}void Camera::pitch(float angle){D3DXMATRIX T;D3DXMatrixRotationAxis(&T, &_right,angle);// rotate _up and _look around _right vectorD3DXVec3TransformCoord(&_up,&_up, &T);D3DXVec3TransformCoord(&_look,&_look, &T);}void Camera::yaw(float angle){D3DXMATRIX T;// rotate around world y (0, 1, 0) always for land objectif( _cameraType == LANDOBJECT )D3DXMatrixRotationY(&T, angle);// rotate around own up vector for aircraftif( _cameraType == AIRCRAFT )D3DXMatrixRotationAxis(&T, &_up, angle);// rotate _right and _look around _up or y-axisD3DXVec3TransformCoord(&_right,&_right, &T);D3DXVec3TransformCoord(&_look,&_look, &T);}void Camera::roll(float angle){// only roll for aircraft typeif( _cameraType == AIRCRAFT ){D3DXMATRIX T;D3DXMatrixRotationAxis(&T, &_look,angle);// rotate _up and _right around _look vectorD3DXVec3TransformCoord(&_right,&_right, &T);D3DXVec3TransformCoord(&_up,&_up, &T);}}void Camera::getViewMatrix(D3DXMATRIX* V){// Keep camera's axes orthogonal to eachotherD3DXVec3Normalize(&_look, &_look);D3DXVec3Cross(&_up, &_look, &_right);D3DXVec3Normalize(&_up, &_up);D3DXVec3Cross(&_right, &_up, &_look);D3DXVec3Normalize(&_right, &_right);// Build the view matrix: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 Camera::setCameraType(CameraType cameraType){_cameraType = cameraType;}


再来看一下DirectX3D.cpp的代码:

#include "DirectX3D.h"// vertex formatsconst DWORD d3d::Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;bool d3d::InitD3D(HINSTANCE hInstance,int width, int height,bool windowed,D3DDEVTYPE deviceType,IDirect3DDevice9** device){//// Create the main application window.//WNDCLASS wc;wc.style         = CS_HREDRAW | CS_VREDRAW;wc.lpfnWndProc   = (WNDPROC)d3d::WndProc; wc.cbClsExtra    = 0;wc.cbWndExtra    = 0;wc.hInstance     = hInstance;wc.hIcon         = LoadIcon(0, IDI_APPLICATION);wc.hCursor       = LoadCursor(0, IDC_ARROW);wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);wc.lpszMenuName  = 0;wc.lpszClassName = "Direct3D9App";if( !RegisterClass(&wc) ) {::MessageBox(0, "RegisterClass() - FAILED", 0, 0);return false;}HWND hwnd = 0;hwnd = ::CreateWindow("Direct3D9App", "Direct3D9App", WS_EX_TOPMOST,0, 0, width, height,0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/); if( !hwnd ){::MessageBox(0, "CreateWindow() - FAILED", 0, 0);return false;}::ShowWindow(hwnd, SW_SHOW);::UpdateWindow(hwnd);//// Init D3D: //    //第一步    //要初始化IDirect3D 首先必须获取IDirect3D9的指针,使用一个专门的Direct3D函数就可以很容易做到     IDirect3D9 * _d3d9;    //这个对象的主要有两个用途:设备枚举以及创建IDirect3DDevice9类型的对象。设备枚举是指获取系统中可用的的每块图形卡的  //性能,显示模型,格式以及其他信息。这个函数调用失败会返回一个NULL指针。  if(NULL == (_d3d9 = Direct3DCreate9(D3D_SDK_VERSION))){    return FALSE;  }  //第二步  //创建一个代表主显卡的IDirect3DDevice9类型对象时,必须指定使用该对象进行顶点运算的类型。如果可以,我们希望使用硬件顶点运算  //但是由于并非所有的显卡都支持硬件顶点运算,我们必须首先检查图形卡是否支持该类型的运算。  //要进行检查,必须先根据主显卡的性能参数初始化一个IDirect3DDevice9类型的对象。我们使用如下方法来完成初始化:     /*  HRESULT IDirect3D9:GetDeviceCaps(      UINT Adapter,      D3DDEVTYPE DeviceType,      D3DCAPS9 * pCaps;     )        Adapter : 指定物理显卡的序号。  DeviceType:指定设备类(例如硬件设备(D3DDEVTYPE_HAL)或软件设备(D3DDEVTYPE_REF));  pCaps 返回已初始化的设备性能结构实例。        */         D3DCAPS9 caps;          int vp = 0; //代表顶点如何操作        _d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);          if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT){                        vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;           }          else          {                        vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;          }          //第三步是填充D3DPRESENT_PARAMETER结构          //该结构用于指定所要创建的IDirect3DDevice9类型对象的一些特性,该结构定义如下:          /*          typedef struct _D3DPRESENT_PARAMETERS_{                        UINT BackBufferWidth;                        UINT BackBufferHeight;                        UINT BackBufferFormat;                        UINT BackBufferCount;                        D3DMULTISAMPLE_TYPE MultiSampleType;                        DWORD MultiSampleQuality;                        D3DSWAPEFFECT SwapEffect;                        HWND  hDeviceWindow;                        BOOL  Windowed;                        BOOL  EnableAutoDepthStencil;                        D3DFORMAT AutoDepthStencilFormat;                        DWORD  Flags;                        UINT   FullScreen_RefreshRateInHz;                        UINT   PresentationInterval;          };          */                /*          BackBufferWidth: 后台缓存中表面的宽度,单位为像素。        BackBufferHeight:后台缓存中表面的高度,单位为像素。        BackBufferFormat:后台缓存的像素格式(如32位像素格式:D3DFMT_A8R8G8B8);        BackBufferCount: 所需使用的后台缓存的个数,通常指定为1,表明我们仅需要一个后台缓存。        MultiSampleType: 后台缓存所使用的多重采样类型。        MultiSampleQuality:多重采样的质量水平。        SwapEffect:D3DSWAPEFFECT 枚举类型的一个成员。该枚举类型指定了交换链中的缓存的页面置换方式。指定D3DSWAPEFFECT_DISCARD时效率最高。        hDeviceWindow:与设备相关的窗口句柄。指定了所要进行绘制的应用程序窗口。        Windowed:为true时表示窗口模式,false时为全屏模式        EnableAutoDepthStencil:设为true,则Direct3D自动创建并维护深度缓存或模板缓存。        AutoDepthStencilFormat:深度缓存或模板缓存的像素格式(例如,用24位表示深度并将8位保留供模板缓存使用,D3DFMT_D24S8).        Flags:一些附加的特性。可以指定为0,表示无标记,或D3DPRESENTFLAG集合中的一个成员,其中两个成员较常用。                 D3DPRESENTFLAG_LOCKABLE_DEPTHBUFFER 指定为可锁定的后台缓存。注意,使用一个可锁定的后台缓存会降低性能。                 D3DPRESENTFLAG_DISCARD_DEPTHBUFFER 指定当下一个后台缓存提交时,哪个深度或模块缓存将被丢弃。丢弃的意思是深度或模板缓存存储区                                                    中的内容别丢弃或无效。这样可以提升性能。        FullScreen_RefreshRateInHz: 刷新频率,如果想使用默认的刷新频率,则可将该参数指定为D3DPRESENT_RATE_DEFAULT;        PresentationInterval:D3DPRESENT集合的一个成员,其中有两个比较常用。                      D3DPRESENT_INTERVAL_IMMEDIATE 立即提交。                      D3DPRESENT_INTERVAL_DEFAULT 由Direct3D来选择提交频率,通常该值等于刷新频率。       */                   D3DPRESENT_PARAMETERS d3dpp;                    ZeroMemory(&d3dpp, sizeof(d3dpp));                   d3dpp.BackBufferWidth            = 800;                   d3dpp.BackBufferHeight           = 600;                   d3dpp.BackBufferFormat           = D3DFMT_A8R8G8B8;                   d3dpp.BackBufferCount            = 1;                   d3dpp.MultiSampleType            = D3DMULTISAMPLE_NONE;                   d3dpp.MultiSampleQuality         = 0;                   d3dpp.SwapEffect                 = D3DSWAPEFFECT_DISCARD;                    d3dpp.hDeviceWindow              = hwnd;                   d3dpp.Windowed                   = true;                   d3dpp.EnableAutoDepthStencil     = true;                    d3dpp.AutoDepthStencilFormat     = D3DFMT_D24S8;                   d3dpp.Flags                      = 0;                   d3dpp.FullScreen_RefreshRateInHz = 0;                   d3dpp.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;               //第四步 创建IDirectDevice9类型的对象               /*                         HRESULT IDirect3D9::CreateDevice(                               UINT Adapter,                               D3DDEVTYPE DeviceType,                               HWND hFocusWindow,                               DWORD BehaviorFlags,                               D3DPRESENT_PARAMETERS *pPresentationParameters,                               IDirect3DDevice9 ** ppReturnedDeviceInterface                             );       Adapter:指定我们希望用已创建的IDirect3DDevice9对象代表哪块物理显卡。       DeviceType:指定需要使用的设备类型()如,硬件设备用D3DDEVTYPE_HAL,或D3DDEVTYPE_REF代表软件设备。       hFocusWindow:与设备相关的窗口句柄。通常情况下是指设备所要进行绘制的目标窗口。       为了达到预期的目的,该句柄与D3DPRESENT_PARAMETER结构的数据成员hDeviceWindow应为同一个句柄。       BehaviorFlags:该参数可为D3DCREATE_HARDWARE_VERTEXPROCESSING或D3DCREATE_SOFTWARE_VERTEXPROCESSING.       pPresentationParameters:一个已经完成初始化的D3DPRESENT_PARAMETERS类型的实例,该实例定义了设备的一些特性。       ppReturnedDeviceInterface:返回所创建的设备。*/  if(FAILED(_d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,      hwnd, vp, &d3dpp, device)))             return FALSE;        _d3d9->Release();      return TRUE;}int d3d::EnterMsgLoop( bool (*ptr_display)(float timeDelta) ){MSG msg;::ZeroMemory(&msg, sizeof(MSG));static float lastTime = (float)timeGetTime(); while(msg.message != WM_QUIT){if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){::TranslateMessage(&msg);::DispatchMessage(&msg);}else        {float currTime  = (float)timeGetTime();float timeDelta = (currTime - lastTime)*0.001f;ptr_display(timeDelta);lastTime = currTime;        }    }    return msg.wParam;}D3DLIGHT9 d3d::InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color){D3DLIGHT9 light;::ZeroMemory(&light, sizeof(light));light.Type      = D3DLIGHT_DIRECTIONAL;light.Ambient   = *color * 0.4f;light.Diffuse   = *color;light.Specular  = *color * 0.6f;light.Direction = *direction;return light;}D3DLIGHT9 d3d::InitPointLight(D3DXVECTOR3* position, D3DXCOLOR* color){D3DLIGHT9 light;::ZeroMemory(&light, sizeof(light));light.Type      = D3DLIGHT_POINT;light.Ambient   = *color * 0.4f;light.Diffuse   = *color;light.Specular  = *color * 0.6f;light.Position  = *position;light.Range        = 1000.0f;light.Falloff      = 1.0f;light.Attenuation0 = 1.0f;light.Attenuation1 = 0.0f;light.Attenuation2 = 0.0f;return light;}D3DLIGHT9 d3d::InitSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color){D3DLIGHT9 light;::ZeroMemory(&light, sizeof(light));light.Type      = D3DLIGHT_SPOT;light.Ambient   = *color * 0.4f;light.Diffuse   = *color;light.Specular  = *color * 0.6f;light.Position  = *position;light.Direction = *direction;light.Range        = 1000.0f;light.Falloff      = 1.0f;light.Attenuation0 = 1.0f;light.Attenuation1 = 0.0f;light.Attenuation2 = 0.0f;light.Theta        = 0.5f;light.Phi          = 0.7f;return light;}D3DMATERIAL9 d3d::InitMtrl(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p){D3DMATERIAL9 mtrl;mtrl.Ambient  = a;mtrl.Diffuse  = d;mtrl.Specular = s;mtrl.Emissive = e;mtrl.Power    = p;return mtrl;}d3d::BoundingBox::BoundingBox(){// infinite small _min.x = d3d::INFINITY;_min.y = d3d::INFINITY;_min.z = d3d::INFINITY;_max.x = -d3d::INFINITY;_max.y = -d3d::INFINITY;_max.z = -d3d::INFINITY;}bool d3d::BoundingBox::isPointInside(D3DXVECTOR3& p){if( p.x >= _min.x && p.y >= _min.y && p.z >= _min.z &&p.x <= _max.x && p.y <= _max.y && p.z <= _max.z ){return true;}else{return false;}}d3d::BoundingSphere::BoundingSphere(){_radius = 0.0f;}bool d3d::DrawBasicScene(IDirect3DDevice9* device, float scale){static IDirect3DVertexBuffer9* floor  = 0;static IDirect3DTexture9*      tex    = 0;static ID3DXMesh*              pillar = 0;HRESULT hr = 0;if( device == 0 ){if( floor && tex && pillar ){// they already exist, destroy themd3d::Release<IDirect3DVertexBuffer9*>(floor);d3d::Release<IDirect3DTexture9*>(tex);d3d::Release<ID3DXMesh*>(pillar);}}else if( !floor && !tex && !pillar ){// 创建顶点缓存,并设置6个顶点坐标,两个三角形组成一个矩形device->CreateVertexBuffer(6 * sizeof(Vertex),0, d3d::Vertex::FVF,D3DPOOL_MANAGED,&floor,0);Vertex* v = 0;floor->Lock(0, 0, (void**)&v, 0);v[0] = Vertex(-20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);v[1] = Vertex(-20.0f, -2.5f,  20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);v[2] = Vertex( 20.0f, -2.5f,  20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);v[3] = Vertex(-20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);v[4] = Vertex( 20.0f, -2.5f,  20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);v[5] = Vertex( 20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);floor->Unlock();        //创建一个圆柱体D3DXCreateCylinder(device, 0.5f, 0.5f, 5.0f, 20, 20, &pillar, 0);//创建纹理D3DXCreateTextureFromFile(device,"desert.bmp",&tex);}else{//// 处理纹理//device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);//启用光照D3DXVECTOR3 dir(0.707f, -0.707f, 0.707f);D3DXCOLOR col(1.0f, 1.0f, 1.0f, 1.0f);D3DLIGHT9 light = d3d::InitDirectionalLight(&dir, &col);device->SetLight(0, &light);device->LightEnable(0, true);device->SetRenderState(D3DRS_NORMALIZENORMALS, true);device->SetRenderState(D3DRS_SPECULARENABLE, true);//// Render//D3DXMATRIX T, R, P, S;D3DXMatrixScaling(&S, scale, scale, scale);// used to rotate cylinders to be parallel with world's y-axisD3DXMatrixRotationX(&R, -D3DX_PI * 0.5f);// 渲染地板D3DXMatrixIdentity(&T);T = T * S;device->SetTransform(D3DTS_WORLD, &T);device->SetMaterial(&d3d::WHITE_MTRL);device->SetTexture(0, tex);device->SetStreamSource(0, floor, 0, sizeof(Vertex));device->SetFVF(Vertex::FVF);device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);// 6个圆柱体device->SetMaterial(&d3d::BLUE_MTRL);device->SetTexture(0, 0);for(int i = 0; i < 5; i++){D3DXMatrixTranslation(&T, -5.0f, 0.0f, -15.0f + (i * 7.5f));P = R * T * S;device->SetTransform(D3DTS_WORLD, &P);pillar->DrawSubset(0);D3DXMatrixTranslation(&T, 5.0f, 0.0f, -15.0f + (i * 7.5f));P = R * T * S;device->SetTransform(D3DTS_WORLD, &P);pillar->DrawSubset(0);}}return true;}


再来看一下wmain.cpp的代码:

#include "DirectX3D.h"#include "camera.h"//#include <fstream>//#include <vector>//// Globals//IDirect3DDevice9* Device = 0; const int Width  = 640;const int Height = 480;/*struct Vertex{Vertex(){}Vertex(float x, float y, float z, float nx, float ny, float nz, float u, float v){_x = x;   _y = y;   _z = z;_nx = nx; _ny = ny; _nz = nz;_u = u;   _v = v;}float _x, _y, _z, _nx, _ny, _nz, _u, _v;static const DWORD FVF;};const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;*///// Framework Functions//Camera TheCamera(Camera::LANDOBJECT);bool Setup(){d3d::DrawBasicScene(Device, 0.0f); //// Set projection matrix.//D3DXMATRIX proj;D3DXMatrixPerspectiveFovLH(&proj,D3DX_PI * 0.25f, // 45 - degree(float)Width / (float)Height,1.0f,1000.0f);Device->SetTransform(D3DTS_PROJECTION, &proj);return true;}//
void Cleanup(){d3d::DrawBasicScene(0, 0.0f);}bool Display(float timeDelta){if( Device ){//// Update: Update the camera.//if( ::GetAsyncKeyState('W') & 0x8000f )TheCamera.walk(4.0f * timeDelta);if( ::GetAsyncKeyState('S') & 0x8000f )TheCamera.walk(-4.0f * timeDelta);if( ::GetAsyncKeyState('A') & 0x8000f )TheCamera.strafe(-4.0f * timeDelta);if( ::GetAsyncKeyState('D') & 0x8000f )TheCamera.strafe(4.0f * timeDelta);if( ::GetAsyncKeyState('R') & 0x8000f )TheCamera.fly(4.0f * timeDelta);if( ::GetAsyncKeyState('F') & 0x8000f )TheCamera.fly(-4.0f * timeDelta);if( ::GetAsyncKeyState(VK_UP) & 0x8000f )TheCamera.pitch(1.0f * timeDelta);if( ::GetAsyncKeyState(VK_DOWN) & 0x8000f )TheCamera.pitch(-1.0f * timeDelta);if( ::GetAsyncKeyState(VK_LEFT) & 0x8000f )TheCamera.yaw(-1.0f * timeDelta);if( ::GetAsyncKeyState(VK_RIGHT) & 0x8000f )TheCamera.yaw(1.0f * timeDelta);if( ::GetAsyncKeyState('N') & 0x8000f )TheCamera.roll(1.0f * timeDelta);if( ::GetAsyncKeyState('M') & 0x8000f )TheCamera.roll(-1.0f * timeDelta);// 这里通过用户的输入处理,不断的设置取景矩阵并进行设置。        // D3DXMATRIX V;TheCamera.getViewMatrix(&V);Device->SetTransform(D3DTS_VIEW, &V);//// Render//Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);Device->BeginScene();d3d::DrawBasicScene(Device, 1.0f);Device->EndScene();Device->Present(0, 0, 0, 0);}return true;}//// WndProc//LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){switch( msg ){case WM_DESTROY:::PostQuitMessage(0);break;case WM_KEYDOWN:if( wParam == VK_ESCAPE )::DestroyWindow(hwnd);break;}return ::DefWindowProc(hwnd, msg, wParam, lParam);}//// WinMain//int WINAPI WinMain(HINSTANCE hinstance,   HINSTANCE prevInstance,    PSTR cmdLine,   int showCmd){if(!d3d::InitD3D(hinstance,640, 480, true, D3DDEVTYPE_HAL, &Device)){::MessageBox(0, "InitD3D() - FAILED", 0, 0);return 0;}if(!Setup()){::MessageBox(0, "Setup() - FAILED", 0, 0);return 0;}d3d::EnterMsgLoop( Display );Cleanup();Device->Release();return 0;}bool ComputeBoundingSphere(ID3DXMesh* mesh, d3d::BoundingSphere* sphere){HRESULT hr = 0;BYTE* v = 0;mesh->LockVertexBuffer(0, (void**)&v);//得到网格的顶点缓存/*我们来看一下D3DX库提供的用来计算一个网格的外接球的函数HRESULT  D3DXComputeBoundingSphere(__in  const D3DXVECTOR3 *pFirstPosition,__in  DWORD NumVertices,__in  DWORD dwStride,__in  D3DXVECTOR3 *pCenter,__in  FLOAT *pRadius);    pFirstPosition:指向顶点数组(该数组的每个元素都描述了对应顶点)中第一个顶点的位置向量的指针。我们可以通过网格对象得到顶点缓存的指针,最后                可转化为该值。NumVertices:该网格中顶点数组中顶点的个数。可通过网格mesh->GetNumVertices()得到dsStride:每个顶点的大小,单位为字节。该值很重要,因为一种顶点结构可能包含了许多该函数所不需要的附加信息,如法向量和纹理坐标等。这样该函数         就需要知道应跳过多少字节才能找到下一个顶点的位置。mesh->GetFVF()可以返回一个描述了顶点格式的DWORD类型值。D3DXGetFVFVertexSize这个 函数可以得到该顶点占用多少个字节。 pCenter:返回的外接球的球心位置。pRadius: 返回外接球的半径。    */hr = D3DXComputeBoundingSphere((D3DXVECTOR3*)v,mesh->GetNumVertices(),D3DXGetFVFVertexSize(mesh->GetFVF()),&sphere->_center,&sphere->_radius);mesh->UnlockVertexBuffer();if( FAILED(hr) )return false;return true;}bool ComputeBoundingBox(ID3DXMesh* mesh, d3d::BoundingBox* box){HRESULT hr = 0;BYTE* v = 0;mesh->LockVertexBuffer(0, (void**)&v);//这里是D3DX中计算一个网格外接体的函数,跟外接球的函数很类似,只是最后两个参数,返回外接体的最大点和最小点。hr = D3DXComputeBoundingBox((D3DXVECTOR3*)v,mesh->GetNumVertices(),D3DXGetFVFVertexSize(mesh->GetFVF()),&box->_min,&box->_max);mesh->UnlockVertexBuffer();if( FAILED(hr) )return false;return true;}


最后来看程序截图:

每日总结:

我们通过维护4个向量---right, up, look 和 position来表示摄像机在世界坐标系中的位置和朝向,有了这些描述工具,我们就可轻松地实现具有6个自由度的摄像机。这样我们就为飞行模拟器和第一人称视角游戏提供了一个灵活的摄像机接口。