Direct3D 11编程总结

来源:互联网 发布:淘宝神笔怎么取消 编辑:程序博客网 时间:2024/06/04 22:34

一、Direct3D 11 知识

1. Direct3D 11资源

1.1 Direct3D资源简介

Direct3D 11利用资源来存储输入输出数据。资源分为:几何、纹理、着色数据。资源可以是有类型的,也可以是无类型的;可以控制资源的读写访问方式,也可以限定是被CPU还是GPU访问。在图形管道阶段最多可以创建128个资源。
一个Direct3D资源的生命周期分为:
(1)利用ID3D11Device接口创建资源;
(2)利用ID3D11DeviceContext接口,及利用上下文将资源绑定到图形管线;
(3)通过Realse方法释放资源。

1.2 Direct3D资源类型

(1)缓存资源:一组指定类型的数据的集合
分为顶点缓存(vetex buffer)、索引缓存(index buffer)和常量缓存(contant buffer)。顶点缓存常用于存储顶点数据,包括顶点的位置、法线、纹理坐标。索引缓存用于存储顶点的在其它顶点缓存中的索引(即偏移量),一个索引缓存可以指向多个顶点缓存。常量缓存用于存储常量数据。
(2)纹理资源:一种结构资源,用于存储纹理
一维纹理:纹理只在某一方向上变化,而在另一个方向上没有变化,相当于高度为1的二维纹理。使用ID3D11Texture1D接口来定义一维纹理。二维纹理:二维纹理是指纹理在相互垂直的两个方向发生变化。每个纹理单元可用u, v向量来表示,使用ID3D11Texture2D接口来定义二维纹理。
三维纹理:每个纹理单元可用u, v, w向量来表示,如下图所示。使用ID3D11Texture3D接口来定义三维纹理。
多重纹理:事先指定一系列分辨率逐渐减小的纹理图像。
纹理数组(texture arrays)是指包含相同结构的一组纹理数据,也就是说数组中的纹理必须有相同的数据结构(维度)和大小(纹理单元个数)。
注意 Direct3D 11 支持多重纹理和纹理数组,但是不能构建三维纹理数组。

1.3 资源的用法

D3D11_USAGE值

描述

CPU访问

GPU访问

D3D11_USAGE_IMMUTABLE

资源创建后不能改变

不能

只读

D3D11_USAGE_DEFAULT

资源内容改变速度最快每帧改变一次

不能

读/写

D3D11_USAGE_DYNAMIC

资源内容可每帧改变多次

D3D11_USAGE_STAGING

资源用於从GPU传递出入数据

读/写

拷贝

视图资源

资源接口

描述

ID3D11DepthStencilView

深度/模板测试时访问资源

ID3D11RenderTargetView

用于访问最终绘制目标的纹理资源

ID3D11ShaderResourceView

访问着色器资源,例如:常量缓存、纹理缓存、纹理采样器等

ID3D11UnorderedAccessView

在像素着色器或者计算着色器中访问随机资源

1.4  Direct3D 11 工程属性设置

Visual Studio 的工程属性的VC++ Directories中加入
Excutable Directories: $(DXSDK_DIR)Utilities\bin\x86
Include Directories: $(DXSDK_DIR)Include
Library Directories: $(DXSDK_DIR)Lib\x86
如果是Effect框架,则还需要在Include Directories中加入 $(DXSDK_DIR)Samples\C++\Effects11\Inc, 在Library Directories 中加入 $(DXSDK_DIR)Samples\C++\Effects11\Debug 或 $(DXSDK_DIR)Samples\C++\Effects11\Release。并且在$(DXSDK_DIR)Samples\C++\Effects11目录下,打开工程Effects11_*.sln,分别在Debug和Release模式下编译整个工程,分别得到Debug和Release模式下的两个lib库文件。
Linker Input 加入d3d11.lib, d3dcompiler.lib, d3dx11d.lib, d3dx9d.lib, dxerr.lib, dxguid.lib, winmm.lib,comctl32.lib, 以上lib也可以在源文件中通过 #pragma comment加入。

2. 初始化Direct3D 11步骤

2.1 描述交换链并创建设备、交换链和立即执行环境


