Shader第二十八讲 Compute Shaders
http://blog.sina.com.cn/s/blog_471132920102w97k.html
首先简单介绍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:组内线程id
DispatchThreadID:线程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并且支持随机读写(RenderTexture enableRandomWrite=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脚本
- using UnityEngine;
- using System.Collections;
- public class SetTexColor_1 : MonoBehaviour {
- public Material mat;
- public ComputeShader shader;
- void Start()
- {
- RunShader ();
- }
- void RunShader()
- {
- ////////////////////////////////////////
- // RenderTexture
- ////////////////////////////////////////
- //1 新建RenderTexture
- RenderTexture tex = new RenderTexture (256, 256, 24);
- //2 开启随机写入
- tex.enableRandomWrite = true;
- //3 创建RenderTexture
- tex.Create ();
- //4 赋予材质
- mat.mainTexture = tex;
- ////////////////////////////////////////
- // Compute Shader
- ////////////////////////////////////////
- //1 找到compute shader中所要使用的KernelID
- int k = 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的名称
- #pragma kernel CSMain
- //2 定义buffer
- RWTexture2D Result;
- //3 kernel函数
- //组内三维线程数
- [numthreads(8,8,1)]
- void CSMain (uint3 id : SV_DispatchThreadID)
- {
- //给buffer赋值
- //纯红色
- //Result[id.xy] = float4(1,0,0,1);
- //基于uv的x给颜色
- float v = 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 创建buffer
ComputeBuffer buffer = new ComputeBuffer(count,40);
参数1是 数组长度(等于2个三维的乘积),参数2是结构体的字节长度如float=4
4 初始化结构体并赋予buffer
buffer.SetData (values);
参数是 struct数组
5 Dispatch
还是 FindKernel-> SetBuffer ->Dispatch Compute Shader
- #pragma kernel CSMain
- struct PBuffer
- {
- float life;
- float3 pos;
- float3 scale;
- float3 eulerAngle;
- };
- RWStructuredBuffer buffer;
- float deltaTime;
- [numthreads(2,2,1)]
- void CSMain (uint3 id : SV_DispatchThreadID)
- {
- int index = 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脚本
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
//Buffer数据结构
struct PBuffer
{
//size 40
public float life;//4
public Vector3 pos;//4x3
public Vector3 scale;//4x3
public Vector3 eulerAngle;//4x3
};
public class Particles_2 : MonoBehaviour {
public ComputeShader shader;
public GameObject prefab;
private List<<font
face="Consolas"> GameObject > pool = new List<<font
face="Consolas"> GameObject >();
int count = 16;
private ComputeBuffer buffer;
void Start()
{
for (int i = 0; i <<font
face="Consolas"> count; i++) {
GameObject obj = Instantiate (prefab) as GameObject;
pool.Add (obj);
}
CreateBuffer ();
}
void CreateBuffer()
{
buffer = new ComputeBuffer(count,40);
PBuffer[] values = new PBuffer[count];
for (int i = 0; i <</span> count; i++) {
PBuffer m = new PBuffer ();
InitStruct (ref m);
values [i] = m;
}
buffer.SetData (values);
}
void InitStruct(ref PBuffer m )
{
m.life = Random.Range(1f,3f);
m.pos = Random.insideUnitSphere * 5f;
m.scale = Vector3.one * Random.Range(0.3f,1f);
m.eulerAngle = new Vector3 (0, Random.Range(0f,180f), 0);
}
void Update()
{
//运行Shader
Dispatch ();
//根据Shader返回的buffer数据更新物体信息
PBuffer[] values = new PBuffer[count];
buffer.GetData(values);
bool reborn = false;
for (int i = 0; i <</span> count; i++) {
if (values [i].life <</span> 0) {
InitStruct (ref values [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<</span>MeshRenderer>().material.SetColor ("_TintColor", new Color(1,1,1,values [i].life));
}
}
if(reborn)
buffer.SetData(values);
}
void Dispatch()
{
shader.SetFloat ("deltaTime", Time.deltaTime);
int kid = shader.FindKernel ("CSMain");
shader.SetBuffer (kid, "buffer", buffer);
shader.Dispatch (kid, 2, 2, 1);
}
void ReleaseBuffer()
{
buffer.Release();
}
private void OnDisable()
{
ReleaseBuffer();
}
}
参考:
《Introduction_to_3D_Game_Programming_with_Directx_11》
Shader第二十八讲 Compute Shaders的更多相关文章
- Linux性能优化实战学习笔记:第二十八讲
一.案例环境描述 1.环境准备 2CPU,4GB内存 预先安装docker sysstat工具 apt install docker.io sysstat nake git 案例总共由三个容器组成: ...
- MySQL实战45讲学习笔记:第二十八讲
一.读写分离架构 在上一篇文章中,我和你介绍了一主多从的结构以及切换流程.今天我们就继续聊聊一主多从架构的应用场景:读写分离,以及怎么处理主备延迟导致的读写分离问题. 我们在上一篇文章中提到的一主多从 ...
- 趣谈Linux操作系统学习笔记:第二十八讲
一.引子 磁盘→盘片→磁道→扇区(每个 512 字节) ext* 定义文件系统的格式 二.inode 与块的存储 1.块 2.不用给他分配一块连续的空间 我们可以分散成一个个小块进行存放 1.优点 2 ...
- NeHe OpenGL教程 第二十八课:贝塞尔曲面
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- SQL注入之Sqli-labs系列第二十八关(过滤空格、注释符、union select)和第二十八A关
开始挑战第二十八关(Trick with SELECT & UNION) 第二十八A关(Trick with SELECT & UNION) 0x1看看源代码 (1)与27关一样,只是 ...
- 第二十八个知识点:什么是公钥密码学的IND-CCA安全定义?
第二十八个知识点:什么是公钥密码学的IND-CCA安全定义? 我们将在这篇博客中讨论公钥加密的IND-CCA安全. IND-CCA安全代表选择明文的不可伪造性.这样的安全方案的思想就是给定一个密文,攻 ...
- 第二十八条:利用有限制通配符来提升API的灵活性
如第二十五条所述,参数化类型是不可变的.类型Type1和Type2而言,不管Type1与Type2的关系,List<Type1>既不是List<Type2>的子类型,也不是也不 ...
- Android UI开发第二十八篇——Fragment中使用左右滑动菜单
Fragment实现了Android UI的分片管理,尤其在平板开发中,好处多多.这一篇将借助Android UI开发第二十六篇——Fragment间的通信. Android UI开发第二十七篇——实 ...
- MySQL实战45讲学习笔记:第二十六讲
一.引子 在上一篇文章中,我和你介绍了几种可能导致备库延迟的原因.你会发现,这些场景里,不论是偶发性的查询压力,还是备份,对备库延迟的影响一般是分钟级的,而且在备库恢复正常以后都能够追上来. 但是,如 ...
随机推荐
- oracle 10g 数据库与客户端冲突导致实例创建无监听问题
同事在oracle 10g上创建一个实例,快结束时弹出一个错误,提示监听失败之类.查看服务,并无生成监听服务.于是删除重来,一连试了好几次,都是如此. 这真是令人心烦意乱.提示里面有说到端口1521, ...
- sanic官方文档解析之Deploying(部署)和Extension(扩展)
1,Deploying(部署) 通过内置的websocket可以很简单的部署sanic项目,之后通过实例sanic.Sanic,我们可以运行run这个方法通过接下来的关键字参数 host (defau ...
- eclipse中怎么删除重复的console
eclipse中不同的应用会开启不同的console,所以并不是重复. 如图: Terminate标志/操作按钮,可以停止当前的执行,以及标志此Console是Terminated状态: Remove ...
- JavaScript精粹
序:好书是需要不断品读的!再读语言精粹,顺便做点笔记. 1.NaN是一个数值,它表示一个不能产生正常结果的运算结果.NaN不等于任何值,包括它自己.检测是否为NaN: isNaN(number). ...
- LightOJ1259 Goldbach`s Conjecture —— 素数表
题目链接:https://vjudge.net/problem/LightOJ-1259 1259 - Goldbach`s Conjecture PDF (English) Statistic ...
- Js中获取显示器、浏览器以及窗口等的宽度与高度的方法
网页可见区域宽:document.body.clientWidth 网页可见区域高:document.body.clientHeight 网页可见区域宽:document.body.offsetWid ...
- 写给精明Java开发者的测试技巧
我们都会为我们的代码编写测试,不是吗?毫无疑问,我知道这个问题的答案可能会从 “当然,但你知道怎样才能避免写测试吗?” 到 “必须的!我爱测试”都有.接下来我会给你几个小建议,它们可以让你编写测试变得 ...
- MYSQL进阶学习笔记十七:MySQL定期维护!(视频序号:进阶_36)
知识点十八:MySQL定期维护(37) 一.Mysql的定时器 所谓的定时器,指的是在某个时间段去执行同样的代码.比如闹钟.每到指定的时间闹铃就会响.同样的,我们这个定时器,只要满足我们的一个定时条件 ...
- 通过dom4j写.xml文件
步骤: 1.左键选中src,点击红圈2: 2.新建类: 3.开始写代码: package com.bjsxt.xml; import java.io.File; import java.io.File ...
- codeforces 463A Caisa and Sugar 解题报告
题目链接:http://codeforces.com/problemset/problem/463/A 题目意思:某个人有 s dollar的钱,有 n 种类型的糖果,第 i 种糖果的价值为 xi d ...