程序与技术分享:Directx11学习笔记【三】第一个D3D11程序

简介: 程序与技术分享:Directx11学习笔记【三】第一个D3D11程序

在先前的解决方案中新建一个新的Win32项目FirstD3D11Demo。在写代码之前,我们必须先添加dx11所需要的库。为了链接dx库,右键项目选择属性->vc++目录,在包含目录中添加你所安装的SDK根目录\Include,在库目录中添加 根目录\lib\x86(或x64),在链接器->输入的附加依赖项中添加d3d11.lib、d3dx11.lib、dxerr.lib。


第一次使用d3d,首先应该从初始化开始。


初始化d3d11的步骤主要有以下几个:


1、定义我们要检查的设备类型和特征级别


2、创建d3d设备,渲染环境和交换链


3、创建渲染对象


4、设置视口观察区(ViewPort)


下面将对一些概念和用到的d3d对象和函数作具体说明。


数据格式


D3D应用程序中,无论是纹理图片,还是创建的缓冲区,都有着特定的数据格式。D3D11支持有限的数据格式,以枚举变量形式存在,如下几种:


DXGI_FORMAT_R32G32B32_FLOAT: 3个32位单精度符点数组成,比如用于代表三维空间坐标,以及24位颜色;


DXGI_FORMAT_R16G16B16A16_UNORM: 4个16位数组成,每个成员位于【0,1.0f】之间,UNORM意指:unsigned normalized,即无符号,且归一化的;


DXGI_FORMAT_R32G32_UINT:2个32位数组成,每个成员为无符号整型(unsigned int);


DXGI_FORMAT_R8G8B8A8_UNORM:4个8位数组成,每个成员为【0,1.f】之间;


DXGI_FORMAT_R8G8B8A8_SNORM:4个8位数组成,每个成员为【-1.0f, 1.0f】之间,SNORM意指:signed normalized;


DXGI_FORMAT_R8G8B8A8_SINT:4个8位数组成,每个成员为有符号整型;


特征级别(Feature Level)


特征级别定义了一系列支持不同d3d功能的相应等级,如果一个用户的硬件不支持某一特征等级,程序可以选择较低的等级来运行。


下面是d3d定义的几个不同级别代表不同的d3d版本


typedef enum D3D_FEATURE_LEVEL {


D3D_FEATURE_LEVEL_9_1 = 0x9100,


D3D_FEATURE_LEVEL_9_2 = 0x9200,


D3D_FEATURE_LEVEL_9_3 = 0x9300,


D3D_FEATURE_LEVEL_10_0 = 0xa000,


D3D_FEATURE_LEVEL_10_1 = 0xa100,


D3D_FEATURE_LEVEL_11_0 = 0xb000,


D3D_FEATURE_LEVEL_11_1 = 0xb100,


D3D_FEATURE_LEVEL_12_0 = 0xc000,


D3D_FEATURE_LEVEL_12_1 = 0xc100


} D3D_FEATURE_LEVEL;


在初始化过程中,我们可以提供一组不同的特征等级,程序会从第一个开始逐个检测,碰到第一个合适的来创建设备。因此我们在数组中从高到低放置特征等级提供给初始化程序。


交换链(SwapChain)


为了实现平滑的动画,至少需要两个缓冲区,一个前缓冲区用于显示,一个后缓冲区用于下一帧的绘制,每次绘制完一帧后通过交换前、后缓冲区对应的指针来显示新一帧,并在之前的前缓冲区(当前的后缓冲区)上开始继续绘制下一帧。交换链可以有3个或者更多缓冲区,但一般情况下两个够用了。通常在游戏中,我们有两种颜色缓存,一个主缓存,一个辅助缓存,这就是所谓的前向和后向缓存。主缓存是显示在屏幕上的,辅助缓存则是用于下一帧的绘制。在d3d11中交换链对应的接口为IDXGISwapChain。


深度/模板缓冲区:Depth/Stencil Buffer