2.2 创建目标视图并绑定到渲染管线


2.3 配置视口


3. 渲染图像的设置步骤

3.1 创建顶点缓存

// 创建顶点缓存: 位置和纹理坐标SIMPLEVERTEX vertices[] = {// 左侧三角形{ XMFLOAT3(-1.0f, 1.0f, 0.0f), XMFLOAT2(0.0f, 0.0f) }, //a{ XMFLOAT3(1.0f, 1.0f, 0.0f), XMFLOAT2(1.0f, 0.0f) }, //b{ XMFLOAT3(-1.0f, -1.0f, 0.0f), XMFLOAT2(0.0f, 1.0f) }, //c// 右侧三角形{ XMFLOAT3(-1.0f, -1.0f, 0.0f), XMFLOAT2(0.0f, 1.0f) }, //c{ XMFLOAT3(1.0f, 1.0f, 0.0f), XMFLOAT2(1.0f, 0.0f) }, //b{ XMFLOAT3(1.0f, -1.0f, 0.0f), XMFLOAT2(1.0f, 1.0f) }, //d};g_iVsNum = ARRAYSIZE(vertices); // 顶点数量D3D11_BUFFER_DESC bd; // 用于描述缓存类型ZeroMemory(&bd, sizeof(bd));bd.Usage = D3D11_USAGE_DEFAULT; // 资源内容改变速度最快每帧改变一次bd.ByteWidth = sizeof(SIMPLEVERTEX) * g_iVsNum;bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; // 表示顶点缓存bd.CPUAccessFlags = 0;D3D11_SUBRESOURCE_DATA InitData; // 用于初始化顶点缓存ZeroMemory(&InitData, sizeof(InitData));InitData.pSysMem = vertices; // 初始化数据hr = g_pd3dDevice->CreateBuffer(&bd, &InitData, &g_pVertexBuffer); // 创建顶点缓存UINT uiStride = sizeof(SIMPLEVERTEX);UINT uiOffset = 0;g_pImmediateContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &uiStride, &uiOffset); // 顶点缓存绑定到管线g_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // 指定图元类型为三角形

3.2 读取与编译HLSL文件及创建顶点布局对象

Effect框架设置步骤

Effect框架设置步骤

D3DX11CompileFromFile编译FX文件,并返回ID3DBlob指针,即FX文件指针。但两者使用方法不同,如下

填充D3DX11CompileFromFile的FX文件名和顶点入口函数名,以及顶点着色器版本”vs_5_0”, 返回ID3DBlob指针pVSBlob

填充D3DX11CompileFromFile的FX文件名,着色器版本”fx_5_0”

创建顶点着色器

g_pd3dDevice->CreateVertexShader(pVSBlob->GetBufferPointer(),  pVSBlob->GetBufferSize(), nullptr,&g_pVertexShader);

使用ID3DBlob指针以及D3DX11CreateEffectFromMemory创建ID3DX11Effect对象

绑定到FX文件中

g_pImmediateContext->VSSetShader(g_pVertexShader, nullptr, 0);

创建ID3DX11EffectTechnique g_pTechnique = g_pEffect->GetTechniqueByIndex(0);

获得通道
D3DX11_PASS_DESC PassDesc;
g_pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);

设置顶点布局对象D3D11_INPUT_ELEMENT_DESC
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

由pVSBlob创建顶点布局对象g_pVertexLayout

g_pd3dDevice->CreateInputLayout(layout, uiNumElements, pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &g_pVertexLayout);

利用PassDesc创建顶点布局对象g_pVertexLayout

g_pd3dDevice->CreateInputLayout(layout, uiNumElements, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &g_pVertexLayout);

绑定顶点布局对象到渲染管线  g_pImmediateContext->IASetInputLayout(g_pVertexLayout);

填充D3DX11CompileFromFile的FX文件名和像素入口函数名,以及像素着色器版本”ps_5_0”, 返回ID3DBlob指针pPSBlob

 

由pPSBlob创建像素着色器

g_pd3dDevice->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &g_pPixelShader);

 

绑定到FX文件中

g_pImmediateContext->PSSetShader(g_pPixelShader, nullptr, 0);


