本篇没有使用微软的 DXUT 架构,自己动手学习着搭建了一个基于DX11程序DEMO的,借助Nivadia SDK 11 提供的DEMO进行研究学习,希望尽快的投入到图形学研究学习最核心的部分。
Nivadia SDK 11Demo <1>
---高斯模糊 I <ConstantTimeGaussianBlur>
软件环境:DirectX+ VS2015 + 3dmax 2015
第一次使用d3d11写DEMO,首先应该从初始化开始。
1 初始化d3d11环境
1.1初始化d3d11的步骤主要有以下几个:
1、 定义我们要检查的设备类型和特征级别
2、 填充一个DXGI_SWAP_CHAIN_DESC结构体,用于描述了所要创建的交换链特性
3、 使用D3D10CreateDeviceAndSwapChain函数创建ID3D10Device设备接口和IDXGISwapChain交换链接口
4、 为交换链后台缓冲区创建一个渲染目标视图
5. 创建深度 / 模板缓冲区以及相关的深度 / 模板视图。
6. 将渲染目标视图和深度 / 模板视图绑定到渲染管线的输出合并器阶段,使它们可以被 Direct3D 使用。
7. 设置视口。
1.2下面将对一些概念和用到的d3d对象和函数作具体说明。
以下代码来自bool D3DX11Base::Initialize( HINSTANCE hInstance, HWND hwnd ){}
1.2.1设备类型和特征级别
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE,D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,D3D_DRIVER_TYPE_SOFTWARE,
D3D_DRIVER_TYPE_UNKNOWN
};
D3D_DRIVER_TYPE_HARDWARE:一般使用该参数,表示使用D硬件加快渲染速度。
D3D_DRIVER_TYPE_REFERENCE:创建引用设备。引用设备为Direct3D的软件实现,具有Direct3D所有功能,但运行速度非常慢。使用引用设备主要用于:测试硬件不支持的代码,例如在一块不支持Direct3D 11的显卡上测试Direct3D 11代码;测试驱动程序缺陷,当代码能在引用设备上正常运行,而在硬件上不能正常运行时,说明硬件驱动程序可能存在缺陷。
Software:用于软件光栅化设备。如果DriverType是D3D_DRIVER_TYPE_SOFTWARE,此值不能为NULL,且必须先安装一个软件光栅化设备。否则设置为NULL,因为使用硬件加速渲染
unsigned int totalDriverTypes = ARRAYSIZE(driverTypes);
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,D3D_FEATURE_LEVEL_9_1
};
unsigned int totalfeatureLevels = ARRAYSIZE(featureLevels);
1.2.2 填充一个描述了交换链特性DXGI_SWAP_CHAIN_DESC结构体
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc,sizeof(swapChainDesc));
swapChainDesc.BufferCount = 1;//指定交换链中缓冲区的数量
swapChainDesc.BufferDesc.Width =width; //宽度
swapChainDesc.BufferDesc.Height =height;
swapChainDesc.BufferDesc.Format =DXGI_FORMAT_R8G8B8A8_UNORM;//像素格式
swapChainDesc.BufferDesc.RefreshRate.Numerator= 60;//刷新率
swapChainDesc.BufferDesc.RefreshRate.Denominator= 1;
swapChainDesc.BufferUsage =DXGI_USAGE_RENDER_TARGET_OUTPUT; //CPU访问后台缓冲区的选项
swapChainDesc.OutputWindow =hwnd;
swapChainDesc.Windowed =true;// 指定图形绘制窗口
swapChainDesc.SampleDesc.Count = 1;//每像素多重采样个数
swapChainDesc.SampleDesc.Quality = 0;//图像质量等级,等级越高,性能越低,可选范围是到
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; //将后台缓冲区内容复制到前台缓冲区的方式
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; //交换链行为
1.2.3创建设备和交换链接口
HRESULT result;
unsigned int driver = 0;
IDXGIAdapter *pAdapter_Set =NULL;
for ( driver=0;driver < totalDriverTypes; ++driver )
{
//创建设备和交换链
result = D3D11CreateDeviceAndSwapChain( 0, driverTypes[driver], 0,creationFlags, featureLevels,totalfeatureLevels,D3D11_SDK_VERSION, &swapChainDesc, &swapChain_,&d3dDevice_, &featureLevel_, &d3dContext_ );
if (SUCCEEDED(result))
{
driverType_ = driverTypes[driver];
break;
}
}
if (FAILED(result))
{
DXTRACE_MSG(L"创建 Direct3D设备失败!");
return false;
}
1.2.4创建一个渲染目标视图
先声明两个接口:指向ID3D11RenderTargetView类型的渲染目标视图接口和指向ID3D11Texture2D类型(或者其他资源格式)的后台缓冲区.
通过交换链接口调用IDXGISwapChain::GetBuffer方法来获取后台缓冲区指针。同时应注意,每次调用该方法后,后台缓冲区的COM引用计数会向上递增一次,所以应配对使用Release方法。GetBuffer方法如下:
1 HRESULTIDXGISwapChain::GetBuffer(UINTBuffer,
const IID &riid, void **ppSurface);
Buffer:表示所要获取的后台缓冲区的索引。由于后台缓冲区数量可以大于,所以必须指定索引。
riid:缓冲区接口类型。通常为D纹理,即ID3D11Texture2D。
ppSurface:指向所返回的后台缓冲区的指针。
通过设备接口调用ID3D11Device::CreateRenderTargetView方法创建渲染目标视图。CreateRenderTargetView方法如下:
1 HRESULTID3D10Device::CreateRenderTargetView(
2 ID3D10Resource*pResource,
3 const D3D10_RENDER_TARGET_VIEW_DESC *pDesc,
4 ID3D10RenderTargetView**ppRTView);
pResource:指定将要作为渲染目标的资源。如将后台缓冲区作为渲染目标。
pDesc:指向D3D10_RENDER_TARGET_VIEW_DESC结构体的指针,该结构体描述渲染目标即参数pResource所指定的资源中的元素的数据类型。如果创建资源时使用强类型,则可为空,表示以资源自身格式为视图格式。
ppRTView:指向创建后所要返回的渲染目标视图对象的指针。
示例代码如下:
ID3D11Texture2D* backBufferTexture;
result = swapChain_->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&backBufferTexture );
if (FAILED(result))
{
DXTRACE_MSG(L"获取交换链后台缓存失败!");
return false;
}
else if (SUCCEEDED(result))
{
UpdateBackBufferDesc();
}
result = d3dDevice_->CreateRenderTargetView( backBufferTexture, 0, &backBufferTarget_ );
if (backBufferTexture)
backBufferTexture->Release();
if (FAILED(result))
{
//DXTRACE_MSG("创建渲染目标视图失败!");
return false;
}
1.2.5 创建深度/模板缓冲区及其视图
ID3D11DepthStencilView* pDSV = NULL;
// Create depth stencil texture
ID3D11Texture2D* pDepthStencil = NULL;
D3D11_TEXTURE2D_DESC descDepth;//深度/模板缓冲区描述
descDepth.Width =backBufferSurfaceDesc.Width;//宽度、高度
descDepth.Height =backBufferSurfaceDesc.Height;
descDepth.MipLevels = 1;//多级渐进纹理层数量
descDepth.ArraySize = 1;//纹理数量
descDepth.Format =DXGI_FORMAT_D24_UNORM_S8_UINT;//纹理元素格式
descDepth.SampleDesc.Count = 1;//多重采样
descDepth.SampleDesc.Quality = 0;
descDepth.Usage =D3D11_USAGE_DEFAULT;//纹理用途
descDepth.BindFlags =D3D11_BIND_DEPTH_STENCIL;//管线绑定标志值
descDepth.CPUAccessFlags = 0;//CPU对资源访问权限
descDepth.MiscFlags = 0;//与深度/模板无关的一个标志值
result = d3dDevice_->CreateTexture2D(&descDepth,NULL, &pDepthStencil);
// Create the depth stencil view
D3D11_DEPTH_STENCIL_VIEW_DESCdescDSV;
descDSV.Format =descDepth.Format;
descDSV.Flags = 0;
if (descDepth.SampleDesc.Count > 1)
descDSV.ViewDimension =D3D11_DSV_DIMENSION_TEXTURE2DMS;
else
descDSV.ViewDimension =D3D11_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;
result = d3dDevice_->CreateDepthStencilView(pDepthStencil, &descDSV,&pDSV);
1.2.6将视图绑定到输出合并器阶段
为后台缓冲区和深度/模板缓冲区创建视图后,使用ID3D10Device::OMSetRenderTargets方法将视图绑定到管线输出合并阶段,使这些资源成为管线的渲染目标和深度/模板缓冲区。
void ID3D10Device::OMSetRenderTargets( UINTNumViews, ID3D10RenderTargetView*const *ppRenderTargetViews,ID3D10DepthStencilView *pDepthStencilView);
NumViews:将要绑定的渲染目标数量。
ppRenderTargetViews:指向将要绑定的渲染目标视图数组的首元素的指针。
pDepthStencilView:指向将要绑定的深度/模板视图。
将视图绑定到管线示例:
//绑定视图到输出合并器阶段
pd3dDevice->OMSetRenderTargets(1,&pRenderTargetView,pDepthStencilView);
1.2.8 设置视口
通常D场景将会渲染到整个后台缓冲区,通过提交给前台缓冲区后,把图像布满整个客户窗口区。
若只希望将场景渲染到一个子矩形区域中,而非布满整个缓冲区,则可以通过修改视口来实现。
//填充视口
D3D11_VIEWPORT viewPort;
viewPort.Width =static_cast<float>(pBBufferSurfaceDesc_->Width);
viewPort.Height =static_cast<float>(pBBufferSurfaceDesc_->Height);
viewPort.MinDepth = 0.0f;
viewPort.MaxDepth = 1.0f;
viewPort.TopLeftX = 0.0f;
viewPort.TopLeftY = 0.0f;
//设置视口
3dContext_->RSSetViewports(1, &viewPort);
以上为DX11设备初始化必要步骤,具体代码详见DEMO。
备注:DX11学习笔记参考文档 http://blog.csdn.net/bonchoix/article/details/8288868
2 创建一个天空盒
基本流水线是:
顶点着色器----光栅化(照相过程)---像素着色器 Vertex Shader------Rasterizer-----Pixel Shader 我们则按照3D流水线管线一步步创建我们所需要的数据:
2.1输入装配阶段
输入装配(Input Assembler,简称IA)阶段从内存读取几何数据(顶点和索引)并将这些数据组合为几何图元(例如,三角形、直线)。(索引将在随后的小节中讲解。简单地说,索引规定了顶点的组织形式,解释了该以何种方式组成图元。)
2.1.1创建Input Layout
创建顶点结构声明在Direct3D 11中,input layout是Direct3D对象,它能以一种GPU能够理解的方式来描述顶点结构体。每个顶点属性能够用D3D11_INPUT_ELEMENT_DESC结构体来描述。一个应用程序定义一个或多个的D3D11_INPUT_ELEMENT_DESC 结构体数组。使用这些数组来创建input layout对象来描述顶点。在顶点结构中,针对每个成员,我们要定义一个相应的D3D11_INPUT_ELEMENT_DESC。这个结构定义了该成员相应的有关信息,定义如下:
1. typedef struct D3D11_INPUT_ELEMENT_DESC {
2. LPCSTR SemanticName;
3. UINT SemanticIndex;
4. DXGI_FORMAT Format;
5. UINT InputSlot;
6. UINT AlignedByteOffset;
7. D3D11_INPUT_CLASSIFICATION InputSlotClass;
8. UINT InstanceDataStepRate;
9. } D3D11_INPUT_ELEMENT_DESC;
const D3D11_INPUT_ELEMENT_DESC g_aVertexLayout[]
{
{ "POSITION", 0,DXGI_FORMAT_R32G32B32A32_FLOAT , 0, 0,D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
顶点渲染器将紧密地跟顶点格式联系在一起。原因在于创建一个顶点格式对象需要顶点渲染器的输入签名,我们将使用D3DX11CompileFromFile函数所返回的ID3DBlob对象来检索二进制数据,该二进制数据代表顶点渲染器的输入签名。一旦我们获得这个数据,我们就能够调用ID3D11Device::CreateInputLayout()函数来创建顶点格式对象,和调用ID3D11DeviceContext::IASetInputLayout()来激活这个顶点格式。代码如下:
//
2
ID3DX11Effect*类型变量,代表编译好的Effect,通过它获取相应technique11中相应的pass的描述信息,通过pass描述信息即可获取我们这里需要的两个参数,如上代码中所示,意指shader中输入结构信息,及该信息的字节长度;最后一个参数即我们要创建的input layout接口的地址。
ID3DBlob* pBlobVS= NULL;
ID3DBlob*pBlobPS_Color =NULL;
ID3DBlob*pBlobPS_Gray =NULL;
//Create the shaders
V_RETURN(CompileShaderFromFile(L"skybox11.hlsl","SkyboxVS","vs_4_0", &pBlobVS) );
V_RETURN(CompileShaderFromFile(L"skybox11.hlsl","SkyboxPS_Color","ps_4_0", &pBlobPS_Color) );
V_RETURN(CompileShaderFromFile(L"skybox11.hlsl","SkyboxPS_Gray","ps_4_0", &pBlobPS_Gray) );
V_RETURN(pd3dDevice->CreateVertexShader(pBlobVS->GetBufferPointer(),pBlobVS->GetBufferSize(),NULL, &m_pVertexShader) );
V_RETURN(pd3dDevice->CreatePixelShader(pBlobPS_Color->GetBufferPointer(),pBlobPS_Color->GetBufferSize(),NULL, &m_pPixelShader_Color) );
V_RETURN(pd3dDevice->CreatePixelShader(pBlobPS_Gray->GetBufferPointer(),pBlobPS_Gray->GetBufferSize(),NULL, &m_pPixelShader_Gray) );
3 定义好这个,下一步就是用它来创建Input Layout了,相应的函数如下
V_RETURN(pd3dDevice->CreateInputLayout(g_aVertexLayout, 3 1,pBlobVS->GetBufferPointer(),
pBlobVS->GetBufferSize(),&m_pVertexLayout11 ) );
pd3dImmediateContext->IASetInputLayout(m_pVertexLayout11 );
4 创建纹理采样器
// Setup linear sampler
D3D11_SAMPLER_DESC sampleDesc;
sampleDesc.Filter =D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampleDesc.AddressU =D3D11_TEXTURE_ADDRESS_WRAP;
sampleDesc.AddressV =D3D11_TEXTURE_ADDRESS_WRAP;
sampleDesc.AddressW =D3D11_TEXTURE_ADDRESS_WRAP;
sampleDesc.MipLODBias = 0.0f;
sampleDesc.MaxAnisotropy = 1;
sampleDesc.ComparisonFunc =D3D11_COMPARISON_ALWAYS;
sampleDesc.BorderColor[0] =sampleDesc.BorderColor[1] =sampleDesc.BorderColor[2] =sampleDesc.BorderColor[3] = 0;
sampleDesc.MinLOD = 0;
sampleDesc.MaxLOD =D3D11_FLOAT32_MAX;
V_RETURN(pd3dDevice->CreateSamplerState( &sampleDesc, &m_pSamplerState ) );
2.1.2 // 创建一个顶点缓冲区
创建一个顶点缓冲区,我们要填充两个结构体D3D11_BUFFER_DESC和D3D11_SUBRESOURCE_DATA,然后调用ID3D11Device::CreateBuffer()函数
struct SKYBOX_VERTEX
{
D3DXVECTOR4 pos;
};
void CSkyBox::OnD3D11ResizedSwapChain(constDXGI_SURFACE_DESC* pBackBufferSurfaceDesc)
{
HRESULT hr;
if (m_pd3dDevice11 ==NULL)
return;
SKYBOX_VERTEX* pVertex = new SKYBOX_VERTEX[4];
if (!pVertex)
return;
float fHeightW = -1.0f -(1.0f / (float)pBackBufferSurfaceDesc->Width);
float fHeightH = -1.0f -(1.0f / (float)pBackBufferSurfaceDesc->Height);
float fLowW = 1.0f + ( 1.0f / (float)pBackBufferSurfaceDesc->Width);
float fLowH = 1.0f + ( 1.0f / (float)pBackBufferSurfaceDesc->Height);
pVertex[0].pos= D3DXVECTOR4( fLowW, fLowH, 1.0f, 1.0f );
pVertex[1].pos= D3DXVECTOR4( fLowW, fHeightH, 1.0f, 1.0f );
pVertex[2].pos= D3DXVECTOR4( fHeightH, fLowH, 1.0f, 1.0f );
pVertex[3].pos= D3DXVECTOR4( fHeightH, fHeightH, 1.0f, 1.0f );
UINT uiVertBufSize = 4 * sizeof(SKYBOX_VERTEX);
//vertex Buffer
D3D11_BUFFER_DESC vbdesc;
vbdesc.Usage =D3D11_USAGE_IMMUTABLE;
vbdesc.BindFlags =D3D11_BIND_VERTEX_BUFFER;
vbdesc.ByteWidth =uiVertBufSize;
vbdesc.MiscFlags = 0;
vbdesc.CPUAccessFlags = 0;
vbdesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
InitData.pSysMem =pVertex;
V( m_pd3dDevice11->CreateBuffer( &vbdesc, &InitData, &m_pVB11 ) );
SAFE_DELETE_ARRAY(pVertex);
}
D3D11_SUBRESOURCE_DATA描述在将要复制到顶点缓冲区的实际数据ID3D11DeviceContext::IASetVertexBuffers()将它绑定到device上。
pd3dImmediateContext->IASetVertexBuffers( 0, 1,pBuffers, &uStrides, &uOffsets );
pd3dImmediateContext->IASetIndexBuffer(NULL,DXGI_FORMAT_R32_UINT, 0);
pd3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
PrimitiveTopology是关于GPU如何获得要渲染顶点的拓扑关系的。
2.1.3渲染天空盒
我们已经创建了两个渲染器用于渲染,顶点渲染器和像素渲染器。顶点渲染器负责将三角形的各个顶点变换到正确的位置。像素渲染器负责三角形内部的最后输出颜色的计算。为了使用这两个渲染器,我们必须分别调用ID3D11DeviceContext::VSSetShader()函数和ID3D11DeviceContext::PSSetShader()函数。最后我们要做的就是调用ID3D11DeviceContext::Draw()函数,该函数命令GPU使用当前的顶点缓冲区,当前的顶点格式,当前的primitive topology进行渲染。Draw()的第一个参数要传给GPU的顶点个数。第二个参数表示顶点缓冲区的起始索引。代码如下:
pd3dImmediateContext->VSSetShader(m_pVertexShader,NULL, 0);
pd3dImmediateContext->PSSetShader(bColor ?m_pPixelShaderColor : m_pPixelShaderGray,NULL, 0);
//获取subresource中数据的指针, 同时拒绝GPU读取
D3D11_MAPPED_SUBRESOURCE MappedResource;
V(pd3dImmediateContext->Map(m_pcbVSPerObject, 0,D3D11_MAP_WRITE_DISCARD, 0, &MappedResource));
CB_VS_PER_OBJECT* pVSPerObject = (CB_VS_PER_OBJECT*)MappedResource.pData;
//pVSPerObject->m_WorldViewProj=InverseTranspose(*pmWorldViewProj);
D3DXMatrixInverse(&pVSPerObject->m_WorldViewProj,NULL, pmWorldViewProj);
pd3dImmediateContext->Unmap(m_pcbVSPerObject, 0);
pd3dImmediateContext->VSSetConstantBuffers(0, 1, &m_pcbVSPerObject);
pd3dImmediateContext->PSSetSamplers(0, 1, &m_pSamplerState);
pd3dImmediateContext->PSSetShaderResources(0, 1, &m_pEnvironmentRV11);
ID3D11DepthStencilState* pDepthStencilStateStored11 = NULL;
UINT StencilRef;
pd3dImmediateContext->OMGetDepthStencilState(&pDepthStencilStateStored11, &StencilRef);
绘制场景离不开深度缓冲区,而深度缓冲区与Render Target是一一对应的,因此不能使用后缓冲区对应的那个深度缓冲区了,需要我们自己再创建一个,创建方法是完全一样的(惟一的区别是,这里不需要模板缓冲区,因此创建方式参见code。
pd3dImmediateContext->OMSetDepthStencilState(m_pDepthStencilState11, 0);
pd3dImmediateContext->Draw( 4, 0 );
此时就需要将渲染所需要的状态和命令在CPU上统计好,打包发送给硬件。在这一阶段,Draw需要完成很多工作,比如脏属性的检查以减少传输量,比如渲染状态的正确性和一致性检查等等,一般来说GPU命令的生成也可以放在这里完成。
此处程序并没有开启深度和模板测试功能
pd3dImmediateContext->OMSetDepthStencilState(pDepthStencilStateStored11,StencilRef);
3 高斯模糊
3.1高斯模糊程序框架
参见Nivadia SDK 11Demo <1>
---高斯模糊 II <ConstantTimeGaussianBlur>
3.2高斯模糊算法
Nivadia SDK 11Demo <1>
---高斯模糊 II <ConstantTimeGaussianBlur>
4渲染流程的核心思想
4.1初始化DX11设备环境
初始化DX11设备环境接口函数InitD3D()成功:保存后台缓冲区关联的渲染目标视图和视口
// Save render target and viewport
ID3D11DeviceContext* pd3dImmediateContext = NULL;
pd3dDevice->GetImmediateContext(&pd3dImmediateContext);
pd3dImmediateContext->OMGetRenderTargets(1, &m_pRTV_Default, &m_pDSV_Default);
UINT nViewPorts = 1;
pd3dImmediateContext->RSGetViewports(&nViewPorts,m_VP_Default);
4.2 创建需要的各种资源视图
HRESULT CD3DGaussianBlur::CreateResources(ID3D11Device*pd3dDevice, constDXGI_SURFACE_DESC* pBackBufferSurfaceDesc)
{
HRESULT hr;
//该纹理需要绑定到管线的两个阶段:RenderTarget和Shader Resource。
// Render target texture for scene rendering
D3D11_TEXTURE2D_DESC tex_desc;
D3D11_RENDER_TARGET_VIEW_DESCrtv_desc;
D3D11_SHADER_RESOURCE_VIEW_DESCsrv_desc;
D3D11_UNORDERED_ACCESS_VIEW_DESCuav_desc;
ZeroMemory(&tex_desc,sizeof(D3D11_TEXTURE2D_DESC));
ZeroMemory(&rtv_desc,sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
ZeroMemory(&srv_desc,sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
ZeroMemory(&uav_desc,sizeof(D3D11_UNORDERED_ACCESS_VIEW_DESC));
tex_desc.ArraySize = 1;//该纹理数组中包含1张纹理
//指定两个绑定阶段:Render Target和 Shader Resource
tex_desc.BindFlags =D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
tex_desc.Usage =D3D11_USAGE_DEFAULT;
tex_desc.Format =m_bColorBlur ? DXGI_FORMAT_R16G16B16A16_FLOAT :DXGI_FORMAT_R32_FLOAT;
tex_desc.Width =pBackBufferSurfaceDesc->Width;
tex_desc.Height =pBackBufferSurfaceDesc->Height;
tex_desc.MipLevels = 1;
tex_desc.SampleDesc.Count = 1;
V_RETURN(pd3dDevice->CreateTexture2D(&tex_desc,NULL, &m_pTex_Scene));
//需要针对Render Target和Shader Resource分别对其创建相应的视图:
rtv_desc.Format =tex_desc.Format;
rtv_desc.ViewDimension =D3D11_RTV_DIMENSION_TEXTURE2D;
rtv_desc.Texture2D.MipSlice = 0;//每个视图只使用最高层的mip链
V_RETURN(pd3dDevice->CreateRenderTargetView(m_pTex_Scene, &rtv_desc, &m_pRTV_Scene));
//然后是Shader Resource视图:
srv_desc.Format =tex_desc.Format;
srv_desc.ViewDimension =D3D11_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MipLevels = 1;
srv_desc.Texture2D.MostDetailedMip = 0;
V_RETURN(pd3dDevice->CreateShaderResourceView(m_pTex_Scene, &srv_desc, &m_pSRV_Scene));
注意到,这里创建纹理资源视图用到的纹理,与之前作为Render Target的纹理,是同一个!在物理内存中只存在一个纹理,但我们将其用于两个地方:作为Render Target,以及作为Shader Resource。换句话说,该纹理可以被绑定到渲染管线的多个阶段,只要在创建纹理时指明其可以被绑定到的阶段(D3D11_BIND_RENDER_TARGET |D3D11_BIND_SHADER_RESOURCE| D3D11_BIND_XXX),这样对于每个不同的用途,分别创建相应的视图(View)即可。这正是D3D11处理纹理的方法,详细情况可以参考D3D11中纹理的使用。
// RW texture for output
tex_desc.BindFlags =D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
tex_desc.Format =m_bColorBlur ? DXGI_FORMAT_R32_UINT :DXGI_FORMAT_R32_FLOAT;
tex_desc.SampleDesc.Count = 1;
V_RETURN(pd3dDevice->CreateTexture2D(&tex_desc,NULL, &m_pTex_Output));
uav_desc.Format =tex_desc.Format;
uav_desc.ViewDimension =D3D11_UAV_DIMENSION_TEXTURE2D;
uav_desc.Texture2D.MipSlice = 0;
//创建UnorderAccessView,让GPU能够读取和写入相应的buffer
//经过高斯模糊计算着色器高斯模糊算法计算,借助m_pUAV_Output视图资源映射写入到纹理
V_RETURN(pd3dDevice->CreateUnorderedAccessView(m_pTex_Output, &uav_desc, &m_pUAV_Output));
srv_desc.Format =tex_desc.Format;
srv_desc.ViewDimension =D3D11_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MipLevels = 1;
srv_desc.Texture2D.MostDetailedMip = 0;
V_RETURN(pd3dDevice->CreateShaderResourceView(m_pTex_Output, &srv_desc, &m_pSRV_Output));
return S_OK;
}
4.3 Render渲染
void CD3DGaussianBlur::Render()
{
// Set FP16x4 texture as the output RT
//将m_pRTV_Scene视图绑定到管线输出合并阶段,使这些资源成为管线的渲染目标
ID3D11RenderTargetView*rtv_array[1];
rtv_array[0] = m_pRTV_Scene;
d3dContext_->OMSetRenderTargets(1,rtv_array, m_pDSV_Default);
// Render background cubemap
// 把天空盒场景渲染到m_pRTV_Scene目标缓冲区里边render to target
if( m_pSkyBox )
m_pSkyBox->D3D11Render( &mWorldViewProjection,m_bColorBlur, d3dContext_ );
// Resore default render target so that g_pRTV_Downscalecan be unbound.
//设置回设备默认创建m_pRTV_Default
rtv_array[0] = m_pRTV_Default;
d3dContext_->OMSetRenderTargets(1,rtv_array, m_pDSV_Default);
// Perform Gaussian filtering with repeated box filters
//绑定HLSL和Directx中的相关变量从一个线程组中执行计算着色器命令
ApplyGaussianFilter(d3dContext_);
// Display the filtering result
// 在物理内存中只存在一个纹理, m_pUAV_Output与m_pSRV_Output共用同一块纹理
// 对纹理进行像素着色器计算
ShowImage(d3dContext_);
// The D3D states must be restored at the end of frame.Otherwise the runtime
// will complain unreleased resource due to D3DX11Effect.
RestoreDefaultStates(d3dContext_);
// Show the frame on the primary surface.
swapChain_->Present(0, 0);
}
源码下载地址: