Computer Shader

本文详细介绍了Unity中的Compute Shader,包括其创建、使用方法及线程、线程组和调度的概念。通过示例展示了如何在C#脚本中设置参数并调用Compute Shader,以及如何利用它实现自定义后处理效果和粒子系统。Compute Shader利用GPU的强大并行计算能力,为游戏开发提供了高效解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        ComputerShader是一种特殊的shader,得利于GPU强大的并行计算力,computershader可以轻松完成一些重复且量大的计算任务。


ComputerShader用法

ComputerShader部分:

        ComputerShader是以.comCputer为后缀的文件。可以在Project面板中右键-Creat-Shader-ComputerShader来创建它。打开之后可以看到文件中自带的几行示例:

 #pragma kernal CSMain :预编译命令,和我们熟悉的#pragma vertex vert类似,用于指定要编译的核心函数名,这里函数名就是CSMain,可以在c#中通过这个名称指定调用。

RWTexture2D<float4> Result :声明一张存储float4类型的纹理,RW代表是可读可写的;可写意味着我们会将它作为输出纹理来使用,与之对应的还有可读不可写的输入纹理Texture2D<>。除纹理之外,还可以声明其他类型的值如float、int、struct、RWStructuredBuffer<>等用于在c#中传递数据。

[numthreads(8,8,1)] :用于指定一个三维的线程组大小,x,y,z分量分别指定了三个维度的长度。关于线程、线程组、组内线程索引、线程组索引等易混淆的概念在下文中详细解释。

void CSMain(uint3 id : SV_DispatchTreadID){...} :ComputerShader的核心-函数主体部分,一个computershader可以有多个这样的函数,只需要在开头处加上相应的预编译命令#pragma kernal functionName即可。这个函数是没有返回值的,因为我们会把要输出的数据存储在可写纹理中,只需要在c#中申请rt并指定给computershader即可。

函数的参数unit3 id :SV_DispatchThreadID是unit3类型的线程id,关于线程编号如何来算下文详述。

c#部分:

        要使用computershader,就需要在脚本中传递参数,然后创建commendBuffer来调用其中的函数。(在urp管线下)通过Project面板中右键-Creat-Rendering-URP Renderer Feature来创建一个脚本。关于RendererFeature的用法不是本文重点,不再详述。

通过CommandBuffer.SetComputeFloatParam()、CommandBuffer.SetComputeTextureParam()等函数可以向指定的ComputerShader传参数:

//首先在commandbuffer池中申请一个对象
CommandBuffer cmd = CommandBufferPool.Get("ComputerExample");

//根据colorBuffer的大小申请一个临时rt
int temptex = Shader.PropertToID("tempTex");
var camData = renderingData.cameraData;
var tempDesc = camData.cameraTargetDescriptor;
//打开随机写入
tempDesc.enableRandomWrite = true; 
cmd.GetTemporaryRT(temptex,tempDesc);

//向computerShader传值
cmd.SetComputeFloatParam(myCS,"csFloatParam",1.52f);
cmd.SetComputeTextureParam(myCS,"Result",temptex);  //将temptex作为输出rt传入

//执行函数
cmd.DispatchCompute(myCS,0,(int)tempDesc.width/10,(int)tempDesc.height/10,1);
//myCS为指定的computershader名,0为kernal索引(类似shader中的pass索引),后边三个参数为线程组参数
//还可以:cmd.Dispatch(kernalid,(int)tempDesc.width,(int)tempDesc.height,1);

//将结果绘制到colorBuffer中
cmd.Blit(tempDesc,Sour);
//释放commandBuffer
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);

线程、线程组与调度

Thread(线程) :shader中最小的调度单元,一个执行中的函数即一个线程 。

ThreadGroup(线程组):由线程组成的三维数组,存储几十到几百个线程。上面的[numthreads(8,8,1)]便表示一个线程组,其中有64=8*8*1个线程。

Dispatch(调度) :调度的单位是线程组,一次调度会包含多个线程组,即调度是由线程组组成的三维数组,存储了几百到几千个线程。如c#脚本中的cmd.Dispatch(Kernelid,3,2,1),表示调度6=3*2*1个由[numthreads(8,8,1)]构成的线程组。

DispatchThreadID(调度线程ID):kernel函数默认的传入参数,是一个三个分量组成的数组,表示线程在所有线程调度中的绝对位置,即第x个线程组的第y号线程。具体算法很简单,用组号乘上每组内的线程数,再加上线程在组内的相对位置即可:

DispatchThreadID = GroupThreadID+GroupID*numthreads

 以上图这个位置为例,它在组内的相对位置为(2,1,0),它所在的线程组的位置为(0,1,0),因此它的调度线程ID为:(2,5,0)=(2,1,0)+(0,1,0)*(4,4,3)。

GroupThreadID(组内线程ID)和GroupID(线程组ID):kernel函数接受传入参数时可以选择SV_GroupThreadIDSV_GroupID来接受组内线程id线程组id作为参数。它们也是一个三分量的数组,分别表示线程在组内的相对位置线程所在线程组组在调度中的位置


ComputerShader是一个灵活且强大的工具,可以用它来实现自定义的后处理效果,还可以利用它快速并行运算的特点来构建一个粒子系统,用来实现粒子流体、粒子特效等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值