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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou

c++基础版

c++基础版 Windows环境搭建第一个C++程序c++程序运行原理注释常亮字面常亮符号常亮 变量数据类型整型实型常量类型确定char类型字符串布尔类型 控制台输入随机数产生枚举定义数组数组便利 指针基础野指针空指针指针运算动态内存分配 结构体结构体默认值结构体数组结构体指针结构体指针数组函数无返回值函数和void类型地址传递函数传递数组 引用函数引用传参返回指针的正确写法函数返回数组

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口int main(int argc, char *argv[]){// argc是命令行参数个数,argv是

【MRI基础】TR 和 TE 时间概念

重复时间 (TR) 磁共振成像 (MRI) 中的 TR(重复时间,repetition time)是施加于同一切片的连续脉冲序列之间的时间间隔。具体而言,TR 是施加一个 RF(射频)脉冲与施加下一个 RF 脉冲之间的持续时间。TR 以毫秒 (ms) 为单位,主要控制后续脉冲之前的纵向弛豫程度(T1 弛豫),使其成为显著影响 MRI 中的图像对比度和信号特性的重要参数。 回声时间 (TE)