=。= 准备开始花1个小时探讨这个复杂的 Compute Shader的, 结果从家里拷贝做的 Demo 在女朋友的小霸王笔记本上运行不起来。。只能明天回家在写了。。
就是这个破显卡 🙁 Intel HD Graphics 3000
—–
I’m back…
话说, Compute Shader 能做什么呢,可以把大量的并行计算丢到GPU去计算,因为GPU并行计算的能力爆表
举例来说呢
1.大规模粒子特效,计算粒子的位置,大小,颜色等等(当然传统粒子也可以做到这点,通过传入时间值和带有时间的公式来计算位置,但是Compute Shader 可以实现带有逻辑更复杂的效果, 而且编码更加简单)
2.烟雾,液体的模拟
3.可以做贪食蛇 =.= 像这样
请不要无脑复制转载本文, 由 dreamfairy 原创, 转载请注明出处 本文地址 http://www.dreamfairy.cn/blog/2016/06/09/unity3d-compute-shader/
Compute Shader 是 SM5.0 Dx11 的技术,目前只能用在PC上
Unity3D 创就 Compute Shader的位置和创建普通的Shader 在同一个菜单下
一个默认的Compute Shader是这样的
// Each #kernel tells which function to compile; you can have many kernels #pragma kernel CSMain // Create a RenderTexture with enableRandomWrite flag and set it // with cs.SetTexture RWTexture2D<float4> Result; [numthreads(8,8,1)] void CSMain (uint3 id : SV_DispatchThreadID) { // TODO: insert actual code here! Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0.0); }
#pragma kernel CSMain 是定义Compute Shader的入口函数名
RWTexture2D Result; 是 Compute Shader 的输入输出纹理 (RW开头,表示 Read And Writhe) 当然 Compute Shader支持自定义构造体,后面说
[numthreads(8,8,1)] 表示,每个线程组创建多少个 Thread, 说到这个就要提到调用 Compute Shader的API了
在CS代码中调用API为 Shader.Dispatch(kernel, r, g, b);
线程组的数量是 r * g * b, 比如 Shader.Dispatch(kernel, 2, 3, 4); 且 [numthreads(8,8,1)] 则线程数为 2 * 3 * 4 = 24 个线程组,每个线程组 8 * 8 * 1 个线程, 总计 24 * 64 = 1536 个线程
不过线程数也是有上线的,微软给了个这么个图表
The allowable parameter values for numthreads depends on the compute shader version. Compute Shader Maximum Z Maximum Threads (X*Y*Z) cs_4_x 1 768 cs_5_0 64 1024
然后说说 CSMain 函数的参数 uint3 id : SV_DispatchThreadID
它表示当前运行的线程的id号, id.xyz 表示当前在 numthreads 中的索引
说了辣么多,先来个简单的例子, 用Compute Shader 做 uv 滚动的效果
private string state = ""; private const string stateRed = "FillWithRed"; private const string stateBlack = "FillWithBlack"; RenderTexture rt = new RenderTexture(nTexWidth, nTexWidth, 0); rt.enableRandomWrite = true; rt.Create(); int kernel = shader.FindKernel(stateRed); shader.SetTexture(kernel, "Result", rt); ChangeState(FillWithRed);
创建 RenderTexture 要记得开启 enableRandomWrite , 否则Shader中无法写入数据
然后就是获取 Shader 中 CSMain函数的指针 int kernel = shader.FindKernel(stateRed);
之后
StartCoroutine(StartGPGPU()); IEnumerator StartGPGPU() { while (true) { count = count + changeValue; if(count > nTexWidth) { ChangeState(state == stateBlack ? stateRed : stateBlack); } shader.Dispatch(kernel, nTexWidth, count, 1); yield return new WaitForEndOfFrame(); } }
我们用一个携程来做一个uv循环滚动, x 轴不变, y 轴从下往上滚动
nTexWidth 这里定义的数值是 256 。 当Y轴处理的像素超过256后,做一次从0开始涂黑过程。 你可以把线程和像素点一一对应起来, 线程在像素点所在位置进行对应位置涂色操作
Shader 部分
#pragma kernel FillWithRed #pragma kernel FillWithBlack RWTexture2D<float4> Result; [numthreads(1,1,1)] void FillWithRed (uint3 id : SV_DispatchThreadID) { // TODO: insert actual code here! Result[id.xy] = float4(1,0,0,1); } [numthreads(1,1,1)] void FillWithBlack (uint3 id : SV_DispatchThreadID) { // TODO: insert actual code here! Result[id.xy] = float4(0,1,0,1); }
后面的贪食蛇,粒子,液体后面再说, 先看 LGD vs VG