Direct3D11学习:(五)演示程序框架
转载请注明出处: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():
#ifndef HR
#define HR(x) \
{ \
HRESULT hr = (x); \
if(FAILED(hr)) \
{ \
DXTrace(__FILE__, (DWORD)__LINE__, hr, L#x, true); \
} \
}
#endif
在这个宏中,使用了DX的错误处理库dxerr.lib库中的函数DXTrace。当函数的返回值表明调用失败时,我们把返回值传递给DXTrace函数。如果hr=S_FALSE时,弹出消息框显示错误信息。
HR()宏使用方法:
HR(m_pD3DDevice->CheckMultisampleQualityLevels(
DXGI_FORMAT_R8G8B8A8_UNORM, 4, &m_4xMsaaQuality));
D3D11Util.h和D3D11Util.cpp文件中其他的工具在这里就不介绍了,有兴趣的朋友可以在文章最后的下载源码处下载浏览源码。
2.2 核心类D3D11App
D3D11App是我们学习D3D11中所有应用程序类的基类,它提供了用于创建主应用程序窗口、运行应用程序消息循环、处理窗口消息和初始化D3D11的函数。另外,这个类还定义了一些框架函数。所有的D3D11应用程序类都继承于D3D11App类,重载它的virtual框架函数,并创建一个D3D11App派生类的单例对象。D3D11App类的定义如下:
class D3D11App
{
public:
D3D11App(HINSTANCE hInstance);
virtual ~D3D11App(); // 获取应用程序实例句柄
HINSTANCE AppInst()const;
// 获取主窗口句柄
HWND MainWnd()const;
// 后台缓存区的长宽比
float AspectRatio()const;
// 应用程序消息循环
int Run(); // 框架方法
// 派生类需要重载这些方法实现所需的功能 virtual bool Init();
virtual void OnResize();
virtual void UpdateScene(float dt)=0;
virtual void DrawScene()=0;
virtual LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); // 处理鼠标输入事件的便捷重载函数
virtual void OnMouseDown(WPARAM btnState, int x, int y){ }
virtual void OnMouseUp(WPARAM btnState, int x, int y) { }
virtual void OnMouseMove(WPARAM btnState, int x, int y){ } protected: // 创建窗口
bool InitMainWindow(); // 初始化D3D
bool InitDirect3D(); // 计算帧率
void CalculateFrameStats(); protected: HINSTANCE m_hAppInst; // 应用程序实例句柄
HWND m_hMainWnd; // 主窗口句柄
bool m_appPaused; // 程序是否处在暂停状态
bool m_minimized; // 程序是否最小化
bool m_maximized; // 程序是否最大化
bool m_Resizing; // 程序是否处在改变大小的状态
UINT m_4xMsaaQuality; // 4X MSAA质量等级 GameTimer m_timer; // 用于记录deltatime和游戏时间 ID3D11Device* m_pD3DDevice; // D3D11设备
ID3D11DeviceContext* m_pD3DImmediateContext; // 上下文
IDXGISwapChain* m_pSwapChain; // 交换链
ID3D11Texture2D* m_pDepthStencilBuffer; // 深度缓冲区
ID3D11RenderTargetView* m_pRenderTargetView; // 渲染目标视图
ID3D11DepthStencilView* m_pDepthStencilView; // 深度缓冲视图
D3D11_VIEWPORT m_screenViewport; // 视口 std::wstring m_mainWndCaption; // 窗口标题
D3D_DRIVER_TYPE m_D3DDriverType; // 是否使用硬件加速
int m_clientWidth; // 窗口大小
int m_clientHeight; // 窗口大小
bool m_enable4xMsaa; // 是否使用4XMSAA
};
上面的代码中有注释,从注释中我们可以看到成员函数和变量的作用。
下面的部分我们将详细介绍D3D11App一部分成员函数。
2.3 D3D11App部分成员函数介绍
在这节中我们主要介绍D3D11App类的成员函数中的框架方法。所谓框架方法,就是D3D11App中的虚函数,在之后学习的每个演示程序中,我们可以重载这些方法来实现特定示例中的代码细节。D3D11App类实现的这种结构可以将所有的初始化代码、消息处理代码和其他代码安排得井井有条,使派生类专注于实现演示程序的特定代码。下面是对这些框架方法的描述:
(1)Init:该方法包含应用程序的初始化代码,比如分配资源、初始化对象和设置灯光。该方法在D3D11App的实现中包含InitMainWindow和InitDirect3D方法的调用语句;所以,当在派生类中重载该方法时,应首先调用该方法的D3D11App版本,就像下面这样:
void TestApp::Init()
{
if(!D3D11App::Init())
return false;
/* 剩下的初始化代码从这里开始 */
}
(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中进行处理。
另外,除了上述的五个框架方法之外,为了使用起来更方便,我们还提供了三个虚函数,用于处理鼠标点击、释放和移动的事件。
virtual void OnMouseDown(WPARAM btnState, int x, int y){ }
virtual void OnMouseUp(WPARAM btnState, int x, int y) { }
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:
class TestApp : public D3D11App
{
public:
TestApp(HINSTANCE hInstance); void UpdateScene(float dt);
void DrawScene();
};
(2)添加TestApp类的成员函数实现
//
// TestApp Implement
// TestApp::TestApp(HINSTANCE hInstance)
: D3D11App(hInstance)
{
m_mainWndCaption = L"2_D3DTimingAndAnimation";
} void TestApp::UpdateScene(float dt)
{ } void TestApp::DrawScene()
{
assert(m_pD3DImmediateContext);
assert(m_pSwapChain); m_pD3DImmediateContext->ClearRenderTargetView(m_pRenderTargetView, reinterpret_cast<const float*>(&Colors::LightSteelBlue));
m_pD3DImmediateContext->ClearDepthStencilView(m_pDepthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0); HR(m_pSwapChain->Present(0, 0));
}
(3)添加程序入口
// 程序入口
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
PSTR cmdLine, int showCmd)
{
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif TestApp theApp(hInstance); if( !theApp.Init() )
return 0; return theApp.Run();
}
到此为止,我们已经利用程序框架成功地生成了一个演示程序。
有兴趣看源码的朋友可以点击这里下载,源码为3_D3DFrame文件夹。
三、结语
基本上,我们用不着做任何实际工作就可以实现这个程序,因为基类D3D11App已经实现了它所需要的大部分功能。
有了程序框架之后,我们在学习过程中的演示程序的编写就简单多了,直接导入程序框架,只写我们关心的核心代码就可以了。
Direct3D11学习:(五)演示程序框架的更多相关文章
- (转)MyBatis框架的学习(五)——一对一关联映射和一对多关联映射
http://blog.csdn.net/yerenyuan_pku/article/details/71894172 在实际开发中我们不可能只是对单表进行操作,必然要操作多表,本文就来讲解多表操作中 ...
- Direct3D11学习:(八)Effects介绍
转载请注明出处:http://www.cnblogs.com/Ray1024 一.概述 Effects框架是一组用于管理着色器程序和渲染状态的工具代码.例如,你可能会使用不同的effect绘制水.云. ...
- Direct3D11学习:(七)绘图基础——彩色立方体的绘制
转载请注明出处:http://www.cnblogs.com/Ray1024 一.概述 在前面的几篇文章中,我们详细介绍了Direct3D渲染所需要的数学基础和渲染管道理论知识.从这篇文章开始,我们就 ...
- Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析
经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...
- Java并发包源码学习之AQS框架(三)LockSupport和interrupt
接着上一篇文章今天我们来介绍下LockSupport和Java中线程的中断(interrupt). 其实除了LockSupport,Java之初就有Object对象的wait和notify方法可以实现 ...
- Java并发包源码学习之AQS框架(一)概述
AQS其实就是java.util.concurrent.locks.AbstractQueuedSynchronizer这个类. 阅读Java的并发包源码你会发现这个类是整个java.util.con ...
- [源码解析] 深度学习分布式训练框架 Horovod (1) --- 基础知识
[源码解析] 深度学习分布式训练框架 Horovod --- (1) 基础知识 目录 [源码解析] 深度学习分布式训练框架 Horovod --- (1) 基础知识 0x00 摘要 0x01 分布式并 ...
- [源码解析] 深度学习分布式训练框架 horovod (5) --- 融合框架
[源码解析] 深度学习分布式训练框架 horovod (5) --- 融合框架 目录 [源码解析] 深度学习分布式训练框架 horovod (5) --- 融合框架 0x00 摘要 0x01 架构图 ...
- [源码解析] 深度学习分布式训练框架 horovod (10) --- run on spark
[源码解析] 深度学习分布式训练框架 horovod (10) --- run on spark 目录 [源码解析] 深度学习分布式训练框架 horovod (10) --- run on spark ...
随机推荐
- 调试X Server
发现错误 运行X之后出现了错误: Program received signal SIGSEGV, Segmentation fault. 0x00007fbc3336fb63 in ?? () fr ...
- hdu 1845
一看题意就是二分匹配问题,建边是双向的,两个集合都是n个点 这题的图很特殊,每个点都要与三个点相连,在纸上画了六个点的图就感觉此图最大匹配肯定是六,除以2就是原图的匹配了,就感觉这样的图肯定会达到满匹 ...
- 在Android界面特效中如何做出和墨迹天气及UC中左右拖动的效果
(国内知名Android开发论坛eoe开发者社区推荐:http://www.eoeandroid.com/) 在Android界面特效中如何做出和墨迹天气及UC中左右拖动的效果 相信这么多手机APP中 ...
- Android开发之蓝牙 --修改本机蓝牙设备的可见性,并扫描周围可用的蓝牙设备
一. 修改本机蓝牙设备的可见性 二. 扫描周围可用的蓝牙设备 一. 清单文件AdroidManifest.xml: <uses-permission android:name="an ...
- fresco Bitmap too large to be uploaded into a texture
fresco加载图片方法 布局文件引入 xmlns:fresco="http://schemas.android.com/apk/res-auto" <com.faceboo ...
- C# inline-hook / api-hook
我查阅了一下相关C#方面的资料,却没有发现有提供过关于api-hook方面的资 料包括应用库由此本人编写一套inline-hook的库用于支持x64.x86上的基于在 clr的公共语言,如: c#.c ...
- gem install 出现Errno::ECONNRESET: Connection reset by peer - SSL_connect (https://api.rubygems.org
在安装了rvm来管理多版本的ruby之后,想在不同环境下安装一些gems,结果gem install puma 之后,发现一次又一次失败. gem install 出现Errno::ECONNRESE ...
- FancyTree 状态保持
FancyTree非常优秀的树控件 examples:http://wwwendt.de/tech/fancytree/ 如何将树节点的选中状态保存下来,无论刷新页面还是,ajax重复请求,都保持在上 ...
- 高吞吐量的分布式发布订阅消息系统Kafka--安装及测试
一.Kafka概述 Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据. 这种动作(网页浏览,搜索和其他用户的行动)是在现代网络上的许多社会功能的一个关键因 ...
- mysql查询结果添加序列号
第一种方法: select (@i:=@i+1) as i,table_name.* from table_name,(select @i:=0) as it 第二种方 ...