Shader- Compute Shaders(转)

首先简单介绍GPGPU programming

和CPU Random Memory Accesses(随机内存获取)不同,GPU是用平行架构处理 大量的并行数据,例如vertex和fragment就是分开计算的。使用GPU并利用这种特性来进行非图形计算被称为GPGPU编程(General Purpose GPU Programming)。大量并行无序数据的少分支逻辑(少if)适合GPGPU,例如粒子间互不影响的粒子系统。GPGPU平台或接口有DirectCompute,OpenCL,CUDA等。

从此图可以看出 CPU和GPU之间的数据传输是瓶颈。故当使用GPGPU时,对Texture的逐像素处理不需要传回CPU,因而速度比较快。

Compute Shader

Compute Shader下文简称cs

【概念】

Compute Shaders是在GPU运行却又在普通渲染管线之外的程序。用于运行GPGPU program。

平行算法被拆分成很多线程组,而线程组包含很多线程。例如一个线程处理一个像素点。而一定要注意这种处理是无序的随机的,并不一定是固定的处理顺序,例如不一定是从左到右挨个处理像素点。

线程组

A Thread Group 运行在一个GPU单元 (A single multiprocesser),如果GPU有16个

multiprocesser,那么程序至少要分成16个 Thread Group使得每个multiprocesser都参与计算。

组之间不分享内存。

线程

一个线程组包含n个线程,每32个thread称为一个warp(nvidia:warp=32 ,ati:wavefront=64,因此未来此数字可能会更高)。从效率考虑,一个线程组包含的线程数最好的warp的倍数,256是一个比较合适的数字。

【实现步骤

(1)在compute shader里 通过对贴图或者buffer进行数据读写

(2)在cs脚本里设置shader的贴图或者buffer并运行

【规则语法】1 Compute Shaders的文件后缀为.compute

2 使用#pragma指出内核。

一个Compute Shader至少需要一个内核。

例如#pragma kernel FillWithRed

也可以接宏定义

#pragma kernel KernelOne SOME_DEFINE DEFINE_WITH_VALUE=1337 #pragma kernel KernelTwo OTHER_DEFINE

3 函数语法

下面通过一个完整简单的ComputeShader演示

#pragma kernel FillWithRed

RWTexture2D< float4 > res;

[numthreads(1,1,1)]

void FillWithRed (uint3 id : SV_DispatchThreadID)

{

res[id.xy] = float4(1,0,0,1);

}

这一段代码只是输出红色至res贴图.

一 前缀

在核的前缀用三个纬度,定义了一个线程组内线程的数量,如果只用2个纬度那么,最后一个参数为1即可

[numthreads(thread_group_size_x,thread_group_size_y,1)]

GroupID:线程组id

ThreadIID:组内线程idDispatchThreadID:线程DispatchId(DispatchThreadID=GroupID*组内线程数 +ThreadId)

这些id都是从0开始二 资源类型

cs可以读取两种类型的资源:buffer ,texture

【buffer】

例如顶点缓冲就是一种buffer,很多时候我们去定义struct数组并作为buffer传入cs

自己定义buffer(必须是固定size):

struct Data

{

float x;

};

StructuredBuffer< Data > b;

RWStructuredBuffer< Data > b;

buffer的添加是append,消耗是consume

texture:

只读 Texture2d< float4 > xx;

读写 RWTexture2d< float4 > xx;RWTexture2d< float2 > xx;//RG_int在Unity里读写的只能是RenderTexture并且支持随机读写(RenderTextureenableRandomWrite=true)

三 其他

1 每个线程都有一个对应的id:SV_DispatchThreadID

对贴图进行采样不能用Sample 而是SampleLevel,额外的参数是mipmap level ,0为最高级,1为次级,2...

2 int格子转换至[0,1]uv范围

例如一张512x512的贴图

Texture2d tex;

tex.SampleLevel(samPoint,float2(id.x,id.y)/512)

blur需要所有所有像素都sample完,因此需要同步:

GroupMemoryBarrierWithGroupSync();[例一:基本贴图计算]

将一张贴图所有像素点赋予红色,很简单。

CS脚本

usingUnityEngine;

usingSystem.Collections;

