首先要涉及到的是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相关推荐

  1. Directx11学习笔记【二】 将HelloWin封装成类

    我们把上一个教程的代码封装到一个类中来方便以后的使用. 首先新建一个空工程叫做MyHelloWin,添加一个main.cpp文件,然后新建一个类叫做MyWindow,将于窗体有关的操作封装到里面 My ...

  2. DirectX11 学习笔记3 - 创建一个立方体 和 轴

    该方案将在进一步的程序 面向对象. 独立的模型类.更像是一个框架. 其中以超过遇到了一个非常有趣的问题,.获得一晚.我读了好几遍,以找到其他的列子.必须放在某些功能Render里面实时更新,而不是仅仅 ...

  3. DirectX11学习笔记十 imGUI入坑

    下载地址   Immediate Mode Graphical User interface,Immediate模式,不保存UI对象,用静态方法每帧创建,Unity里的GUI就是这种模式,适合小游戏或 ...

  4. Directx11学习笔记【三】 第一个D3D11程序

    在先前的解决方案中新建一个新的Win32项目FirstD3D11Demo.在写代码之前,我们必须先添加dx11所需要的库.为了链接dx库,右键项目选择属性->vc++目录,在包含目录中添加你所安 ...

  5. JAVA学习笔记(九)- 初始化块与静态代码块

    初始化块 /** 初始化块* 初始化变量方式:声明时.构造函数.代码块* 使用static关键字修饰的代码块,称为静态代码块* * 执行顺序:静态代码块>代码块>构造方法* * 静态代码块 ...

  6. JAVA学习笔记--数组初始化

    JAVA中,数组只是相同类型的.用一个标识符名称封装到一起的一个对象序列或基本类型数据序列.数组通过方括号下标操作符[]来定义和使用,要定义一个数组只需在类型名后面加上一个方括号即可,如: int[] ...

  7. DSO源码解析学习笔记(初始化)

    FullSystem.cpp入口 main 主线程用于显示 ImageFolderReader文件读取 支持从zip压缩文件读取 getUndistorterForFile读取相机配置文件 对配置文件 ...

  8. 基于STM32F103的USB学习笔记4 - 初始化

    1. IO初始化 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);     GPIO_InitSt ...

  9. Swift学习笔记14——初始化(Initialization)和析构(Deinitialization)其一

    所谓的初始化,就是当你创建一个类.结构体.枚举类的时候,完成存储属性的值的初始化,和其他一些初始化工作.准备好这个实例以供使用. 反过来,当一个实例不再使用的时候,由析构过程释放这个实例所占用资源. ...

  10. Spring学习笔记-Bean初始化销毁

    1.bean的初始化操作 1.1通过在配置文件中,设置bean的init-method方法,destory-method方法,spring框架会通过反射机制来调用 java类 package com. ...

最新文章

  1. VS2005与VSOrcasExpress对LINQ各有所属~
  2. java plus方法_Java.math.BigDecimal.plus()方法实例
  3. pycharm 安装 jupyter
  4. python opengl_从Python开始,学习OpenGL(一)
  5. TypeScript学习(三):联合类型及推论
  6. 决策树(六)--随机森林
  7. 纳米软件案例之陶瓷样品测试系统,原位观察力学测试纳米压痕仪-扫描电子显微镜SEM联用...
  8. Windows部署的gogs开机自启动
  9. 正则表达式匹配中文及符号、英文及符号数字空格换行符及常用正则表达式
  10. 靠问卷调查做副业,在家月入过万:聪明的人,从不挣辛苦钱!
  11. win10弹出计算机内存不足,如何解决win10系统提示计算机内存不足的问题
  12. 基本软件开发模型:瀑布模型、V型模型、迭代模型、增量模型、螺旋模型、大爆炸模型、敏捷模型、原型模型、W模型 特点分析与总结
  13. 什么牌子的蓝牙耳机耐用?类似airpods pro的降噪耳机推荐
  14. 一步步学习SPD2010--第一章节--探索SPD2010(3)--理解SharePoint Designer 2010新功能
  15. PAT-A1010解题报告
  16. 计算机科学引论2021中文,计算机科学引论(2021英文精编版)
  17. 1949 年的国庆节(10 月 1 日)是星期六,今年……(C语言)
  18. 用数据说话,从北上广回三线老家,开一间自助洗车店能挣多少钱?
  19. CVPR2021 最佳论文 Giraffe,当之无愧的最佳,或开创新的篇章
  20. RocketMQ - nameSrv和Broker

热门文章

  1. 中柏笔记本-U盘重装系统(系统重装)
  2. 华为如何显示我的电脑连接到服务器地址,我的电脑如何访问服务器地址
  3. 【LabVIEW之小技巧】四舍五入实现方法
  4. amount和number的区别
  5. MSP430学习总结(二)——GPIO
  6. 100%解决 微信备份聊天数据提示当前网络状况复杂,请尝试使用其他网络或提示手机和电脑不在同一网络的方法。
  7. Apache Drill安装测试
  8. mysql 字符编码查询
  9. Qt5生成exe文件更改图标
  10. 跳过linux系统开机密码,linux开机跳过输入用户名密码|景安