Direct3D 10学习笔记(一)——初始化

时间:2023-03-09 17:32:23
Direct3D 10学习笔记(一)——初始化

本篇将简单整理Direct3D 10的初始化,具体内容参照《 Introduction to 3D Game Programming with DirectX 10》(中文版有汤毅翻译的电子书《DirectX 10 3D游戏编程入门》)。

Direct3D 10的初始化可分为以下几个步骤:

1.填充一个DXGI_SWAP_CHAIN_DESC结构体,用于描述了所要创建的交换链特性。

2.使用D3D10CreateDeviceAndSwapChain函数创建ID3D10Device设备接口和IDXGISwapChain交换链接口。

3.为交换链后台缓冲区创建一个渲染目标视图。

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

5.将渲染目标视图和深度/模板视图绑定到渲染管线的输出合并器阶段,使它们可以被 Direct3D 使用。

6.设置视口。

1.描述交换链

填充一个DXGI_SWAP_CHAIN_DESC结构体,该结构体定义如下:

 typedef struct DXGI_SWAP_CHAIN_DESC
{
DXGI_MODE_DESC BufferDesc;
DXGI_SAMPLE_DESC SampleDesc;
DXGI_USAGE BufferUsage;
UINT BufferCount;
HWNDOutput Window;
BOOL Windowed;
DXGI_SWAP_EFFECT SwapEffect;
UINT Flags;
}DXGI_SWAP_CHAIN_DESC;

BufferDesc:指定显示模式,其中的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;

SampleDesc:进行多重采样设置,其中的DXGI_SAMPLE_DESC定义如下:

 typedef struct DXGI_SAMPLE_DESC
{
UINT Count;//每像素多重采样个数
UINT Quality;//图像质量等级,等级越高,性能越低,可选范围是0到ID3D10Device::CheckMultisampleQualityLevels
}DXGI_SAMPLE_DESC;

默认的sample模式是:Count=1,Quality=0,即不采用多重采样。

BufferUsage:设置CPU访问后台缓冲区的选项。后台缓冲区用于shader input或者render-target output阶段。一般使用DXGI_USAGE_RENDER_TARGET_OUTPUT。

BufferCount:指定交换链中缓冲区的数量。

OutputWindow:指定图形绘制窗口。Windowed标识显示方式。若Windowed为TRUE,以窗口方式显示;若为FALSE,以全屏方式显示。

SwapEffect:指定系统将后台缓冲区的内容复制到前台缓冲区的方式。其枚举如下:

 typedef enum DXGI_SWAP_EFFECT
{
DXGI_SWAP_EFFECT_DISCARD = ,
DXGI_SWAP_EFFECT_SEQUENTIAL =
}DXGI_SWAP_EFFECT;

DXGI_SWAP_EFFECT_DISCARD:后台缓存复制到前台缓存后,清除后台缓存内容。这个选项,让显卡驱动程序选择最高效的显示模式;只能在交换链中只有一个后台缓冲区的情况下使用。

DXGI_SWAP_EFFECT_SEQUENTIAL:后台缓存复制到前台缓存后,保留后台缓存原内容不变。这个选项,让交换链按顺序显示所有后台缓冲区中的内容。不能使用多重采样反走样。

Flags:可选的标志值,用于描述交换链的选项。该成员是DXGI_SWAP_CHAIN_FLAG枚举类型,其枚举定义如下:

 typedef enum DXGI_SWAP_CHAIN_FLAG
{
DXGI_SWAP_CHAIN_FLAG_NONPREROTATED = ,
DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH = ,
DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE = ,
DXGI_SWAP_CHAIN_FLAG_RESTRICTED_CONTENT = ,
DXGI_SWAP_CHAIN_FLAG_RESTRICT_SHARED_RESOURCE_DRIVER = ,
DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY =
}DXGI_SWAP_CHAIN_FLAG;

DXGI_SWAP_CHAIN_FLAG_NONPREROTATED :关闭图像自动翻转,当把前台缓冲区的内容转换到显示器上时,即不进行图像翻转。若程序想自己控制图像翻转显示的话,通过使用这个标志避免带宽性能损失。在全屏模式下才有效。

DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH :让程序通过调用IDXGISwapChain::ResizeTarget来转换模式。当应用程序切换到全屏模式时,Direct3D会自动选择与当前的后台缓冲区设置最匹配的显示模式。

如果未指定Flags标志值,当应用程序切换到全屏模式时,Direct3D会使用当前的桌面显示模式。

描述交换链示例:

     //交换链描述
DXGI_SWAP_CHAIN_DESC sd; //后台缓冲区描述 //宽度、高度
sd.BufferDesc.Width = WINDOW_WIDTH;
sd.BufferDesc.Height = WINDOW_HEIGHT; //像素格式
sd.BufferDesc.Format = DXGI_FORMAT_R8G8_UNORM; //刷新率
sd.BufferDesc.RefreshRate.Numerator = ;
sd.BufferDesc.RefreshRate.Denominator = ; //扫描方式
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; //按比例伸缩
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //多重采样描述 //每像素多重采样个数
sd.SampleDesc.Count = ; //图像质量等级,可选范围为0到ID3D10Device::CheckMultisampleQualityLevels
sd.SampleDesc.Quality = ; //CPU访问后台缓冲区的选项
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; //后台缓冲区数量
sd.BufferCount = ; //进行渲染的窗口句柄
sd.OutputWindow = hMainWnd; //窗口/全屏显示
sd.Windowed = TRUE; //将后台缓冲区内容复制到前台缓冲区的方式
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; //交换链行为
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

2.创建设备接口和交换链接口

先声明两个接口:指向ID3D10Device类型的设备接口和指向IDXGISwapChain类型的交换链接口。

这两个接口可以通过下面函数进行创建:

 HRESULT D3D10CreateDeviceAndSwapChain(
IDXGIAdapter *pAdapter,
D3D10_DRIVER_TYPE DriverType,
HMODULE Software,
UINT Flags,
UINT SDKVersion,
DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,
IDXGISwapChain **ppSwapChain,
ID3D10Device **ppDevice);

pAdapter:指定显卡序号,表示要为哪个物理显卡创建设备对象。设为空值时,表示使用主显卡。

DriverType:指定Direct3D设备类型。该成员是D3D_DRIVER_TYPE枚举类型,该枚举类型定义如下:

 typedef enum D3D10_DRIVER_TYPE
{
D3D10_DRIVER_TYPE_HARDWARE = ,
D3D10_DRIVER_TYPE_REFERENCE = ,
D3D10_DRIVER_TYPE_NULL = ,
D3D10_DRIVER_TYPE_SOFTWARE = ,
D3D10_DRIVER_TYPE_WARP = ,
}D3D10_DRIVER_TYPE;

D3D10_DRIVER_TYPE_HARDWARE:一般使用该参数,表示使用3D硬件加快渲染速度。

D3D10_DRIVER_TYPE_REFERENCE:创建引用设备。引用设备为Direct3D的软件实现,具有Direct3D所有功能,但运行速度非常慢。使用引用设备主要用于:测试硬件不支持的代码,例如在一块不支持Direct3D 10的显卡上测试Direct3D 10代码;测试驱动程序缺陷,当代码能在引用设备上正常运行,而在硬件上不能正常运行时,说明硬件驱动程序可能存在缺陷。

Software:用于软件光栅化设备。如果DriverType是D3D_DRIVER_TYPE_SOFTWARE,此值不能为NULL,且必须先安装一个软件光栅化设备。否则设置为NULL,因为使用硬件加速渲染。

Flags:可选的设备创建标志值。该成员是D3D10_CREATE_DEVICE_FLAG枚举类型,该枚举类型定义如下:

 typedef enum D3D10_CREATE_DEVICE_FLAG
{
D3D10_CREATE_DEVICE_SINGLETHREADED = 0x1,
D3D10_CREATE_DEVICE_DEBUG = 0x2,
D3D10_CREATE_DEVICE_SWITCH_TO_REF = 0x4,
D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS = 0x8,
D3D10_CREATE_DEVICE_ALLOW_NULL_FROM_MAP = 0x10,
D3D10_CREATE_DEVICE_BGRA_SUPPORT = 0x20,
D3D10_CREATE_DEVICE_STRICT_VALIDATION = 0x200
}D3D10_CREATE_DEVICE_FLAG;

D3D10_CREATE_DEVICE_DEBUG:当以debug模式生成程序时,参数应设为D3D10_CREATE_DEVICE_DEBUG,以激活调试层。在指定调试标志后,Direct3D会向VS输出窗口发送调试信息。否则,即当以release模式生成程序时,Flags设为空。

SDKVersion:SDK版本,始终设为 D3D10_SDK_VERSION。

