Direct3D基础--初始化

2023-11-20 21:20
文章标签 基础 初始化 direct3d

本文主要是介绍Direct3D基础--初始化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文为 Introduction to 3D Game Programming with DirectX 11 读书笔记

本篇博客介绍Direct3D的基础,按照书上章节顺序介绍

      • Direct3D初始化
        • 图形学的基本概念与DirectX的类型
          • COM
          • 纹理和数据资源格式
          • The Swap Chain and Page Flipping
          • Depth Buffering
          • Texture Resource Views
          • Multisampling Theory
          • Direct3D中的Multisampling
          • Feature Levels
        • 初始化DIRECT3D
          • Create the Device and Context
          • Check 4X MSAA Quality Support
          • Describe the Swap Chain
          • Create the Swap Chain
          • Create the Render Target View
          • Create the Depth/Stencil Buffer and View
          • Bind the Views to the Output Merger Stage
          • Set the Viewport
        • 框架代码

Direct3D初始化

Direct3D是底层图形API,它使得在渲染3D世界的时候自动使用3D硬件加速。

图形学的基本概念与DirectX的类型
COM

DirectX使用COM(Component Object Model)编程,使用COM组件编程的好处是内存管理会方便很多。当我们需要指向实现COM接口的对象指针的时候,可以通过特殊函数或者其他COM接口的方法;当不需要对象的时候只要调用Release方法就可以了。Release方法定义在IUnknown COM接口中,所有COM对象都继承该类。

纹理和数据资源格式

DirectX使用的存储纹理信息的格式是DXGI_FORMAT枚举类型,当然可以用于做其他操作,不一定只用于纹理。一些常用的格式的枚举类型如下:

  1. DXGI_FORMAT_R32G32B32_FLOAT: Each element has three 32-bit floating-point components.
  2. DXGI_FORMAT_R16G16B16A16_UNORM: Each element has four 16-bit components mapped to the [0, 1] range.
  3. DXGI_FORMAT_R32G32_UINT: Each element has two 32-bit unsigned integer components.
  4. DXGI_FORMAT_R8G8B8A8_UNORM: Each element has four 8-bit unsigned components mapped to the [0, 1] range.
  5. DXGI_FORMAT_R8G8B8A8_SNORM: Each element has four 8-bit signed components mapped to the [-1, 1] range.
  6. DXGI_FORMAT_R8G8B8A8_SINT: Each element has four 8-bit signed integer components mapped to the [-128, 127] range.
  7. DXGI_FORMAT_R8G8B8A8_UINT: Each element has four 8-bit unsigned integer components mapped to the [0, 255] range.
The Swap Chain and Page Flipping

一般的渲染系统都至少会使用两个buffer用于显示,back bufferfront bufferfront buffer用于输出显示,back buffer用于填充当前渲染的输出,然后在下一帧的时候交换两个buffer。这样可以防止只有一个buffer式的画面撕裂
Swap chain

Depth Buffering

深度图记录的是最终要输出的buffer中每个像素点的在camera坐标空间中深度信息。深度值在0~1之间,0表示距离camera最近的点(在视椎体的near面上),1表示距离camera最远的点(在视椎体的far面上)。depth buffer用于做深度测试,可以保证在前面的物体才会被渲染出来。
Eye Depth
depth buffer也是一个纹理,比必须用特定的文件格式创建。用于创建depth buffer额格式如下:

  1. DXGI_FORMAT_D32_FLOAT_S8X24_UINT: Specifies a 32-bit floating-point depth buffer, with 8-bits (unsigned integer) reserved for the stencil buffer mapped to the [0, 255] range and 24-bits not used for padding.
  2. DXGI_FORMAT_D32_FLOAT: Specifies a 32-bit floating-point depth buffer.
  3. DXGI_FORMAT_D24_UNORM_S8_UINT: Specifies an unsigned 24-bit depth buffer mapped to the [0, 1] range with 8-bits (unsigned integer) reserved for the stencil buffer mapped to the [0, 255] range.
  4. DXGI_FORMAT_D16_UNORM: Specifies an unsigned 16-bit depth buffer mapped to the [0, 1] range.
