zoukankan      html  css  js  c++  java
  • ComputeShader基础用法系列之二

    上一节我们介绍了如何让一个最简单的ComputeShader跑起来并且使我们看到效果。接下来我们看看如何向ComputeShader中传值以及如何回读到CPU。

    ComputeShader类提供了一些传值方法,基础类型如SetFloat,SetInt,SetVector,SetMatrix,集合如SetMatrixArray,SetFloats,SetInts,SetBuffer等。我们具体看一下怎么使用。

    在我们上一节compute文件基础上,在RWTexture2D<float4> Result;下声明一个float4 color;如下:

    #pragma kernel CSMain
    RWTexture2D<float4> Result;
    float4 color;
    
    [numthreads(8,8,1)]
    void CSMain (uint3 id : SV_DispatchThreadID)
    {
        Result[id.xy] = color;
    }

    然后再C#中SetVector,如下:

    public ComputeShader compute;
        // Start is called before the first frame update
        public Renderer renderer;
    
        public Color color;
        private int kernel;
        void Start()
        {
            kernel = compute.FindKernel("CSMain");
            RenderTexture rt = new RenderTexture(512,512,32);
            rt.enableRandomWrite = true;
            rt.Create();
            compute.SetTexture(kernel,"Result",rt);
            renderer.material.SetTexture("_BaseMap",rt);
        }
    
        // Update is called once per frame
        void Update()
        {
            compute.SetVector("color",color);
            compute.Dispatch(kernel,64,64,1);
        }

    当我们不断的在CPU传入不同的颜色值时,贴图的颜色就会发生变化:

    在举个传float的例子,我们加入一个时间变量,在Compute文件中加入time属性,返回结果修改为如下:

    Result[id.xy] = color + sin(time).xxxx;

    c#中:

    compute.SetFloat("time",Time.time);

    纹理就会随着时间不断变化颜色:

     如果我们要传一个集合呢,比如说我们要做一个颜色列表,让GPU根据线程ID取不同的颜色值,可以如下(color可以隐式转换为vec4,但是color数组不行,需要手动转换成vec4数组):

    compute.SetVectorArray("colors",colorVectors);

    compute文件中如下:

    #pragma kernel CSMain
    RWTexture2D<float4> Result;
    float4 colors[10];
    float time;
    [numthreads(8,8,1)]
    void CSMain (uint3 id : SV_DispatchThreadID)
    {
        int colorIndex = id.x%10;
        Result[id.xy] = colors[colorIndex] + sin(time).xxxx;
    }

    最后结果如下:

     为了进行下面的内容,我们先做一个例子,通过ComputeShader进行物体的Transform设置,这里面涉及到传入数据到GPU以及回读到CPU。

    Transform这种结构数据需要使用StructuredBuffer类型进行传递,因为需要读写,所以我们用RWStructuredBuffer。但是我们会发现ComputeShader类只给了设置数据的方法,但是没有返回数据到CPU的方法。这时我们就需要使用ComputeBuffer进行数据传递和回读。

    c#代码如下:

    using UnityEngine;
    
    public class ComputeShaderTest : MonoBehaviour
    {
        struct InstanceData
        {
            public Vector3 position;
            public Vector3 rotation;
            public Vector3 scale;
        }
        public ComputeShader compute;
        public GameObject prefab;
        public int count = 32;
        // Start is called before the first frame update
        private int kernel;
        void Start()
        {
            kernel = compute.FindKernel("CSMain");
            ComputeBuffer cb = new ComputeBuffer(count,36);
            var instanceData = new InstanceData[count];
            cb.SetData(instanceData);
            compute.SetBuffer(kernel,"data",cb);
            compute.Dispatch(kernel,4,1,1);
            cb.GetData(instanceData);
            for (int i = 0; i < count; i++)
            {
                var go = GameObject.Instantiate(prefab);
                go.transform.position = instanceData[i].position;
                go.transform.localEulerAngles = instanceData[i].rotation;
                go.transform.localScale = instanceData[i].scale;
            }
            cb.Release();
        }
    }

    compute文件如下:

    // Each #kernel tells which function to compile; you can have many kernels
    #pragma kernel CSMain
    struct InstanceData{
        float3 position;
        float3 rotation;
        float3 scale;
    };
    RWStructuredBuffer<InstanceData> data;
    [numthreads(8,1,1)]
    void CSMain (uint3 id : SV_DispatchThreadID)
    {
        int idx = id.x;
        data[idx].position = float3(id.x,0,0);
        data[idx].rotation = float3(id.x,0,0);
        data[idx].scale = float3(idx,idx,idx);
    }

    最后效果如图:

     可以看到我们通过ComputeBuffer的SetData和GetData传递和回读数据,需要注意的是ComputeBuffer是有大小限制的,大小为不能超过2048的、4的倍数的一个自然数。

     我们最后看一下传递float数组的例子,根据一个float数组划线:

    c#代码如下:

    using UnityEngine;
    
    public class ComputeShaderTest : MonoBehaviour
    {
        public ComputeShader compute;
        // Start is called before the first frame update
        public Renderer renderer;
        private int kernel;
        void Start()
        {
            float[] fArray = new float[]
            {
               1, 1, 0, 1, 0, 0, 0, 1,
            };
            kernel = compute.FindKernel("CSMain");
            RenderTexture rt = new RenderTexture(512,512,32);
            rt.enableRandomWrite = true;
            rt.Create();
            compute.SetTexture(kernel,"Result",rt);
            compute.SetFloats("fValues",fArray);
            compute.Dispatch(kernel,64,64,1);
            renderer.material.SetTexture("_BaseMap",rt);
        }
    }

    compute代码如下:

    #pragma kernel CSMain
    RWTexture2D<float4> Result;
    float fValues[8];
    
    [numthreads(8,8,1)]
    void CSMain (uint3 id : SV_DispatchThreadID)
    {
        uint idx = id.x;
        float colorFactor = fValues[idx%8];
        Result[id.xy] = float4(colorFactor,colorFactor, colorFactor, 0);
    }

    结果如下:

     我们看到computeshader的结果并没有根据我们传入的结果进行分布,而是隔了8个像素画一次。那么问题出在了哪里呢?因为内存对齐!

    我们设置float数组必须满足内存对齐16字节,因为GPU运算单元是4-component的,是基于向量运算的(即float4),所以我们传入的float数组(长度为8)到GPU的时候,只有第1个和第5个元素会被当做float值,其他元素都当做补齐元素了,这样就导致了数据丢失。而最后的结果之所以是8个像素一条线,是因为我们再compute shader中声明的float数组是8长度的。但是我们只向GPU传入了2长度。所以剩下的会被自动补0。那么我们就必须修改CPU传入数组为:

    float[] fArray = new float[]
            {
               1, 0,0,0,
               1,0,0,0,
               0,0,0,0,
               1,0,0,0,
               0,0,0,0,
               0,0,0,0,
               0,0,0,0,
               1,0,0,0
            };

    结果如下:

     这样结果就正确了。接下来我们会学习基于ComputeShader的一些用法和注意的问题。

  • 相关阅读:
    ArcGIS Pro 简明教程(2)基础操作和简单制图
    ArcGIS Pro 简明教程(1)Pro简介
    ArcGIS Pro 简明教程(3)数据编辑
    LocaSpaceViewer深度讲解(一)瓦片服务与数据下载
    迭代最近点算法 Iterative Closest Points
    应用Fast ICP进行点集或曲面配准 算法解析
    八叉树-OcTree
    2016年10月校招体会
    静态代码块&非静态代码块&构造函数
    avtivity与view
  • 原文地址:https://www.cnblogs.com/shenyibo/p/13755718.html
Copyright © 2011-2022 走看看