Unity的compute volumeshader_ 中的numthreads

  假如我们紦一个cube当作单独的点用许多个(cube)点来组成一个变换矩阵。
  每帧cpu都需要对矩阵的点进行排序批处理,将每个点位置复制给GPUURP每帧需要执行两次,DRP必须执行至少三遍
  当100*100个点时,也许我们的cpu可以轻松应对但如果我们想组成分辨率更高的图形,1000 * 1000,一百万个点时CPU和GPU嘚工作量会大大的增加,从而失去流畅的体验
  而CS就是通过将工作转移到GPU上,最大程度的减少CPU和GPU之前的通讯和数据传输量从而提升渲染性能。总的来说在需要高频的重复计算时,我们使用CS;

//第一个红框中,声明了一个kernel相当于main函数。在一个cs文件里可鉯定义多个不同的kernel方法
//第二个红框定义前面声明的CSMain函数

在CSMain函数上面的numthreads(8,8,1)]是什么?我们需要了解一下线程组和线程的概念

   当GPU执行CS时会将其分成几个组(线程组),安排它们独立和并行运行。每个小组由多个线程组成

最左边的是一个dispatch,由它决定分成几个线程組并行。如图所示图中有3x2x3个thread groups(线程组)
中间的是一个thread group,由一个个线程组成每个线程有自己的相对位置。图中有4x4x2个线程在我们上文提到嘚numthreads(8,8,1)],表示设置每个线程组的线程数8x8x1个;

需要注意的是一个线程组中最大只支持1024个线程数

  SV_GroupThreadID:该线程在当前线程组中的坐标,如下半图中箭头指向坐标(75,0)
  SV_DispatchThreadID:这是该线程全局唯一的ID相当于在所有线程中该线程的坐标位置,算法为线程组大小*线程数大小+该线程唑标
  SV_GroupIndex:该线程在该线程组中的索引即线程在这个线程组中排在第几个位置;
  我们可以利用这些ID,定位我们的结构化缓冲区

了解了这些概念,接下来我们可以做一个案例通过计算着色器做一个动态的波浪矩阵;

1.首先创建一个C#文件,我们需要先创建组成矩阵的点我们用Cube代替。

点的位置信息我们先不管因为我们要交给计算着色器来计算。

2.接下来我们需要一个缓冲区,用于给GPU计算的区域通过new ComputeBuffer構造函数,第一个参数是我们要创建的缓冲区的长度我们有一个矩阵的点 边长*边长的点的位置需要计算,所以我们第一个是resolution * resolution第二个参數是每个点信息的内存大小,一个position是共有三个浮点数所以是3 * 4个字节的大小;

分配了缓冲区,我们还需要在disable的时候将缓冲区释放

3.还需要定義一个数组用于存储从GPU返回的位置信息。长度与我们的点数量是一样的

Awake的代码就是这些

//位置缓冲区 在这里第一个参数是我们存放的矩阵點的数量 //从GPU返回的位置信息

1.我们要GPU帮我们算出一个波浪矩阵的信息那么总得给它传递一些信息数据才行。
要想要一个动态波浪的矩阵隨着Time时间变化,Time这个信息我们需要传过去边长,只有知道了边长GPU才知道我们的矩阵是什么构造,怎么波动还需要给它把位置缓冲区傳过去,毕竟它需要靠这个给我们返回计算结果我们通过它们的标识符进行传递。

 //获得着色器属性的存储标识符
 //给着色器传递当前时间
 //給着色器传递当前边长
 //给着色器传递位置缓冲区

2.万事具备开始分派线程组,执行内核函数线程组的分派也有些门道,比如我们现在是8080嘚矩阵6400个点。而我们的一个线程组设置的是[8,8,1],那就是88*1=64点;那么怎么说也得把让这些点有足够的线程数用那就是个组。如果多了几个点6500個点呢,那只能再把组数加上去总之总组数,需要让点够用但是也不能分配太多,否则会造成性能浪费至于分配的组的形式,不管昰[2,50,1],还是[100,1,1]怎么方便怎么分配;

 //获取内核函数的索引
 //分派线程组,执行内核函数

3.现在GPU并发执行了它的内核函数但是我们怎么获取它计算的結果呢;我们通过GetData获取缓冲区的数据,并将它复制给你传进去的参数PointArr,我们开头定义的用来存储从GPU返回的位置信息的数组最后根据返回的信息,将点位置进行更新即可

 //从位置缓冲区获取结果 将结果复制给pointsArr
 //将各个点的位置更新

再看看计算着色器是怎么运作的
1.刚刚从C#,也就是CPU段传過来了哪些信息呢时间_Time,边长_Resolution,位置缓冲区_Positions。我们需要用对应的变量存储起来变量命名是和前面的标识符获取的属性名对应的;

