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. oracle 10g 数据库与客户端冲突导致实例创建无监听问题

    同事在oracle 10g上创建一个实例,快结束时弹出一个错误,提示监听失败之类.查看服务,并无生成监听服务.于是删除重来,一连试了好几次,都是如此. 这真是令人心烦意乱.提示里面有说到端口1521, ...

  2. sanic官方文档解析之Deploying(部署)和Extension(扩展)

    1,Deploying(部署) 通过内置的websocket可以很简单的部署sanic项目,之后通过实例sanic.Sanic,我们可以运行run这个方法通过接下来的关键字参数 host (defau ...

  3. eclipse中怎么删除重复的console

    eclipse中不同的应用会开启不同的console,所以并不是重复. 如图: Terminate标志/操作按钮,可以停止当前的执行,以及标志此Console是Terminated状态: Remove ...

  4. JavaScript精粹

    序:好书是需要不断品读的!再读语言精粹,顺便做点笔记. 1.NaN是一个数值,它表示一个不能产生正常结果的运算结果.NaN不等于任何值,包括它自己.检测是否为NaN:  isNaN(number). ...

  5. LightOJ1259 Goldbach`s Conjecture —— 素数表

    题目链接:https://vjudge.net/problem/LightOJ-1259 1259 - Goldbach`s Conjecture    PDF (English) Statistic ...

  6. Js中获取显示器、浏览器以及窗口等的宽度与高度的方法

    网页可见区域宽:document.body.clientWidth 网页可见区域高:document.body.clientHeight 网页可见区域宽:document.body.offsetWid ...

  7. 写给精明Java开发者的测试技巧

    我们都会为我们的代码编写测试,不是吗?毫无疑问,我知道这个问题的答案可能会从 “当然,但你知道怎样才能避免写测试吗?” 到 “必须的!我爱测试”都有.接下来我会给你几个小建议,它们可以让你编写测试变得 ...

  8. MYSQL进阶学习笔记十七:MySQL定期维护!(视频序号:进阶_36)

    知识点十八:MySQL定期维护(37) 一.Mysql的定时器 所谓的定时器,指的是在某个时间段去执行同样的代码.比如闹钟.每到指定的时间闹铃就会响.同样的,我们这个定时器,只要满足我们的一个定时条件 ...

  9. 通过dom4j写.xml文件

    步骤: 1.左键选中src,点击红圈2: 2.新建类: 3.开始写代码: package com.bjsxt.xml; import java.io.File; import java.io.File ...

  10. codeforces 463A Caisa and Sugar 解题报告

    题目链接:http://codeforces.com/problemset/problem/463/A 题目意思:某个人有 s dollar的钱,有 n 种类型的糖果,第 i 种糖果的价值为 xi d ...