【DirectX学习笔记】02_D3D初始化

Direct3D初始化过程分为以下几个步骤:

  1. 创建设备和设备上下文;

  2. 检测设备支持的多重采样质量等级;

  3. 填充用于描述交换链的结构体;

  4. 创建交换链;

  5. 为交换链的后台缓冲区创建渲染目标视图;

  6. 创建深度/模板缓冲区以及相关的深度/模板视图;

  7. 将渲染目标视图和深度/模板视图绑定到渲染管线的输出合并阶段;

  8. 设置视口。

一、创建设备和设备上下文

ID3D11Device接口用于检测显示适配器功能和分配资源;

ID3D11DeviceContext接口用于设置管线状态、将资源绑定到图形管线和生成渲染命令;

设备和上下文用如下函数创建:

HRESULT  D3D11CreateDevice (
    IDXGIAdapter  *pAdapter,
    D3D_DRIVER_TYPE  DriverType,
    HMODULE  Software ,
    UINT  Flags ,
    CONST  D3D_FEATURE_LEVEL  *pFeatureLevels ,
    UINT  FeatureLevels ,
    UINT  SDKVersion,
    ID3D11Device  **ppDevice ,
    D3D_FEATURE_LEVE L  *pFeatureLevel,
    ID3D11DeviceContext  **ppImmediateContext
);

参数信息如下:

pAdapter

指定要为哪个物理显卡创建设备对象。当该参数设为空值时,表示使用主显卡。

DriverType

D3D_DRIVER_TYPE_HARDWARE:表示使用3D硬件来加快渲染速度。但是,也可以有两个其他选择:

D3D_DRIVER_TYPE_REFERENCE:创建所谓的引用设备(reference device)。引用设备是Direct3D的软件实现,它具有Direct3D的所有功能(只是运行速度非常慢,因为所有的功能都是用软件来实现的)。引用设备随DirectX SDK一起安装,只用于程序员,而不应该用于程序发布。使用引用设备有两个原因:

  • 测试硬件不支持的代码;例如,在一块不支持Direct3D 11的显卡上测试一段Direct3D 11的代码。

  • 测试驱动程序缺陷。当代码能在引用设备上正常运行,而不能在硬件上正常工作时,说明硬件的驱动程序可能存在缺陷。

D3D_DRIVER_TYPE_SOFTWARE:创建一个用于模拟3D硬件的软件驱动器。要使用软件驱动器,你必须自己创建一个,或使用第三方的软件驱动器。与下面要说的WARP驱动器不同,Direct3D不提供软件驱动器。

D3D_DRIVER_TYPE_WARP:创建一个高性能的Direct3D 10.1软件驱动器。WARP代表Windows Advanced Rasterizati on Platform。

Software

用于支持软件光栅化设备(software rasterizer)。总是将该参数设为空值

Flags

可选的设备创建标志值。当以release模式生成程序时,该参数通常设为0(无附加标志值);当以debug模式生成程序时,该参数应设为:D3D11_CREATE_DEVICE_DEBUG:用以激活调试层。

pFeatureLevels

D3D_FEATURE_LEVEL数组,元素的顺序表示要特征等级的测试顺序

FeatureLevels

pFeatureLevels数组中的元素D3D_FEATURE_LEVELs的数量,若pFeatureLevels设置为null,则这个值为0

SDKVersion

始终设为D3D11_SDK_VERSION

ppDevice

返回创建后的设备对象

pFeatureLevel

返回pFeatureLevels数组中第一个支持的特征等级(如果pFeatureLevels 为null,则返回可支持的最高等级)

ppImmediateContext

返回创建后的设备上下文

 调用该函数的代码如下:

UINT createDeviceFlags = 0;
#if  defined(DEBUG)||defined(_DEBUG)
    createDeviceFlags  |= D3D11_CREATE_DEVICE_DEBUG;
