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演示
  1. #pragma kernel FillWithRed
  2. RWTexture2D< float4 > res;
  3. [numthreads(1,1,1)]
  4. void FillWithRed (uint3 id : SV_DispatchThreadID)
  5. {
  6. res[id.xy] = float4(1,0,0,1);
  7. }
这一段代码只是输出红色至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脚本
  1. using UnityEngine;
  2. using System.Collections;
  3. public class SetTexColor_1 : MonoBehaviour {
  4. public Material mat;
  5. public ComputeShader shader;
  6. void Start()
  7. {
  8. RunShader ();
  9. }
  10. void RunShader()
  11. {
  12. ////////////////////////////////////////
  13. //    RenderTexture
  14. ////////////////////////////////////////
  15. //1 新建RenderTexture
  16. RenderTexture tex = new RenderTexture (256, 256, 24);
  17. //2 开启随机写入
  18. tex.enableRandomWrite = true;
  19. //3 创建RenderTexture
  20. tex.Create ();
  21. //4 赋予材质
  22. mat.mainTexture = tex;
  23. ////////////////////////////////////////
  24. //    Compute Shader
  25. ////////////////////////////////////////
  26. //1 找到compute shader中所要使用的KernelID
  27. int k = shader.FindKernel ("CSMain");
  28. //2 设置贴图    参数1=kid  参数2=shader中对应的buffer名 参数3=对应的texture, 如果要写入贴图,贴图必须是RenderTexture并enableRandomWrite
  29. shader.SetTexture (k, "Result", tex);
  30. //3 运行shader  参数1=kid  参数2=线程组在x维度的数量 参数3=线程组在y维度的数量 参数4=线程组在z维度的数量
  31. shader.Dispatch (k, 256 / 8, 256 / 8, 1);
  32. }
  33. }
Compute Shader
  1. //1 定义kernel的名称
  2. #pragma kernel CSMain
  3. //2 定义buffer
  4. RWTexture2D Result;
  5. //3 kernel函数
  6. //组内三维线程数
  7. [numthreads(8,8,1)]
  8. void CSMain (uint3 id : SV_DispatchThreadID)
  9. {
  10. //给buffer赋值
  11. //纯红色
  12. //Result[id.xy] = float4(1,0,0,1);
  13. //基于uv的x给颜色
  14. float v = id.x/256.0f;
  15. Result[id.xy] = float4(v,0,0,1);
  16. }
