Compute Shader 是为了在Unity中使用GPGPU,即通用目的图形计算单元,它独立于渲染管线,通用目的也说明了它可以用于多种目的,与CPU相比,GPU的优势在于多线程并行计算,当你想对大量数据进行相同的计算的时候,会快很多。
以下是使用说明:
#pragma kernel CSMain
#define GroupLength_x 8
RWStructuredBuffer buffer;
[numthreads(4,4,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
buffer[id.x + id.y * GroupLength_x] = id.x + id.y * GroupLength_x;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
---------------------------------------------------------------------------------
public class _t1 : MonoBehaviour {
public ComputeShader cs;
// Use this for initialization
void Start () {
ComputeBuffer buffer = new ComputeBuffer(4 * 4 * 2 * 2, sizeof(int));
cs.SetBuffer(0, "buffer", buffer);
cs.Dispatch(0, 2, 2, 1);
int[] output = new int[buffer.count];
buffer.GetData(output);
foreach (int i in output)
{
print(i);
}
buffer.Release();
}
}
上面为computeshader中的代码,下面为在脚本中调用的代码,输出结果为0到63。
先说存在的陌生的东西
1.RWStructuredBuffer用与脚本和shader之间的数据传输,你可以向其传入任何类型,一个特别的用法是像其传一个自己创建的结构体类型。
2.脚本中的ComputeBuffer是就是存储输入数据和输出数据的容器。
3.SetBuffer(0,“buffer”,buffer)将脚本中的ComputeBuffer与shader中的RWStructuredBuffer关联起来,第一个参数为Kernel索引。
4.GetData()将ComputerBuffer中的数据传输到具体数据结构中。
5.Release()ComputerBuffer用完记得释放。
接下来重点说线程与线程组:
一个线程组中可包含多个线程,可以有多个线程组。我们需要进行设置有两个部分,一是线程矩阵,矩阵最多可以有三个维度,二是每个维度的线程组数。
[numthreads(4,4,1)]用于确定线程矩阵,在这里是4*4的二维矩阵。
Dispatch(0,2,2,1) 用于确定各个维度的线程组数,第一个参数为Kernel索引。
确定了线程矩阵和线程组数之后,就可以准确的写出该线程矩阵的最终形态了。
最终矩阵为二维矩阵,一维长度为4*2,即线程组中线程数*线程组数,同理,二维长度也为4*2,即线程组中线程数*线程组数。
这时候 uint3 id :SV_DispatchThreadID 就发挥作用了,id就是当前线程在最终矩阵中的索引。
除了SV_DispatchThreadID,还有一些语义,如SV_GroupThreadID和SV_GroupID,这两个语义配合起来用就能达到上述相同的效果,它们分别表示当前线程在线程组中的索引与当前线程所在线程组的索引。
上面的例子说完了,下面说一些额外的。
1.Cumputer.FindKernel(string s) 用于通过名字来寻找shader中Kernel的索引。
2.为什么要用线程组的概念,为什么线程要使用矩阵的形式而非一维数组?
首先,一个线程组所能容纳的线程是有限的,目前为1024,当然这和硬件有关,所以才使用多个线程组的概念来应对大量数据的处理,其次一个线程组中的线程可以共享内存,因此合理的线程组分配和矩阵设计在确定的算法的实现中可以大大的提高效率。
3.如果向shader中传入全局变量,即只用于shader中的计算?
以float为例。
单个float——SetFloat
float2、float3、float4——SetVector Vector4
float4x4、float4x4[] ——Setfloats 数组