一日一Shader·CS版点阵图【CS_0】

突然发觉,要绘制一堆点,好像有另一种方式——ComputeShader。这个之前有研究过,但是一直没怎么用过,不过作为在GPU上计算的渲染方式,倒是挺适合做统一规则的大量元素的绘制。

今天就先试着将昨天的点阵图用CS的方式实现看看,顺便就当作是温习一下知识点。

先看鼓捣出来的一堆效果:

其实要用CS做图还是比较麻烦的,因为涉及的文件挺多的,而且都很重要。

先从shader脚本开始说起,因为它是最简单的。这个脚本是图像渲染的最后一步,即图像数据传入该脚本后,该脚本负责渲染。其中关键是StructuredBuffer数据,里面装的是经过compute脚本处理后的数据。

Shader "MyShader/CS_SS_0"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Pass
        {
            CGPROGRAM            
            #pragma target 5.0
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            fixed4 _Color;
            StructuredBuffer<float3> points;//由cs脚本传入值,结构化的颜色缓存
            struct appdata
            {
                  uint id:SV_VERTEXID;//每个点都有指定的id
            };
            struct v2f
            {
                  float4 pos:SV_POSITION;
                  float3 uv :TEXCOORD0;
            };
            v2f vert(appdata v){
                  v2f o;
                  o.pos=mul(UNITY_MATRIX_VP,float4(points[v.id],1));
                  return o;
            }
            fixed4 frag(v2f i):SV_Target{                                 
                  return _Color;
            }                    
            ENDCG
        }
    }
}

然后说c#脚本,它是负责管理各文件数据的,比如创建材质、将compute脚本处理后的数据传入材质等:

using UnityEngine;
public class CS_0 : MonoBehaviour
{
    public ComputeShader comShader;
    private Material mat;
    private ComputeBuffer buffer;
    public int size = 1;
    private int number;//点的数量
    private int kernelId;
    public MeshTopology meshType;
    public float twirl = 0.001f;
    private float angle;
    public Color color;
    private void Start()
    {
        mat = new Material(Shader.Find("MyShader/CS_SS_0"));
        mat.SetColor("_Color", color);
        number = 512 * 8 * 8 * 8;
        buffer = new ComputeBuffer(number, 12);//初始化缓存区,12是指结构体points的字节长度,float=4,float3=12
        kernelId = comShader.FindKernel("CS_0");//找到入口的id
    }
    private void OnRenderObject()
    {
        comShader.SetBuffer(kernelId, "Result", buffer);//根据id,将缓存区关联上
        comShader.Dispatch(kernelId, size, size, size);//派遣size组的点,当size=1时,即刚好为CS_0的numthreads范围   
        mat.SetPass(0);//打开材质球
        mat.SetBuffer("points", buffer);

        Graphics.DrawProceduralNow(meshType, number);//绘制到现有的材质球上(会自动找到打开的材质球)MeshTopology有好几种,一般用点或线;DrawProceduralNow函数是将打开的材质绘制到GPU上
        angle += Time.deltaTime;
        if (angle > 360) angle -= 360;
        comShader.SetFloat("angle", angle);
        comShader.SetFloat("twirl", twirl);
    }
    private void OnDestroy()
    {
        buffer.Release();//释放
    }
}

最后说compute文件,这就是将在GPU中计算的脚本:

#pragma kernel CS_0
float angle;
float twirl;
RWStructuredBuffer<float3>Result;
struct points{
    float3 pos;
};
inline uint Index(uint3 id)
{
       return id.x + id.y *64  + id.z *64*8*8;//根据每个点的id(x,y,z)计算出来:每一层有8*8个点,y加1就要加64位,z加1就要加64*8位,另外还要乘numthreads最后一位,为什么呢?
}
inline float3 Rot(float3 pos)
{      
      float xyz=angle*(pos.x+pos.y+pos.z-pos.x*pos.y*pos.z)*twirl;
      float s=sin(xyz);
      float c=cos(xyz);
      float3x3 m=float3x3 (
           s,c,0,
           s,-c,0,
           0,0,1
      );
      return mul(m,pos);
      // return float3(s*pos.z,c*pos.z,pos.z*c/s);
}
[numthreads(8,8,8)]//一组空间的范围,即每次传值都是渲染一组
void CS_0(uint3 id : SV_DispatchThreadID,uint3 TID:SV_GROUPTHREADID)
{
       uint f=Index(id);      
       Result[f]=Rot(id);//返回计算后的点     
}

关于这块的脚本,用的是HLSL语言,用着跟CG差不多。我只是用矩阵变换的方式做了一下旋转,其实还有好多顶点计算方式。今天就先到这,明天可以继续研究一下CS。

最后放上我设置的参数:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值