Texture Resource Views

纹理可用于渲染管线中的不同阶段;常见的应用是将纹理作为render target(如,DIrect3D最终将渲染输出到纹理中)和作为shader resource(如,纹理将会被shader采样)。这两个不同目的的texture resource在创建的时候要被绑定不同的flag:

D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE

当然resource是不能直接绑定到管线上的,必须要通过相应纹理的resource view才行。比如,要使用texture作为render target和shader resource,那么我们就必须要创建两个view: ID3D11RenderTargetViewID3D11ShaderResourceView
Resource view做两件事。首先告诉Direct3D怎么使用resource,然后如果resource的格式被定义为typeless,那么就必须在创建view的时候声明类型。

Multisampling Theory

多重采样可以抗锯齿
Aliasing and Antialiasing
可以由上图看出锯齿的视觉效果,第二条线就是应用了抗锯齿的基础。这里介绍一个最简单的看锯齿技术supersampling,超采样通过把back buffer和depth buffer变为屏幕分辨率的4倍,然后将3D场景渲染到这个大的back buffer,最后在要输出back buffer到屏幕的时候,将back buffer下采样,比如选四个像素,然后做颜色值的平均,最终得到输出像素颜色值。
但是超采样需要相当于原先4倍的内存,而且对每个子像素颜色值的计算是最耗性能的,所以非常的不高效。
MultiSampling
Direct3D支持一个稍微做了让步的抗锯齿技术MSAA (MultiSampling Anti-Aliasing),上图说明了MSAA的技术细节。
相比于超采样,MSAA使用了颜色值共享,每个像素的颜色值只需要计算一次,但是这是一个妥协,所以超采样的准确度更高。

Direct3D中的Multisampling
typedef struct DXGI_SAMPLE_DESC {UINT Count;   //每个像素要采样的数量UINT Quality; //指定quality level
} DXGI_SAMPLE_DESC, *LPDXGI_SAMPLE_DESC;//不同的纹理格式和Sample数量决定了quality的等级,这就是查询quality level的方法
HRESULT ID3D11Device::CheckMultisampleQualityLevels(DXGI_FORMAT Format, UINT SampleCount, UINT *pNumQualityLevels);//D3D11支持的最大sampling数量
#define D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT ( 32 )

back buffer和depth buffer要使用相同的DXGI_SAMPLE_DESC结构

Feature Levels
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;//把下面的结构体用于初始化Direct3D,会逐个检查是否支持
D3D_FEATURE_LEVEL featureLevels[4] =
{D3D_FEATURE_LEVEL_11_0, // First check D3D 11 supportD3D_FEATURE_LEVEL_10_1, // Second check D3D 10.1 supportD3D_FEATURE_LEVEL_10_0, // Next, check D3D 10 supportD3D_FEATURE_LEVEL_9_3   // Finally, check D3D 9.3 support
};
初始化DIRECT3D

书上介绍的过程已经很好了

  1. Create the ID3D11Device and ID3D11DeviceContext interfaces using the D3D11CreateDevice function.
  2. Check 4X MSAA quality level support using the ID3D11Device::CheckMultisampleQualityLevels method.
  3. Describe the characteristics of the swap chain we are going to create by filling out an instance of the DXGI_SWAP_CHAIN_DESC structure.
  4. Query the IDXGIFactory instance that was used to create the device, and create an IDXGISwapChain instance.
  5. Create a render target view to the swap chain’s back buffer.
  6. Create the depth/stencil buffer and its associated depth/stencil view.
  7. Bind the render target view and depth/stencil view to the output merger stage of the rendering pipeline so that they can be used by Direct3D.
  8. Set the viewport.
Create the Device and Context
  1. The ID3D11Device interface is used to check feature support, and allocate resources.
  2. The ID3D11DeviceContext interface is used to set render states, bind resources to the graphics pipeline, and issue rendering commands.
