【DirectX11】【学习笔记(1)】初始化DirectX11
首先要涉及到的是Directx11环境的配置以及基础Win32程序框架的编写,这一部分和DirectX9.0是类似的。
这里就不再赘述,写完DirectX11后会再开一个DirectX9专栏,记录上述部分。这里先预留。不过相关代码本节内容都会贴出来,所以没有看到以上两步的朋友也没有关系。接下来就直接进入主题:对DirectX11对象初始化。
首先是本次笔记思维导图
一.创建全局对象
首先确定:Swapchain是一个COM接口对象。
COM是什么?
根据MSDN文档所述:COM是一种允许对象跨进程和计算机边界进行交互的技术。也就是说它允许我们跨过我们自身创建的进程,可以和别的进程进行交互,包括数据访问之类的。COM通过指定操作与对象关联的数据的唯一方法是通过对象上的接口。
学过C++的同学肯定对C++接口有一定了解。COM在某种意义上使用单词interface,与Visual C ++编程中通常使用的不同。 C ++接口指的是类支持的所有函数,并且对象的客户端可以调用它来与之交互。 COM接口是指COM类实现的预定义相关函数组,但特定接口不一定代表该类支持的所有函数(可能只代表其中一部分函数)
1.Swapchain
Swapchain用来交换前后台缓冲之间的数据。这个技术也叫做双缓存技术。当我们渲染场景的时候,一般我们都是把它渲染到后台缓冲中,当我们把后台缓冲展示到屏幕上的时候,它已经被渲染好了。否则的话,我们就会在屏幕上看到一个扫描线(这是光栅化阶段,对屏幕进行填充颜色,之后的章节会讲到)
2.代表GPU硬件设备的接口
而DitectX11把D3d11Device接口一分为二,这样可以支持多线程。
1.ID3D11DeviceContext接口(用来处理和调用渲染相关的函数)
2.ID3D11Device(用来处理调用和渲染无关的函数)
一分为二的原因:当我们在加载一个模型或者创建一个物体的时候,我们可以调用ID3D11Device对象,这个时候,我们可以同时调用ID3D11DeviceContext对象来对我们的场景进行渲染。
3.render target view
上文已经提到过了,我们需要先渲染到后台缓存,接着再把后台缓存的数据渲染到屏幕上。
render target view就是我们的后台缓存,本质上是个2d的纹理图。之后会被渲染管线中的其它部分调用,最终渲染到屏幕上。
以上四个全局对象声明代码如下:
IDXGISwapChain* SwapChain;
ID3D11Device* d3d11Device;
ID3D11DeviceContext* d3d11DevCon;
ID3D11RenderTargetView* renderTargetView;
【总结】我们把内容先渲染到render target view中,通过SwapChain交换到屏幕上。ID3D11Device用于调用非渲染函数,ID3D11DeviceContext用于调用渲染函数。
二.相关函数
程序中函数执行代码如下:
if(!InitializeDirect3d11App(hInstance)) //Initialize Direct3D
{MessageBox(0, L"Direct3D Initialization - Failed",L"Error", MB_OK);return 0;
}if(!InitScene()) //Initialize our scene
{MessageBox(0, L"Scene Initialization - Failed",L"Error", MB_OK);return 0;
}messageloop();ReleaseObjects();
以下分别介绍上面所用到的函数
2.1Initializing Direct3D 11
(参数为我们程序实例的句柄,关于句柄的理解可参考我这一篇博客)
由于该函数涵盖内容较多,我写了一个思维导图
函数实现如下:
bool InitializeDirect3dApp(HINSTANCE hInstance)
{
HRESULT hr;//Describe our Buffer
DXGI_MODE_DESC bufferDesc;ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));bufferDesc.Width = Width;
bufferDesc.Height = Height;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;//Describe our SwapChain
DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = TRUE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;//Create our SwapChain
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL,D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon);//Create our BackBuffer
ID3D11Texture2D* BackBuffer;
hr = SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer );//Create our Render Target
hr = d3d11Device->CreateRenderTargetView( BackBuffer, NULL, &renderTargetView );
BackBuffer->Release();//Set our Render Target
d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, NULL );return true;
}
HRESULT对象,用来进行错误检查。
2.1.1后台缓存描述
函数中第一件事是描述后台缓冲,为此我们创建了一个DXGI_MODE_DESC对象,然后我们清空对象内存(防止其中的某个参数已经被赋值),然后我们再填充这个结构体。DXGI_MODE_DESC 结构体:
typedef struct DXGI_MODE_DESC {UINT Width;UINT Height;DXGI_RATIONAL RefreshRate;DXGI_FORMAT Format;DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;DXGI_MODE_SCALING Scaling;
} DXGI_MODE_DESC, *LPDXGI_MODE_DESC;
每个参数解释如下:
Width - 缓冲区宽度
Height - 缓冲区高度
RefreshRate - 这是一个DXGI_RATIONAL类型值,描述了刷新率,单位HZ。
Format - 这是一个DXGI_FORMAT枚举值 描述了我们展示的格式。我们可以采用DXGI_FORMAT_R8G8B8A8_UNORM
这是一个32位无符号整形,RBGA分别占8位。
ScanlineOrdering - DXGI_MODE_SCANLINE_ORDER 枚举值 描述了光栅器渲染平面的方式,因为在上文Swapchain中讲过:这个过程我们看不到。所以我们把值设为DXGI_MODE_SCALING_UNSPECIFIED,意思是不指定,方式不重要。
Scaling - DXGI_MODE_SCALING枚举值,解释了一个图像时如何被拉伸显示到显示器中的。
DXGI_MODE_SCALING_UNSPECIFIED, 意思是不指定值。
DXGI_MODE_SCALING_CENTERED,意思是图像位于屏幕中间,不拉伸
DXGI_MODE_SCALING_STRETCHED,意思是图像被拉伸到屏幕大小。
上述赋值过程代码如下:
DXGI_MODE_DESC bufferDesc;ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));bufferDesc.Width = Width;
bufferDesc.Height = Height;
bufferDesc.RefreshRate.Numerator = 60;
bufferDesc.RefreshRate.Denominator = 1;
bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
2.1.2SwapChain描述
在上面一个步骤,我们已经把后台缓冲描述结构填好了。现在我们创建一个SwapChain描述结构体
同样先清除内存,再填充结构。结构体如下:
typedef struct DXGI_SWAP_CHAIN_DESC {DXGI_MODE_DESC BufferDesc;DXGI_SAMPLE_DESC SampleDesc;DXGI_USAGE BufferUsage;UINT BufferCount;HWND OutputWindow;BOOL Windowed;DXGI_SWAP_EFFECT SwapEffect;UINT Flags;
} DXGI_SWAP_CHAIN_DESC;
BufferDesc - 这是一个DXGI_MODE_DESC 结构,我们把之前填充好的后台结构填到这里。
SampleDesc - 这是一个DXGI_SAMPLE_DESC结构,描述多重采样。(多重采样是用来使图像更加平滑,毕竟像素块不是无限小的,小的像素可能像一个个小方块,有点马赛克的感觉)
BufferUsage - 这是一个DXGI_USAGE 枚举值,描述了CPU对于后台缓冲的权限。我们这里对其赋值DXGI_USAGE_RENDER_TARGET_OUTPUT ,因为我们会对它进行渲染。
BufferCount- 我们用到的后台缓冲数。双缓冲,我们设置为1,三缓冲,设置为2。依次类推
OutputWindow - 窗口句柄
Windowed - true为窗口化,false为全屏(全屏退出可能导致程序冻结)
SwapEffect - 这是一个DXGI_SWAP_EFFECT 枚举值,描述了显卡驱动如何交换前后缓冲。我们这里设置为DXGI_SWAP_EFFECT_DISCARD ,让驱动自己决定最有效的方式。
Flags - DXGI_SWAP_CHAIN_FLAG枚举值,这是一个描述SwapChain其他行为的标志位。现在可能比较有用的是DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH,该值允许我们转换Windowed 参数时,改变显示区域大小。
填充代码如下:
DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));swapChainDesc.BufferDesc = bufferDesc;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = TRUE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
2.1.3创建设备和SwapChain
现在我们要创建第一部分提到的全局对象。包括:direct3d device, device context, 和Swap Chain。
通过调用D3D11CreateDeviceAndSwapChain()函数。函数参数如下:
HRESULT D3D11CreateDeviceAndSwapChain(__in IDXGIAdapter *pAdapter,__in D3D_DRIVER_TYPE DriverType,__in HMODULE Software,__in UINT Flags,__in const D3D_FEATURE_LEVEL *pFeatureLevels,__in UINT FeatureLevels,__in UINT SDKVersion,__in const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,__out IDXGISwapChain **ppSwapChain,__out ID3D11Device **ppDevice,__out D3D_FEATURE_LEVEL *pFeatureLevel,__out ID3D11DeviceContext **ppImmediateContext
);
pAdapter- 指向一个视频容器。我们设为NULL
DriverType - 一个D3D_DRIVER_TYPE 枚举值。描述了direct3d如何被实施,我们设为D3D_DRIVER_TYPE_HARDWARE ,表示我们程序将在GPU运行
Software - HMODULE 句柄,用来实施软件光栅化。
Flags - 可以设置多个D3D11_CREATE_DEVICE_FLAG 类型值
pFeatureLevels - 指向一堆D3D_FEATURE_LEVEL枚举值的指针,表明现在Directx特点的版本,我们设为NULL,使用最高版本。
FeatureLevels - 上一个参数中的元素个数,设为NULL
SDKVersion - SDK版本。我们设为D3D11_SDK_VERSION
pSwapChainDesc - 指向DXGI_SWAP_CHAIN_DESC 结构体的指针,我们在上面已经创建好了。
ppSwapChain - 指向接口对象的指针,用来接收创建好的对象。
ppDevice - 指向设备的指针。
pFeatureLevel - 指向一个D3D_FEATURE_LEVEL 值的指针,保存最高等级特点。
ppImmediateContext - 指向ID3D11DeviceContext 指针。
函数调用如下:
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL,D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon);
2.1.4创建后台缓冲
创建后台缓冲,然后这个缓冲会用来创建我们的our render target view。
首先调用SwapChain对象的getbuffer函数
HRESULT GetBuffer([in] UINT Buffer,[in] REFIID riid,[in, out] void **ppSurface
);
Buffer - 因为我们把swapChainDesc.SwapEffect设置成了DXGI_SWAP_EFFECT_DISCARD。所以驱动只能获得第一块缓存,我们设为0
riid - 这是更改后台缓冲区的接口类型的引用ID。我们把它设为2d texture (ID3D11Texture2D)
ppSurface - 指针指向创建的后台缓冲
调用代码如下:
ID3D11Texture2D* BackBuffer;
hr = SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer );
2.1.5创建Render Target
创建Target View ,这个之后会发送给管线的其他阶段。
调用函数
HRESULT CreateRenderTargetView([in] ID3D11Resource *pResource,[in] const D3D11_RENDER_TARGET_VIEW_DESC *pDesc,[out] ID3D11RenderTargetView **ppRTView
);
pResource - 刚才创建的后台缓冲
pDesc - D3D11_RENDER_TARGET_VIEW_DESC 结构体,我们设置NULL来创建一个访问mipmap level 0中所有子资源的视图。
ppRTView - 指向renderTargetView指针。
创建好renderTargetView之后我们就不需要后台缓冲了。(这是因为,后台缓冲指针指向了交换链中的后台缓冲BUFFER,现在rendertargetview已经获得了这个指针区域,所以不再需要另一个指针指向它了)
图解:
2.1.6设置 Render Targets
我们在初始化做的最后一件事,就是把RenderTargets绑定到管线的输出阶段上。这个功能同时也会绑定深度和模板缓存,但现在由于还没有创建,所以暂时设置为空
void OMSetRenderTargets([in] UINT NumViews,[in] ID3D11RenderTargetView *const **ppRenderTargetViews,[in] ID3D11DepthStencilView *pDepthStencilView
);
NumViews - 绑定的rendertargets个数
ppRenderTargetViews - 指向一组要绑定的render target views
pDepthStencilView - 指向深度模板缓存的指针。
函数调用
d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, NULL );
2.2Clean Up
在释放对象时候,我们要删除所有创建的COM对象,否则会导致内存泄漏。
2.3Initialize Scene
在这个功能里,我们会放置我们的对象,加载模型,声音。但现在还只是开始,所以我们在函数里不做任何实现
2.4Update Scene
这个功能用来更新场景,像是改变物体位置什么的。目前我们只改变背景颜色。
void UpdateScene()
{//Update the colors of our scenered += colormodr * 0.00005f;green += colormodg * 0.00002f;blue += colormodb * 0.00001f;if(red >= 1.0f || red <= 0.0f)colormodr *= -1;if(green >= 1.0f || green <= 0.0f)colormodg *= -1;if(blue >= 1.0f || blue <= 0.0f)colormodb *= -1;
}
2.5Render Scene
void DrawScene()
{//Clear our backbuffer to the updated colorD3DXCOLOR bgColor( red, green, blue, 1.0f );d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor);//Present the backbuffer to the screenSwapChain->Present(0, 0);
}
在这个函数里我们只会做渲染场景的事情。
主要用到了SwapChain的present函数。这个功能就是交换前后缓冲。
2.5最后是我们程序的消息循环函数。这是我们代码运行的主体。
int messageloop(){
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while(true)
{BOOL PeekMessageL( LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg);if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){if (msg.message == WM_QUIT)break;TranslateMessage(&msg); DispatchMessage(&msg);}else{
///**************new**************// run game codeUpdateScene();DrawScene();///**************new**************}
}
return msg.wParam;
}
主要是进行消息检查,当没有消息的时候,我们就更新场景,然后渲染。
好了。本节内容就到这里拉,忙忙碌碌写了一个上午,这是第一次系统记录看过的内容。还有一部分内容是关于错误检查的,但是本章内容我感觉有点太多拉,所以我就把内容放到下一节吧!
本节内容代码可以在我的Github仓库找到!Github传送门
游戏开发路途遥远,但我相信只要坚持,总能到达彼岸!
如果我的文章对于你学习DirectX11有点帮助,欢迎评论给出建议,让我们一起学习进步!
———————— 小明 2018.11.15 14.53
【DirectX11】【学习笔记(1)】初始化DirectX11相关推荐
- Directx11学习笔记【二】 将HelloWin封装成类
我们把上一个教程的代码封装到一个类中来方便以后的使用. 首先新建一个空工程叫做MyHelloWin,添加一个main.cpp文件,然后新建一个类叫做MyWindow,将于窗体有关的操作封装到里面 My ...
- DirectX11 学习笔记3 - 创建一个立方体 和 轴
该方案将在进一步的程序 面向对象. 独立的模型类.更像是一个框架. 其中以超过遇到了一个非常有趣的问题,.获得一晚.我读了好几遍,以找到其他的列子.必须放在某些功能Render里面实时更新,而不是仅仅 ...
- DirectX11学习笔记十 imGUI入坑
下载地址 Immediate Mode Graphical User interface,Immediate模式,不保存UI对象,用静态方法每帧创建,Unity里的GUI就是这种模式,适合小游戏或 ...
- Directx11学习笔记【三】 第一个D3D11程序
在先前的解决方案中新建一个新的Win32项目FirstD3D11Demo.在写代码之前,我们必须先添加dx11所需要的库.为了链接dx库,右键项目选择属性->vc++目录,在包含目录中添加你所安 ...
- JAVA学习笔记(九)- 初始化块与静态代码块
初始化块 /** 初始化块* 初始化变量方式:声明时.构造函数.代码块* 使用static关键字修饰的代码块,称为静态代码块* * 执行顺序:静态代码块>代码块>构造方法* * 静态代码块 ...
- JAVA学习笔记--数组初始化
JAVA中,数组只是相同类型的.用一个标识符名称封装到一起的一个对象序列或基本类型数据序列.数组通过方括号下标操作符[]来定义和使用,要定义一个数组只需在类型名后面加上一个方括号即可,如: int[] ...
- DSO源码解析学习笔记(初始化)
FullSystem.cpp入口 main 主线程用于显示 ImageFolderReader文件读取 支持从zip压缩文件读取 getUndistorterForFile读取相机配置文件 对配置文件 ...
- 基于STM32F103的USB学习笔记4 - 初始化
1. IO初始化 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); GPIO_InitSt ...
- Swift学习笔记14——初始化(Initialization)和析构(Deinitialization)其一
所谓的初始化,就是当你创建一个类.结构体.枚举类的时候,完成存储属性的值的初始化,和其他一些初始化工作.准备好这个实例以供使用. 反过来,当一个实例不再使用的时候,由析构过程释放这个实例所占用资源. ...
- Spring学习笔记-Bean初始化销毁
1.bean的初始化操作 1.1通过在配置文件中,设置bean的init-method方法,destory-method方法,spring框架会通过反射机制来调用 java类 package com. ...
最新文章
- VS2005与VSOrcasExpress对LINQ各有所属~
- java plus方法_Java.math.BigDecimal.plus()方法实例
- pycharm 安装 jupyter
- python opengl_从Python开始,学习OpenGL(一)
- TypeScript学习(三):联合类型及推论
- 决策树(六)--随机森林
- 纳米软件案例之陶瓷样品测试系统,原位观察力学测试纳米压痕仪-扫描电子显微镜SEM联用...
- Windows部署的gogs开机自启动
- 正则表达式匹配中文及符号、英文及符号数字空格换行符及常用正则表达式
- 靠问卷调查做副业,在家月入过万:聪明的人,从不挣辛苦钱!
- win10弹出计算机内存不足,如何解决win10系统提示计算机内存不足的问题
- 基本软件开发模型:瀑布模型、V型模型、迭代模型、增量模型、螺旋模型、大爆炸模型、敏捷模型、原型模型、W模型 特点分析与总结
- 什么牌子的蓝牙耳机耐用?类似airpods pro的降噪耳机推荐
- 一步步学习SPD2010--第一章节--探索SPD2010(3)--理解SharePoint Designer 2010新功能
- PAT-A1010解题报告
- 计算机科学引论2021中文,计算机科学引论(2021英文精编版)
- 1949 年的国庆节(10 月 1 日)是星期六,今年……(C语言)
- 用数据说话,从北上广回三线老家,开一间自助洗车店能挣多少钱?
- CVPR2021 最佳论文 Giraffe,当之无愧的最佳,或开创新的篇章
- RocketMQ - nameSrv和Broker