DX11初始化
今天来介绍Direct3D-11的初始化方法。初始化Direct3D-11与初始化Direct3D-9有点不同。总体来看,初始化Direct3D-11分为以下几个步骤
- 描述交换链
- 创建D3D设备
- 创建设备上下文接口
- 创建交换链接口
- 创建目标渲染视图
- 创建深度模板缓存视图(可选)
- 设置视口
描述交换链
首先来介绍什么是交换链。所谓交换链就是一个链表,在链表上的每一个节点用来描述每一帧画面,每一次绘制的时候都会根据链表上的指定节点来绘制当前画面。这是链的描述。
至于交换呢,就表明这个链表是不断地在循环的,需要马上绘制的节点可以称之为前台,就好像聚光灯下的舞台,马上就要上演节目。而以后要绘制到窗口的节点就是后台,就好像是下一个节目。
通过交换链不断地循环,D3D把每一帧画面保存到后台中,并不断地把后台缓存中渲染好的画面呈现到窗口上。
交换链通过一个结构体来描述。要描述交换链,只需要填充这个结构体即可。
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 BufferDesc:描述后台缓冲区的结构体。用来描述显示模式。
- DXGI_SAMPLE_DESC SampleDesc:描述多重采样的参数。
- DXGI_USAGE BufferUsage:一种枚举类型,用于描述CPU对后台缓冲区的用法。后台缓存可以用作着色器的输入或者渲染目标的输出。
- UINT BufferCount:描述交换链中后台缓存的数量,通常设为1
- HWND OutputWindow:指定输出窗口句柄
- BOOL Windowed:描述是否是全屏显示。
- DXGI_SWAP_EFFECT SwapEffect:用于描述前台缓存在绘制之后D3D应该作何选择。
- UINT Flags:用于描述交换链的行为。设为0即可。
对于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;
- UINT Width:描述窗口分辨率宽度。
- UINT Height:描述窗口分辨率高度。
- DXGI_RATIONAL RefreshRate:用于描述刷新频率的结构体。
- DXGI_FORMAT Format:用于描述显示格式的枚举类型。设置为DXGI_FORMAT_R8G8B8A8_UNORM即可
- DXGI_MODE_SCANLINE_ORDER ScanlineOrdering:用于描述扫描线绘制模式的枚举。可以设置为0
- DXGI_ODE_SCALING Scaling:用于描述缩放形式的枚举类型,默认使用DXGI_MODE_SCALING_UNSPECIFIED。
这里涉及到的DXGI_RATIONAL结构体。成分如下
typedef struct DXGI_RATIONAL
{
UINT Numerator;
UINT Denominator;
} DXGI_RATIONAL;
- UINT 类型的Numerator:表示刷新频率的分子
- UINT 类型的Denominator:表示刷新频率的分母
- Numerator=60,Denominator=1表示每秒刷新6次
现在我们回到DXGI_SWAP_CHAIN_DESC中,其中的DXGI_SAMPLE_DESC。
typedef struct DXGI_SAMPLE_DESC
{
UINT Count;
UINT Quality;
} DXGI_SAMPLE_DESC;
- UINT类型的Count:描述每一个像素多重采样的次数。
- UINT类型的Quality:描述图像品质等级。有效范围是0~1,小于CheckMultisampleQualityLevels的返回值。
- 默认值为Count=1,Quality=0;
对以上进行总结消化之后,就可以来描述我们需要的交换链了。
DXGI_SWAP_CHAIN_DESC scDesc;
::ZeroMemory(&scDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
scDesc.OutputWindow = hwnd; //描述输出窗口
scDesc.BufferCount = 1;//缓存数量
scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;//描述后台缓存格式
scDesc.BufferDesc.Width = WINDOW_WIDTH; //描述后台缓存分辨率宽度
scDesc.BufferDesc.Height = WINDOW_HEIGHT;//描述后台缓存分辨率高度
scDesc.BufferDesc.RefreshRate.Denominator = 1;//刷新频率的分母
scDesc.BufferDesc.RefreshRate.Numerator = 60;//刷新频率的分子,这两项表明每秒刷新6次
scDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
scDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scDesc.Flags = 0;
scDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
scDesc.SampleDesc.Count = 1; //这里不使用多重采样,因此数量设为1
scDesc.SampleDesc.Quality = 0; //不使用多重采样,设为0
scDesc.Windowed = true; //设置为窗口模式
描述目标功能级别
交换链描述完成之后,还需要描述目标功能级别,告诉Direct3D使用哪一套Shader.它由一个D3D_FEATURE_LEVEL枚举类型描述,其中包含的内容如下:
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;
其具体含义可以去查阅MSDN,我们可以这么用如下:
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_0,//D3D11所支持的特征,包括shader model 5
D3D_FEATURE_LEVEL_10_1,//D3D10.1所支持的特征,包括shader model 4
D3D_FEATURE_LEVEL_10_0,//D3D10.0所支持的特征,包括shader model 4
};
创建设备和交换链以及设备上下文
经过层层铺垫,终于来到了这最关键的一步了。那就是通过D3D11CreateDeviceAndSwapChain创建Direct3D设备接口以及交换链。Direct3D设备接口由ID3D11Device定义,它用于创建资源。而设备上下文接口由ID3D11DeviceContext定义,它代表设备上下文生成渲染指令。交换链由IDXGISwapChain定义,之前已经介绍过了,这里就不再重复。
在MSDN上能查到D3D11CreateDeviceAndSwapChain的原型如下
HRESULT D3D11CreateDeviceAndSwapChain(
_In_opt_ IDXGIAdapter *pAdapter,
D3D_DRIVER_TYPE DriverType,
HMODULE Software,
UINT Flags,
_In_opt_ const D3D_FEATURE_LEVEL *pFeatureLevels,
UINT FeatureLevels,
UINT SDKVersion,
_In_opt_ const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,
_Out_opt_ IDXGISwapChain **ppSwapChain,
_Out_opt_ ID3D11Device **ppDevice,
_Out_opt_ D3D_FEATURE_LEVEL *pFeatureLevel,
_Out_opt_ ID3D11DeviceContext **ppImmediateContext
);
每个参数的含义如下:
* IDXGIAdapter *pAdapter:创建设备时使用的视频适配器指针,使用NULL表示使用默认的适配器.
* D3D_DRIVER_TYPE DriverType:描述驱动类型的枚举体,主要是说明使用什么方式去实现Direct3D特征.一般我们常用两种:D3D_DRIVER_TYPE_HARDWARE以及D3D_DRIVER_TYPE_SOFTWARE。其他的参数可以查看MSDN对该枚举体的描述
* HMODULE Software:该参数指向一个DLL,该DLL实现了软件光栅化处理。如果上一个参数设为D3D_DRIVER_TYPE_SOFTWARE,则不可以设为NULL,否则就取NULL。
* UINT Flags:描述用于创建设备的参数,它的取值定义在D3D11_CREATE_DEVICE_FLAG枚举体中,可以按位OR进行结合,一般设为0就可以了
* D3D_FEATURE_LEVEL *pFeatureLevels:目标功能级别集合,就是之前我们创建的数组
* UINT FeatyreLevels:这里输入上一个数组中元素的个数。
* UINT SKDVersion:Direct3D的版本号,我们使用D3D11_SDK_VERSION即可
* DXGI_SWAP_CHAIN_DESC *pSwapChainDesc:毫无疑问,这里填我们之前描述交换链时使用的结构体.
* IDXGISwapChain **ppSwapChain:待初始化的交换链接口.
* ID3D11Device **ppDevice:待初始化的设备接口.
* D3D_FEATURE_LEVEL *pFeatureLevel:这是一个输出参数,它指向刚才输入的数组中第一个被设备支持的目标功能级别.这个可以用来作判断哪一个功能级别可以匹配。一般我们不需要,把它设为NULL.
* ID3D11DeviceContext **ppImmediateContext:待初始化的立即执行上下文.
创建目标渲染视图
前面提到了交换链,也介绍了交换链的概念,我们绘图的时候就是要将数据绘到后台去,通过交换链不断地交换到前台显示。
因此要创建目标渲染视图,必须首先获得后台缓冲区。然后通过它创建目标渲染视图,接着,将创建好的目标渲染视图通过执行上下文设置一下。执行上下文描述了下一次绘图的状态,一般而言,目标渲染视图只有一个,因此设置一次,终生受用啊。
因此,我们可以这样获取后台缓存
ID3D11Texture2D *pBackBuffer = NULL;//ID3D11Texture2D类型的,后台缓存指针
//调用GetBuffer()函数得到后台缓存对象,并存入&pBackBuffer;
hr = (*swapChain)->GetBuffer(0,//缓存索引,一般设为0
__uuidof(ID3D11Texture2D), //缓存类型
(LPVOID*)&pBackBuffer);//缓存指针
然后通过设备指针创建目标渲染视图
(*device)->CreateRenderTargetView(pBackBuffer,//上面创建好的后台缓存
NULL,//设置为NULL得到默认的渲染目标视图
renderTargetView);//返回创建好的渲染目标视图
//InitD3D函数传递的实参
创建深度模板缓存视图
要创建深度模板缓存视图,首先需要创建深度缓存,要创建深度缓存首先需要描述它,然后通过设备创建即可.
要描述深度模板缓存,需要填充一个结构体D3D11_TEXTURE2D_DESC,该结构体用于描述2D纹理(View实际上就是一个二维的图像嘛),在MSDN上可以查到该结构体的原型如下:
typedef struct D3D11_TEXTURE2D_DESC1 {
UINT Width;
UINT Height;
UINT MipLevels;
UINT ArraySize;
DXGI_FORMAT Format;
DXGI_SAMPLE_DESC SampleDesc;
D3D11_USAGE Usage;
UINT BindFlags;
UINT CPUAccessFlags;
UINT MiscFlags;
} D3D11_TEXTURE2D_DESC1;
- UINT Width:纹理宽度,我们可以取窗口宽度来描述
- UINT Height:纹理高度,我们可以取窗口高度来描述
- UINT MipLevels:纹理映射级数,取1即可,表示使用多重采样纹理
- UINT ArraySize:设置在纹理数组中纹理的个数,取1即可
- DXGI_FORMAT Format:描述缓存格式的枚举类型,在深度模板缓存中,我们使用DXGI_FORMAT_D24_UNORM_S8_UINT.
- DXGI_SAMPLE_DESC SampleDesc:描述多重采样参数的结构体。这里可以不设置
- D3D11_USAGE Usage:标识了纹理是如何读取和写入的。可以取默认值D3D11_USAGE_DEFAULT
- UINT BindFlags:标识纹理如何绑定到管线的。可以参考D3D11_BIND_FLAG枚举类型,这里我们使用D3D11_BIND_DEPTH_STENCIL
- UINT CPUAccessFlags:标识如何允许CPU访问该缓存,如果不需CPU访问要可以设为0.
- UINT MscFlags:描述不常见的资源选项。这里我们不需要,设为0即可
&esmp;我们可以这样来描述一个深度缓存
D3D11_TEXTURE2D_DESC dsDesc;
dsDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; //这里表示24位用于深度缓存,8位用于模板缓存
dsDesc.Width = 800; //深度模板缓存的宽度
dsDesc.Height = 600; //深度模板缓存的高度
dsDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; //绑定标识符
dsDesc.MipLevels = 1;
dsDesc.ArraySize = 1;
dsDesc.CPUAccessFlags = 0; //CPU访问标识符,0为默认值
dsDesc.SampleDesc.Count = 1; //多重采样的属性,本例中不采用多重采样即,
dsDesc.SampleDesc.Quality = 0; //所以Count=1,Quality=0
dsDesc.MiscFlags = 0;
dsDesc.Usage = D3D11_USAGE_DEFAULT;
描述完深度缓存之后,就需要创建深度缓存,创建的过程是通过设备的CreateTexture2D来创建的,与目标渲染视图的创建过程一样,只是后者是通过交换链获取,而前者需要描述创建罢了.
device->CreateTexture2D(&dsDesc, 0, depthStencilBuffer);
接着我们就可以直接拿着创建好的深度模板缓存,来创建深度模板缓存视图了
device->CreateDepthStencilView(*depthStencilBuffer, 0, depthStencilView);
设置目标渲染视图和深度模板缓存视图
这两个视图在刚才已经创建好了,我们直接使用设备上下文设置即可,只需要调用设备上下文的OMSetRenderTargets方法就可以完成设置
该方法的原型如下
void OMSetRenderTargets(
[in] UINT NumViews,
[in, optional] ID3D11RenderTargetView *const *ppRenderTargetViews,
[in, optional] ID3D11DepthStencilView *pDepthStencilView
);
- UINT NumViews:字面意思,视图的数量
- ID3D11RenderTargetView *const *ppRenderTargetViews:刚才创建好的目标渲染视图
- ID3D11DepthStencilView *pDepthStencilView:刚才创建好的深度缓存模板视图
下面是一个使用示例:
immediateContext->OMSetRenderTargets(1, //绑定的目标视图的个数
renderTargetView, //渲染目标视图,InitD3D函数传递的实参
*depthStencilView); //绑定模板
设置视口
视口描述的是观察视角,在Direct3D中D3D11_VIEWPORT定义了视口的属性。关于视口的描述和DX9的一样,可以看看我在学习DX9时关于四大变换的笔记。
初步封装代码
#pragma once
#include<windows.h>
#include<d3dcompiler.h>
#include<xnamath.h>
#include<d3d11.h>
#include<d3dx11.h>
#pragma comment(lib,"d3d11.lib")
#pragma comment(lib,"d3dx11.lib")
#pragma comment(lib,"d3dcompiler.lib")
#pragma comment(lib,"dxguid.lib")
#pragma comment(lib,"winmm.lib")
#define SAFE_RELEASE(p) {if((p)){(p)->Release();}}
class DirectSystem
{
//私有字段
private:
//基础接口
ID3D11Device *m_pDevice; //DX设备接口,用于生成各种各样的对象
ID3D11DeviceContext *m_pDeviceContext; //DX设备上下文,用于生成设备渲染指令
//视图区域
ID3D11RenderTargetView *m_pRenderTargetView;//DX渲染目标视图,字面意思
ID3D11DepthStencilView *m_pDepthStencilView;//DX深度模板缓存视图
ID3D11Texture2D *m_pDepthStencil; //深度模板缓存
//图形接口
IDXGISwapChain *m_pSwapChain; //DX图形接口,交换链
public:
DirectSystem()
:m_pDevice(NULL),
m_pDeviceContext(NULL),
m_pRenderTargetView(NULL),
m_pDepthStencilView(NULL),
m_pDepthStencil(NULL),
m_pSwapChain(NULL)
{
}
HRESULT Direct3D_Init(HWND hwnd,int WINDOW_WIDTH,int WINDOW_HEIGHT) {
//描述交换链
DXGI_SWAP_CHAIN_DESC scDesc;
::ZeroMemory(&scDesc, sizeof(scDesc));
scDesc.OutputWindow = hwnd; //描述输出窗口
scDesc.BufferCount = 1;//缓存数量
scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;//描述后台缓存格式
scDesc.BufferDesc.Width = WINDOW_WIDTH; //描述后台缓存分辨率宽度
scDesc.BufferDesc.Height = WINDOW_HEIGHT;//描述后台缓存分辨率高度
scDesc.BufferDesc.RefreshRate.Denominator = 1;//刷新频率的分母
scDesc.BufferDesc.RefreshRate.Numerator = 60;//刷新频率的分子,这两项表明每秒刷新6次
scDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
scDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scDesc.Flags = 0;
scDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
scDesc.SampleDesc.Count = 1; //这里不使用多重采样,因此数量设为1
scDesc.SampleDesc.Quality = 0; //不使用多重采样,设为0
scDesc.Windowed = true; //设置为窗口模式
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0, //D3D11.0 所支持的功能特征
D3D_FEATURE_LEVEL_10_1, //D3D10.1 所支持的功能特征
D3D_FEATURE_LEVEL_10_0 //D3D10.0 所支持的功能特征
};
UINT numFeature = ARRAYSIZE(featureLevels);
HRESULT te;
//创建设备,设备上下文,交换链
if (FAILED(te=D3D11CreateDeviceAndSwapChain(
NULL,
D3D_DRIVER_TYPE_HARDWARE,NULL,0,
featureLevels,numFeature,
D3D11_SDK_VERSION,&scDesc,
&m_pSwapChain,&m_pDevice,NULL,&m_pDeviceContext)))
{
MessageBox(NULL, L"Create Device and Swapchain Error", L"Error", NULL);
return E_FAIL;
}
//获取后台缓冲区
ID3D11Texture2D *pBack = NULL;
if (FAILED(m_pSwapChain->GetBuffer(0,__uuidof(ID3D11Texture2D),(void**)&pBack))) {
MessageBox(NULL, L"GetBuffer Error", L"Error", NULL);
return E_FAIL;
}
//创建渲染目标视图
HRESULT hr;
hr=m_pDevice->CreateRenderTargetView(pBack, NULL, &m_pRenderTargetView);
SAFE_RELEASE(pBack);
if (FAILED(hr))
{
MessageBox(NULL, L"Create Render Target View Error", L"Error", NULL);
return E_FAIL;
}
//描述深度模板缓存
D3D11_TEXTURE2D_DESC dsDesc;
::ZeroMemory(&dsDesc, sizeof(dsDesc));
dsDesc.ArraySize = 1;
dsDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
dsDesc.CPUAccessFlags = 0;
dsDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
dsDesc.Height = WINDOW_HEIGHT;
dsDesc.Width = WINDOW_WIDTH;
dsDesc.MipLevels = 1;
dsDesc.MiscFlags = 0;
dsDesc.SampleDesc.Count = 1;
dsDesc.SampleDesc.Quality = 0;
dsDesc.Usage = D3D11_USAGE_DEFAULT;
if (FAILED(m_pDevice->CreateTexture2D(&dsDesc, NULL, &m_pDepthStencil)))
{
MessageBox(NULL, L"Create Depth Stencil Buffer Error", L"Error", NULL);
return E_FAIL;
}
if (FAILED(m_pDevice->CreateDepthStencilView(m_pDepthStencil, 0, &m_pDepthStencilView)))
{
MessageBox(NULL, L"Create Depth Stencil Buffer View Error", L"Error", NULL);
return E_FAIL;
}
m_pDeviceContext->OMSetRenderTargets(1, &m_pRenderTargetView, m_pDepthStencilView);
//第四步,设置视口大小,D3D11默认不会设置视口,此步骤必须手动设置
D3D11_VIEWPORT vp; //创建一个视口的对象
vp.TopLeftX = 0; //视口左上角的横坐标
vp.TopLeftY = 0; //视口左上角的总坐标
vp.Width = WINDOW_WIDTH; //视口的宽
vp.Height = WINDOW_HEIGHT; //视口的高
vp.MinDepth = 0.0f; //深度值的下限,**由于深度值是[0, 1]所以下限值是0
vp.MaxDepth = 1.0f; //深度值的上限,上限值是1
m_pDeviceContext->RSSetViewports(1, &vp);
return S_OK;
}
void Direct3D_Render()
{
if (m_pDevice)
{
FLOAT color[4] = { 1.0f,0.0f,0.0f,0.0f };
m_pDeviceContext->ClearRenderTargetView(m_pRenderTargetView,color);
m_pSwapChain->Present(NULL, NULL);
}
}
bool SetUp()
{
return true;
}
~DirectSystem()
{
SAFE_RELEASE(m_pDevice);
SAFE_RELEASE(m_pDeviceContext);
SAFE_RELEASE(m_pRenderTargetView);
SAFE_RELEASE(m_pDepthStencilView);
SAFE_RELEASE(m_pDepthStencil);
SAFE_RELEASE(m_pSwapChain);
}
};
最后的运行效果将会是一个红色的窗口。
Copyright© by 寒江雪
Date:2016.12.25