转载请注明出处:http://www.cnblogs.com/Ray1024

一、概述

在此系列最开始的文章Direct3D11学习:(一)开发环境配置中,我们运行了一个例子BoxDemo,看过这个例子源码的朋友都会发现,代码量比较大,但是Win32窗口初始化和Direct3D11初始化工作占用了很多大一部分代码,然而,我们真正关心的绘制代码并不是这些。

为了避免以后每次创建演示程序都需要重复的初始化工作,把我们的注意力集中在演示程序度所要表达的特定细节上,我们把重复的初始化代码封装到一个简单的程序框架D3D11App中,位于D3D11App.h和D3D11App.cpp文件中。D3D11App.h和D3D11App.cpp文件包含了窗口初始化和D3D11初始化的核心代码,可以在新的演示程序中包含它,导入该框架,直接编写我们的核心代码就可以了。

二、演示程序框架

2.1 D3D11Util.h文件

在介绍演示程序框架之前,我们需要介绍一组文件D3D11Util.h和D3D11Util.cpp。

这组文件包含了一些有用的工具代码,都是程序中常用的工具。比如COM对象安全释放的宏ReleaseCOM()、HRESULT值的错误处理宏HR()和常用颜色值定义等等。

这里解释一下HRESULT值的错误处理宏HR():

  1. #ifndef HR
  2. #define HR(x) \
  3. { \
  4. HRESULT hr = (x); \
  5. if(FAILED(hr)) \
  6. { \
  7. DXTrace(__FILE__, (DWORD)__LINE__, hr, L#x, true); \
  8. } \
  9. }
  10. #endif

在这个宏中,使用了DX的错误处理库dxerr.lib库中的函数DXTrace。当函数的返回值表明调用失败时,我们把返回值传递给DXTrace函数。如果hr=S_FALSE时,弹出消息框显示错误信息。

HR()宏使用方法:

  1. HR(m_pD3DDevice->CheckMultisampleQualityLevels(
  2. DXGI_FORMAT_R8G8B8A8_UNORM, 4, &m_4xMsaaQuality));

D3D11Util.h和D3D11Util.cpp文件中其他的工具在这里就不介绍了,有兴趣的朋友可以在文章最后的下载源码处下载浏览源码。

2.2 核心类D3D11App

D3D11App是我们学习D3D11中所有应用程序类的基类,它提供了用于创建主应用程序窗口、运行应用程序消息循环、处理窗口消息和初始化D3D11的函数。另外,这个类还定义了一些框架函数。所有的D3D11应用程序类都继承于D3D11App类,重载它的virtual框架函数,并创建一个D3D11App派生类的单例对象。D3D11App类的定义如下:

  1. class D3D11App
  2. {
  3. public:
  4. D3D11App(HINSTANCE hInstance);
  5. virtual ~D3D11App();
  6.  
  7. // 获取应用程序实例句柄
  8. HINSTANCE AppInst()const;
  9. // 获取主窗口句柄
  10. HWND MainWnd()const;
  11. // 后台缓存区的长宽比
  12. float AspectRatio()const;
  13. // 应用程序消息循环
  14. int Run();
  15.  
  16. // 框架方法
  17. // 派生类需要重载这些方法实现所需的功能
  18.  
  19. virtual bool Init();
  20. virtual void OnResize();
  21. virtual void UpdateScene(float dt)=0;
  22. virtual void DrawScene()=0;
  23. virtual LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  24.  
  25. // 处理鼠标输入事件的便捷重载函数
  26. virtual void OnMouseDown(WPARAM btnState, int x, int y){ }
  27. virtual void OnMouseUp(WPARAM btnState, int x, int y) { }
  28. virtual void OnMouseMove(WPARAM btnState, int x, int y){ }
  29.  
  30. protected:
  31.  
  32. // 创建窗口
  33. bool InitMainWindow();
  34.  
  35. // 初始化D3D
  36. bool InitDirect3D();
  37.  
  38. // 计算帧率
  39. void CalculateFrameStats();
  40.  
  41. protected:
  42.  
  43. HINSTANCE m_hAppInst; // 应用程序实例句柄
  44. HWND m_hMainWnd; // 主窗口句柄
  45. bool m_appPaused; // 程序是否处在暂停状态
  46. bool m_minimized; // 程序是否最小化
  47. bool m_maximized; // 程序是否最大化
  48. bool m_Resizing; // 程序是否处在改变大小的状态
  49. UINT m_4xMsaaQuality; // 4X MSAA质量等级
  50.  
  51. GameTimer m_timer; // 用于记录deltatime和游戏时间
  52.  
  53. ID3D11Device* m_pD3DDevice; // D3D11设备
  54. ID3D11DeviceContext* m_pD3DImmediateContext; // 上下文
  55. IDXGISwapChain* m_pSwapChain; // 交换链
  56. ID3D11Texture2D* m_pDepthStencilBuffer; // 深度缓冲区
  57. ID3D11RenderTargetView* m_pRenderTargetView; // 渲染目标视图
  58. ID3D11DepthStencilView* m_pDepthStencilView; // 深度缓冲视图
  59. D3D11_VIEWPORT m_screenViewport; // 视口
  60.  
  61. std::wstring m_mainWndCaption; // 窗口标题
  62. D3D_DRIVER_TYPE m_D3DDriverType; // 是否使用硬件加速
  63. int m_clientWidth; // 窗口大小
  64. int m_clientHeight; // 窗口大小
  65. bool m_enable4xMsaa; // 是否使用4XMSAA
  66. };

上面的代码中有注释,从注释中我们可以看到成员函数和变量的作用。

下面的部分我们将详细介绍D3D11App一部分成员函数。

2.3 D3D11App部分成员函数介绍

在这节中我们主要介绍D3D11App类的成员函数中的框架方法。所谓框架方法,就是D3D11App中的虚函数,在之后学习的每个演示程序中,我们可以重载这些方法来实现特定示例中的代码细节。D3D11App类实现的这种结构可以将所有的初始化代码、消息处理代码和其他代码安排得井井有条,使派生类专注于实现演示程序的特定代码。下面是对这些框架方法的描述:

(1)Init:该方法包含应用程序的初始化代码,比如分配资源、初始化对象和设置灯光。该方法在D3D11App的实现中包含InitMainWindow和InitDirect3D方法的调用语句;所以,当在派生类中重载该方法时,应首先调用该方法的D3D11App版本,就像下面这样:

  1. void TestApp::Init()
  2. {
  3. if(!D3D11App::Init())
  4. return false;
  5. /* 剩下的初始化代码从这里开始 */
  6. }

(2) OnResize:该方法在D3D11App::MsgProc收到WM_SIZE消息时调用。当窗口的尺寸改变时,一些与客户区大小相关的Direct3D属性也需要改变。尤其是需要重新创建后台缓冲区和深度/模板缓冲区,使它们与窗口客户区的大小一致。后台缓冲区的大小可以通过调用IDXGISwapChain::ResizeBuffers方法来进行调整。而深度/模板缓冲区必须被销毁,然后根据新的大小重新创建。另外,渲染目标视图和深度/模板视图也必须重新创建。OnResize方法在D3D11App的实现中包含了调整后台缓冲区和深度/模板缓冲区的代码;详情请直接参见源代码。除缓冲区外,依赖于客户区大小的其他属性(例如,投影矩阵)也必须重新创建。我们把该方法作为框架的一部分是因为当窗口大小改变时,客户代码可能需要执行一些它自己的逻辑。

(3)UpdateScene:该抽象方法每帧都会调用,用于随着时间更新3D应用程序(例如,实现动画和碰撞检测、检查用户输入、计算每秒帧数等等)。

(4)DrawScene:该抽象方法每帧都会调用,用于将3D场景的当前帧绘制到后台缓冲区。当绘制当前帧时,我们调用了IDXGISwapChain::Present方法将后台缓冲区的内容呈现在屏幕上。

(5)MsgProc:该方法是主应用程序窗口的消息处理函数。通常,当你只需重载该方法,就可以处理未由D3D11App::MsgProc处理(或者没按照你所希望的方式处理)的消息。如果你重载了这个方法,那么那些你没有处理的消息都会送到D3D11App::MsgProc中进行处理。

另外,除了上述的五个框架方法之外,为了使用起来更方便,我们还提供了三个虚函数,用于处理鼠标点击、释放和移动的事件。

  1. virtual void OnMouseDown(WPARAM btnState, int x, int y){ }
  2. virtual void OnMouseUp(WPARAM btnState, int x, int y) { }
  3. virtual void OnMouseMove(WPARAM btnState, int x, int y){ }

你可以重载这些方法处理鼠标事件,而用不着重载MsgProc方法。这些方法的第一个参数WPARAM都是相同的,保存了鼠标按键的状态(例如,哪个鼠标按键被按下),第二、三个参数是光标在客户区域的(x,y)坐标。

2.4 将程序框架导入新建演示程序

这个框架还需要另外一个部分:游戏计时器,上一篇文章我们介绍过了游戏计时器的实现,这里就不提了。

我们将上面提到的三组文件(D3D11Util.h和D3D11Util.cpp、D3D11App.h和D3D11App.cpp、GameTimer.h和GameTimer.cpp)放到一个文件夹Common中,这样每个演示程序都可以使用,避免多次复制了。我们的演示程序需要使用程序框架时,需要导入框架,即把Common文件夹中的文件添加到演示程序中,并把Common文件夹的目录添加到演示程序项目属性的包含目录中。

这样,程序框架就成功地导入到新建演示程序中了,我们接下来就可以使用程序框架编写演示程序了。

2.5 使用框架编写演示程序

(1)创建一个继承自主框架类D3D11App的类TestApp:

  1. class TestApp : public D3D11App
  2. {
  3. public:
  4. TestApp(HINSTANCE hInstance);
  5.  
  6. void UpdateScene(float dt);
  7. void DrawScene();
  8. };

(2)添加TestApp类的成员函数实现

  1. //
  2. // TestApp Implement
  3. //
  4.  
  5. TestApp::TestApp(HINSTANCE hInstance)
  6. : D3D11App(hInstance)
  7. {
  8. m_mainWndCaption = L"2_D3DTimingAndAnimation";
  9. }
  10.  
  11. void TestApp::UpdateScene(float dt)
  12. {
  13.  
  14. }
  15.  
  16. void TestApp::DrawScene()
  17. {
  18. assert(m_pD3DImmediateContext);
  19. assert(m_pSwapChain);
  20.  
  21. m_pD3DImmediateContext->ClearRenderTargetView(m_pRenderTargetView, reinterpret_cast<const float*>(&Colors::LightSteelBlue));
  22. m_pD3DImmediateContext->ClearDepthStencilView(m_pDepthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0);
  23.  
  24. HR(m_pSwapChain->Present(0, 0));
  25. }

(3)添加程序入口

  1. // 程序入口
  2. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
  3. PSTR cmdLine, int showCmd)
  4. {
  5. #if defined(DEBUG) | defined(_DEBUG)
  6. _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
  7. #endif
  8.  
  9. TestApp theApp(hInstance);
  10.  
  11. if( !theApp.Init() )
  12. return 0;
  13.  
  14. return theApp.Run();
  15. }