3.3 读入图像纹理

Effect框架步骤

Effect框架步骤

从图像g_szFile中读入图像,创建纹理资源视图g_pTexResViewFromFile,
D3DX11CreateShaderResourceViewFromFile(g_pd3dDevice, g_szFile, NULL, NULL, &g_pTexResViewFromFile, NULL );


使用PSSetShaderResources将资源与FX文件中的纹理资源绑定
g_pImmediateContext->PSSetShaderResources( 0, 1, &g_pTextureRV);
这里的0和1分别表示为fx文件中着色器资源的序号和总数

将纹理资源视图g_pTexResViewFromFile绑定到Effect文件中的变量g_txTexture
g_pEffectVariable =
g_pEffect->GetVariableByName("g_txTexture")->AsShaderResource();
hr = g_pEffectVariable->SetResource(g_pTexResViewFromFile);

3.4 渲染Render

Effect框架

Effect框架

g_pImmediateContext->Draw(g_iVsNum, 0);
g_pSwapChain->Present(0, 0);

g_pTechnique->GetDesc(&TechDesc);
for (UINT i = 0; i < TechDesc.Passes; i++)
{
g_pTechnique->GetPassByIndex(i)->Apply(0, g_pImmediateContext);
g_pImmediateContext->Draw(g_iVsNum, 0);
}
g_pSwapChain->Present(0, 0);

3.5  C++文件与Fx文件的参数传递

Effect框架

Effect框架

创建常量缓存 ID3D11Buffer* g_pBufferColor
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(PSCOLOR);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = 0;
g_pd3dDevice->CreateBuffer(&bd, nullptr, &g_pBufferColor);

Effect框架下可以使用的变量类型如下:
ID3DX11EffectVectorVariable*
ID3DX11EffectScalarVariable*
ID3DX11EffectMatrixVariable*
ID3DX11EffectStringVariable*
ID3DX11EffectSamplerVariable*(采样器)
ID3DX11EffectShaderResourceVariable*

将PsColor中的数据与g_pBufferColor变量绑定

g_pImmediateContext->UpdateSubresource(g_pBufferColor, 0, nullptr, &PsColor, 0, 0);

与Fx文件中的变量绑定,如
g_EffectColorVar =
g_pEffect->GetVariableByName("g_vColorPara")->AsVector();
其他As***函数还包括:AsMatrix, AsScale, AsShaderResource等

传递到Fx文件中:
g_pImmediateContext->VSSetConstantBuffers(0, 1, & g_pBufferColor);
或 像素着色器资源
g_pImmediateContext->PSSetConstantBuffers(0, 1, &g_pBufferColor);
这里的1表示fx文件中常量缓存的数量,0表示常量缓存的序号(从文件顶端开始)
另外fx文件中常量缓存需写成cbuffer的形式, 如:
cbuffer cbColor : register(b0)
{
matrix g_mColor_P;
}

传递参数到Fx文件, 如

g_EffectColorVar->SetFloatVector(arrColor);

不同的ID3DX11Effect***Variable*类型拥有不同的Set***函数,详细可以查SDK

注意:DirectX 11与Direct9中C++文件中矩阵元素的存储顺序与Fx文件要绑定的矩阵元素的存储顺序是有区别的,具体为:
DirectX 11的C++中矩阵的存储顺序与Fx文件的矩阵元素顺序是转置的关系,因此在C++文件中传递变量前要先使用转置函数XMMatrixTranspose进行转置,如下
psColorPara.mColorPara = XMMatrixTranspose (XMMATRIX(
g_arrPara[0], g_arrPara[1], g_arrPara[2], 1.0,
g_arrPara[3], g_arrPara[4], g_arrPara[5], 1.0,
g_arrPara[6], g_arrPara[7], g_arrPara[8], 1.0,
g_arrPara[9], g_arrPara[10], g_arrPara[11], 1.0));
然后,传递到fx文件中g_pImmediateContext->UpdateSubresource( g_pColor, 0, nullptr, &psColorPara, 0, 0);
而在DirectX 9中 C++文件的矩阵元素顺序和Fx文件中的是相同的,因此无需转置
g_ColorPara = D3DXMATRIX(
g_arrPara[0], g_arrPara[1], g_arrPara[2], 1.0, // H
g_arrPara[3], g_arrPara[4], g_arrPara[5], 1.0, // S
g_arrPara[6], g_arrPara[7], g_arrPara[8], 1.0, // I,
g_arrPara[9], g_arrPara[10], g_arrPara[11], 1.0); // W,
hr = g_pEffect->SetMatrix(g_hColorParaHandle, &g_ColorPara);