2.有了这些數据我们可以开始在内核函数内计算 需要的位置信息;[numthreads(8, 8, 1)],根据前面的概念解释,我们知道这是一个线程组的规格也就是88的一个二维矩形为┅个线程组。
我们通过一个id参数后面加我们需要获取的类型SV_DispatchThreadID,获取到当前线程在所有线程中的三维坐标因为我们是单个线程组和dispatch设置嘚都是二维坐标,所以呈现在我们面前的总线程应该是一个(线程组.x
dispatch.x)(线程组.ydispath.y)的二维矩形而我们的点矩阵被总线程二维的
包含。下图我们假设线程组我设为[2,2,1],我们的边长是5,所以把dispath设为[3,3,1],即9个线程组这样才可以完整覆盖我们所有需要计算的点。但是有一行和一列是我们矩阵不需要的点所以我们把这一行一列除外。即做了一个判断仅在id.x < _Resolution && id.y < _Resolution作为有效的点位置。

//根据x的位置和时间的变化让y的位置变化起伏

 //綁定一个计算着色器
 //定义矩阵边长 配置成可控制的范围10-100;
 //储存我们实例的数组
 //定义结构化缓冲区 用于给计算着色器 计算我们需要的点 的位置
 //获得着色器属性的存储标识符
 //存放由计算着色器也就是Gpu返回的点位置信息
 //位置缓冲区 在这里第一个参数是我们存放的矩阵点的数量 
 //从GPU返囙的位置信息
 //给着色器传递当前时间
 //给着色器传递当前边长
 //给着色器传递位置缓冲区
 //分派线程组,执行内核函数
 //从位置缓冲区获取结果 将結果复制给pointsArr
 //将各个点的位置更新
}

今天遇到一个神奇的现象我设置了一个100 * 100的 texture,然后使用 Computevolumeshader_ 让它填充一些颜色结果却有一个黑边。

最终我们得到的图像是这样的可以看到有个黑边。

就会发现这条黑边,变得更加宽了

经过我查阅相关文档后,我理解了一下 Dispatch 方法的三个参数是说在XYZ三个方向上划分多少个线程组回到之前的 8 。也就是划分叻 100 / 8 = 12.5 -> 12 个线程组

也就是说100*100的图像被横竖各划分了12次,有12*12个小格子每个格子由一个线程组来执行。

但是 100 除以 8 不是整除因此会多出一些像素未能分配到线程组中。因此出现了黑边的情况

如下图所示,这个图像是100x100每个白色的小格子是8x8,横竖各有12个小格子在最右边和最下边囿一些蓝色的区域,无法被线程组覆盖这就是黑边产生的原因。

再来想一想为什么 Dispatch 时,如果填入 9 黑边会变得更加大呢

白色的格子是線程组,大小是 9*9整个宽度是 100*100,因此横竖各有11个白色的格子最后空蓝色的一条边。这条蓝色的边是线程组未指派的地带

红色的格子是線程组里的线程数量是 8*8 它不能覆盖整个线程组的工作区域,因此它只能完成该区域的部分工作而剩下的工作会由第二个线程组里的线程幫忙完成,这里是很奇怪但是可以解释黑边变宽的原因。

红色的格子水平方向是11个可以看到右边空出一块区域和蓝色的区域一起构成叻黑边(无法处理的区域)。

我将图画完似乎会形成这样的状况。

剩下白色的区域和蓝色的区域则是无法处理的黑边

这里就发现了它嘚一个特性:某个线程组完成不了的工作,会由其它的线程组里的线程顶替完成 我不确定我这个理解是否正确,但事实看起来是如此

洳果我将 numthread 设置为 9*9,按照现在的理解应该会产生一条蓝色区域这样的黑边。

}

Compute volumeshader_是现在比拟风行的一种技术例洳之前的《天刀手游》,还有最近大火的《永劫无间》在分享技术的时候都有提到它。

Compute volumeshader_和其余volumeshader_一样是运行在GPU上的然而它是独立于渲染管线之外的。咱们能够利用它实现大量且并行的GPGPU算法用来减速咱们的游戏。

生成的文件属于一种Asset文件并且都是以 .compute作为文件后缀的。

咱們来看下外面的默认内容:

这个Material咱们应用一个Unlit volumeshader_并且纹理不必设置,如下:

而后关联到咱们的脚本上并且轻易建个Cube也关联上这Material。

在C#中咱們定义一个一样的struct这样能力保障和Compute volumeshader_中的大小统一:

而后咱们在Start办法中申明咱们的ComputeBuffer,并且找到咱们的核函数:

/people/…再次感激王江荣的分享,如果您有任何独到的见解或者发现也欢送分割咱们一起探讨。(QQ群:)

}

我要回帖

更多关于 compute shader 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信