到此为止,我们已经利用程序框架成功地生成了一个演示程序。

有兴趣看源码的朋友可以点击这里下载,源码为3_D3DFrame文件夹。

三、结语

基本上,我们用不着做任何实际工作就可以实现这个程序,因为基类D3D11App已经实现了它所需要的大部分功能。

有了程序框架之后,我们在学习过程中的演示程序的编写就简单多了,直接导入程序框架,只写我们关心的核心代码就可以了。

Direct3D11学习:(五)演示程序框架的更多相关文章

  1. (转)MyBatis框架的学习(五)——一对一关联映射和一对多关联映射

    http://blog.csdn.net/yerenyuan_pku/article/details/71894172 在实际开发中我们不可能只是对单表进行操作,必然要操作多表,本文就来讲解多表操作中 ...

  2. Direct3D11学习:(八)Effects介绍

    转载请注明出处:http://www.cnblogs.com/Ray1024 一.概述 Effects框架是一组用于管理着色器程序和渲染状态的工具代码.例如,你可能会使用不同的effect绘制水.云. ...

  3. Direct3D11学习:(七)绘图基础——彩色立方体的绘制

    转载请注明出处:http://www.cnblogs.com/Ray1024 一.概述 在前面的几篇文章中,我们详细介绍了Direct3D渲染所需要的数学基础和渲染管道理论知识.从这篇文章开始,我们就 ...

  4. Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析

    经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...

  5. Java并发包源码学习之AQS框架(三)LockSupport和interrupt

    接着上一篇文章今天我们来介绍下LockSupport和Java中线程的中断(interrupt). 其实除了LockSupport,Java之初就有Object对象的wait和notify方法可以实现 ...

  6. Java并发包源码学习之AQS框架(一)概述

    AQS其实就是java.util.concurrent.locks.AbstractQueuedSynchronizer这个类. 阅读Java的并发包源码你会发现这个类是整个java.util.con ...

  7. [源码解析] 深度学习分布式训练框架 Horovod (1) --- 基础知识

    [源码解析] 深度学习分布式训练框架 Horovod --- (1) 基础知识 目录 [源码解析] 深度学习分布式训练框架 Horovod --- (1) 基础知识 0x00 摘要 0x01 分布式并 ...

  8. [源码解析] 深度学习分布式训练框架 horovod (5) --- 融合框架

    [源码解析] 深度学习分布式训练框架 horovod (5) --- 融合框架 目录 [源码解析] 深度学习分布式训练框架 horovod (5) --- 融合框架 0x00 摘要 0x01 架构图 ...

  9. [源码解析] 深度学习分布式训练框架 horovod (10) --- run on spark

    [源码解析] 深度学习分布式训练框架 horovod (10) --- run on spark 目录 [源码解析] 深度学习分布式训练框架 horovod (10) --- run on spark ...