pSwapChainDesc:指向DXGI_SWAP_CHAIN_DESC结构体的指针,即指向一开始填充的交换链描述结构体。

ppSwapChain:用于返回创建后的交换链对象。即应指向所要创建的IDXGISwapChain类型的交换链接口。

ppDevice:用于返回创建后的设备对象。即应指向所要创建的ID3D10Device类型的设备接口。

D3D10CreateDeviceAndSwapChain函数调用示例:

     //运行层开关
UINT createDeviceFlags = NULL;
#if defined(DEBUG) || defined(_DEBUG)
createDeviceFlags |= D3D10_CREATE_DEVICE_DEBUG;
#endif //创建设备和交换链
ID3D10Device* pd3dDevice;
IDXGISwapChain* pSwapChain;
D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, D3D10_SDK_VERSION, &sd, &pSwapChain, &pd3dDevice);

3.创建渲染目标视图

先声明两个接口:指向ID3D10RenderTargetView类型的渲染目标视图接口和指向ID3D10Texture2D类型(或者其他资源格式)的后台缓冲区.

通过交换链接口调用IDXGISwapChain::GetBuffer方法来获取后台缓冲区指针。同时应注意,每次调用该方法后,后台缓冲区的COM引用计数会向上递增一次,所以应配对使用Release方法。GetBuffer方法如下:

 HRESULT IDXGISwapChain::GetBuffer(
UINT Buffer,
const IID &riid,
void **ppSurface);

Buffer:表示所要获取的后台缓冲区的索引。由于后台缓冲区数量可以大于1,所以必须指定索引。

riid:缓冲区接口类型。通常为2D纹理,即ID3D10Texture2D。

ppSurface:指向所返回的后台缓冲区的指针。

通过设备接口调用ID3D10Device::CreateRenderTargetView方法创建渲染目标视图。CreateRenderTargetView方法如下:

 HRESULT ID3D10Device::CreateRenderTargetView(
ID3D10Resource *pResource,
const D3D10_RENDER_TARGET_VIEW_DESC *pDesc,
ID3D10RenderTargetView **ppRTView);

pResource:指定将要作为渲染目标的资源。如将后台缓冲区作为渲染目标。

pDesc:指向D3D10_RENDER_TARGET_VIEW_DESC结构体的指针,该结构体描述渲染目标即参数pResource所指定的资源中的元素的数据类型。如果创建资源时使用强类型,则可为空,表示以资源自身格式为视图格式。

ppRTView:指向创建后所要返回的渲染目标视图对象的指针。

渲染目标视图创建示例:

     //创建渲染目标视图
ID3D10RenderTargetView* pRenderTargetView;
ID3D10Texture2D* pBackBuffer;
pSwapChain->GetBuffer(NULL, __uuidof(ID3D10Texture2D), reinterpret_cast<VOID**>(&pBackBuffer));
pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &pRenderTargetView);
pBackBuffer->Release();

4.创建深度/模板缓冲区及其视图

填充一个D3D10_TEXTURE2D_DESC结构体,该结构体定义如下:

 typedef struct D3D10_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;
}D3D10_TEXTURE2D_DESC;

Width:纹理宽度,单位为纹理元素。

Height:纹理高度,单位为纹理元素。

MipLevels:多级渐进纹理层数量。对于深度/模板缓冲区,只需一个多级渐进纹理层。

ArraySize:纹理数组中的纹理数量。对于深度/模板缓冲区,只需一个纹理。

Format:一个DXGI_FORMAT枚举类型成员,指定纹理元素格式。对于深度/模板缓冲区,必须为以下格式之一:DXGI_FORMAT_D32_FLOAT_S8X24_UINT:32位浮点深度缓冲区。为模板缓冲区预留8位(无符号整型),每个模板值取值为[0,255],其余24位闲置。

DXGI_FORMAT_D32_FLOAT:32位浮点深度缓冲区。

DXGI_FORMAT_D24_UNORM_S8_UINT:无符号24位深度缓冲区,每个深度值取值为[0,1]。为模板缓冲区预留8位(无符号整型),每个模板值取值为[0,255]。

DXGI_FORMAT_D16_UNORM:无符号16位深度缓冲区,每个深度值取值为[0,1]。

SampleDesc:多重采样描述结构体。成为Count指定多重采样的数量,Quality指定图像质量级别。