深度缓冲区是与交换链缓冲区大小完全一样的一块显存区域,即每个像素在深度缓冲区中对应相应的位置。在渲染管线的最终的混合阶段(Output Merger Stage),每个片(Fragment)都有一个深度值z,与深度缓冲区对应位置上的深度相比较,如果该片段z更小,则绘制该片段,并覆盖当前的尝试值,否则抛弃该片段。该缓冲区主要用于实现投影在屏幕上同一位置、远近不同的物体之间相同的遮挡效果。此外,灵活配置尝试缓冲区,可以实现很多种高级特效。


多重采样抗锯齿:Multisampling Atialiasing


针对光栅化显示器抗锯齿的方法有多种,在d3d中采用的多重采样方法。即在每个像素点内部,设置多个采样点,绘制多边形边缘时,针对每个采样点判断是否被多边形覆盖,最终的颜色值从采样点中取均值,以对多边形的边缘进行“模糊化",从而减轻锯齿效果。如下图所示,这是一个4重采样的例子,该像素最终的颜色值是多边形本身颜色值的3/4:


支持d3d11的硬件全部支持4重采样,因此我们在后面的程序中将普遍使用4个采样点。在d3d11中通过结构DXGI_SAMPLE_DESC来设置多重采样,其定义如下:


typedef struct DXGI_SAMPLE_DESC {


UINT Count;


UINT Quality;


} DXGI_SAMPLE_DESC;


D3D11_CREATE_DEVICE_FLAG 枚举类型


typedef enum D3D11_CREATE_DEVICE_FLAG {


D3D11_CREATE_DEVICE_SINGLETHREADED = 0x1,


D3D11_CREATE_DEVICE_DEBUG = 0x2,


D3D11_CREATE_DEVICE_SWITCH_TO_REF = 0x4,


D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS = 0x8,


D3D11_CREATE_DEVICE_BGRA_SUPPORT = 0x20,


D3D11_CREATE_DEVICE_DEBUGGABLE = 0x40,


D3D11_CREATE_DEVICE_PREVENT_ALTERING_LAYER_SETTINGS_FROM_REGISTRY = 0x80,


D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT = 0x100,


D3D11_CREATE_DEVICE_VIDEO_SUPPORT = 0x800


} D3D11_CREATE_DEVICE_FLAG;


D3D11_CREATE_DEVICE_SINGLETHREADED


如果使用该常量,你的应用程序将只可以在一个线程中的调用 Dierct3D 11接口。在默认情况下ID3D11Device


对象是一个安全线程。使用这个标志,你可以增强性能。然而,如果你使用这个标志并且你的应用程序使用


多线程调用Dierct3D 11接口,可能导致不可预期的结果。


D3D11_CREATE_DEVICE_DEBUG


创建一个设备支持调用层。


D3D11_CREATE_DEVICE_SWITCH_TO_REF


注意 这个标志不支持Direct3D 11.


D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS


阻止被多线程创建。当使用WARP标志时,WARP和所有光栅不能够被线程调用。这个标志不建议使用。


D3D11_CREATE_DEVICE_BGRA_SUPPORT


Dierct2D需要和Direct3D资源交互。


下面是每个步骤对应的代码:


一、指明驱动设备等级和特征等级


1 HRESULT hResult = S_OK;//返回结果


2


3 RECT rc;


4 GetClientRect(g_hWnd, &rc);//获取窗口客户区大小


5 UINT width = rc.right - rc.left;


6 UINT height = rc.bottom - rc.top;


7


8 UINT createDeviceFlags = 0;


9 #ifdef _DEBUG


10 createDeviceFlags //代码效果参考:http://hnjlyzjd.com/hw/wz_24179.html

|= D3D11_CREATE_DEVICE_DEBUG;

11 #endif


12


13 //驱动类型数组


14 D3D_DRIVER_TYPE driverTypes【】 =