随机推荐

  1. Transistor 晶体管 场效应 双极型 达林顿 CMOS PMOS BJT FET

    Transistor Tutorial Summary Transistor Tutorial Summary Bipolar Junction Transistor Tutorial We can ...

  2. Task中的异常处理

    最简单的方式 var t = new Task(() => { throw new Exception("unknow excption"); }); t.Start(); ...

  3. Linux--U盘安装Ubuntu12.04

    前言 最近一直在研究Android内核驱动开发的相关事宜,使用VMware虚拟机虽然可以更方便的开发,但是对于开发环境硬件的要求还是比较高的,若用于开发,效率太低了,所以考虑使用单独PC去装载Linu ...

  4. velocity 显示List和Map方法

    一.遍历个map类型 1.先看后台java程序Java代码     Map<String,String> paramValues=new HashMap<String, String ...

  5. 用户管理 之 Linux 系统中的超级权限的控制

    在Linux操作系统中,root的权限是最高的,也被称为超级权限的拥有者.普通用户无法执行的操作,root用户都能完成,所以也被称之为超级管理用户. 在系统中,每个文件.目录和进程,都归属于某一个用户 ...

  6. WP主题制作常用标签代码

    WordPress模板结构 style.css : CSS文件index.php : 主页模板archive.php : Archive/Category模板404.php : Not Found 错 ...

  7. 转:HTML5标准与性能之四:asm.js

    HTML5标准与性能之四:asm.js Cong Liu (Intel) 于 星期五, 24/05/2013 - 01:13 提交 之前的几篇文章分别介绍了WebWorkers.Typed Array ...

  8. 关于在mac 配置eclipse c开发

    新建一个c 项目,如下 勾选hello world ANSL project ,勾选右边的MacOSX GCC 安装插件CDT - http://download.eclipse.org/tools/ ...

  9. Lists in Prolog

    Symbols in Prolog: atom variable number list (how to assembly and take them apart)   Lists are very  ...

  10. 【原】Jqxgrid在Java服务器端分页

    研究这个后台分页一天多,特此写个文章记录备忘 jsp页面中有两个需要注意的地方:一个是source中beforeprocessing,另一个是rendergridrows中数据的获取. 说明:grid ...