3.6  Direct3D 11与 Direct3D 9的设置区别

Direct3D 11设置步骤

Direct3D 9 设置步骤

填充交换链、设备和环境描述结构体 DXGI_SWAP_CHAIN_DESC

创建D3D对象 LPDIRECT3D9 g_pD3D

g_pD3D = Direct3DCreate9(D3D_SDK_VERSION))

填充D3D设备描述结构体 D3DPRESENT_PARAMETERS
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

使用以下函数创建交换链、设备和设备环境

D3D11CreateDeviceAndSwapChain

使用函数CreateDevice创建设备g_pd3dDevice
g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice)

使用交换链和设备创建渲染目标视图 CreateRenderTargetView

DX9中没有渲染目标视图

使用设备环境创建视口

DX9中没有视口

编译Fx文件,创建顶点像素着色器(必须)、像素着色器(可选)

编译Fx文件,获得Fx文件中Technique接口。不需创建顶点着色器、像素着色器

创建顶点缓存(顶点布局对象)

顶点缓存中的顶点几何坐标范围为-1到1

创建顶点缓存(顶点布局对象)
顶点缓存中的顶点几何坐标范围窗口客户区的实际位置,如
VERTEX_TYPE vertices[] = // 顶点数据
{
{float(rect.right) - 0.5f, float(rect.top) - 0.5f, 0.0f, 1.0f, (float)1.0, (float)0.0 },
{float(rect.left) - 0.5f, float(rect.bottom) - 0.5f, 0.0f, 1.0f, (float)0.0, (float)1.0 },
{float(rect.left) - 0.5f, float(rect.top) - 0.5f, 0.0f, 1.0f, (float)0.0, (float)0.0 },
};

渲染
g_pImmediateContext->Draw(g_iVsNum, 0);
g_pSwapChain->Present(0, 0);
或:
g_pTechnique->GetDesc(&TechDesc);
for (UINT i = 0; i < TechDesc.Passes; i++)
{
g_pTechnique->GetPassByIndex(i)->Apply(0, g_pImmediateContext);
g_pImmediateContext->Draw(g_iVsNum, 0);
}
g_pSwapChain->Present(0, 0);

渲染
hr = g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(VERTEX_TYPE));
hr = g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
if(SUCCEEDED(g_pd3dDevice->BeginScene()))
{
hr = g_pEffect->Begin(&uiPasses, 0);
for (uiPass=0; uiPass<uiPasses; uiPass++)
{
hr = g_pEffect->BeginPass(uiPass);
hr = g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST , 0 , 2);
hr = g_pEffect->EndPass();
}
hr = g_pEffect->End();
hr = g_pd3dDevice->EndScene();
}
hr = g_pd3dDevice->Present(NULL, NULL, NULL, NULL );

4. 渲染到纹理技术
在很多图像处理算法的实现中,往往渲染一次是无法获得最终的效果,因此需要将前一次渲染的输出在下一次渲染中当作输入纹理进行新的渲染,这时就需要用到渲染到纹理的技术。在图像处理用,常使用渲染到纹理技术来存储图像的中间变量。以下在DirectX 11环境、Effect框架下说明渲染到纹理的实现方法, 步骤如下:
1. 创建新的纹理资源
2. 创建新的渲染目标视图
3. 创建新的着色器纹理资源视图
4. 修改渲染方法Render
以上1,2, 3步骤均在传统渲染方法的框架上增加,无需改动原来的框架。详细方法如下:
4.1 创建新的纹理资源ID3D11Texture2D*
首先声明三个全局资源变量
ID3D11Texture2D* g_pRTT_Texture2D = nullptr; // 2D纹理
ID3D11RenderTargetView* g_pRTT_RenderTargetView = nullptr; // 渲染目标视图
ID3D11ShaderResourceView* g_pRTT_ShaderResView = nullptr; // 着色器纹理资源
注意到在传统渲染方法的框架中是不需要创建ID3D11Texture2D* g_pRTT_Texture2D的。另外 ID3D11ShaderResourceView* g_pRTT_ShaderResView 这个 变量相当于使用函数D3DX11CreateShaderResourceViewFromFile()从图片所创建的资源变量。
(1)描述D3D11_TEXTURE2D_DESC结构体