15 {


16 D3D_DRIVER_TYPE_HARDWARE,


17 D3D_DRIVER_TYPE_WARP,


18 D3D_DRIVER_TYPE_REFERENCE


19 };


20 UINT numDriverTypes = ARRAYSIZE(driverTypes);


21


22 //特征级别数组


23 D3D_FEATURE_LEVEL featureLevels【】 =


24 {


25 D3D_FEATURE_LEVEL_11_0,


26 D3D_FEATURE_LEVEL_10_1,


27 D3D_FEATURE_LEVEL_10_0


28 };


29 UINT numFeatureLevels = ARRAYSIZE(featureLevels);


二、创建设备和交换链


交换链具体定义


typedef struct DXGI_SWAP_CHAIN_DESC {


DXGI_MODE_DESC BufferDesc;


DXGI_SAMPLE_DESC SampleDesc;


DXGI_USAGE BufferUsage;


UINT BufferCount;


HWND OutputWindow;


BOOL Windowed;


//代码效果参考:http://hnjlyzjd.com/xl/wz_24177.html

DXGI_SWAP_EFFECT SwapEffect;

UINT Flags;


} DXGI_SWAP_CHAIN_DESC;


   BufferDesc指定后缓冲区有关特性;


SampleDesc指定多重采样,前面说过;


BufferUsage,对于交换链,为DXGI_USAGE_RENDER_TARGET_OUTPUT;


BufferCount:我们只创建一个后缓冲区(双缓冲),因此为1;


OutputWindow:指定窗口句柄,Win32程序初始化完创建的主窗口;


Windowed:是否全屏;


DXGI_SWAP_EFFECT:通常为DXGI_SWAP_EFFECT_DISCARD;


Flags:可选


其中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为缓冲区大小,一般设为主窗口大小;


Format为缓冲区类型,一般作为渲染对象缓冲区类型为DXGI_FORMAT_R8G8B8A8_UNORM;


其他参数一般为固定的。


交换链的设置


1 //交换链


2 DXGI_SWAP_CHAIN_DESC sd;


3 ZeroMemory(&sd, sizeof(DXGI_SWAP_CHAIN_DESC));//填充


4 sd.BufferCount = 1; //我们只创建一个后缓冲(双缓冲)因此为1


5 sd.BufferDesc.Width = width;


6 sd.BufferDesc.Height = height;


7 sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;


8 sd.BufferDesc.RefreshRate.Numerator = 60;


9 sd.BufferDesc.RefreshRate.Denominator = 1;


10 sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;


11 sd.OutputWindow = g_hWnd;


12 sd.SampleDesc.Count = 1; //1重采样


13 sd.SampleDesc.Quality = 0; //采样等级


14 sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; //常用参数


15 sd.Windowed = TRUE; //是否全屏


创建设备及交换链,d3d设备一般都是设备本身和硬件之间的通信,而d3d上下文是一种描述设备如何绘制的渲染设备上下文,这也包含了渲染状态和其他的绘图信息。


而交换链是设备和上下文将要绘制的渲染目标。


D3D11CreateDeviceAndSwapChain函数原型:


HRESULT D3D11CreateDeviceAndSwapChain(


_Inopt IDXGIAdapter pAdapter,


D3D_DRIVER_TYPE DriverType,


HMODULE Software,


UINT Flags,


_Inopt const D3D_FEATURE_LEVEL pFeatureLevels,


UINT FeatureLevels,


UINT SDKVersion,


_Inopt const DXGI_SWAP_CHAIN_DESC pSwapChainDesc,


_Outopt IDXGISwapChain ppSwapChain,


_Outopt ID3D11Device ppDevice,


_Outopt D3D_FEATURE_LEVEL pFeatureLevel,


_Outopt ID3D11DeviceContext **ppImmediateContext


);


pAdapter来选择相应的图形适配器,设为NULL以选择默认的适配器;


DriverType设置驱动类型,一般毫无疑问选择硬件加速,即D3D_DRIVER_TYPE_HARDWARE,此时下一个参数就是NULL;


