如果开始研究计算着色器了,说明读者已经有一定的D3D11基础,自己也跑过几个程序,那么我希望看完的人能够达到自己完成编写计算着色器文件,完成自己的项目任务。由于我学习D3D11是直接跳过其他着色器的(项目无关),所以有很多基础很差,本文也只提供计算着色器相关的内容,和如何使用计算着色器处理图像的一点经验

这篇笔记很短,主要记录了我在使用计算着色器的一些经验和我认为必须的点。

1. 计算着色器hlsl文件编写(以求解图像的暗通道为例)

2. 着色器文件的编译

3. 计算着色器资源设置

计算着色器HLSL文件编写

本文使用的代码是在X_JUN的代码上面修改的,他的源码可以在这里下载。

hlsl文件通过VS,在项目内创建一个着色器文件夹,直接创建源文件,然后修改后缀为hlsl即可。创建好文件后,记得修改属性页的入口点和hlsl文件主函数名一致,否则无法编译。选择着色器类型和模型,一般D3D11都支持5.0,不同的设备可以查询支持的模型然后选择。

下面是求解图像的暗通道的代码,hlsl代码主要由指定输入输出线程声明,和主函数组成。

Texture2D g_TexA : register(t0);    //输入纹理RWTexture2D<float4> g_Output : register(u0);   //输出纹理

输入输出

着色器的输入输出如上图所示,在这份程序中,输入输出是2D纹理,所以我们需要声明其类型及变量名。后面的resgiter,则是着色器文件的注册机制,如果是纹理(texture),则使用t加上输入序号,输入纹理1则为register(t0)。值得注意的是输出,尽管也是2D纹理,因为是可以读写的,所以使用了RWTexture2D,register(u0)则表示这是一个无序访问视图(可以修改的)类型。这些变量在C++代码中的声明和设置将在后面说道。

此外,如果有时候我们需要输入一些变量作为着色器文件的参数,可以使用 buffer。下面给出了一个简单的例子,这个变量是从CPU传到GPU供着色器使用的,注意传入的buffer的大小必须是16字节的整数倍,否则会报错,无法传入。

cbuffer CB: register(b0)
{uint c1;        // 参数uint c2;          //参数uint useless1;      // 未使用uint useless2;     //未使用
}

线程声明

下面是线程声明,numthread给出了一个线程组内,X,Y,Z方向各多少个线程,单个线程组的线程数不能大于1024(D3D11限制)。线程组个数通过C++中使用Dispatch函数给出,也是分配了XYZ方向有多少线程组(线程组最多为65535),同时Dispatch也是启动计算着色器的函数。线程数限制具体可以查找MSDN文档得到,不同的版本最大线程的限制不一样。

// 一个线程组中的线程数目。线程可以1维展开,也可以
// 2维或3维排布
[numthreads(32, 32, 1)]

这里借一幅参考资料3中的图,来说明线程组和线程的关系。左边就是我们使用dispatch函数分配的线程组个数,其每个组对应了一个位置,右边则是一个线程组内的线程分布,这是由numthread所分配的。

主函数

主程序就是每个线程要要处理的程序。首先看这里我们可以获得的线程的坐标,即主函数输入的参数。这里的ID就是上图中可以看到的坐标。对于要处理图像的任务来说,这让我们可以读出每个输入2D纹理的像素值,可以写出到输出2D纹理的位置。需要注意的是(在我才开始接触的时候混淆的一点),这里的线程坐标和图像坐标并不是同一个。一个线程是处理一个子任务,我们可以让一个线程去处理图像一个4*4微元的值,只要我们知道相应的对应关系,即哪一个线程处理哪一块的图像,我们就可以在hlsl文件中编写相应的功能。

这几个坐标中,使用的较多主要是SV_DispatchThreadID,这是一个三维的向量对应xyz坐标。如代码片段中,用DTid的xy坐标,就可以索引对应纹理的像素值。

基础的着色器语言可以查看参考资料3这本书,有简单介绍,基本和C++相似,但有些不同,比如代码中的float4这种变量。