// 创建Device和DeviceContext的方法
HRESULT D3D11CreateDevice(IDXGIAdapter *pAdapter, //指定我们创建的device所代表的display adapterD3D_DRIVER_TYPE DriverType, //我们将会总是指定D3D_DRIVER_TYPE_HARDWARE来使用3D硬件加速HMODULE Software, //提供软件驱动UINT Flags, //可选的设备创建flag,有D3D11_CREATE_DEVICE_DEBUG,D3D11_CREATE_DEVICE_SINGLETHREADEDCONST D3D_FEATURE_LEVEL *pFeatureLevels, //一个D3D_FEATURE_LEVEL数组,用来检查feature level supportUINT FeatureLevels, //上面数组的长度UINT SDKVersion, //总是指定D3D11_SDK_VERSION.ID3D11Device **ppDevice, //返回的创建的deviceD3D_FEATURE_LEVEL *pFeatureLevel, //返回pFeatureLevels中第一个支持的feature levelID3D11DeviceContext **ppImmediateContext//返回创建的device context
);

例子:

UINT createDeviceFlags = 0;#if defined(DEBUG) || defined(_DEBUG)createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endifD3D_FEATURE_LEVEL featureLevel;
ID3D11Device* md3dDevice;
ID3D11DeviceContext* md3dImmediateContext;
HRESULT hr = D3D11CreateDevice(0, // default adapterD3D_DRIVER_TYPE_HARDWARE,0, // no software devicecreateDeviceFlags,0, 0, // default feature level arrayD3D11_SDK_VERSION,& md3dDevice,& featureLevel,& md3dImmediateContext);
if(FAILED(hr))
{MessageBox(0, L"D3D11CreateDevice Failed.", 0, 0);return false;
}
if(featureLevel != D3D_FEATURE_LEVEL_11_0)
{MessageBox(0, L"Direct3D Feature Level 11 unsupported.", 0, 0);return false;
}
Check 4X MSAA Quality Support
UINT m4xMsaaQuality;
HR(md3dDevice->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, 4, & m4xMsaaQuality));
assert(m4xMsaaQuality > 0 );
Describe the Swap Chain
// the characteristics of the swap chain
typedef struct DXGI_SWAP_CHAIN_DESC {DXGI_MODE_DESC BufferDesc; //我们想创建的back buffer的属性DXGI_SAMPLE_DESC SampleDesc; //multisamples and quality level的数量DXGI_USAGE BufferUsage; //指定为DXGI_USAGE_RENDER_TARGET_OUTPUT 因为我们要渲染到back bufferUINT BufferCount;//swap chain中使用的back buffer的数量HWND OutputWindow;//A handle to the window we are rendering into. win32编程的句柄BOOL Windowed;//选择是窗口化显示还是全屏显示DXGI_SWAP_EFFECT SwapEffect;//指定DXGI_SWAP_EFFECT_DISCARD让显卡驱动选择最搞笑的表现方式UINT Flags;//可选flag
} DXGI_SWAP_CHAIN_DESC;
//对于flag。如果设置为DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH,那
//么在切换到全屏模式的时候,将会选择匹配back buffer设置的最佳display mode;
//如果不指定,那么在切换到全屏模式的时候,还是使用当前的display mode// The DXGI_MODE_DESC type is another structure define
typedef struct DXGI_MODE_DESC
{UINT Width; // desired back buffer widthUINT Height; // desired back buffer heightDXGI_RATIONAL RefreshRate; // display mode refresh rateDXGI_FORMAT Format; // back buffer pixel formatDXGI_MODE_SCANLINE_ORDER ScanlineOrdering; // display scanline modeDXGI_MODE_SCALING Scaling; // display scaling mode
} DXGI_MODE_DESC;

例子:

DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width = mClientWidth; // use window's client area dims
sd.BufferDesc.Height = mClientHeight;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;// Use 4X MSAA?
if(mEnable4xMsaa)
{sd.SampleDesc.Count = 4;// m4xMsaaQuality is returned via CheckMultisampleQualityLevels().sd.SampleDesc.Quality = m4xMsaaQuality-1;
}
// No MSAA
else
{sd.SampleDesc.Count = 1;sd.SampleDesc.Quality = 0;
}
sd.BufferUsage    = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount    = 1;
sd.OutputWindow   = mhMainWnd;
sd.Windowed       = true;
sd.SwapEffect     = DXGI_SWAP_EFFECT_DISCARD;
sd.Flags          = 0;
Create the Swap Chain
HRESULT IDXGIFactory::CreateSwapChain(IUnknown *pDevice,             // Pointer to ID3D11Device.DXGI_SWAP_CHAIN_DESC *pDesc,   // Pointer to swap chain description.IDXGISwapChain **ppSwapChain); // Returns created swap chain interface.

例子:

IDXGIDevice* dxgiDevice = 0;
HR(md3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice));
IDXGIAdapter* dxgiAdapter = 0;
HR(dxgiDevice->GetParent(__uuidof(IDXGIAdapter),(void**))&dxgiAdapter));// Finally got the IDXGIFactory interface.
IDXGIFactory* dxgiFactory = 0;
HR(dxgiAdapter->GetParent(__uuidof(IDXGIFactory),(void**))&dxgiFactory));// Now, create the swap chain.
IDXGISwapChain* mSwapChain;
HR(dxgiFactory->CreateSwapChain(md3dDevice, &sd, &mSwapChain));// Release our acquired COM interfaces (because we are done with them).
ReleaseCOM(dxgiDevice);
ReleaseCOM(dxgiAdapter);
ReleaseCOM(dxgiFactory);

DXGI (DirectX Graphics Infrastructure)是与Direct3D分离的API,用于处理图形相关的事情,如 the swap chain, enumerating graphics hardware, and switching between windowed and full-screen mode.

Create the Render Target View
ID3D11RenderTargetView* mRenderTargetView;
ID3D11Texture2D* backBuffer;
mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&backBuffer));
md3dDevice->CreateRenderTargetView(backBuffer, 0, &mRenderTargetView);
ReleaseCOM(backBuffer);
Create the Depth/Stencil Buffer and View
typedef struct D3D11_TEXTURE2D_DESC {UINT Width; // The width of the texture in texels.UINT Height; // The height of the texture in texels.UINT MipLevels; // The number of mipmap levels.UINT ArraySize; // The number of textures in a texture arrayDXGI_FORMAT Format; // A member of the DXGI_FORMAT enumerated type specifying the format of the texels.DXGI_SAMPLE_DESC SampleDesc; // The number of multisamples and quality levelD3D11_USAGE Usage;UINT BindFlags;UINT CPUAccessFlags;UINT MiscFlags;
} D3D11_TEXTURE2D_DESC;
  • Usage: 指定为D3D11_USAGE枚举类型,表示纹理怎么被使用。
  • BindFlags:一个或多个flag或到一起,来决定resource将会被绑定到管线的哪个位置。
    • D3D11_BIND_DEPTH_STENCIL:纹理将被绑定为depth/stencil buffer
    • D3D11_BIND_RENDER_TARGET:纹理将被绑定为render target
    • D3D11_BIND_SHADER_RESOURCE:纹理被绑定为shader resource
  • CPUAccessFlags: 如果CPU要写入到resource,则指定为D3D11_CPU_ACCESS_WRITE,如果一个resource要有写权限,那么必须使用D3D11_USAGE_DYNAMICD3D11_USAGE_STAGING;如果CPU要读一个buffer,则必须指定D3D11_CPU_ACCESS_READ,如果一个resource要有读权限,那么必须要使用D3D11_USAGE_STAGING。对于depth/stencil buffer,GPU完成所有的操作,所以不必指定。
  • MiscFlags:可选flag

作者建议避免使用D3D11_USAGE_DYNAMICD3D11_USAGE_STAGING,因为会降低性能

For maximum speed, graphics hardware works best when we create all of our resources and upload the data to the GPU, and the resources stay on the GPU where only the GPU reads and writes to the resources
只有在有的操作都由GPU完成的时候,性能才最好

例子:

D3D11_TEXTURE2D_DESC depthStencilDesc;depthStencilDesc.Width = mClientWidth;
depthStencilDesc.Height = mClientHeight;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;// Use 4X MSAA? --must match swap chain MSAA values.
if( mEnable4xMsaa )
{depthStencilDesc.SampleDesc.Count = 4;depthStencilDesc.SampleDesc.Quality = m4xMsaaQuality-1;
}
// No MSAA
else
{depthStencilDesc.SampleDesc.Count = 1;depthStencilDesc.SampleDesc.Quality = 0;
}
depthStencilDesc.Usage          = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags      = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags      = 0;ID3D11Texture2D* mDepthStencilBuffer;
ID3D11DepthStencilView* mDepthStencilView;HR(md3dDevice->CreateTexture2D(&depthStencilDesc, // Description of texture to create.0,&mDepthStencilBuffer)); // Return pointer to depth/stencil buffer.
HR(md3dDevice->CreateDepthStencilView(mDepthStencilBuffer, // Resource we want to create a view to.0,&mDepthStencilView)); // Return depth/stencil view
Bind the Views to the Output Merger Stage
md3dImmediateContext->OMSetRenderTargets(1, &mRenderTargetView, mDepthStencilView);
Set the Viewport
// 指定视椎体的结构
typedef struct D3D11_VIEWPORT {FLOAT TopLeftX;FLOAT TopLeftY;FLOAT Width;FLOAT Height;FLOAT MinDepth;FLOAT MaxDepth;
} D3D11_VIEWPORT;

例子:

D3D11_VIEWPORT vp;vp.TopLeftX = 0.0f;
vp.TopLeftY = 0.0f;
vp.Width = static_cast<float>(mClientWidth);
vp.Height = static_cast<float>(mClientHeight);
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;md3dImmediateContext->RSSetViewports(1, &vp);
框架代码

看书的源码就行,我个人上传了一份,做了少量的修改,在win10上可以直接运行

https://gitee.com/alienity/d3d11

这篇关于Direct3D基础--初始化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/397490

相关文章

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

QT进行CSV文件初始化与读写操作

《QT进行CSV文件初始化与读写操作》这篇文章主要为大家详细介绍了在QT环境中如何进行CSV文件的初始化、写入和读取操作,本文为大家整理了相关的操作的多种方法,希望对大家有所帮助... 目录前言一、CSV文件初始化二、CSV写入三、CSV读取四、QT 逐行读取csv文件五、Qt如何将数据保存成CSV文件前言

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

mysql的基础语句和外键查询及其语句详解(推荐)

《mysql的基础语句和外键查询及其语句详解(推荐)》:本文主要介绍mysql的基础语句和外键查询及其语句详解(推荐),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋... 目录一、mysql 基础语句1. 数据库操作 创建数据库2. 表操作 创建表3. CRUD 操作二、外键

Python基础语法中defaultdict的使用小结

《Python基础语法中defaultdict的使用小结》Python的defaultdict是collections模块中提供的一种特殊的字典类型,它与普通的字典(dict)有着相似的功能,本文主要... 目录示例1示例2python的defaultdict是collections模块中提供的一种特殊的字

Python基础文件操作方法超详细讲解(详解版)

《Python基础文件操作方法超详细讲解(详解版)》文件就是操作系统为用户或应用程序提供的一个读写硬盘的虚拟单位,文件的核心操作就是读和写,:本文主要介绍Python基础文件操作方法超详细讲解的相... 目录一、文件操作1. 文件打开与关闭1.1 打开文件1.2 关闭文件2. 访问模式及说明二、文件读写1.

C#基础之委托详解(Delegate)

《C#基础之委托详解(Delegate)》:本文主要介绍C#基础之委托(Delegate),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 委托定义2. 委托实例化3. 多播委托(Multicast Delegates)4. 委托的用途事件处理回调函数LINQ

Spring组件初始化扩展点BeanPostProcessor的作用详解

《Spring组件初始化扩展点BeanPostProcessor的作用详解》本文通过实战案例和常见应用场景详细介绍了BeanPostProcessor的使用,并强调了其在Spring扩展中的重要性,感... 目录一、概述二、BeanPostProcessor的作用三、核心方法解析1、postProcessB

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::