Flags为可选参数,一般为NULL,可以设为D3D11_CREATE_DEVICE_DEBUG、D3D11_CREATE_DEVICE_SINGLETHREADED,或两者一起,前者让要用于调试时收集信息,后者在确定程序只在单线程下运行时设置为它,可以提高性能;


pFeatureLevels为我们提供给程序的特征等级的一个数组,下一个参数为数组中元素个数;


SDKVersion恒定为D3D11_SDK_VERSION;


ppDevice为设备指针的地址,注意设备是指针类型,这里传递的是指针的地址(二维指针,d3d程序中所有的接口都声明为指针类型!);


pFeatureLevel为最后程序选中的特征等级,我们定义相应的变量,传递它的地址进来;


ppImmediateContext为设备上下文指针的地址,要求同设备指针。


创建设备和交换链的相关代码


1 for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; ++driverTypeIndex)


2 {


3 g_driverType = driverTypes【driverTypeIndex】;


4 hResult = D3D11CreateDeviceAndSwapChain(


5 NULL, //默认图形适配器


6 g_driverType, //驱动类型


7 NULL, //实现软件渲染设备的动态库句柄,如果使用的驱动设备类型是软件设备则不能为NULL


8 createDeviceFlags, //创建标志,0用于游戏发布,一般D3D11_CREATE_DEVICE_DEBUG允许我们创建可供调试的设备,在开发中比较有用


9 featureLevels, //特征等级


10 numFeatureLevels, //特征等级数量


11 D3D11_SDK_VERSION, //sdk版本号


12 &sd,


13 &g_pSwapChain,


14 &g_pd3dDevice,


15 &g_featureLevel,


16 &g_pImmediateContext


17 );


18 if (SUCCEEDED(hResult))


19 break;


20 }


21 if (FAILED(hResult))


22 return hResult;


三、创建和绑定渲染目标视图


一个渲染目标视图是一个由Output MergerStage读取的D3D资源。交换链的主缓存和辅助缓存为彩色的图像,通过调用交换链中的函数GetBuffer来得到它的指针。得到指针后,然后再通过CreateRenderTargetView函数来创建一个渲染目标视图。创建完渲染目标后,就可以调用Release()释放指针到交换链的后台缓存了。当想渲染一个特定的渲染目标的时,要在绘制函数调用前对它进行设置,这个工作是由OMSetRenderTarget函数完成的。


CreateRenderTargetView函数原型


HRESULT CreateRenderTargetView(


【in】 ID3D11Resource pResource,


【in, optional】 const D3D11_RENDER_TARGET_VIEW_DESC pDesc,


【out, optional】 ID3D11RenderTargetView **ppRTView


);


pResource为视图对应资源


pDesc为视图描述


ppRTView要创建的视图,是一个指针的地址


这一部分的代码


1 //创建渲染目标视图


2 ID3D11Texture2D pBackBuffer = NULL;


3 //获取后缓冲区地址


4 hResult = g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID)&pBackBuffer);


5 if (FAILED(hResult))


6 return hResult;


7


8 //创建目标视图


9 hResult = g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_pRenderTargetView);


10 //释放后缓冲


11 pBackBuffer->Release();


12 if (FAILED(hResult))


13 return hResult;


14


15 //绑定到渲染管线


16 g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL);


四、创建视口


视口定义了渲染到屏幕上的面积,对于单屏游戏来说一般为全屏的,这样我们设置视口的width和height为交换链对应的width、height就好了;对于分屏游戏,可以创建两个视口


放在屏幕不同位置,以不同玩家的角度来渲染。


D3D11_VIEWPORT定义


typedef struct D3D11_VIEWPORT {


FLOAT TopLeftX; //视口左上角x坐标,一般视口占满屏幕的,所以为0


FLOAT TopLeftY; //y坐标


FLOAT Width; //视口宽度,一般与后缓冲区一致,以保持图像不变形


FLOAT Height; //高度,同上


FLOAT MinDepth; //最小深度值:0.0f


FLOAT MaxDepth; //最大深度值:1.0f


} D3D11_VIEWPORT;