publicclassSetTexColor_1:MonoBehaviour{

publicMaterialmat;

publicComputeShadershader;

voidStart()

{

RunShader();

}

voidRunShader()

{

////////////////////////////////////////

//RenderTexture

////////////////////////////////////////

//1新建RenderTexture

RenderTexturetex=newRenderTexture(256,256,24);

//2开启随机写入

tex.enableRandomWrite=true;

//3创建RenderTexture

tex.Create();

//4赋予材质

mat.mainTexture=tex;

////////////////////////////////////////

//ComputeShader

////////////////////////////////////////

//1找到computeshader中所要使用的KernelID

intk=shader.FindKernel("CSMain");

//2设置贴图参数1=kid参数2=shader中对应的buffer名参数3=对应的texture,如果要写入贴图,贴图必须是RenderTexture并enableRandomWrite

shader.SetTexture(k,"Result",tex);

//3运行shader参数1=kid参数2=线程组在x维度的数量参数3=线程组在y维度的数量参数4=线程组在z维度的数量

shader.Dispatch(k,256/8,256/8,1);

}

}

Compute Shader

//1定义kernel的名称

#pragmakernelCSMain

//2定义buffer

RWTexture2DResult;

//3kernel函数

//组内三维线程数

[numthreads(8,8,1)]

voidCSMain(uint3id:SV_DispatchThreadID)

{

//给buffer赋值

//纯红色

//Result[id.xy]=float4(1,0,0,1);

//基于uv的x给颜色

floatv=id.x/256.0f;

Result[id.xy]=float4(v,0,0,1);

}

[例二:Buffer使用]

这个例子并不是讲实现粒子系统,而只是演示简单的Buffer使用和传递。

步骤:

shader:

1 定义struct结构体

2 声明struct变量

3 函数里计算

c#:

1 定义对应的struct结构体

2声明struct数组3创建bufferComputeBufferbuffer=newComputeBuffer(count,40);

参数1是 数组长度(等于2个三维的乘积),参数2是结构体的字节长度如float=4

4 初始化结构体并赋予buffer

buffer.SetData(values);

参数是struct数组

5 Dispatch

还是FindKernel->SetBuffer->Dispatch

Compute Shader

#pragmakernelCSMain

structPBuffer

{

floatlife;

float3pos;

float3scale;

float3eulerAngle;

};

RWStructuredBufferbuffer;

floatdeltaTime;

[numthreads(2,2,1)]

voidCSMain(uint3id:SV_DispatchThreadID)

{

intindex=id.x+id.y*2*2;

buffer[index].life-=deltaTime;

buffer[index].pos=buffer[index].pos+float3(0,deltaTime,0);

buffer[index].scale=buffer[index].scale;

buffer[index].eulerAngle=buffer[index].eulerAngle+float3(0,20*deltaTime,0);

}

CS脚本

usingUnityEngine;

usingSystem.Collections;

usingSystem.Collections.Generic;

//Buffer数据结构

structPBuffer

{

//size40

publicfloatlife;//4

publicVector3pos;//4x3

publicVector3scale;//4x3

publicVector3eulerAngle;//4x3

};

publicclassParticles_2:MonoBehaviour{

publicComputeShadershader;

publicGameObjectprefab;

privateList<GameObject>pool=newList<GameObject>();

intcount=16;

privateComputeBufferbuffer;

voidStart()

{

for(inti=0;i<count;i++){

GameObjectobj=Instantiate(prefab)asGameObject;

pool.Add(obj);

}

CreateBuffer();

}

voidCreateBuffer()

{

buffer=newComputeBuffer(count,40);

PBuffer[]values=newPBuffer[count];

for(inti=0;i<count;i++){

PBufferm=newPBuffer();

InitStruct(refm);

values[i]=m;

}

buffer.SetData(values);

}

voidInitStruct(refPBufferm)

{

m.life=Random.Range(1f,3f);

m.pos=Random.insideUnitSphere*5f;

m.scale=Vector3.one*Random.Range(0.3f,1f);

m.eulerAngle=newVector3(0,Random.Range(0f,180f),0);

}

voidUpdate()

{

//运行Shader

Dispatch();

//根据Shader返回的buffer数据更新物体信息

PBuffer[]values=newPBuffer[count];

buffer.GetData(values);

boolreborn=false;

for(inti=0;i<count;i++){

if(values[i].life<0){

InitStruct(refvalues[i]);

reborn=true;

}else{

pool[i].transform.position=values[i].pos;

pool[i].transform.localScale=values[i].scale;

pool[i].transform.eulerAngles=values[i].eulerAngle;

//pool[i].GetComponent<MeshRenderer>().material.SetColor("_TintColor",newColor(1,1,1,values[i].life));

}

}

if(reborn)

buffer.SetData(values);

}

voidDispatch()

{

shader.SetFloat("deltaTime",Time.deltaTime);

intkid=shader.FindKernel("CSMain");

shader.SetBuffer(kid,"buffer",buffer);

shader.Dispatch(kid,2,2,1);

}

voidReleaseBuffer()

{

buffer.Release();

}

privatevoidOnDisable()

{

ReleaseBuffer();

}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容