Usage:表示纹理用途的D3D10_USAGE枚举类型成员,其枚举如下:

 typedef enum D3D10_USAGE
{
D3D10_USAGE_DEFAULT = ,
D3D10_USAGE_IMMUTABLE = ,
D3D10_USAGE_DYNAMIC = ,
D3D10_USAGE_STAGING =
}D3D10_USAGE;

D3D10_USAGE_DEFAULT:表示 GPU会对资源执行读写操作。CPU不能读写这种资源。对于深度/模板缓冲区,使用该标志值,因为GPU会不断地执行读写深度/模板缓冲区的操作。

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

D3D10_USAGE_DYNAMIC:表示CPU会频繁更新资源中的数据。GPU可以从资源中读取数据,而CPU可以向其写入数据。

D3D10_USAGE_STAGING:表示CPU会读取该资源的一个副本,即使得该资源支持从显存到系统内存的数据复制。

BindFlags:指定该资源将会绑定到管线的哪个阶段的D3D10_BIND_FLAG枚举成员,可用位或运算符组合多个标志值。其枚举如下:

 typedef enum D3D10_BIND_FLAG
{
D3D10_BIND_VERTEX_BUFFER = 0x1L,
D3D10_BIND_INDEX_BUFFER = 0x2L,
D3D10_BIND_CONSTANT_BUFFER = 0x4L,
D3D10_BIND_SHADER_RESOURCE = 0x8L,
D3D10_BIND_STREAM_OUTPUT = 0x10L,
D3D10_BIND_RENDER_TARGET = 0x20L,
D3D10_BIND_DEPTH_STENCIL = 0x40L
}D3D10_BIND_FLAG;

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

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

D3D10_BIND_DEPTH_STENCIL:将纹理作为一个深度/模板缓冲区绑定到管线上。

CPUAccessFlags:指定CPU对资源的访问权限的D3D10_CPU_ACCESS_FLAG枚举成员。对于深度/模板缓冲区,只有GPU会进行读写,CPU不会在该缓冲区执行读写,故设该参数为空。D3D10_CPU_ACCESS_FLAG枚举如下:

 typedef enum D3D10_RESOURCE_MISC_FLAG
{
D3D10_RESOURCE_MISC_GENERATE_MIPS = 0x1L,
D3D10_RESOURCE_MISC_SHARED = 0x2L,
D3D10_RESOURCE_MISC_TEXTURECUBE = 0x4L,
D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX = 0x10L,
D3D10_RESOURCE_MISC_GDI_COMPATIBLE = 0x20L
}D3D10_RESOURCE_MISC_FLAG;

D3D10_CPU_ACCESS_WRITE:若CPU需向资源写入数据,则应指定为该标志值。具有写访问权限的资源的 Usage参数应设为D3D10_USAGE_DYNAMIC或D3D10_USAGE_STAGING。

D3D10_CPU_ACCESS_READ:若CPU需从资源读取数据,则应指定为该标志值。具有读访问权限的资源的Usage 参数应设为D3D10_USAGE_STAGING。

MiscFlags:可选的一个D3D10_RESOURCE_MISC_FLAG枚举成员。与深度/模板缓冲区无关,所以设为空。其枚举如下:

 typedef enum D3D10_RESOURCE_MISC_FLAG
{
D3D10_RESOURCE_MISC_GENERATE_MIPS = 0x1L,
D3D10_RESOURCE_MISC_SHARED = 0x2L,
D3D10_RESOURCE_MISC_TEXTURECUBE = 0x4L,
D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX = 0x10L,
D3D10_RESOURCE_MISC_GDI_COMPATIBLE = 0x20L
}D3D10_RESOURCE_MISC_FLAG;

填充作为深度/模板缓冲区的纹理结构体示例:

     //深度/模板缓冲区描述
D3D10_TEXTURE2D_DESC DepthStencilDesc; //宽度、高度
DepthStencilDesc.Width = ClientWidth;
DepthStencilDesc.Height = ClientHeight; //纹理元素格式
DepthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; //纹理数量
DepthStencilDesc.ArraySize = ; //多级渐进纹理层数量
DepthStencilDesc.MipLevels = ; //多重采样
DepthStencilDesc.SampleDesc.Count = ;
DepthStencilDesc.SampleDesc.Quality = ; //纹理用途
DepthStencilDesc.Usage = D3D10_USAGE_DEFAULT; //CPU对资源访问权限
DepthStencilDesc.CPUAccessFlags = NULL; //管线绑定标志值
DepthStencilDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL; //与深度/模板无关的一个标志值
DepthStencilDesc.MiscFlags = NULL;