具体代码


1 //设置viewport


2 D3D11_VIEWPORT vp;


3 vp.Height = (FLOAT)height;


4 vp.Width = (FLOAT)width;


5 vp.MinDepth = 0.0f;


6 vp.MaxDepth = 1.0f;


7 vp.TopLeftX = 0;


8 vp.TopLeftY = 0;


9 g_pImmediateContext->RSSetViewports(1, &vp);


接下来的Render函数中实现了清除显示屏幕的功能


float ClearColor【4】 = { 0.5f, 0.1f, 0.2f, 1.0f }; //red,green,blue,alpha


g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, ClearColor);


g_pSwapChain->Present(0, 0);


下面给出整个工程的代码:


#include


#include


#include


#include


HINSTANCE g_hInstance = NULL;


HWND g_hWnd = NULL;


LPCWSTR g_name = L"FirstD3D11Demo";


D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL; //驱动类型


D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0; //特征等级


ID3D11Device g_pd3dDevice = NULL; //设备


ID3D11DeviceContext g_pImmediateContext = NULL; //设备上下文


IDXGISwapChain g_pSwapChain = NULL; //交换链


ID3D11RenderTargetView *g_pRenderTargetView = NULL; //要创建的视图


相关文章
|
12月前
|
人工智能 缓存 Shell
[笔记]Windows核心编程《二十》DLL的高级操作技术(二)
[笔记]Windows核心编程《二十》DLL的高级操作技术(二)
275 0
|
3月前
|
存储 算法 C++
【Qt应用开发】复刻经典:基于Qt实现Windows风格计算器(加减乘除、删除、归零功能全解析)
在Qt中,"栈"的概念主要体现在两个层面:一是程序设计中的数据结构——栈(Stack),二是用户界面管理中的QStackedWidget控件。下面我将分别简要介绍这两个方面:
120 4
|
5月前
|
Windows
火山中文编程 -- 第一个windows程序
火山中文编程 -- 第一个windows程序
31 0
|
10月前
|
vr&ar 开发工具 iOS开发
visionOS空间计算实战开发教程Day 1:环境安装和编写第一个程序
截至目前visionOS还未在Xcode稳定版中开放,所以需要下载Xcode Beta版。比如我们可以下载Xcode 15.1 beta 2,注意Xcode 15要求系统的版本是macOS Ventura 13.5或更新,也就是说2017年的MacBook Pro基本可以勉强一战,基本上还是推荐使用M系列芯片的电脑进行开发。
138 0
|
11月前
|
计算机视觉 C++
《QT从基础到进阶·二十九》QT,opencv源码调试
《QT从基础到进阶·二十九》QT,opencv源码调试
80 0
|
12月前
|
编译器 分布式数据库 C++
[笔记]Windows核心编程《二十》DLL的高级操作技术(一)
[笔记]Windows核心编程《二十》DLL的高级操作技术
111 0
|
API C++ Windows
Windows入门篇一之MSDN手册的使用和第一个窗口程序
Windows入门篇一之MSDN手册的使用和第一个窗口程序
Windows入门篇一之MSDN手册的使用和第一个窗口程序
|
XML Java 数据格式
HarmonyOS学习路之开发基础——快速入门(编写第一个页面)
在Java UI框架中,提供了两种编写布局的方式:在XML中声明UI布局和在代码中创建布局。这两种方式创建出的布局没有本质差别,为了熟悉两种方式,我们将通过XML的方式编写第一个页面,通过代码的方式编写第二个页面。
|
人工智能 缓存 编译器
[笔记]Windows核心编程《二十》DLL的高级操作技术
Windows核心编程《二十》DLL的高级操作技术
260 0
[笔记]Windows核心编程《二十》DLL的高级操作技术
下一篇
无影云桌面