创建第一个Unity Compute Shader 前面我们提到了,使用dispatch函数来调用给定的compute shader,那么我们来制作自己的第一个compute shader吧。
简单搭建一下场景。
最简单的处理是把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 { public ComputeShader C_1st; public RenderTexture renderTexture; private int _kernel; void Start () { _kernel=C_1st.FindKernel("CSMain" ); renderTexture.enableRandomWrite = true ; C_1st.SetTexture(_kernel,"Result" , renderTexture); C_1st.Dispatch (_kernel,8 ,8 ,1 ); } }
这里另外说明一下:Dispatch函数用于告诉GPU,分配多少个线程组去运算compute shader。我们已经了解到,在compute shader中指定了一个线程组的线程数量,那么我们在计算这个部分的时候,GPU一共使用了8x8x1x8x8x1个线程。
创建了一个renderTexture和一个材质球:
把renderTexture绑定到材质球的自发光和基础色上
点击运行,得到两个报错:
显然,我们不能把已经创建的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可以杯设置为允许随机写入(因为每个线程完成计算的时间是不可确定的)
那么现在我们点击运行,得到结果:
现在我们成功使用了compute shader进行运算。