05/20/2020
05/29/2020 – 总结
06/01/2020 – 如何查看显卡与输出设备,并且创建后台缓冲区和深度模板缓冲区
Direct3D初始化
创建设备(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的接口文档