Texture2D g_TexA : register(t0);              //输入纹理RWTexture2D<float4> g_Output : register(u0);  //输出纹理// 一个线程组中的线程数目。线程可以1维展开,也可以
// 2维或3维排布[numthreads(32, 32, 1)]//每个线程的处理程序,也可以对应每个像素
void CS( uint3 Gid : SV_GroupID,            //线程组IDuint3 DTid : SV_DispatchThreadID,       //当前线程对应的全局IDuint3 GTid : SV_GroupThreadID,          //线程组内的线程IDuint GI : SV_GroupIndex                 //线程组内将线程一维展开后,对应的索引)
{   //求解暗通道(即求解RGB通道里面的最小值)float4 outV;float  min;min = g_TexA[DTid.xy][0];if (min > g_TexA[DTid.xy][1])min = g_TexA[DTid.xy][1];if (min > g_TexA[DTid.xy][2])min = g_TexA[DTid.xy][2];outV[0] = min;outV[1] = 0;outV[2] = 0;outV[3] = g_TexA[DTid.xy][3];//求暗通道里面的最大值g_Output[DTid.xy] = outV;}

着色器文件的编译、着色器创建和使用

编译着色器文件参考资料2的博客文章给出了详细的说明。我主要用到了以下函数。

//读取编译好的cso(complied shader object)文件
HRESULT WINAPI
D3DReadFileToBlob(_In_ LPCWSTR pFileName,_Out_ ID3DBlob** ppContents);//编译hlsl文件
HRESULT WINAPI
D3DCompileFromFile(_In_ LPCWSTR pFileName,_In_reads_opt_(_Inexpressible_(pDefines->Name != NULL)) CONST D3D_SHADER_MACRO* pDefines,_In_opt_ ID3DInclude* pInclude,_In_ LPCSTR pEntrypoint,_In_ LPCSTR pTarget,_In_ UINT Flags1,_In_ UINT Flags2,_Out_ ID3DBlob** ppCode,_Always_(_Outptr_opt_result_maybenull_) ID3DBlob** ppErrorMsgs);

以上函数输出的变量是ID3DBlob **类型,我的理解是我们要使用的着色器对象,使用这个变量我们就可以创建对应文件的着色器。利用以下函数即可创建着色器,其中m_DarkChannel_CS是预先定义的着色器,类型为ID3D11ComputeShader。这里用到了D3D11设备这个对象m_pd3dDevice创建着色器。

/*函数原型
virtual HRESULT STDMETHODCALLTYPE CreateComputeShader( /* [annotation] */ _In_reads_(BytecodeLength)  const void *pShaderBytecode,/* [annotation] */ _In_  SIZE_T BytecodeLength,/* [annotation] */ _In_opt_  ID3D11ClassLinkage *pClassLinkage,/* [annotation] */ _COM_Outptr_opt_  ID3D11ComputeShader **ppComputeShader)
*/m_pd3dDevice->CreateComputeShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, m_DarkChannel_CS.GetAddressOf())

计算着色器资源设置

着色器文件的输入输出都是在C++代码中指定的。

输入通过XXSetShaderResource()来设置(XX是指着色器类型,计算着色器即为CS)。需要注意的是设置着色器资源必须要使用纹理对应的ShaderResourceView类型的资源,所以在C++代码中,必须要要给纹理创建一个着色器资源才能作为着色器的输入。以下代码给出了该函数的定义和使用方法。第一个参数是输入的第几个资源,对应着色器文件中的t0。第二个参数一般是设为1就行;第三个参数是纹理的指针地址,m_pTextureInputA是着色器资源类型的指针。调用方法是通过上下文对象调用函数来绑定到对应着色器对象上。

着色器输入资源设置/*
virtual void STDMETHODCALLTYPE CSSetShaderResources( /* [annotation] */ _In_range_( 0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT - 1 )  UINT StartSlot,/* [annotation] */ _In_range_( 0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT - StartSlot )  UINT NumViews,/* [annotation] */ _In_reads_opt_(NumViews)  ID3D11ShaderResourceView *const *ppShaderResourceViews)
*///设置计算着色器输入资源
m_pd3dImmediateContext->CSSetShaderResources(0, 1, m_pTextureInputA);

输出是通过调用以下无序视图函数来设置的,着色器输出必须是无序访问视图类型,需要提前建立对应的变量。

/*设置计算着色器输出资源,为无序访问视图virtual void STDMETHODCALLTYPE CSSetUnorderedAccessViews( /* [annotation] */ _In_range_( 0, D3D11_1_UAV_SLOT_COUNT - 1 )  UINT StartSlot,/* [annotation] */ _In_range_( 0, D3D11_1_UAV_SLOT_COUNT - StartSlot )  UINT NumUAVs,/* [annotation] */ _In_reads_opt_(NumUAVs)  ID3D11UnorderedAccessView *const *ppUnorderedAccessViews,/* [annotation] */ _In_reads_opt_(NumUAVs)  const UINT *pUAVInitialCounts)
*/m_pd3dImmediateContext->CSSetUnorderedAccessViews(0, 1, m_pTextureOutputA_UAV.GetAddressOf(), nullptr)

完成以上的计算着色器文件编写,D3D初始化,着色器创建,创建需要用的的着色器资源视图和无序访问视图,我们就可以调用着色器时,只需要使用几句C++代码即可完成。

//设置着色器
m_pd3dImmediateContext->CSSetShader(m_DarkChannel_CS.Get(), nullptr, 0);
//设置计算着色器输入资源(着色器资源视图)
m_pd3dImmediateContext->CSSetShaderResources(0, 1, m_pTextureInputA.GetAddressOf());
//设置计算着色器输出资源(无序访问视图)
m_pd3dImmediateContext->CSSetUnorderedAccessViews(0, 1, m_pTextureOutputA_UAV.GetAddressOf(), nullptr);
//分配线程组
m_pd3dImmediateContext->Dispatch(64, 32, 1);

