• 《逐梦旅程 WINDOWS游戏编程之从零开始》笔记4——Direct3D编程基础


    第11章 Direct3D编程基础

    2D游戏是贴图的艺术,3D游戏是渲染的艺术。这句话在我学过了之前的GDI编程之后,前一句算是有所体会,现在是来理解后一句的时候了。

    安装DirectX SDK配置啥的就不说了,直接进入正题,先来个典型的Direct3D程序框架图:

    主要分为5个部分:

    • 创建一个Windows窗口
    • Direct3D的初始化
    • 消息循环
    • 渲染图形
    • 结束应用程序,清除在初始化阶段锁创建的COM对象,退出程序

    至于COM (Component Object Model, 组件对象模型) 接口,是一项能够使DirectX独立于编程语言并具备向下兼容的技术。COM接口对象通过控制对某对象的引用计数个数来决定其生命周期,由其自行经营。

    一个DirectX程序通用框架:

      1 //-----------------------------------【头文件包含部分】---------------------------------------
      2 //    描述:包含程序所依赖的头文件
      3 //------------------------------------------------------------------------------------------------
      4 #include <d3d9.h>
      5 
      6 //-----------------------------------【宏定义部分】--------------------------------------------
      7 //    描述:定义一些辅助宏
      8 //------------------------------------------------------------------------------------------------
      9 #define WINDOW_WIDTH    800                            //为窗口宽度定义的宏,以方便在此处修改窗口宽度
     10 #define WINDOW_HEIGHT    600                            //为窗口高度定义的宏,以方便在此处修改窗口高度
     11 #define WINDOW_TITLE    L"【致我们永不熄灭的游戏开发梦想】Direct3D程序的核心框架"    //为窗口标题定义的宏
     12 #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }   //定义一个安全释放宏,便于后面COM接口指针的释放
     13 
     14 //-----------------------------------【全局函数声明部分】-------------------------------------
     15 //    描述:全局函数声明,防止“未声明的标识”系列错误
     16 //------------------------------------------------------------------------------------------------
     17 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );//窗口过程函数
     18 HRESULT                Direct3D_Init(HWND hwnd);         //在这个函数中进行Direct3D的初始化
     19 HRESULT                Objects_Init(HWND hwnd);         //在这个函数中进行要绘制的物体的资源初始化
     20 VOID                Direct3D_Render(HWND hwnd);     //在这个函数中进行Direct3D渲染代码的书写
     21 VOID                Direct3D_CleanUp( );                //在这个函数中清理COM资源以及其他资源
     22 
     23 //-----------------------------------【WinMain( )函数】---------------------------------------
     24 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
     25 {
     26     //【1】窗口创建四步曲之一:开始设计一个完整的窗口类
     27     WNDCLASSEX wndClass = { 0 };                            //用WINDCLASSEX定义了一个窗口类
     28     wndClass.cbSize = sizeof( WNDCLASSEX ) ;            //设置结构体的字节数大小
     29     wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
     30     wndClass.lpfnWndProc = WndProc;                    //设置指向窗口过程函数的指针
     31     wndClass.cbClsExtra        = 0;                                //窗口类的附加内存,取0就可以了
     32     wndClass.cbWndExtra        = 0;                            //窗口的附加内存,依然取0就行了
     33     wndClass.hInstance = hInstance;                        //指定包含窗口过程的程序的实例句柄。
     34     wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);  //本地加载自定义ico图标
     35     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。
     36     wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);  //为hbrBackground成员指定一个白色画刷句柄    
     37     wndClass.lpszMenuName = NULL;                        //用一个以空终止的字符串,指定菜单资源的名字。
     38     wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";        //用一个以空终止的字符串,指定窗口类的名字。
     39 
     40     //【2】窗口创建四步曲之二:注册窗口类
     41     if( !RegisterClassEx( &wndClass ) )                //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
     42         return -1;        
     43 
     44     //【3】窗口创建四步曲之三:正式创建窗口
     45     HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE,                //喜闻乐见的创建窗口函数CreateWindow
     46         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
     47         WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
     48 
     49     //Direct3D资源的初始化
     50     Direct3D_Init (hwnd);   
     51 
     52     //【4】窗口创建四步曲之四:窗口的移动、显示与更新
     53     MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true);        //调整窗口显示时的位置,使窗口左上角位于(250,80)处
     54     ShowWindow( hwnd, nShowCmd );    //调用ShowWindow函数来显示窗口
     55     UpdateWindow(hwnd);                        //对窗口进行更新,就像我们买了新房子要装修一样
     56 
     57     //【5】消息循环过程
     58     MSG msg = { 0 };  //初始化msg
     59     while( msg.message != WM_QUIT )            //使用while循环
     60     {
     61         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
     62         {
     63             TranslateMessage( &msg );        //将虚拟键消息转换为字符消息
     64             DispatchMessage( &msg );        //该函数分发一个消息给窗口程序。
     65         }
     66         else
     67         {
     68             Direct3D_Render(hwnd);   //进行渲染
     69         }
     70     }
     71     //【6】窗口类的注销
     72     UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);  //程序准备结束,注销窗口类
     73     return 0;  
     74 }
     75 
     76 //-----------------------------------【WndProc( )函数】--------------------------------------
     77 //    描述:窗口过程函数WndProc,对窗口消息进行处理
     78 //------------------------------------------------------------------------------------------------
     79 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )      
     80 {
     81     switch( message )                        //switch语句开始
     82     {
     83     case WM_PAINT:                        // 若是客户区重绘消息
     84         Direct3D_Render(hwnd);                 //调用Direct3D渲染函数
     85         ValidateRect(hwnd, NULL);        // 更新客户区的显示
     86         break;                                    //跳出该switch语句
     87 
     88     case WM_KEYDOWN:                    // 若是键盘按下消息
     89         if (wParam == VK_ESCAPE)    // 如果被按下的键是ESC
     90             DestroyWindow(hwnd);        // 销毁窗口, 并发送一条WM_DESTROY消息
     91         break;                                    //跳出该switch语句
     92 
     93     case WM_DESTROY:                    //若是窗口销毁消息
     94         Direct3D_CleanUp();            //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
     95         PostQuitMessage( 0 );            //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
     96         break;                                    //跳出该switch语句
     97 
     98     default:                                        //若上述case条件都不符合,则执行该default语句
     99         return DefWindowProc( hwnd, message, wParam, lParam );        //调用缺省的窗口过程
    100     }
    101 
    102     return 0;                                    //正常退出
    103 }
    104 
    105 //-----------------------------------【Direct3D_Init( )函数】--------------------------------------
    106 //    描述:Direct3D初始化函数,进行Direct3D的初始化
    107 //------------------------------------------------------------------------------------------------
    108 HRESULT Direct3D_Init(HWND hwnd)
    109 {
    110     if(!(S_OK==Objects_Init(hwnd))) return E_FAIL;     //调用一次Objects_Init,进行渲染资源的初始化
    111     return S_OK;
    112 }
    113 
    114 
    115 
    116 //-----------------------------------【Object_Init( )函数】--------------------------------------
    117 //    描述:渲染资源初始化函数,在此函数中进行要被渲染的物体的资源的初始化
    118 //--------------------------------------------------------------------------------------------------
    119 HRESULT Objects_Init(HWND hwnd)
    120 {
    121     return S_OK;
    122 }
    123 
    124 
    125 //-----------------------------------【Direct3D_Render( )函数】--------------------------------------
    126 //    描述:使用Direct3D进行渲染
    127 //--------------------------------------------------------------------------------------------------
    128 void Direct3D_Render(HWND hwnd)
    129 {
    130     //暂时为空
    131 }
    132 
    133 
    134 //-----------------------------------【Direct3D_CleanUp( )函数】--------------------------------
    135 //    描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
    136 //---------------------------------------------------------------------------------------------------
    137 void Direct3D_CleanUp()
    138 {
    139     //暂时为空
    140 }
    View Code

    关于上面的程序有两点要提到的是:

    1.  #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } 这里的宏定义是定义了一个带参数的宏,我以前以为C++中宏定义只能定义一个全局变量之类的,看来还是理解得不够深入。C++宏定义详解 。

    2. 那个S_OK是WinError.h中预定好的宏,#define S_OK  ((HRESULT)0L),其实就是0。HRESULT在msdn中的定义如下:

    接下来写的Direct3D程序,简单点说,只需做如下这四步就可以了:

    • 在Direct3D_Init()函数里完成Direct3D的相关初始化
    • 在Objects_Init()函数里完成将要绘制的绘制资源的初始化
    • 在Direct3D_Render()函数里完成渲染
    • 在Direct3D_CleanUp()函数里完成资源的清理

    1. Direct3D初始化

    下面来介绍下Direct3D初始化4步曲:

    • 创建Direct3D接口对象
    • 获取设备硬件信息
    • 填充 D3DPRESENT_PARAMETERS 结构体
    • 创建Direct3D设备接口

    第一步是创建Direct3D接口对象,注意Direct3D接口对象和Direct3D设备接口不是一个概念。

    LPDIRECT3D9 pD3D = NULL; //Direct3D接口对象的创建

    上面定义了一个LPDIRECT3D9类型的指针,不过我看到这里的时候觉得好奇怪,定义指针不是应该有个 * 号的么?为什么这里不用加呢? 用编译器查找定义功能,终于在 d3d9.h 中找到了答案:

    typedef struct IDirect3D9 *LPDIRECT3D9, *PDIRECT3D9;

    现在终于明白为什么不需要加 * 号了,因为已经在头文件中定义过了,这里也涉及到了结构体指针的用法,需要自己体会总结一番的。那么下面的问题是IDirect3D9是什么玩意,查了mdsn文档,发现它是一个接口:

    Applications use the methods of the IDirect3D9 interface to create Microsoft Direct3D objects and set up the environment. This interface includes methods for enumerating and retrieving capabilities of the device.

    然后要将该对象初始化,用 Direct3DCreate9 函数:

    Create an IDirect3D9 object and return an interface to it.

    IDirect3D9* Direct3DCreate9(
       UINT SDKVersion
    );

    这个函数返回指向IDirect3D9接口的指针,并进行DirectX的版本协商。所以初始化的第一步可以这样写:

    // 【Direct3D初始化四步曲之一,创接口】:创建Direct3D接口对象, 以便用该Direct3D对象创建Direct3D设备对象
        //--------------------------------------------------------------------------------------
        LPDIRECT3D9  pD3D = NULL; //Direct3D接口对象的创建
        if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口对象,并进行DirectX版本协商
            return E_FAIL;

    第二步是获取设备信息,使用的是GetDeviceCaps方法,获取指定设备的性能参数,会把所获取的硬件设备的信息保存到一个叫 D3DCAPS9 的预设好的结构体中。

    IDirect3D9::GetDeviceCaps method:

    HRESULT GetDeviceCaps(
      [in]  UINT       Adapter,
      [in]  D3DDEVTYPE DeviceType,
      [out] D3DCAPS9   *pCaps
    );

    第一个参数是显卡的序号,通常使用默认值D3DADAPTER_DEFAULT,表示使用当前显卡。第二个参数是D3DDEVTYPE类型的,表示设备类型,取值为D3DDEVTYPE结构体的某一成员:

    typedef enum D3DDEVTYPE { 
      D3DDEVTYPE_HAL          = 1,
      D3DDEVTYPE_NULLREF      = 4,
      D3DDEVTYPE_REF          = 2,
      D3DDEVTYPE_SW           = 3,
      D3DDEVTYPE_FORCE_DWORD  = 0xffffffff
    } D3DDEVTYPE, *LPD3DDEVTYPE;

     第3个参数是 用于接受设备信息的D3DCAPS9结构体指针,查了下mdsn文档,里面的成员太多了,就不贴出来了。

    顶点是3D图形学中的基本元素(说道这里,其实我并没有学过有关计算机图形学的内容,直接来学DirectX是不是有点太突兀了),而Direct3D中有硬件顶点运算(显卡支持,速度快)和软件顶点运算这两种方式。所以我们应该优先考虑硬件顶点运算方式。但是老的显卡也许不支持硬件顶点运算,所以我们需要在Direct3D初始化过程中,通过GetDeviceCaps方法检查显卡支持顶点运算模式。

    Direct3D初始化的第二步可以这样写:

       D3DCAPS9 caps; int vp = 0;
        if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) )
        {
            return E_FAIL;
        }
        if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
            vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;   //支持硬件顶点运算,我们就采用硬件顶点运算,妥妥的
        else
            vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件顶点运算,无奈只好采用软件顶点运算

    注:

    上面的 caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT 这里做了个按位与运算,我原来以为是逻辑与。至于这里为什么作个逻辑与运算便可以判断是否支持硬件顶点运算,我也不是很明白。

    下面是一些字段在MSDN中的解释:.

    D3DDEVTYPE_HAL:Hardware rasterization(光栅化). Shading is done with software, hardware, or mixed transform and lighting.

    D3DCAPS9里的成员DevCaps: Flags identifying the capabilities of the device

    D3DDEVCAPS_HWTRANSFORMANDLIGHT:#define D3DDEVCAPS_HWTRANSFORMANDLIGHT  0x00010000L /* Device can support transformation and lighting in hardware and DRAWPRIMITIVES2EX must be also */

    #define FAILED(hr) (((HRESULT)(hr)) < 0)

    第三步是填充 D3DPRESENT_PARAMETERS 结构体,这个结构体很重要

    typedef struct _D3DPRESENT_PARAMETERS_
    {
        UINT                BackBufferWidth;  //后台缓冲区宽度
        UINT                BackBufferHeight;  //后台缓冲区高度
        D3DFORMAT           BackBufferFormat;  //后台缓冲区的保存像素格式
        UINT                BackBufferCount;  
        D3DMULTISAMPLE_TYPE MultiSampleType;  //多重采样类型
        DWORD               MultiSampleQuality;  //多重采样的格式
        D3DSWAPEFFECT       SwapEffect;    //指定Direct3D如何将后台缓冲区的内容复制到前台缓存中
        HWND                hDeviceWindow;  //窗口句柄,指定在哪个窗口上进行绘制
        BOOL                Windowed;    //窗体的显示模式,TRUE表示窗口模式,FALSE表示全屏模式
        BOOL                EnableAutoDepthStencil;  //是否为应用程序自动管理深度缓存
        D3DFORMAT           AutoDepthStencilFormat;  
        DWORD               Flags;    //附加属性,通常为0
        /* FullScreen_RefreshRateInHz must be zero for Windowed mode */
        UINT                FullScreen_RefreshRateInHz;     //全屏幕时指定屏幕的刷新率
        UINT                PresentationInterval;          //指定后台缓冲区与前台缓冲区的最大交换频率
    } D3DPRESENT_PARAMETERS;

     有好多专业名词,看得也不是很明白。Direct3D初始化的第三步代码:

      D3DPRESENT_PARAMETERS d3dpp; 
        ZeroMemory(&d3dpp, sizeof(d3dpp));
        d3dpp.BackBufferWidth            = WINDOW_WIDTH;
        d3dpp.BackBufferHeight           = WINDOW_HEIGHT;
        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;

     注:ZeroMemory函数:Fills a block of memory with zeros.用0来填充一块内存区域

    void ZeroMemory(
      [in] PVOID  Destination,    //A pointer to the starting address of the block of memory to fill with zeros.
      [in] SIZE_T Length      //The size of the block of memory to fill with zeros, in bytes.
    );

     Direct3D初始化第四步是正式创建Direct3D设备接口,其实就是利用第一步创建的Direct3D接口对象,调用其IDirect3D9::CreateDevice方法:

    HRESULT CreateDevice(
      [in]          UINT                  Adapter,  //显卡序号
      [in]          D3DDEVTYPE            DeviceType,  //设备类型
      [in]          HWND                  hFocusWindow,  //窗口句柄,指定当Direct3D程序从前台变换到后台时的提示窗口
      [in]          DWORD                 BehaviorFlags,  //设备行为标识
      [in, out]     D3DPRESENT_PARAMETERS *pPresentationParameters,  //第三步创建的结构体
      [out, retval] IDirect3DDevice9      **ppReturnedDeviceInterface   //指定我们创建的Direct3D设备接口的指针,使用这个指针,已完成之后的绘制过程
    );

     Direct3D初始化第四步代码:

    if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 
            hwnd, vp, &d3dpp, &g_pd3dDevice)))
            return E_FAIL;

     Direct3D初始化的完整代码如下:

      1 #include <d3d9.h>
      2 
      3 //-----------------------------------【库文件包含部分】---------------------------------------
      4 #pragma comment(lib,"winmm.lib")  //调用PlaySound函数所需库文件
      5 #pragma comment(lib,"d3d9.lib")
      6 #pragma comment(lib,"d3dx9.lib")
      7 
      8 //-----------------------------------【宏定义部分】--------------------------------------------
      9 #define WINDOW_WIDTH    800                            //为窗口宽度定义的宏,以方便在此处修改窗口宽度
     10 #define WINDOW_HEIGHT    600                            //为窗口高度定义的宏,以方便在此处修改窗口高度
     11 #define WINDOW_TITLE    L"【致我们永不熄灭的游戏开发梦想】Direct3D初始化四步曲 示例程序"    //为窗口标题定义的宏
     12 #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }   //定义一个安全释放宏,便于后面COM接口指针的释放
     13 
     14 //-----------------------------------【全局变量声明部分】-------------------------------------
     15 LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; //Direct3D设备对象
     16 
     17 
     18 //-----------------------------------【全局函数声明部分】-------------------------------------
     19 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );//窗口过程函数
     20 HRESULT                    Direct3D_Init(HWND hwnd);         //在这个函数中进行Direct3D的初始化
     21 HRESULT                    Objects_Init(HWND hwnd);         //在这个函数中进行要绘制的物体的资源初始化
     22 VOID                            Direct3D_Render(HWND hwnd);     //在这个函数中进行Direct3D渲染代码的书写
     23 VOID                            Direct3D_CleanUp( );                //在这个函数中清理COM资源以及其他资源
     24 
     25 //-----------------------------------【WinMain( )函数】--------------------------------------
     26 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
     27 {
     28     //【1】窗口创建四步曲之一:开始设计一个完整的窗口类
     29     WNDCLASSEX wndClass = { 0 };                            //用WINDCLASSEX定义了一个窗口类
     30     wndClass.cbSize = sizeof( WNDCLASSEX ) ;            //设置结构体的字节数大小
     31     wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
     32     wndClass.lpfnWndProc = WndProc;                    //设置指向窗口过程函数的指针
     33     wndClass.cbClsExtra        = 0;                                //窗口类的附加内存,取0就可以了
     34     wndClass.cbWndExtra        = 0;                            //窗口的附加内存,依然取0就行了
     35     wndClass.hInstance = hInstance;                        //指定包含窗口过程的程序的实例句柄。
     36     wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);  //本地加载自定义ico图标
     37     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。
     38     wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);  //为hbrBackground成员指定一个白色画刷句柄    
     39     wndClass.lpszMenuName = NULL;                        //用一个以空终止的字符串,指定菜单资源的名字。
     40     wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";        //用一个以空终止的字符串,指定窗口类的名字。
     41 
     42     //【2】窗口创建四步曲之二:注册窗口类
     43     if( !RegisterClassEx( &wndClass ) )                //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
     44         return -1;        
     45 
     46     //【3】窗口创建四步曲之三:正式创建窗口
     47     HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE,                //喜闻乐见的创建窗口函数CreateWindow
     48         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
     49         WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
     50 
     51     //Direct3D资源的初始化,成功或者失败都用messagebox予以显示
     52     if (S_OK==Direct3D_Init (hwnd))
     53     {
     54         MessageBox(hwnd, L"Direct3D初始化完成~!", L"浅墨的消息窗口", 0); //使用MessageBox函数,创建一个消息窗口  
     55     }
     56     else
     57     {
     58         MessageBox(hwnd, L"Direct3D初始化失败~!", L"浅墨的消息窗口", 0); //使用MessageBox函数,创建一个消息窗口 
     59     }
     60 
     61     //【4】窗口创建四步曲之四:窗口的移动、显示与更新
     62     MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true);        //调整窗口显示时的位置,使窗口左上角位于(250,80)处
     63     ShowWindow( hwnd, nShowCmd );    //调用ShowWindow函数来显示窗口
     64     UpdateWindow(hwnd);                        //对窗口进行更新,就像我们买了新房子要装修一样
     65 
     66 
     67     PlaySound(L"NightElf3.wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP); //循环播放背景音乐 
     68 
     69     //【5】消息循环过程
     70     MSG msg = { 0 };  //初始化msg
     71     while( msg.message != WM_QUIT )            //使用while循环
     72     {
     73         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
     74         {
     75             TranslateMessage( &msg );        //将虚拟键消息转换为字符消息
     76             DispatchMessage( &msg );        //该函数分发一个消息给窗口程序。
     77         }
     78         else
     79         {
     80             Direct3D_Render(hwnd);   //进行渲染
     81         }
     82     }
     83     //【6】窗口类的注销
     84     UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);  //程序准备结束,注销窗口类
     85     return 0;  
     86 }
     87 
     88 //-----------------------------------【WndProc( )函数】--------------------------------------
     89 //    描述:窗口过程函数WndProc,对窗口消息进行处理
     90 //------------------------------------------------------------------------------------------------
     91 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )      
     92 {
     93     switch( message )                        //switch语句开始
     94     {
     95     case WM_PAINT:                        // 若是客户区重绘消息
     96         Direct3D_Render(hwnd);                 //调用Direct3D渲染函数
     97         ValidateRect(hwnd, NULL);        // 更新客户区的显示
     98         break;                                    //跳出该switch语句
     99 
    100     case WM_KEYDOWN:                    // 若是键盘按下消息
    101         if (wParam == VK_ESCAPE)    // 如果被按下的键是ESC
    102             DestroyWindow(hwnd);        // 销毁窗口, 并发送一条WM_DESTROY消息
    103         break;                                    //跳出该switch语句
    104 
    105     case WM_DESTROY:                    //若是窗口销毁消息
    106         Direct3D_CleanUp();            //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
    107         PostQuitMessage( 0 );            //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
    108         break;                                    //跳出该switch语句
    109 
    110     default:                                        //若上述case条件都不符合,则执行该default语句
    111         return DefWindowProc( hwnd, message, wParam, lParam );        //调用缺省的窗口过程
    112     }
    113 
    114     return 0;                                    //正常退出
    115 }
    116 
    117 //-----------------------------------【Direct3D_Init( )函数】--------------------------------------
    118 //    描述:Direct3D初始化函数,进行Direct3D的初始化
    119 //------------------------------------------------------------------------------------------------
    120 HRESULT Direct3D_Init(HWND hwnd)
    121 {
    122     //--------------------------------------------------------------------------------------
    123     // 【Direct3D初始化四步曲之一,创接口】:创建Direct3D接口对象, 以便用该Direct3D对象创建Direct3D设备对象
    124     //--------------------------------------------------------------------------------------
    125     LPDIRECT3D9  pD3D = NULL; //Direct3D接口对象的创建
    126     if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口对象,并进行DirectX版本协商
    127         return E_FAIL;
    128 
    129     //--------------------------------------------------------------------------------------
    130     // 【Direct3D初始化四步曲之二,取信息】:获取硬件设备信息
    131     //--------------------------------------------------------------------------------------
    132     D3DCAPS9 caps; int vp = 0;
    133     if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) )
    134     {
    135         return E_FAIL;
    136     }
    137     if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
    138         vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;   //支持硬件顶点运算,我们就采用硬件顶点运算,妥妥的
    139     else
    140         vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件顶点运算,无奈只好采用软件顶点运算
    141 
    142     //--------------------------------------------------------------------------------------
    143     // 【Direct3D初始化四步曲之三,填内容】:填充D3DPRESENT_PARAMETERS结构体
    144     //--------------------------------------------------------------------------------------
    145     D3DPRESENT_PARAMETERS d3dpp; 
    146     ZeroMemory(&d3dpp, sizeof(d3dpp));
    147     d3dpp.BackBufferWidth            = WINDOW_WIDTH;
    148     d3dpp.BackBufferHeight           = WINDOW_HEIGHT;
    149     d3dpp.BackBufferFormat           = D3DFMT_A8R8G8B8;
    150     d3dpp.BackBufferCount            = 1;
    151     d3dpp.MultiSampleType            = D3DMULTISAMPLE_NONE;
    152     d3dpp.MultiSampleQuality         = 0;
    153     d3dpp.SwapEffect                 = D3DSWAPEFFECT_DISCARD; 
    154     d3dpp.hDeviceWindow              = hwnd;
    155     d3dpp.Windowed                   = true;
    156     d3dpp.EnableAutoDepthStencil     = true; 
    157     d3dpp.AutoDepthStencilFormat     = D3DFMT_D24S8;
    158     d3dpp.Flags                      = 0;
    159     d3dpp.FullScreen_RefreshRateInHz = 0;
    160     d3dpp.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;
    161 
    162     //--------------------------------------------------------------------------------------
    163     // 【Direct3D初始化四步曲之四,创设备】:创建Direct3D设备接口
    164     //--------------------------------------------------------------------------------------
    165     if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 
    166         hwnd, vp, &d3dpp, &g_pd3dDevice)))
    167         return E_FAIL;
    168 
    169     SAFE_RELEASE(pD3D) //LPDIRECT3D9接口对象的使命完成,我们将其释放掉
    170 
    171 
    172     if(!(S_OK==Objects_Init(hwnd))) return E_FAIL;     //调用一次Objects_Init,进行渲染资源的初始化
    173     return S_OK;
    174 }
    175 
    176 
    177 
    178 //-----------------------------------【Object_Init( )函数】--------------------------------------
    179 //    描述:渲染资源初始化函数,在此函数中进行要被渲染的物体的资源的初始化
    180 //--------------------------------------------------------------------------------------------------
    181 HRESULT Objects_Init(HWND hwnd)
    182 {
    183     return S_OK;
    184 }
    185 
    186 
    187 //-----------------------------------【Direct3D_Render( )函数】--------------------------------------
    188 //    描述:使用Direct3D进行渲染
    189 //--------------------------------------------------------------------------------------------------
    190 void Direct3D_Render(HWND hwnd)
    191 {
    192     //暂时为空
    193 }
    194 
    195 
    196 //-----------------------------------【Direct3D_CleanUp( )函数】--------------------------------
    197 //    描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
    198 //---------------------------------------------------------------------------------------------------
    199 void Direct3D_CleanUp()
    200 {
    201     //暂时为空
    202 }
    View Code

    2. Direct3D动画显示技术——交换链

    件重点部分来说:交换链由两个或者两个以上的表面组成,每个表面都是存储着2D图形的一个线性数组,其中每个元素都表示这屏幕上的一个像素。对于三维物体,Direct3D使用深度缓冲区为最终绘制的图像的每个像素都存储一个深度信息。

    前台缓冲区和后台缓冲是位于系统内存或显存里的内存块,对应于将要显示的二维显示区域。前台缓冲区是显示在屏幕上的,后台缓冲区则主要用于图形绘制的准备工作。图像在经过后台缓冲区中的处理后,就可以和前台缓冲区进行一个交换操作,即交换链页面翻转。通过前台缓冲区和后台缓冲区的配合,运用交换链技术,就可以流畅而高效地绘制出漂亮的动画图像来。

    第二个要注意的是页面的翻转是经过交换指向表面内存的指针来实现的

    3. 粗略理解固定功能渲染流水线体系

    空间中的物体需要使用三维坐标来描述,但是显示器是二维的,所以在屏幕上渲染一个三维场景时,首先需要将描述物体的三维坐标变换为二维坐标,这在Direct3D中成为顶点坐标变换。

     

    4. Direct3D设备接口

    简而言之就是在初始化Direct3D的第三步填充 D3DPRESENT_PARAMETERS 时,第八个参数指定了在哪个窗口进行绘制,在第四步中:

    (pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,hwnd, vp, &d3dpp, &g_pd3dDevice)))

     倒数第二个参数指定的是D3DPRESENT_PARAMETERS的实例d3dpp,这样就间接地把hwnd和Direct3D设备联系起来了,之后便可以通过 g_pd3dDevice 指针进行各种绘制操作了。

    5. Direct3D中绘制二维文本

    ID3DXFont接口负责Direct3D应用程序中创建字体以及实现二维文本的绘制,该接口封装了Windows字体和Direct3D设备指针。

    D3DXCreateFont函数,用于创建字体:

    HRESULT D3DXCreateFont(
      _In_  LPDIRECT3DDEVICE9 pDevice,  //Direct3D设备指针
      _In_  INT               Height,  //字体高度
      _In_  UINT              Width,  //字体宽度
      _In_  UINT              Weight,  //字体的权重值
      _In_  UINT              MipLevels,  //字体的过滤属性
      _In_  BOOL              Italic,    //是否是斜体
      _In_  DWORD             CharSet,  //使用的字符集
      _In_  DWORD             OutputPrecision,  //输出文本精度
      _In_  DWORD             Quality,  //输出质量
      _In_  DWORD             PitchAndFamily,  //字体的索引号,通常是奢设为0
      _In_  LPCTSTR           pFacename,  //字体名称,如“微软雅黑”
      _Out_ LPD3DXFONT        *ppFont  //存储我们新创建的字体指针,我们要靠这个它来进行字体绘制相关操作
    );

    DrawText函数,用于绘制文本:

    INT DrawText(
      [in] LPD3DX10SPRITE pSprite,  //指定字符串所属的ID3DXSprite对象接口,设为0表示在当前窗口绘制字符串
      [in] LPCTSTR        pString,  //指定我们将要绘制的字符串内容
      [in] INT            Count,   //指定绘制字符个数,-1表示自动绘制到字符串结束  
      [in] LPRECT         pRect,   //绘制字符串的矩形区域位置 
      [in] UINT           Format,   //指定在上面矩形区域的摆放属性,比如水平居中什么的
      [in] D3DXCOLOR      Color    //指定字符串颜色
    );

    6. Direct3D渲染五部曲 

    五个步骤:

    • 清屏操作
    • 开始场景
    • 正式绘制
    • 结束场景
    • 翻转显示

    第一步:清屏操作

    绘制画面之前需要调用IDirect3DDevice9接口的Clear方法将后台缓冲区中的内容清空,并设置表面填充颜色等。

    HRESULT Clear(
      [in]       DWORD    Count,  //指定接下来的一个参数pRect指向的矩形数组中矩形的数量
      [in] const D3DRECT  *pRects,  //要清空的目标矩形区域
      [in]       DWORD    Flags,  //指定需要清空的缓冲区
      [in]       D3DCOLOR Color,  //指定在清空颜色缓冲区后每个像素对应的颜色值
      [in]       float    Z,    //指定清空深度缓冲区后每个像素对应的深度值
      [in]       DWORD    Stencil  //指定清空缓冲区之后模版缓冲区中每个像素对应的模版值
    );

    调用实例:

    g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    第二步:开始绘制

    g_pd3dDevice->BeginScene();

    注意的是,IDirect3DDevice9::BeginScene() 和 IDirect3DDevice9::EndScene() 是成对出现的。

    第三步:正式绘制

    在BeginScene()方法和EndScene()方法之间,通常很长,这一步的代码取决于我们想要绘制什么样的内容。

    第四步:结束绘制

    调用EndScene()方法:

    g_pd3dDevice->EndScene();  

    第五步:翻转显示

    调用Present()函数,一个很重要的函数:

    HRESULT Present(
      [in] const RECT    *pSourceRect,  //指向复制源矩形区域指针。一般设为NULL
      [in] const RECT    *pDestRect,  //指向复制目标矩形区域指针。一般也设为NULL
      [in]       HWND    hDestWindowOverride,  //指向当前绘制的窗口句柄,设为0或NULL表示取之前初始化第三步填充D3DPRESENT_PARAMETERS结构题中的 hDeviceWindows值,显然一般设为NULL
      [in] const RGNDATA *pDirtyRegion  //指向最小更新区域的指针,一般设为NULL
    );

     四个设为NULL的参数。。。。。额。。。还真好记。

    g_pd3dDevice->Present(NULL, NULL, NULL, NULL);  // 翻转与显示

     来个示例程序,功能是获取每秒帧数函数:

      1 #include <d3d9.h>
      2 #include <d3dx9.h>
      3 #include <tchar.h>
      4 
      5 //-----------------------------------【库文件包含部分】---------------------------------------
      6 #pragma comment(lib,"d3d9.lib")
      7 #pragma comment(lib,"d3dx9.lib")
      8 
      9 //-----------------------------------【宏定义部分】--------------------------------------------
     10 #define WINDOW_WIDTH    800                            //为窗口宽度定义的宏,以方便在此处修改窗口宽度
     11 #define WINDOW_HEIGHT    600                            //为窗口高度定义的宏,以方便在此处修改窗口高度
     12 #define WINDOW_TITLE    L"【致我们永不熄灭的游戏开发梦想】Direct3D渲染五步曲 示例程序"    //为窗口标题定义的宏
     13 #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }   //定义一个安全释放宏,便于后面COM接口指针的释放
     14 
     15 //-----------------------------------【全局变量声明部分】-------------------------------------
     16 //    描述:全局变量的声明
     17 //------------------------------------------------------------------------------------------------
     18 LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; //Direct3D设备对象
     19 ID3DXFont*                     g_pFont=NULL;    //字体COM接口
     20 float                                 g_FPS = 0.0f;       //一个浮点型的变量,代表帧速率
     21 wchar_t                             g_strFPS[50];    //包含帧速率的字符数组
     22 
     23 
     24 //-----------------------------------【全局函数声明部分】-------------------------------------
     25 //    描述:全局函数声明,防止“未声明的标识”系列错误
     26 //------------------------------------------------------------------------------------------------
     27 LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );//窗口过程函数
     28 HRESULT                    Direct3D_Init(HWND hwnd);         //在这个函数中进行Direct3D的初始化
     29 HRESULT                    Objects_Init(HWND hwnd);         //在这个函数中进行要绘制的物体的资源初始化
     30 VOID                            Direct3D_Render(HWND hwnd);     //在这个函数中进行Direct3D渲染代码的书写
     31 VOID                            Direct3D_CleanUp( );                //在这个函数中清理COM资源以及其他资源
     32 float                            Get_FPS();
     33 
     34 //-----------------------------------【WinMain( )函数】--------------------------------------
     35 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)
     36 {
     37     //【1】窗口创建四步曲之一:开始设计一个完整的窗口类
     38     WNDCLASSEX wndClass = { 0 };                            //用WINDCLASSEX定义了一个窗口类
     39     wndClass.cbSize = sizeof( WNDCLASSEX ) ;            //设置结构体的字节数大小
     40     wndClass.style = CS_HREDRAW | CS_VREDRAW;    //设置窗口的样式
     41     wndClass.lpfnWndProc = WndProc;                    //设置指向窗口过程函数的指针
     42     wndClass.cbClsExtra        = 0;                                //窗口类的附加内存,取0就可以了
     43     wndClass.cbWndExtra        = 0;                            //窗口的附加内存,依然取0就行了
     44     wndClass.hInstance = hInstance;                        //指定包含窗口过程的程序的实例句柄。
     45     wndClass.hIcon=(HICON)::LoadImage(NULL,L"icon.ico",IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE);  //本地加载自定义ico图标
     46     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。
     47     wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);  //为hbrBackground成员指定一个白色画刷句柄    
     48     wndClass.lpszMenuName = NULL;                        //用一个以空终止的字符串,指定菜单资源的名字。
     49     wndClass.lpszClassName = L"ForTheDreamOfGameDevelop";        //用一个以空终止的字符串,指定窗口类的名字。
     50 
     51     //【2】窗口创建四步曲之二:注册窗口类
     52     if( !RegisterClassEx( &wndClass ) )                //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口
     53         return -1;        
     54 
     55     //【3】窗口创建四步曲之三:正式创建窗口
     56     HWND hwnd = CreateWindow( L"ForTheDreamOfGameDevelop",WINDOW_TITLE,                //喜闻乐见的创建窗口函数CreateWindow
     57         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
     58         WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
     59 
     60     //Direct3D资源的初始化,调用失败用messagebox予以显示
     61     if (!(S_OK==Direct3D_Init (hwnd)))
     62     {
     63         MessageBox(hwnd, _T("Direct3D初始化失败~!"), _T("浅墨的消息窗口"), 0); //使用MessageBox函数,创建一个消息窗口 
     64     }
     65 
     66     //【4】窗口创建四步曲之四:窗口的移动、显示与更新
     67     MoveWindow(hwnd,250,80,WINDOW_WIDTH,WINDOW_HEIGHT,true);        //调整窗口显示时的位置,使窗口左上角位于(250,80)处
     68     ShowWindow( hwnd, nShowCmd );    //调用ShowWindow函数来显示窗口
     69     UpdateWindow(hwnd);                        //对窗口进行更新,就像我们买了新房子要装修一样
     70 
     71     //【5】消息循环过程
     72     MSG msg = { 0 };  //初始化msg
     73     while( msg.message != WM_QUIT )            //使用while循环
     74     {
     75         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
     76         {
     77             TranslateMessage( &msg );        //将虚拟键消息转换为字符消息
     78             DispatchMessage( &msg );        //该函数分发一个消息给窗口程序。
     79         }
     80         else
     81         {
     82             Direct3D_Render(hwnd);   //进行渲染
     83         }
     84     }
     85     //【6】窗口类的注销
     86     UnregisterClass(L"ForTheDreamOfGameDevelop", wndClass.hInstance);  //程序准备结束,注销窗口类
     87     return 0;  
     88 }
     89 
     90 //-----------------------------------【WndProc( )函数】--------------------------------------
     91 //    描述:窗口过程函数WndProc,对窗口消息进行处理
     92 //------------------------------------------------------------------------------------------------
     93 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )      
     94 {
     95     switch( message )                        //switch语句开始
     96     {
     97     case WM_PAINT:                        // 若是客户区重绘消息
     98         Direct3D_Render(hwnd);                 //调用Direct3D渲染函数
     99         ValidateRect(hwnd, NULL);        // 更新客户区的显示
    100         break;                                    //跳出该switch语句
    101 
    102     case WM_KEYDOWN:                    // 若是键盘按下消息
    103         if (wParam == VK_ESCAPE)    // 如果被按下的键是ESC
    104             DestroyWindow(hwnd);        // 销毁窗口, 并发送一条WM_DESTROY消息
    105         break;                                    //跳出该switch语句
    106 
    107     case WM_DESTROY:                    //若是窗口销毁消息
    108         Direct3D_CleanUp();            //调用自定义的资源清理函数Game_CleanUp()进行退出前的资源清理
    109         PostQuitMessage( 0 );            //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息
    110         break;                                    //跳出该switch语句
    111 
    112     default:                                        //若上述case条件都不符合,则执行该default语句
    113         return DefWindowProc( hwnd, message, wParam, lParam );        //调用缺省的窗口过程
    114     }
    115 
    116     return 0;                                    //正常退出
    117 }
    118 
    119 //-----------------------------------【Direct3D_Init( )函数】--------------------------------------
    120 //    描述:Direct3D初始化函数,进行Direct3D的初始化
    121 //------------------------------------------------------------------------------------------------
    122 HRESULT Direct3D_Init(HWND hwnd)
    123 {
    124     //--------------------------------------------------------------------------------------
    125     // 【Direct3D初始化四步曲之一,创接口】:创建Direct3D接口对象, 以便用该Direct3D对象创建Direct3D设备对象
    126     //--------------------------------------------------------------------------------------
    127     LPDIRECT3D9  pD3D = NULL; //Direct3D接口对象的创建
    128     if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口对象,并进行DirectX版本协商
    129         return E_FAIL;
    130 
    131     //--------------------------------------------------------------------------------------
    132     // 【Direct3D初始化四步曲之二,取信息】:获取硬件设备信息
    133     //--------------------------------------------------------------------------------------
    134     D3DCAPS9 caps; int vp = 0;
    135     if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) )
    136     {
    137         return E_FAIL;
    138     }
    139     if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
    140         vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;   //支持硬件顶点运算,我们就采用硬件顶点运算,妥妥的
    141     else
    142         vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件顶点运算,无奈只好采用软件顶点运算
    143 
    144     //--------------------------------------------------------------------------------------
    145     // 【Direct3D初始化四步曲之三,填内容】:填充D3DPRESENT_PARAMETERS结构体
    146     //--------------------------------------------------------------------------------------
    147     D3DPRESENT_PARAMETERS d3dpp; 
    148     ZeroMemory(&d3dpp, sizeof(d3dpp));
    149     d3dpp.BackBufferWidth            = WINDOW_WIDTH;
    150     d3dpp.BackBufferHeight           = WINDOW_HEIGHT;
    151     d3dpp.BackBufferFormat           = D3DFMT_A8R8G8B8;
    152     d3dpp.BackBufferCount            = 1;
    153     d3dpp.MultiSampleType            = D3DMULTISAMPLE_NONE;
    154     d3dpp.MultiSampleQuality         = 0;
    155     d3dpp.SwapEffect                 = D3DSWAPEFFECT_DISCARD; 
    156     d3dpp.hDeviceWindow              = hwnd;
    157     d3dpp.Windowed                   = true;
    158     d3dpp.EnableAutoDepthStencil     = true; 
    159     d3dpp.AutoDepthStencilFormat     = D3DFMT_D24S8;
    160     d3dpp.Flags                      = 0;
    161     d3dpp.FullScreen_RefreshRateInHz = 0;
    162     d3dpp.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;
    163 
    164     //--------------------------------------------------------------------------------------
    165     // 【Direct3D初始化四步曲之四,创设备】:创建Direct3D设备接口
    166     //--------------------------------------------------------------------------------------
    167     if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 
    168         hwnd, vp, &d3dpp, &g_pd3dDevice)))
    169         return E_FAIL;
    170 
    171     SAFE_RELEASE(pD3D) //LPDIRECT3D9接口对象的使命完成,我们将其释放掉
    172 
    173         if(!(S_OK==Objects_Init(hwnd))) return E_FAIL;     //调用一次Objects_Init,进行渲染资源的初始化
    174     return S_OK;
    175 }
    176 
    177 
    178 
    179 //-----------------------------------【Object_Init( )函数】--------------------------------------
    180 //    描述:渲染资源初始化函数,在此函数中进行要被渲染的物体的资源的初始化
    181 //--------------------------------------------------------------------------------------------------
    182 HRESULT Objects_Init(HWND hwnd)
    183 {
    184     //创建字体
    185     if(FAILED(D3DXCreateFont(g_pd3dDevice, 36, 0, 0, 1, false, DEFAULT_CHARSET, 
    186         OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, _T("微软雅黑"), &g_pFont)))
    187         return E_FAIL;
    188     srand(timeGetTime());      //用系统时间初始化随机种子 
    189     return S_OK;
    190 }
    191 
    192 
    193 //-----------------------------------【Direct3D_Render( )函数】-------------------------------
    194 //    描述:使用Direct3D进行渲染
    195 //--------------------------------------------------------------------------------------------------
    196 void Direct3D_Render(HWND hwnd)
    197 {
    198     //--------------------------------------------------------------------------------------
    199     // 【Direct3D渲染五步曲之一】:清屏操作
    200     //--------------------------------------------------------------------------------------
    201     g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    202 
    203     //定义一个矩形,用于获取主窗口矩形
    204     RECT formatRect;
    205     GetClientRect(hwnd, &formatRect);
    206 
    207     //--------------------------------------------------------------------------------------
    208     // 【Direct3D渲染五步曲之二】:开始绘制
    209     //--------------------------------------------------------------------------------------
    210     g_pd3dDevice->BeginScene();                     // 开始绘制
    211     //--------------------------------------------------------------------------------------
    212     // 【Direct3D渲染五步曲之三】:正式绘制,在这里我们写了四段文字
    213     //--------------------------------------------------------------------------------------
    214     //在窗口右上角处,显示每秒帧数
    215     int charCount = swprintf_s(g_strFPS, 20, _T("FPS:%0.3f"), Get_FPS() );
    216     g_pFont->DrawText(NULL, g_strFPS, charCount , &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_XRGB(255,39,136));
    217 
    218     //在纵坐标100处,写第一段文字
    219     formatRect.top = 100;//指定文字的纵坐标
    220     g_pFont->DrawText(0, _T("【致我们永不熄灭的游戏开发梦想】"), -1, &formatRect, DT_CENTER, 
    221         D3DCOLOR_XRGB(68,139,256));
    222 
    223     //在纵坐标250处,写第二段文字
    224     formatRect.top = 250;
    225     g_pFont->DrawText(0, _T("游戏开发的世界,我们来降服你了~!"), -1, &formatRect, 
    226         DT_CENTER, D3DCOLOR_XRGB(255,255,255));
    227 
    228     //在纵坐标400处,写第三段文字
    229     formatRect.top = 400;
    230     g_pFont->DrawText(0, _T("闪闪惹人爱"), -1, &formatRect, DT_CENTER, 
    231         D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256));//采用随机RGB值,做出“闪闪惹人爱”的特效
    232 
    233 
    234     //--------------------------------------------------------------------------------------
    235     // 【Direct3D渲染五步曲之四】:结束绘制
    236     //--------------------------------------------------------------------------------------
    237     g_pd3dDevice->EndScene();                       // 结束绘制
    238     //--------------------------------------------------------------------------------------
    239     // 【Direct3D渲染五步曲之五】:显示翻转
    240     //--------------------------------------------------------------------------------------
    241     g_pd3dDevice->Present(NULL, NULL, NULL, NULL);  // 翻转与显示
    242 }
    243 
    244 //-----------------------------------【Get_FPS( )函数】------------------------------------------
    245 //    描述:用于计算每秒帧速率的一个函数
    246 //--------------------------------------------------------------------------------------------------
    247 float Get_FPS()
    248 {
    249     //定义四个静态变量
    250     static float  fps = 0; //我们需要计算的FPS值
    251     static int     frameCount = 0;//帧数
    252     static float  currentTime =0.0f;//当前时间
    253     static float  lastTime = 0.0f;//持续时间
    254 
    255     frameCount++;//每调用一次Get_FPS()函数,帧数自增1
    256     currentTime = timeGetTime()*0.001f;//获取系统时间,其中timeGetTime函数返回的是以毫秒为单位的系统时间,所以需要乘以0.001,得到单位为秒的时间
    257 
    258     //如果当前时间减去持续时间大于了1秒钟,就进行一次FPS的计算和持续时间的更新,并将帧数值清零
    259     if(currentTime - lastTime > 1.0f) //将时间控制在1秒钟
    260     {
    261         fps = (float)frameCount /(currentTime - lastTime);//计算这1秒钟的FPS值
    262         lastTime = currentTime; //将当前时间currentTime赋给持续时间lastTime,作为下一秒的基准时间
    263         frameCount    = 0;//将本次帧数frameCount值清零
    264     }
    265 
    266     return fps;
    267 }
    268 
    269 //-----------------------------------【Direct3D_CleanUp( )函数】--------------------------------
    270 //    描述:资源清理函数,在此函数中进行程序退出前资源的清理工作
    271 //---------------------------------------------------------------------------------------------------
    272 void Direct3D_CleanUp()
    273 {
    274     //释放COM接口对象
    275     SAFE_RELEASE(g_pFont)
    276     SAFE_RELEASE(g_pd3dDevice)
    277 }
    View Code

    注:


    参考博客:

    【Visual C++】游戏开发笔记三十二 浅墨DirectX提高班之一 DirectX大局观认知篇

    【Visual C++】游戏开发笔记三十三 浅墨DirectX提高班之二 化腐朽为神奇:DirectX初始化四步曲

    【Visual C++】游戏开发笔记三十四 浅墨DirectX提高班之三 起承转合的艺术:Direct3D渲染五步曲

  • 相关阅读:
    全网显示 IP 归属地,是怎么实现的?
    雪花算法SnowFlake生成唯一ID
    Mac电脑清空搜狗输入法联想记忆词库
    git merge 冲突解决和删除远程分支
    微软宣布IE浏览器6月正式退役 27年历史承载网民回忆
    情侣头像动漫:超宠老婆的情侣头像动漫高清
    链家40岁员工删除公司9T数据,被判7年
    ArcGIS Pro二次开发改变一个按钮的名称
    How to access or set Text Symbol from Table Frame using ArcGIS Pro SDK
    ArcGIS Pro二次开发SDK删除一个按钮
  • 原文地址:https://www.cnblogs.com/f91og/p/7173544.html
Copyright © 2020-2023  润新知