1.对计算着色器的理解:
计算着色器跟他的名字一样,就是进行计算的着色器。但是它的用途不仅局限于图形的运算,也可以进行通用的运算。计算着色器的计算能力非常强,这就使得在gpu进行通用运算后再传输回cpu,比直接在cpu中运算花费的时间更短。
计算着色器不属于渲染管线,但可以对任意阶段进行读取操作。
2.线程组与线程
计算着色器利用gpu的并行处理器来开启多线程计算,从而达到高性能计算。一个线程组包含多个线程,让多处理器同时来处理两个及以上的线程组会有更好的效果,不至于当有线程组等待资源时导致计算的堵塞。
每个线程组都有共享的内存可供线程访问,但线程组之间不可互相访问。
一个warp对应32个线程,ATI对应下的wavement对应64个线程。
m_pd3dImmediateContext->Dispatch(32, 32, 1);//表示xyz三个维度的线程组为32/32/1
假设上图所示的网格是一个纹理,那么它的线程就是2x2x1,线程组就为3x2x1,所以这个位图就是6x4x1的像素大小。
3.HLSL代码的理解
Texture2D g_TexA : register(t0);
Texture2D g_TexB : register(t1);
RWTexture2D<float4> g_Output : register(u0);
[numthreads(16, 16, 1)]
void CS( uint3 DTid : SV_DispatchThreadID )
{
g_Output[DTid.xy] = g_TexA[DTid.xy] * g_TexB[DTid.xy];
}
Texture2D 表明了一个纹理,其包含RGBA,坐标信息等,等价于Texture2D<unorm float4> ,这是因为Texture2D省略了数据格式。
RWTexture2D<float4> 可读可写,不能像那样可以省略数据类型。u0表明无序访问第0个寄存器。
[numthreads(16, 16, 1)] 设置了一个线程组的线程为16x16x1个数目。
SV_DispatchThreadID 线程在3D网格的位置(xyz)。
g_TexA[DTid.xy] * g_TexB[DTid.xy] 取在xy位置的纹理数据分量相乘。
4.C++代码
主要流程:
初始化部分:
UAV unorderAccessView 可无序读写纹理
SRV ShaderResourceView 读取纹理
主题 | 说明 |
---|---|
常量缓冲区包含着色器常量数据。 其优点在于数据持续存在,并且可由任意 GPU 着色器访问,直到需要更改数据。 | |
顶点缓冲区保存顶点列表的数据。 每个顶点的数据都可能包含位置、颜色、法向矢量、纹理坐标等。 索引缓冲区将整数索引(偏移量)保存到顶点缓冲区中,并用于定义和呈现组成完整的顶点列表的一部分的对象。 | |
着色器资源视图通常以方便着色器访问纹理的方式围绕纹理。 无序的访问视图提供类似的功能,但支持以任何顺序读取和写入到纹理(或其他资源)。 | |
采样是读取纹理中的输入值或读取其他资源的过程。 “取样器”是从资源中读取的任何对象。 | |
呈现目标使场景能够呈现到临时中间缓冲区,而不是呈现到要呈现到屏幕的后台缓冲区。 利用此功能,可在图形管道中将可呈现的复杂场景用作反射纹理或用于其他用途,也可将其用于在呈现前向场景添加其他像素着色器效果。 | |
深度模板视图提供用于保存深度和模板信息的格式和缓冲区。 深度缓冲区用于剔除由较近的对象对其进行遮挡时,对查看者不可见的像素的绘制。 模板缓冲区可用于剔除定义的形状外部的所有绘制。 | |
流输出视图支持将顶点、分割和几何图形着色器附带的顶点信息流式传输回应用程序以供进一步使用。 例如,已经由这些着色器导致失真的对象可以写回到应用程序,从而为物理引擎或其他引擎提供更准确的输入。 但在实践中,流输出视图是图形管道中不常使用的功能。 | |
利用光栅器有序视图,可以消除一些深度缓冲区限制,特别是使多个包含透明度的纹理应用于同一像素。 |
参考:视图 - UWP applications | Microsoft Docs
管线部分:
前四个阶段是属于计算着色器阶段。最后的api是将合成的纹理输出到文件中。
代码:
HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Texture\\flare.dds",
nullptr, m_pTextureInputA.GetAddressOf()));
HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Texture\\flarealpha.dds",
nullptr, m_pTextureInputB.GetAddressOf()));
// 创建用于UAV的纹理,必须是非压缩格式
D3D11_TEXTURE2D_DESC texDesc;
texDesc.Width = 512;
texDesc.Height = 512;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE |
D3D11_BIND_UNORDERED_ACCESS;
texDesc.CPUAccessFlags = 0;
texDesc.MiscFlags = 0;
HR(m_pd3dDevice->CreateTexture2D(&texDesc, nullptr, m_pTextureOutputA.GetAddressOf()));
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
HR(m_pd3dDevice->CreateTexture2D(&texDesc, nullptr, m_pTextureOutputB.GetAddressOf()));
// 创建无序访问视图
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
uavDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
uavDesc.Texture2D.MipSlice = 0;
HR(m_pd3dDevice->CreateUnorderedAccessView(m_pTextureOutputA.Get(), &uavDesc,
m_pTextureOutputA_UAV.GetAddressOf()));
uavDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
HR(m_pd3dDevice->CreateUnorderedAccessView(m_pTextureOutputB.Get(), &uavDesc,
m_pTextureOutputB_UAV.GetAddressOf()));
// 创建计算着色器
ComPtr<ID3DBlob> blob;
HR(CreateShaderFromFile(L"HLSL\\TextureMul_R32G32B32A32_CS.cso",
L"HLSL\\TextureMul_R32G32B32A32_CS.hlsl", "CS", "cs_5_0", blob.GetAddressOf()));
HR(m_pd3dDevice->CreateComputeShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_pTextureMul_R32G32B32A32_CS.GetAddressOf()));
HR(CreateShaderFromFile(L"HLSL\\TextureMul_R8G8B8A8_CS.cso",
L"HLSL\\TextureMul_R8G8B8A8_CS.hlsl", "CS", "cs_5_0", blob.GetAddressOf()));
HR(m_pd3dDevice->CreateComputeShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_pTextureMul_R8G8B8A8_CS.GetAddressOf()));
assert(m_pd3dImmediateContext);
//#if defined(DEBUG) | defined(_DEBUG)
// ComPtr<IDXGraphicsAnalysis> graphicsAnalysis;
// HR(DXGIGetDebugInterface1(0, __uuidof(graphicsAnalysis.Get()), reinterpret_cast<void**>(graphicsAnalysis.GetAddressOf())));
// graphicsAnalysis->BeginCapture();
//#endif
m_pd3dImmediateContext->CSSetShaderResources(0, 1, m_pTextureInputA.GetAddressOf());
m_pd3dImmediateContext->CSSetShaderResources(1, 1, m_pTextureInputB.GetAddressOf());
// DXGI Format: DXGI_FORMAT_R32G32B32A32_FLOAT
// Pixel Format: A32B32G32R32
m_pd3dImmediateContext->CSSetShader(m_pTextureMul_R32G32B32A32_CS.Get(), nullptr, 0);
m_pd3dImmediateContext->CSSetUnorderedAccessViews(0, 1, m_pTextureOutputA_UAV.GetAddressOf(), nullptr);
m_pd3dImmediateContext->Dispatch(32, 32, 1);
// DXGI Format: DXGI_FORMAT_R8G8B8A8_SNORM
// Pixel Format: A8B8G8R8
m_pd3dImmediateContext->CSSetShader(m_pTextureMul_R8G8B8A8_CS.Get(), nullptr, 0);
m_pd3dImmediateContext->CSSetUnorderedAccessViews(0, 1, m_pTextureOutputB_UAV.GetAddressOf(), nullptr);
m_pd3dImmediateContext->Dispatch(32, 32, 1);
//#if defined(DEBUG) | defined(_DEBUG)
// graphicsAnalysis->EndCapture();
//#endif
HR(SaveDDSTextureToFile(m_pd3dImmediateContext.Get(), m_pTextureOutputA.Get(), L"..\\Texture\\flareoutputA.dds"));
HR(SaveDDSTextureToFile(m_pd3dImmediateContext.Get(), m_pTextureOutputB.Get(), L"..\\Texture\\flareoutputB.dds"));
MessageBox(nullptr, L"请打开Texture文件夹观察输出文件flareoutputA.dds和flareoutputB.dds", L"运行结束", MB_OK);
6.数据格式
C++对应的HLSL中RWTexture2D<T>的数据格式
DXGI_FORMAT | HLSL类型 |
---|---|
DXGI_FORMAT_R32_FLOAT | float |
DXGI_FORMAT_R32G32_FLOAT | float2 |
DXGI_FORMAT_R32G32B32A32_FLOAT | float4 |
DXGI_FORMAT_R32_UINT | uint |
DXGI_FORMAT_R32G32_UINT | uint2 |
DXGI_FORMAT_R32G32B32A32_UINT | uint4 |
DXGI_FORMAT_R32_SINT | int |
DXGI_FORMAT_R32G32_SINT | int2 |
DXGI_FORMAT_R32G32B32A32_SINT | int4 |
DXGI_FORMAT_R16G16B16A16_FLOAT | float4 |
DXGI_FORMAT_R8G8B8A8_UNORM | unorm float4 |
DXGI_FORMAT_R8G8B8A8_SNORM | snorm float4 |
参考文章:DirectX11 With Windows SDK--26 计算着色器:入门 - X_Jun - 博客园 (cnblogs.com)