通过以上代码,就可以实现利用D3D11的计算着色器求出图像暗通道图像,如下图所示。

参考资料:

1. HLSL编译着色器的三种方法

2. 计算着色器:入门

3. Practical Rendering and Computation with Direct3D 11(非常重要,这本书前面还讲了一些HLSL语言的基础,值得一看。)

D3D11计算着色器配置与编程相关推荐

  1. 《OpenGL编程指南(原书第8版)》——计算着色器

    原文  http://www.csdn.net/article/2014-11-21/2822754 主题 OpenGL 数学 概述 由于图形处理器每秒能够进行数以亿计次的计算,它已成为一种性能十分惊 ...

  2. OpenGL深入探索——《OpenGL编程指南(原书第8版)》——计算着色器

    转载自 <OpenGL编程指南(原书第8版)>--计算着色器 概述 由于图形处理器每秒能够进行数以亿计次的计算,它已成为一种性能十分惊人的器件.过去,这种处理器主要被设计用于承担实时图形渲 ...

  3. Directx 计算着色器(compute shader)

    原文 :http://www.cnblogs.com/Ninputer/archive/2009/12/11/1622190.html 博者注:计算着色器调试(http://msdn.microsof ...

  4. OpenGL超级宝典(第7版)笔记11 帧缓存运算 计算着色器 清单 3.13

    OpenGL超级宝典(第7版)笔记11 帧缓存运算 计算着色器 清单 3.13 文章目录 OpenGL超级宝典(第7版)笔记11 帧缓存运算 计算着色器 清单 3.13 1 帧缓存运算 1.1 裁剪测 ...

  5. DirectX11进阶8_计算着色器(入门、流体模拟)

    一.计算着色器:入门 GPU通常被设计为从一个位置或连续的位置读取并处理大量的内存数据(即流操作),而CPU则被设计为专门处理随机内存的访问. 由于顶点数据和像素数据可以分开处理,GPU架构使得它能够 ...

  6. DirectX11 With Windows SDK--27 计算着色器:双调排序

    前言 上一章我们用一个比较简单的例子来尝试使用计算着色器,但是在看这一章内容之前,你还需要了解下面的内容: 章节 26 计算着色器:入门 深入理解与使用缓冲区资源(结构化缓冲区/有类型缓冲区) Vis ...

  7. Unity计算着色器 01

    序 计算着色器,是什么? 官方文档,启动! Unity - Manual: Compute shaders (unity3d.com) 大概是这个画风: 就看到了有<GPU>这个字眼,还说 ...

  8. 计算着色器(Compute Shader)

    图形处理器(Graphics Processing Unit,简称GPU)每秒能够进行数以亿次的计算,目前其已成为一种性能十分惊人的器件.通常,GPU主要用来承担实时图形渲染中的海量数学运算,然而,其 ...

  9. 计算着色器(Compute Shaders)

    原文 : https://catlikecoding.com/unity/tutorials/basics/compute-shaders/ 1 将工作转移到GPU(Moving Work to th ...

最新文章

  1. spark编程基础--5.2键值对RDD
  2. 推荐3款 Docker 认证的实用免费插件,帮助您快速构建云原生应用程序!
  3. 使用kuberbuilder创建工程示例
  4. boost::spirit模块实现自定义用作容器数据的测试程序
  5. 大数据平台蓝图_数据科学面试蓝图
  6. 【超级鼠标键盘锁】项目工程下载地址
  7. ceph docker mysql_使用Docker部署单机版Ceph
  8. js 字符串转换成数字(转)
  9. 模块之序列化模块json
  10. java爬虫基础知识,Java网络爬虫基础知识
  11. 深入浅出vuejspdf下载_vue下载pdf
  12. 数学笔记(四)线性代数知识点总结
  13. Python学生成绩计算和平均值
  14. Moore-Penrose伪逆(Moore-Penrose广义逆)
  15. visio流程图添加连接点
  16. minigui源码学习
  17. Shape 文件格式解释
  18. 病来如山倒,病去如抽丝
  19. 一个生成随机密码的WPF小程序
  20. (Python实现中文分词最大匹配算法)研究生命的起源

热门文章

  1. git提交代码到码云过程
  2. 22 | 如何让MySQL临时提升下性能
  3. 短信验证码接口风险分析
  4. scrapy使用布隆过滤器
  5. 【java 通过视频url下载】
  6. 短信群发限制,你想了解的都在这里
  7. 用计算机术语写祝福,2010给计算机老师的新年祝福短信!!!急求!!!
  8. 微信小程序隐私技术保护:HTTPS授权确认
  9. dbus PHP,[已解决] DBus的问题
  10. 全国计算机报名吉林,2017年9月吉林全国计算机等级考试报名通知