博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Unity3D教程宝典之Shader篇:第二十八讲Compute Shaders
阅读量:6327 次
发布时间:2019-06-22

本文共 6202 字,大约阅读时间需要 20 分钟。

转载自

 

                            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演示
  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();
    }
}

 

 

推荐一个you tube的 ComputeShader效果个人空间(手动去掉链接中的空格)

https://www.you tube.com/user/iz3qb3

 

参考:

《Introduction_to_3D_Game_Programming_with_Directx_11》

你可能感兴趣的文章
图片时钟
查看>>
Unity-2017.3官方实例教程Space-Shooter(一)
查看>>
makefile中重载与取消隐藏规则示例
查看>>
Spring知识点小结(一)
查看>>
Linux 内核版本号查看
查看>>
4-3 简单求和 (10分)
查看>>
Python环境部署
查看>>
【BZOJ1085】【SCOI2005】骑士精神 [A*搜索]
查看>>
【Java多线程】JUC包下的工具类CountDownLatch、CyclicBarrier和Semaphore
查看>>
【git】error: Your local changes to the following files
查看>>
LeetCode – Refresh – Binary Tree Level Order Traversal ii
查看>>
夜间模式的开启与关闭,父模板的制作
查看>>
EMMA 覆盖率工具
查看>>
WPF中获取系统本身自带的控件模板(XAML)
查看>>
Aircrack-ng官方文档翻译[中英对照]---Aireplay-ng
查看>>
cxImage控件使用
查看>>
js返回顶部
查看>>
手机测试用例-时钟测试用例
查看>>
Hamming校验码
查看>>
第六十一课、智能指针类模板
查看>>