• ComputeShader基础用法系列之一


    Compute Shader是Unity5.0之后推出的功能,主要的作用就是利用GPU的大规模并行计算的特性进行一些适合大规模数据的计算,即SIMD(单指令多数据)模式。

    在编写Compute Shader之前,首先要了解Compute Shader的基本原理。

    传统的Shader编程基本上都是在渲染管线的框架中进行的,而Compute Shader是一段独立的GPU程序,不需要借助渲染管线的框架。但这也意味着更加灵活,需要掌握更偏向底层的一些知识。在Shader编程中,我们无需指定shader使用多少线程,但是再Compute Shader中则需要。GPU线程组结构如下图(这里白嫖一张图):

     线程组是由一层套娃操作形成的,外层的三个维度的线程组里面又套了一个三维度的线程组,外面一层的线程的指定由Dispatch时候传入的参数决定,而内层的线程指定由numthreads()方法指定。

    Compute Shader使用的语言用hlsl就可以。一下先给出一个简单的例子来描述Compute Shader原理。

    Compute Shader文件主要由一下组成部分:

    1.Kernel,声明ComputeShader的接口函数(即宿主程序调用的函数)

    2.定义变量

    3.实现Kernel指定的方法,如下:

    // Each #kernel tells which function to compile; you can have many kernels
    #pragma kernel CSMain
    
    // Create a RenderTexture with enableRandomWrite flag and set it
    // with cs.SetTexture
    RWTexture2D<float4> Result;
    
    [numthreads(8,8,1)]
    void CSMain (uint3 id : SV_DispatchThreadID)
    {
        Result[id.xy] = float4(id.xy,0.0, 0.0);
    }

    任何GPU程序都是需要一个宿主程序(即CPU程序)的,在Unity中通过ComputeShader类进行操作,compute是一个ComputeShader类的对象:

    int kernel = compute.FindKernel("CSMain");
    compute.Dispatch(kernel,32,32,1);

    FindKernel中找的就是ComputeShader中的接口函数名字。

    为了方便显示执行效果,我们申请一张RenderTexture查看执行结果,最后如下:

    void Start()
        {
            int kernel = compute.FindKernel("CSMain");
            RenderTexture rt = new RenderTexture(512,512,24);
            rt.enableRandomWrite = true;
            rt.Create();
            compute.SetTexture(kernel,"Result",rt);
            compute.Dispatch(kernel,32,32,1);
            display.material.SetTexture("_BaseMap",rt);
        }

    申请一张RT,然后设置到compute shader中,调用dispatch,最后将RT给到材质的纹理查看结果。

    结果如下:

     我们发现几个现象:

    1.图片的大小与写入的颜色范围不一致

    2.基本上看不到其他的颜色,在边缘处有很细的绿色线和红色线

    我们首先来看一下为什么写入的颜色范围有问题呢?这就得回到我们ComputeShader中的一个非常重要的东西:ThreadID。

    我们可以看到对于接口函数的声明:void CSMain (uint3 id : SV_DispatchThreadID) 中有个语义SV_DispatchThreadID,这个语义代表了id这个参数指的是当前执行程序的线程ID,id是一个uint3类型,分别制定了线程的xyz,这个xyz就是上面白嫖的那张图中的SV_DispatchThreadID。然后再看看我们ComputeShader是如何返回的:

    Result[id.xy] = float4(id.xy,0.0, 0.0);

    其中我们通过id.xy作为纹理纹素的序列写入到纹理当前执行的线程ID的xy,反应在颜色上就是RG通道,这就是为什么大面积黄色,xy在大面积部分都是>=1的,只有在边缘当x=0时,只剩G通道,所以是绿色,当y=0时,只剩R通道,所以是红色。而线条的宽度正体现了这个线程处理的范围——一个线程处理一个像素。

    那么这样也好理解为什么我们的颜色写入范围不对了,我们申请的RT大小是512*512的,而我们Dispatch的线程是32*32,ComputeShader中的线程是8*8,那么最后处理的范围就是32*8=256的范围,即512贴图的四分之一。所以当我们把Dispatch的线程指定为64时,就把范围纠正过来了,如下:

     这样,就可以进行一些简单的实际操作了,比如要隔64个像素画一条线,就可以如下:

    float2 uv = float2(id.x%64,id.y%64);
    Result[id.xy] = float4(uv,0.0, 0.0);

     如果画个圆,就算个中心距离中心的距离,如下:

    float2 uv = float2(id.x%64,id.y%64);
        float dis = 1-distance(uv,float2(32,32))/32.0;
        Result[id.xy] = float4(dis,0.0,0.0, 0.0);

    效果如下:

     具体其他的实现可以自己研究。之后会接着介绍ComputeShader的其他用法。

  • 相关阅读:
    旅行喵 React Native 技术实践
    微信、QQ这类IM App怎么做——谈谈Websocket
    IOS热更新-JSPatch实现原理+Patch现场恢复
    jquery 插件开发及extend
    JS Nice – JavaScript 代码美化和格式化工具
    ExtJS与jQuery的一点细节上的对比
    JQuery实现图片的预加载与延时加载
    十个实用但IE不支持的CSS属性
    如何通过预加载器提升网页加载速度
    遭遇Asp.Net长文件名下载的问题和解决办法
  • 原文地址:https://www.cnblogs.com/shenyibo/p/13712440.html
Copyright © 2020-2023  润新知