D3D11_TEXTURE2D_DESC Texture2dDesc; // 描述纹理的使用方法ZeroMemory(&Texture2dDesc, sizeof(Texture2dDesc));Texture2dDesc.Width = uiWidth; // 与交换链中的相同Texture2dDesc.Height = uiHeight; // 与交换链中的相同Texture2dDesc.MipLevels = 1;Texture2dDesc.ArraySize = 1;Texture2dDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;Texture2dDesc.SampleDesc.Count = 1;Texture2dDesc.SampleDesc.Quality = 0;Texture2dDesc.Usage = D3D11_USAGE_DEFAULT;Texture2dDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;Texture2dDesc.CPUAccessFlags = 0;Texture2dDesc.MiscFlags = 0;

(2)创建纹理g_pRTT_Texture2D

hr = g_pd3dDevice->CreateTexture2D(&Texture2dDesc, NULL, &g_pRTT_Texture2D); //创建纹理
4.2 创建新的渲染目标视图
D3D11_RENDER_TARGET_VIEW_DESC RenderTargetViewDesc; // 描述渲染目标视图的用法RenderTargetViewDesc.Format = Texture2dDesc.Format;RenderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;RenderTargetViewDesc.Texture2D.MipSlice = 0;// Create the render target view.hr = g_pd3dDevice->CreateRenderTargetView(g_pRTT_Texture2D, &RenderTargetViewDesc, &g_pRTT_RenderTargetView);

4.3 创建新的着色器纹理资源视图g_pRTT_ShaderResView
D3D11_SHADER_RESOURCE_VIEW_DESC ShaderResourceViewDesc;ShaderResourceViewDesc.Format = Texture2dDesc.Format;ShaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;ShaderResourceViewDesc.Texture2D.MostDetailedMip = 0;ShaderResourceViewDesc.Texture2D.MipLevels = 1;//Create the shader resource viewhr = g_pd3dDevice->CreateShaderResourceView(g_pRTT_Texture2D, &ShaderResourceViewDesc, &g_pRTT_ShaderResView);

4.4 修改渲染方法Render()
hr = g_pTechnique->GetDesc(&g_TechDesc);hr = g_pEffectTexVar->SetResource(g_pTexResViewFromFile); //从图片读入的纹理资源视图// 在g_pRTT_RenderTargetView中渲染,即渲染到纹理g_pRTT_Texture2Dg_pImmediateContext->OMSetRenderTargets(1, &g_pRTT_RenderTargetView, nullptr); for (UINT i = 0; i < g_TechDesc.Passes; i++){if (1 == i){// 将第一次渲染的纹理视图传入hr = g_pEffectTexVar->SetResource(g_pRTT_ShaderResView);// 在最终的目标视图g_pRenderTargetView中渲染,即不需要渲染到纹理g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, nullptr); }hr = g_pTechnique->GetPassByIndex(i)->Apply(0, g_pImmediateContext);g_pImmediateContext->Draw(g_iVsNum, 0);}g_pSwapChain->Present(0, 0);

technique11 Dehaze{pass P0 // 第一次渲染{SetVertexShader(CompileShader(vs_5_0, VS()));SetPixelShader(CompileShader(ps_5_0, PS_DarkChannel()));}pass P1 // 第二次渲染{SetVertexShader(CompileShader(vs_5_0, VS()));SetPixelShader(CompileShader(ps_5_0, PS_GuideFilter()));}}


需要说明的是:每次渲染需要绑定新的渲染目标视图,并将上一次渲染的纹理资源视图传递到Fx文件的Texture2D纹理中。

0 0
原创粉丝点击