使用深度/模板缓冲区之前,还需创建其视图,过程与创建渲染目标视图相似。

使用方法ID3D10Device::CreateTexture2D创建深度/模板缓冲区。

 HRESULT ID3D10Device::CreateTexture2D(
const D3D10_TEXTURE2D_DESC *pDesc,
const D3D10_SUBRESOURCE_DATA *pInitialData,
D3D10Texture2D **ppTexture2D);

pDesc:指向前面填充的D3D10_TEXTURE2D_DESC结构体的指针。

pInitialData:指向初始化数据的指针,这些数据用于填充纹理。对于深度/模板缓冲区,由于在执行深度缓存和模板操作时,Direct3D会自动向该缓冲区写入数据,故将该参数设为空值。

ppTexture2D:指向创建后所要返回的深度/模板缓冲区的指针。

使用与ID3D10Device::CreateDepthStencilView创建深度/模板缓冲区视图。

 HRESULT CreateDepthStencilView(
ID3D10Resource *pResource,
const D3D10_DEPTH_STENCIL_VIEW_DESC *pDesc,
ID3D10DepthStencilView **ppDepthStencilView);

pResource:指定将要作为深度/模板缓冲区的资源。

pDesc:指向D3D10_RENDER_TARGET_VIEW_DESC结构体的指针,该结构体描述渲染目标即参数pResource所指定的资源中的元素的数据类型。如果创建资源时使用强类型,则可为空,表示以资源自身格式为视图格式。

ppDepthStencilView:指向创建后所要返回的深度/模板缓冲区对象的指针。

创建深度/模板缓冲区及其视图示例:

     //创建深度/模板缓冲区及其视图
ID3D10Texture2D* pDepthStencilBuffer;
ID3D10DepthStencilView* pDepthStencilView;
pd3dDevice->CreateTexture2D(&DepthStencilDesc, nullptr, &pDepthStencilBuffer);
pd3dDevice->CreateDepthStencilView(pDepthStencilBuffer, nullptr, &pDepthStencilView);

5.将视图绑定到输出合并器阶段

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

 void ID3D10Device::OMSetRenderTargets(
UINT NumViews,
ID3D10RenderTargetView *const *ppRenderTargetViews,
ID3D10DepthStencilView *pDepthStencilView);

NumViews:将要绑定的渲染目标数量。

ppRenderTargetViews:指向将要绑定的渲染目标视图数组的首元素的指针。

pDepthStencilView:指向将要绑定的深度/模板视图。

将视图绑定到管线示例:

     //绑定视图到输出合并器阶段
pd3dDevice->OMSetRenderTargets(, &pRenderTargetView, pDepthStencilView);

6.设置视口

通常3D场景将会渲染到整个后台缓冲区,通过提交给前台缓冲区后,把图像布满整个客户窗口区。

若只希望将场景渲染到一个子矩形区域中,而非布满整个缓冲区,则可以通过修改视口来实现。

后台缓冲区的子矩形区域称为视口,以下面的结构体描述:

 typedef struct D3D10_VIEWPORT
{
INT TopLeftX;
INT TopLeftY;
UINT Width;
UINT Height;
FLOAT MinDepth;
FLOAT MaxDepth;
}D3D10_VIEWPORT;

TopLeftX:相对于窗口客户区左上角的横坐标,最左上角的第一个像素点横坐标为0像素。

TopLeftY:相对于窗口客户区左上角的纵坐标,最左上角的第一个像素点纵坐标为0像素。

Width:子矩形区域宽度。

Height:子矩形高度。

MinDepth:深度缓冲区最小值,由于Direct3D的深度缓冲区取值为[0,1],因此通常取为0。

MaxDepth:深度缓冲区最大值,由于Direct3D的深度缓冲区取值为[0,1],因此通常取为1。

填充完D3D10_VIEWPORT结构体后,使用ID3D10Device::RSSetViewports设置视口。

 void ID3D10Device::RSSetViewports(
UINT NumViewports,
const D3D10_VIEWPORT *pViewports);

NumViewports:视口数量。

pViewports:指向进行设置的视口的指针。

视口设置示例:

     //视口设置

     //填充视口
D3D10_VIEWPORT vp;
vp.TopLeftX = ;
vp.TopLeftY = ;
vp.Width = ClientWidth;
vp.Height = ClientHeight;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f; //设置视口
pd3dDevice->RSSetViewports(, &vp);