[例二: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
  1. #pragma kernel CSMain
  2. struct PBuffer
  3. {
  4. float life;
  5. float3 pos;
  6. float3 scale;
  7. float3 eulerAngle;
  8. };
  9. RWStructuredBuffer buffer;
  10. float deltaTime;
  11. [numthreads(2,2,1)]
  12. void CSMain (uint3 id : SV_DispatchThreadID)
  13. {
  14. int index = id.x + id.y * 2 * 2;
  15. buffer[index].life -= deltaTime;
  16. buffer[index].pos = buffer[index].pos + float3(0,deltaTime,0);
  17. buffer[index].scale = buffer[index].scale;
  18. buffer[index].eulerAngle = buffer[index].eulerAngle + float3(0,20*deltaTime,0);
  19. }
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的更多相关文章

  1. Linux性能优化实战学习笔记:第二十八讲

    一.案例环境描述 1.环境准备 2CPU,4GB内存 预先安装docker sysstat工具 apt install docker.io sysstat nake git 案例总共由三个容器组成: ...

  2. MySQL实战45讲学习笔记:第二十八讲

    一.读写分离架构 在上一篇文章中,我和你介绍了一主多从的结构以及切换流程.今天我们就继续聊聊一主多从架构的应用场景:读写分离,以及怎么处理主备延迟导致的读写分离问题. 我们在上一篇文章中提到的一主多从 ...

  3. 趣谈Linux操作系统学习笔记:第二十八讲

    一.引子 磁盘→盘片→磁道→扇区(每个 512 字节) ext* 定义文件系统的格式 二.inode 与块的存储 1.块 2.不用给他分配一块连续的空间 我们可以分散成一个个小块进行存放 1.优点 2 ...

  4. NeHe OpenGL教程 第二十八课:贝塞尔曲面

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  5. SQL注入之Sqli-labs系列第二十八关(过滤空格、注释符、union select)和第二十八A关

    开始挑战第二十八关(Trick with SELECT & UNION) 第二十八A关(Trick with SELECT & UNION) 0x1看看源代码 (1)与27关一样,只是 ...

  6. 第二十八个知识点:什么是公钥密码学的IND-CCA安全定义?

    第二十八个知识点:什么是公钥密码学的IND-CCA安全定义? 我们将在这篇博客中讨论公钥加密的IND-CCA安全. IND-CCA安全代表选择明文的不可伪造性.这样的安全方案的思想就是给定一个密文,攻 ...

  7. 第二十八条:利用有限制通配符来提升API的灵活性

    如第二十五条所述,参数化类型是不可变的.类型Type1和Type2而言,不管Type1与Type2的关系,List<Type1>既不是List<Type2>的子类型,也不是也不 ...

  8. Android UI开发第二十八篇——Fragment中使用左右滑动菜单

    Fragment实现了Android UI的分片管理,尤其在平板开发中,好处多多.这一篇将借助Android UI开发第二十六篇——Fragment间的通信. Android UI开发第二十七篇——实 ...

  9. MySQL实战45讲学习笔记:第二十六讲

    一.引子 在上一篇文章中,我和你介绍了几种可能导致备库延迟的原因.你会发现,这些场景里,不论是偶发性的查询压力,还是备份,对备库延迟的影响一般是分钟级的,而且在备库恢复正常以后都能够追上来. 但是,如 ...

随机推荐

  1. 九度OJ 1111:单词替换 (查找)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:4473 解决:1268 题目描述: 输入一个字符串,以回车结束(字符串长度<=100).该字符串由若干个单词组成,单词之间用一个空格隔 ...

  2. ByteBuf(图解1)

    目录 源码工程 写在前面 Netty ByteBuf 优势 手动获取与释放ByteBuf 自动获取和释放 ByteBuf 方式一:TailHandler 自动释放 方式二:SimpleChannelI ...

  3. Vue 单页面应用 SEO SPA single page application advantages and disadvantages

    处理 Vue 单页面应用 SEO 的另一种思路 - muwoo - 博客园 https://www.cnblogs.com/tiedaweishao/p/7493971.html SPA网站SEO完美 ...

  4. Machine Learning in Action(3) 朴素贝叶斯算法

    贝叶斯决策一直很有争议,今年是贝叶斯250周年,历经沉浮,今天它的应用又开始逐渐活跃,有兴趣的可以看看斯坦福Brad Efron大师对其的反思,两篇文章:“Bayes'Theorem in the 2 ...

  5. php设计模式之--组合模式

    php组合模式主要用于上下级关系,可以新增叶子和树枝,分析如下代码即可明白组合模式的含义: <?php header('Content-Type:text/html;charset=utf-8' ...

  6. ThinkPHP Widget模块开发流程

    初识ThinkPHP的Widget,现把模块开发的流程发布如下,也方便以后自己查阅: 一.新建数据库表self_modules,sql代码如下 CREATE TABLE `self_modules` ...

  7. Vue中的methods、watch、computed

    看到这个标题就知道这篇文章接下来要讲的内容,我们在使用vue的时候methods.watch.computed这三个特性一定经常使用,因为它们是非常的有用,但是没有彻底的理解它们的区别和各自的使用场景 ...

  8. chrome最小字体12px

    http://www.mamicode.com/info-detail-512021.html http://www.divcss5.com/wenji/w738.shtml

  9. POJ1006 Biorhythms —— 中国剩余定理

    题目链接:https://vjudge.net/problem/POJ-1006 Biorhythms Time Limit: 1000MS   Memory Limit: 10000K Total ...

  10. codeforces B. Trees in a Row 解题报告

    题目链接:http://codeforces.com/problemset/problem/402/B 题目意思:给出n个数和公差k,问如何调整使得ai + 1 - ai = k.(1 ≤ i < ...