ConstantTimeGaussianBlur 学习笔记<高斯模糊>

本篇没有使用微软的 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 &riidvoid **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);

}

源码下载地址:

https://github.com/magrlemon/GaussianBlur.git

共同探讨学习欢迎联系  QQ:1296393088 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值