DX11(二)-Direct3D初始化

05/20/2020
05/29/2020 – 总结
06/01/2020 – 如何查看显卡与输出设备,并且创建后台缓冲区和深度模板缓冲区

创建设备(Device)和上下文(Context)

  • 创建Direct3D 11设备(ID3D11Device)和上下文(ID3D11DeviceContext),它们可以被看作是物理图形设备硬件的软控制器。— 相当于显卡的控制面板
  • 通过软件跟硬件交互,如在显存中分配资源,清空后台缓冲区,将资源绑定到各种管线阶段、绘制几何体
  • ID3D11Device 接口用于检测显示适配器(显卡)和创建着色器,顶点布局等
  • 大量的Check/Create方法
ID3D11Device* d3dDevice; //初始化部分见下面
d3dDevice->CreateVertexShader(...);//顶点着色器
d3dDevice->CreateInputLayer(...);//创建顶点布局
d3dDevice->CreatePixelShader(...);//像素着色器
  • ID3D11DeviceContext 接口用于设置管线状态,将资源绑定到图形管线上生成渲染命令,Device创建了资源,但是设备上下文使用这些资源并操控管道的接口
    • ID311DeviceContext::ClearRenderTargetView函数要求绘图设备清空渲染目标
    • 大量的Get/Set方法
//使用好的着色器
ID3D11DeviceContext* d3dImmediateContext; //初始化见下面
d3dImmediateContext->VSSetShader(...);
d3dImmediateContext->IASetInputLayer(...);
d3dImmediateContext->PSSetShader(...);

名字的代表

  • I 代表COM智能指针相当于C++一个类使用
  • D3D 11 代表 Direct3D 11

通过D3D11CreateDevie创建

HRESULT D3D11CreateDevoe(//函数声明
	IDXGIAdapter *pAdapter; //指定要为哪个物理显卡创建设备对象,空值为主显卡
	D3D_DRIVER_TYPE DriverType,
	HMODULE Software, //光栅化设备
	UINT Flag, 	//Debug and release 不一样,需要有调试信息
	CONST D3D_FEATURE_LEVEL *pFeatureLevels,
	UINT FeatureLevels, 
	UINT SDKVersion,
	ID3D11Device **ppDevice,	//返回创建后的设备对象
	D3D_FEATURE_LEVEL * pFeaturelevel, 
	ID3D11DeviceContext **ppImmediateContext //设备的上下文
);

//使用
D3D_FEATURE_LEVELfeaturelevel;
ID3D11Device *md3dDevice;
ID3D11DeviceContext* md3dContext;
HRESULT hr = D3D11CreateDevice(  //创建Device和Context
	0,
	D3D_Driver_TYPE_HARDWARE,
	0,
	cerateDeviceFlags,
	0,0,
	D3D11_SDK_VERSION,
	&md3dDevice,  //指向指针的地址
	&featureLevel,
	&md3dContext
)

多重采样

因为计算机显示器上的像素分辨率有限,所以绘制一条任意直线,该直线不是平滑的,这是阶梯(锯齿,aliasing)效应。

缓解锯齿效应

  • 提高显示器的分辨率,或者缩小像素尺寸,可以缓解阶梯效应
  • 再无法提高显示器分辨率时候,可以使用抗锯齿技术

抗锯齿技术(antialiasing)

超级采样(supersampling)

它把后台缓冲和深度缓冲的大小提高到屏幕分辨率的四倍。

  • 后台缓冲会将4个像素的颜色取平均值后得到一个像素的最终颜色
  • 以软件的方式提升分辨率
  • 但是代价昂贵,由于像素数量和所需的内存数量增加为原来的4倍

多重采样(multisampling)

通过一个像素的子像素进行采样计算出附近像素的最终颜色

采样类型加检查采样质量支持

typedef struct DXGI_SAMPLE_DESC{
	UINT Count;  //采样数量
	UINT Quality; //质量级别
}DXGI_SAMPLE_DESC, *LPDXGI_SAMPLE_DESC;

UINT m4xMsaaQuality;
HR(md3dDevice ->checkMultisampleQualityLevels(
DXGI_FORMAT_R8G8B8A8_UNORM,
4,
&m4xMsaaQuality //始终大于0
));

交换链(double buffer)

避免动画闪烁,最好的做法有两个缓冲区,后台缓冲区(back buffer)执行所有的动画帧绘制工作,绘制完成之后,交换前台与后台缓冲区,把后台缓冲区作为一个完成的帧显式在屏幕上

  • 将一帧完整显示到屏幕上所消耗的时间小于屏幕的垂直刷新时间
  • DXGI交换链使后备缓冲区成为渲染管线的输出对象
  • 由IDXGISwapChain接口表示,保存了前后缓冲区纹理,并提供了用于调整缓冲区尺寸的方法和呈现方法
    • IDXGISwapChain::ResizeBuffers
    • IDGISwapChain::Present
  • 后台缓冲区是一个纹理,可以称为像素,因为后台缓冲区存储的是颜色信息
  • 后台缓冲区需要跟踪应用程序窗口的大小

描述交换链(代码部分)

描述我们将要创建的交换链的特性

typedef struct DXGI_SWAP_CHAIN_DESC
{
	DXGI_MODE_DESC bufferDesc,  //struct 里有后台缓冲区的属性,比如高宽度,像素格式
	DXGI_SAMPLE_DESC SampleDesc, //多重采样struct数量和质量
	DXGI_USAGE BufferUsage,	//设置场景渲染到后台缓冲区 == DXGI_USAGE_RENDER_TARGET_OUTPUT
	UINT BufferCount,	//交换链的数量,一个后台缓冲区完成双缓存
	HWND OutputWindow,	//渲染到的窗口句柄
	BOOL Windowed,		//true窗口模式,false为全屏
	DXGI_SWAP_EFFECT SwapEffect; //让显卡驱动程序选择最高效的显式模式
	UINT FLAGS		//	当应用切换到全屏模式,Direct3D会自动选择与当前的后台缓冲区设置最匹配
					//如果为指定,当切换全屏,会使用当前桌面显式模式
}DXGI_SWAP_CHAIN_DESC;


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; //像素格式, rgba,24位颜色
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_INSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;//缩放模式

//多重采样设置

//其他设置

创建交换链

方式1:不支持Direct3D 11.1

IDXGIFactory实例的IDXGIFactory::CreateSwapChain方法来创建

IDXGIFactory* dxgiFactory = 0;
ID3D11Device* md3dDevice;
DXGI_SWAP_CHAIN_DESC sd;
IDXGISwapChain* mSwapChain;


dxgiFactory->CreateSwapChain(md3dDevice,&sd,&mSwapChain);

方式2:支持Direct3D 11.1

// 填充各种结构体用以描述交换链
DXGI_SWAP_CHAIN_DESC1 sd;
//...

DXGI_SWAP_CHAIN_FULLSCREEN_DESC fd; //见后续补充描述
//..
// 为当前窗口创建交换链
HR(dxgiFactory2->CreateSwapChainForHwnd(m_pd3dDevice.Get(), m_hMainWnd, &sd, &fd, nullptr, m_pSwapChain1.GetAddressOf()));
HR(m_pSwapChain1.As(&m_pSwapChain));

为后备缓冲区创建渲染目标视图

渲染目标视图用于将渲染管线的运行结果输出给其绑定的资源

// 重设交换链并且重新创建渲染目标视图
ComPtr<ID3D11Texture2D> backBuffer;
HR(m_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(backBuffer.GetAddressOf())));
HR(m_pd3dDevice->CreateRenderTargetView(backBuffer.Get(), nullptr, m_pRenderTargetView.GetAddressOf()));

深度/模板缓冲区(Z-buffer)

  • 同时有深度和模板值
  • 不包含图像数据的纹理对象
  • 深度值范围在0.0到1.0之间,0.0表示离观察者最近的物体,1.0表示离观察者最远的距离
    • 比较深度值,越小的数通过深度测试,并更新缓冲区,这叫做深度缓存技术(Z-buffer)
  • 深度缓冲区与后台缓冲区的每个元素一 一对应

创建深度缓冲区(2D纹理)

深度缓冲区(depth buffer)是一种纹理资源

//先描述
typedef struct D3D11_TEXTURE2D_DESC
{
    UINT Width;         // 缓冲区宽度
    UINT Height;        // 缓冲区高度
    UINT MipLevels;     // Mip等级
    UINT ArraySize;     // 纹理数组中的纹理数量,默认1
    DXGI_FORMAT Format; // 缓冲区数据格式
    DXGI_SAMPLE_DESC SampleDesc;    // MSAA采样描述
    D3D11_USAGE Usage;  // 数据的CPU/GPU访问权限
    UINT BindFlags;     // 使用D3D11_BIND_FLAG枚举来决定该数据的使用类型
    UINT CPUAccessFlags;    // 使用D3D11_CPU_ACCESS_FLAG枚举来决定CPU访问权限
    UINT MiscFlags;     // 使用D3D11_RESOURCE_MISC_FLAG枚举,这里默认0
} 	D3D11_TEXTURE2D_DESC;   


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?
if (mEnable4xMsaa)
{
	depthStencilDesc.SampleDesc.Count = 4;
	depthStencilDesc.SampleDesc.Quality = m_4xMsaaQuality - 1;
}
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;

m_pd3dDevice->CreateTexture2D(&depthStencilDesc, nullptr, m_pDepthStencilBuffer.GetAddressOf());
m_pd3dDevice->CreateTexture2D(&depthStencilDesc, nullptr, m_pDepthStencilBuffer.GetAddressOf());

创建深度/模板视图

将创建好的深度/模板缓冲区绑定到深度/模板视图中

HRESULT ID3D11Device::CreateDepthStencilView( 
    ID3D11Resource *pResource,                      // [In] 需要绑定的资源
    const D3D11_DEPTH_STENCIL_VIEW_DESC *pDesc,     // [In] 深度缓冲区描述,这里忽略
    ID3D11DepthStencilView **ppDepthStencilView);   // [Out] 获取到的深度/模板视图
);
m_pd3dDevice->CreateDepthStencilView(m_pDepthStencilBuffer.Get(), nullptr, m_pDepthStencilView.GetAddressOf());

视口设置

将视图输出到窗口的特定范围

m_ScreenViewport.TopLeftX = 0;
m_ScreenViewport.TopLeftY = 0;
m_ScreenViewport.Width    = static_cast<float>(mClientWidth);
m_ScreenViewport.Height   = static_cast<float>(mClientHeight);
m_ScreenViewport.MinDepth = 0.0f;
m_ScreenViewport.MaxDepth = 1.0f;

m_pd3dImmediateContext->RSSetViewports(1, &m_ScreenViewport);

纹理资源视图

  • 为纹理创建相关的资源视图,当把纹理作为一个渲染目标和着色器资源时
    • 渲染目标视图(ID3D11RenderTargetView)
    • 着色器资源视图(ID3D11ShaderResourceView)

资源视图功能

  • 告诉Direct3D如何使用资源,例如资源绑定得到管线阶段
  • 如果在创建资源时指定的是弱类型(typeless)格式,那么在为它创建最远视图时就必须指定明确的资源类型

总结

基本都需要先描述,再创建!

  • 创建Direct3D设备和Direct3D设备上下文(D3D11CreateDevice),选择驱动类型与特性等级
  • Direct3D设备检测多重采样的级别 (CheckMultisampleQualityLevels)
  • 创建DXGI交换链(CreateSwapChain),并为后台缓冲区创建渲染目标视图(CreateRenderTargetView)
  • 创建深度缓冲区/2D纹理(CreateTexture2D)以及深度模板视图(CreateDepthStencilView)
  • 为渲染管线的输出合并阶段设置渲染目标(OMSetRenderTargets = 设置TargetView和DepthStencilView)
  • 设置视口变换的位置,窗口原点位置,大小和深度(RSSetViewports)

后续补充

采样与交换链Direct3D 11.1的描述

typedef struct DXGI_SAMPLE_DESC
{
    UINT Count;                     // MSAA采样数
    UINT Quality;                   // MSAA质量等级
} DXGI_SAMPLE_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;

typedef struct DXGI_RATIONAL
{
    UINT Numerator;                 // 刷新率分子
    UINT Denominator;               // 刷新率分母
} DXGI_RATIONAL;

DXGIFactory 查看显卡数目与输出设备1

#include <iostream>
#include <d3d11_1.h>

#include <windows.h>
#include <DXGI.h>
#include <vector>
using namespace std;



// 添加所有要引用的库
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "dxguid.lib")




std::string WStringToString(const std::wstring& wstr)
{
	std::string str(wstr.length(), ' ');
	std::copy(wstr.begin(), wstr.end(), str.begin());
	return str;
}