#endif
D3D_FEATURE_LEVEL featureLevel;
ID3D11Device *  md3dDevice;
ID3D11Device Context*  md3dImmediate Context;
HRESULT  hr = D3D11CreateDevice(
    0,                     //  默认显示适配器
    D3D_DRIVER_TYPE_HARDWARE ,
    0,                     //  不使用软件设备
    createDeviceFlags ,
    0, 0,               //  默认的特征等级数组
    D3D11_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 FeatureLevel 11 unsupported.", 0, 0);
    return  false;
}

二、检测多重采样质量支持

创建完设备后,就可以检查多重采样质量等级了:
UINT  m4xMsaaQuality;
HR(md3dDevice ->CheckMultisampleQualityLevels(
    DXGI_FORMAT_R8G8B8A8_UNORM, 4, &  m4xMsaaQuality));
assert(m4xMsaaQuality > 0);

三、描述交换链

此步骤需要填充一个DXGI_SWAP_CHAIN_DESC结构体来描述我们将要创建的交换链的特性。该结构体的定义如下:

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;

 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;

 参数信息如下:

BufferDesc该结构体描述了我们所要创建的后台缓冲区的属性。主要关注的属性有:宽度、高度和像素格式
SampleDesc多重采样数量和质量级别
BufferUsage设为DXGI_USAGE_RENDER_TARGET_OUTPUT,因为要将场景渲染到后台缓冲区(即,将它用作渲染目标)
BufferCount交换链中的后台缓冲区数量;一般只用一个后台缓冲区来实现双缓存
OutputWindow将要渲染到的窗口的句柄
Windowed当设为true时,程序以窗口模式运行;当设为false时,程序以全屏(full-screen)模式运行
SwapEffect设为DXGI_SWAP_EFFECT_DISCARD,让显卡驱动程序选择最高效的显示模式
Flags可选的标志值

 调用该函数的代码如下:

DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width    = mClientWidth;    // 使用窗口客户区宽度
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;
// 是否使用4X MSAA?
if(mEnable4xMsaa)
{
    sd.SampleDesc.Count = 4;
    // m4xMsaaQuality是通过CheckMultisampleQualityLevels()方法获得的
    sd.SampleDesc.Quality = m4xMsaaQuality-1;
}
// NoMSAA
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;

四、创建交换链

交换链(IDXGISwapChain)是通过IDXGIFactory实例的IDXGIFactory::CreateSwapChain方法创建的:

HRESULT IDXGIFactory::CreateSwapChain(
    IUnknown *pDevice , // 指向ID3D11Device的指针
    DXGI_SWAP_CHAIN_DESC *pDesc, // 指向一个交换链描述的指针
    IDXGISwapChain **ppSwapChain); // 返回创建后的交换链

五、创建渲染目标视图

创建渲染目标视图代码实现过程如下:

ID3D11RenderTargetView* mRenderTargetView;
ID3D11Texture2D* backBuffer;
mSwapChain->GetBuffer(0,__uuidof(ID3D11Texture2D),reinterpret_cast<void**>(&backBuffer));
md3dDevice->CreateRenderTargetView(backBuffer, 0, &mRenderTargetView);
ReleaseCOM(backBuffer);

1.IDXGISwapChain::GetBuffer方法用于获取一个交换链的后台缓冲区指针。该方法的第一个参数表示所要获取的后台缓冲区的索引值(由于后台缓冲区的数量可以大于1,所以这里必须指定索引值)。在示例程序中,只使用一个后台缓冲区,所以该索引值设为0。第二个参数是缓冲区的接口类型,它通常是一个2D纹理(ID3D11Texture2D)。第三个参数返回指向后台缓冲区的指针。

2.使用ID3D11Device::CreateRenderTargetView方法创建渲染目标视图。

第一个参数指定了将要作为渲染目标的资源,在上面的例子中,渲染目标是后台缓冲区(即为后台缓冲区创建了一个渲染目标视图)。

第二个参数是一个指向D3D11_RENDER_TARGET_VIEW_DESC结构体的指针,该结构体描述了资源中的元素的数据类型。如果在创建资源时使用的是某种强类型格式(即,非弱类型格式),则该参数可以为空,表示以资源的第一个mipmap层次(后台缓冲区也只有一个mipmap层次)作为视图格式。第三个参数通过指针返回了创建后的渲染目标视图对象。

