创建第一个Unity Compute Shader

前面我们提到了,使用dispatch函数来调用给定的compute shader,那么我们来制作自己的第一个compute shader吧。

简单搭建一下场景。

sc001 (4).png

最简单的处理是把RGB三个通道平均一下,产生一个灰度图像。

1
2
3
4
5
6
7
8
9
10
#pragma kernel CSMain
RWTexture2D<float4> Result;

[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
float4 color = Result[id.xy];
float grey=0.33*(color.x+color.y+color.z);
Result[id.xy] = float4(grey,grey,grey,0);
}

接下来是在C#脚本中调用它。这里作者踩了一个坑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class My1stCom : MonoBehaviour
{
// Start is called before the first frame update
public ComputeShader C_1st;
// public int size;
public RenderTexture renderTexture;
private int _kernel;

void Start()
{
//获取核函数的序号
_kernel=C_1st.FindKernel("CSMain");
renderTexture.enableRandomWrite = true;
//设置渲染目标
C_1st.SetTexture(_kernel,"Result", renderTexture);
//由于作者只想调用一次,所以直接在start里调用了
C_1st.Dispatch (_kernel,8,8,1);
}
}

这里另外说明一下:Dispatch函数用于告诉GPU,分配多少个线程组去运算compute shader。我们已经了解到,在compute shader中指定了一个线程组的线程数量,那么我们在计算这个部分的时候,GPU一共使用了8x8x1x8x8x1个线程。

创建了一个renderTexture和一个材质球:

2024-04-30-20-52-30-image.png

把renderTexture绑定到材质球的自发光和基础色上

2024-04-30-20-53-38-image.png

点击运行,得到两个报错:

2024-04-30-20-47-19-image.png

显然,我们不能把已经创建的rendertexuture设置为允许随机读写,并且它不能被识别为纹理单元(UAV)

修改思路,先输出一张UV试试。

由于只能获取到线程的位置,因此只能得到线程的UV图,那么修改代码:

1
2
3
4
5
6
7
#pragma kernel CSMain
RWTexture2D<float4> Result;
[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
Result[id.xy] =float4((id.xy/8.0)%1,0,0);
}

这里我们使用这个线程在线程组中的位置来计算颜色,由于线程组是8x8的大小,那么我们需要除以8得到小数,然后再求1的余数,得到小数部分,确保不会超出1(超出1的部分是不可预知的,不同设备上的表现可能不同,hlsl并没有提供舍弃整数部分的API,因而这样处理。)

修改C#代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class My1stCom : MonoBehaviour
{
public ComputeShader C_1st;
public int size;
private int _kernel;
public Material _mat;
void Start()
{
//创建一个RenderTexture
RenderTexture renderTexture= new RenderTexture (size, size, 0);;
//获取核函数的序号
_kernel=C_1st.FindKernel("CSMain");
//设置允许随机读写
renderTexture.enableRandomWrite = true;
renderTexture.Create ();
//设置为自发光材质
_mat.SetTexture ("_EmissionMap", renderTexture);
//设置渲染目标
C_1st.SetTexture(_kernel,"Result", renderTexture);
//调用shader
C_1st.Dispatch (_kernel, Mathf.CeilToInt(size / 8f), Mathf.CeilToInt(size / 8f), 1);
}
void Update()
{

}
}

这里我们使用了代码来创建RenderTexture,这样的RenderTexture可以杯设置为允许随机写入(因为每个线程完成计算的时间是不可确定的)

那么现在我们点击运行,得到结果:

2024-04-30-21-54-19-image.png

现在我们成功使用了compute shader进行运算。