int main() {
	// 参数定义  
	IDXGIFactory* pFactory;
	IDXGIAdapter* pAdapter;
	std::vector <IDXGIAdapter*> vAdapters;            // 显卡  


	// 显卡的数量  
	int iAdapterNum = 0;


	// 创建一个DXGI工厂  
	HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&pFactory));

	if (FAILED(hr))
		return -1;

	// 枚举适配器, 查看有多少显卡
	while (pFactory->EnumAdapters(iAdapterNum, &pAdapter) != DXGI_ERROR_NOT_FOUND)
	{
		vAdapters.push_back(pAdapter);
		++iAdapterNum;
	}

	// 信息输出   
	cout << "===============获取到" << iAdapterNum << "块显卡===============" << endl;
	for (size_t i = 0; i < vAdapters.size(); i++)
	{
		// 获取信息  
		DXGI_ADAPTER_DESC adapterDesc;
		vAdapters[i]->GetDesc(&adapterDesc);
		wstring aa(adapterDesc.Description);
		std::string bb = WStringToString(aa);
		// 输出显卡信息  
		cout << "系统视频内存:" << adapterDesc.DedicatedSystemMemory / 1024 / 1024 << "M" << endl;
		cout << "专用视频内存:" << adapterDesc.DedicatedVideoMemory / 1024 / 1024 << "M" << endl;
		cout << "共享系统内存:" << adapterDesc.SharedSystemMemory / 1024 / 1024 << "M" << endl;
		cout << "设备描述:" << bb.c_str() << endl;
		cout << "设备ID:" << adapterDesc.DeviceId << endl;
		cout << "PCI ID修正版本:" << adapterDesc.Revision << endl;
		cout << "子系统PIC ID:" << adapterDesc.SubSysId << endl;
		cout << "厂商编号:" << adapterDesc.VendorId << endl;

		// 输出设备, 查看显示器 
		IDXGIOutput* pOutput;
		std::vector<IDXGIOutput*> vOutputs;
		// 输出设备数量  
		int iOutputNum = 0;
		while (vAdapters[i]->EnumOutputs(iOutputNum, &pOutput) != DXGI_ERROR_NOT_FOUND)
		{
			vOutputs.push_back(pOutput);
			iOutputNum++;
		}

		cout << "-----------------------------------------" << endl;
		cout << "获取到" << iOutputNum << "个显示设备:" << endl;
		cout << endl;

		for (size_t n = 0; n < vOutputs.size(); n++)
		{
			// 获取显示设备信息 ,可以支持多种显示模式
		
			DXGI_OUTPUT_DESC outputDesc; 
			vOutputs[n]->GetDesc(&outputDesc);

			// 获取设备支持  
			UINT uModeNum = 0;
			DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM;
			UINT flags = DXGI_ENUM_MODES_INTERLACED;

			vOutputs[n]->GetDisplayModeList(format, flags, &uModeNum, 0);
			
			//全屏宽度,高度,刷新率
			DXGI_MODE_DESC* pModeDescs = new DXGI_MODE_DESC[uModeNum];
			vOutputs[n]->GetDisplayModeList(format, flags, &uModeNum, pModeDescs); //

			cout << "显示设备名称:" << outputDesc.DeviceName << endl;
			cout << "显示设备当前分辨率:" << outputDesc.DesktopCoordinates.right - outputDesc.DesktopCoordinates.left << "*" << outputDesc.DesktopCoordinates.bottom - outputDesc.DesktopCoordinates.top << endl;
			cout << endl;

			// 所支持的分辨率信息  
			cout << "分辨率信息:" << endl;
			for (UINT m = 0; m < uModeNum; m++)
			{
				cout << "== 分辨率:" << pModeDescs[m].Width << "*" << pModeDescs[m].Height << "     刷新率" << (pModeDescs[m].RefreshRate.Numerator) / (pModeDescs[m].RefreshRate.Denominator) << endl;
			}
		}
		vOutputs.clear();

	}
	vAdapters.clear();



	return 0;

}

Introduction to 3D Game Programming with DirectX 11 第四章
更加详细的介绍由X_Jun提供
GitHub项目参考
微软的ID3D11Device的接口文档
微软提供的ID3D11DeviceContext的接口文档


  1. 获取设备显示信息转载于sagakiss ↩︎

猜你喜欢

转载自blog.csdn.net/weixin_44200074/article/details/106231513