3.每调用一次IDXGISwapChain::GetBuffer方法,后台缓冲区的COM引用计数就会向上递增一次,这便是在代码片段的结尾处释放它(ReleaseCOM)的原因。

六、创建深度模板缓冲区和视图

深度缓冲区只是一个存储深度信息的2D纹理(如果使用模板,则模板信息也在该缓冲区中)。要创建纹理首先填充D3D11_TEXTURE2D_DESC结构体来描述所要创建的纹理,然后再调用ID3D11Device::CreateTexture2D方法。该结构体的定义如下:

typedef struct D3D11_TEXTURE2D_DESC {
    UINT Width;
    UINT Height;
    UINT MipLevels;
    UINT ArraySize;
    DXGI_FORMAT Format;
    DXGI_SAMPLE_DESC SampleDesc;
    D3D10_USAGE Usage;
    UINT BindFlags;
    UINT CPUAccessFlags;
    UINT MiscFlags;
} D3D11_TEXTURE2D_DESC;

 参数信息如下:

Width

纹理的宽度,单位为纹理元素(texel)
Height纹理的高度,单位为纹理元素(texel)
MipLevels多级渐近纹理层(mipmap level)的数量
ArraySize在纹理数组中的纹理数量。对于深度/模板缓冲区来说,只需要一个纹理
Format一个DXGI_FORMAT枚举类型成员,它指定了纹理元素的格式。对于深度/模板缓冲区来说,它必须DXGI_FORMAT枚举列出的格式之一
SampleDesc多重采样数量和质量级别
Usage

表示纹理用途的D3D11_USAGE枚举类型成员。有4个可选值:

D3D11_USAGE_DEFAULT:表示GPU(graphics processing unit,图形处理器)会对资源执行读写操作。CPU不能读写这种资源。对于深度/模板缓冲区,使用D3D11_USAGE_DEFAULT标志值,因为GPU会执行所有读写深度/模板缓冲区的操作;

D3D10_USAGE_IMMUTABLE:表示在创建资源后,资源中的内容不会改变。这样可以获得一些内部优化,因为GPU会以只读方式访问这种资源。除了在创建资源时CPU会写入初始化数据外,其他任何时候CPU都不会对这种资源执行任何读写操作;

D3D10_USAGE_DYNAMIC:表示应用程序(CPU)会频繁更新资源中的数据内容(例如,每帧更新一次)。GPU可以从这种资源中读取数据,而CPU可以向这种资源中写入数据;

D3D10_USAGE_STAGING:表示应用程序(CPU)会读取该资源的一个副本(即,该资源支持从显存到系统内存的数据复制操作);

BindFlags

指定该资源将会绑定到管线的哪个阶段。对于深度/模板缓冲区,该参数应设为D3D11_BIND_DEPTH_STENCIL。其他可用于纹理的绑定标志值还有:

D3D11_BIND_RENDER_TARGET:将纹理作为一个渲染目标绑定到管线上;

D3D11_BIND_SHADER_RESOURCE:将纹理作为一个着色器资源绑定到管线上;

CPUAccessFlags指定CPU对资源的访问权限。如果CPU需要向资源写入数据,则应指定D3D11_CPU_ACCESS_WRITE。具有写访问权限的资源的Usage参数应设为D3D11_USAGE_DYNAMICD3D11_USAGE_STAGING。如果CPU需要从资源读取数据,则应指定D3D11_CPU_ACCESS_READ。具有读访问权限的资源的Usage参数应设为D3D11_USAGE_STAGING。对于深度/模板缓冲区来说,只有GPU会执行读写操作;所以,将该参数设为0,因为CPU不会在深度/模板缓冲区上执行读写操作
MiscFlags可选的标志值,与深度/模板缓冲区无关,所以设为0

在使用深度/模板缓冲区之前,必须为它创建一个绑定到管线上的深度/模板视图。过程与创建渲染目标视图的过程相似。下面的代码示范了如何创建深度/模板纹理以及与它对应的深度/模板视图:

D3D11_TEXTURE2D_DESC depthStencilDesc;
depthStencilDesc.Width                = mClientWidth;
depthStencilDesc.Height              = mClientHeight;
depthStencilDesc.MipLevels            = 1;
depthStencilDesc.ArraySize            = 1;
depthStencilDesc.Format              = DXGI_FORMAT_D24_UNORM_S8_UINT;
// 是否使用4X MSAA?——必须与交换链的MSAA的值匹配
if( mEnable4xMsaa)
{
    depthStencilDesc.SampleDesc.Count  = 4;
    depthStencilDesc.SampleDesc.Quality  = m4xMsaaQuality-1;
}
//  不使用MSAA
else
{
    depthStencilDesc.SampleDesc.Count  =  1;
    depthStencilDesc.SampleDesc.Quality  = 0;
}
depthStencilDesc.Usage                = D3D10_USAGE_DEFAULT;
depthStencilDesc.BindFlags            = D3D10_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags      = 0;
depthStencilDesc.MiscFlags            = 0;
ID3D10Texture2D* mDepthStencilBuffer;
ID3D10DepthStencilView* mDepthStencilView;
HR(md3dDevice->CreateTexture2D(
    &depthStencilDesc, 0, &mDepthStencilBuffer));
HR(md3dDevice->CreateDepthStencilView(
    mDepthStencilBuffer, 0, &mDepthStencilView));

CreateTexture2D的第二个参数是一个指向初始化数据的指针,这些初始化数据用来填充纹理。不过,由于个纹理被用作深度/模板缓冲区,所以不需要为它填充任何初始化数据。当执行深度缓存和模板操作时,Direct3D会自动向深度/模板缓冲区写入数据。所以,将第二个参数指定为空值。

CreateDepthStencilView的第二个参数是一个指向D3D11_DEPTH_STENCIL_VIEW_DESC的指针。这个结构体描述了资源中这个元素数据类型(格式)。如果资源是一个有类型的格式(非typeless),这个参数可以为空值,表示创建一个资源的第一个mipmap等级的视图(深度/模板缓冲也只能使用一个 mipmap等级)。因为指定了深度/模板缓冲的格式,所以将这个参数设置为空值。

七、将视图绑定到输出合并阶段

为后台缓冲区和深度模板缓冲区创建好视图后,就可以将这些视图绑定到渲染管线的输出合并阶段,使这些资源成为管线的渲染目标和深度/模板缓冲区:

md3dImmediateContext->OMSetRenderTargets(
    1,&mRenderTargetView,mDepthStencilView);

第一个参数是将要绑定的渲染目标的数量;在这里仅绑定了一个渲染目标,不过该参数可以为着色器同时绑定多个渲染目标。

第二个参数是将要绑定的渲染目标视图数组中的第一个元素的指针。第三个参数是将要绑定到管线的深度/模板视图。

八、设置视口

通常会把3D场景渲染到整个后台缓冲区上。不过,有时只希望把3D场景渲染到后台缓冲区的一个子矩形区域中。后台缓冲区的子矩形区域称为视口,结构体描述如下:
typedef struct D3D11_VIEWPORT {
    FLOAT TopLeftX;
    FLOAT TopLeftY;
    FLOAT Width;
    FLOAT Height;
    FLOAT MinDepth;
    FLOAT MaxDepth;
} D3D11_VIEWPORT;

前4个数据成员定义了相对于窗口客户区的视口矩形范围。MinDepth成员表示深度缓冲区的最小值,MaxDepth表示深度缓冲区的最大值。在填充了D3D11_VIEWPORT结构体之后,使用ID3D11Device::RSSetViewports方法设置Direct3D的视口。下面的例子创建和设置了一个视口,该视口与整个后台缓冲区的大小相同:

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

第一个参数是绑定的视图的数量(可以使用超过1的数量用于高级的效果),第二个参数指向一个viewports的数组。

参考博客:https://enjoyphysics.cn/Article1508

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值