Point Cloud Compute Shader

Hello,

I’m completely new to (compute) shaders and running into some issues trying to render a point cloud utilizing the GPU. With some googling and tinkering I’ve gathered together some code which doesn’t compile :frowning:

What I have is 2 array’s, one ushort[ ] with depth data and one color32[ ] with rgba data that i wish to render (and update every frame). The issue with compilation through some googling seems to perhaps be related to how i’m trying to have two StructuredBuffer’s?

If anyone has any idea i’d be glad to hear about it xD Pointers are also welcome!

Shader "PointCloud"
{
    Properties
    {
        width("Width", int) = 800
        height("Height", int) = 600
    }
    SubShader
    {
        Pass
        {
            ZTest Always
            CGPROGRAM        
            #pragma vertex vertex_shader
            #pragma fragment pixel_shader
            #pragma target 5.0

            #include "UnityCG.cginc"

            StructuredBuffer<float4> colors;
            StructuredBuffer<half> points;
            int width, height;
           
            struct type
            {
                float4 color : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            type vertex_shader(uint id : SV_VertexID)
            {
                type vs;
                half position = points[id];
                float4 rgba = colors[id];

                float centerX = width * 0.5;
                float centerY = height * 0.5;

                int posX = id % width;
                int posY = id / width;

                float z = position * 0.001;
                float x = (posX - centerX - 0.5) * z * 0.002;
                float y = (posY - centerY - 0.5) * z * 0.002;

                vs.color = rgba;
                vs.vertex = UnityObjectToClipPos(float4(float3(x, y, z), 1.0));
                return vs;
            }

            fixed4 frag(type i) : SV_Target
            {
                fixed4 col = colors[i];
                return col;
            }
           
            ENDCG
        }
    }
}
// https://github.com/przemyslawzaworski
using UnityEngine;

public class PointCloud : MonoBehaviour
{
    public Material material;
    protected int number = 512 * 512;
    protected ComputeBuffer depthbuffer;
    protected ComputeBuffer colorbuffer;

    protected float[] depth;
    protected Vector4[] color;

    static readonly float[,] m = new float[,] { {0.8f,0.01f}, {0.01f,0.8f}};  
   
    float hash (Vector2 p )   //generates pseudorandom number from (0..1) range
    {
        return Mathf.Abs( (Mathf.Sin( p.x*12.9898f+p.y*78.233f )  * 43758.5453f) % 1);
    }
   
    float lerp (float a,float b, float t)
    {
        return Mathf.Lerp(a,b,t);
    }

    float noise(Vector2 p)   //makes random tiles with bilinear interpolation to create smooth surface
    {
        Vector2 i = new Vector2(Mathf.Floor(p.x),Mathf.Floor(p.y));
        Vector2 u = new Vector2 (Mathf.Abs (p.x % 1),Mathf.Abs (p.y % 1));
        u = new Vector2 (u.x*u.x*(3.0f-2.0f*u.x),u.y*u.y*(3.0f-2.0f*u.y));
        Vector2 a = new Vector2 (0.0f,0.0f);
        Vector2 b = new Vector2 (1.0f,0.0f);
        Vector2 c = new Vector2 (0.0f,1.0f);
        Vector2 d = new Vector2 (1.0f,1.0f);
        float r = lerp(lerp(hash(i+a),hash(i+b),u.x),lerp(hash(i+c),hash(i+d),u.x),u.y);
        return r*r;
    }
           
    float fbm( Vector2 p )   //deforms tiles to get more organic looking surface
    {
        float f = 0.0f;
        f += 0.5000f*noise( p ); p = p*2.02f;  p = new Vector2(p.x*m[0,0]+p.y*m[0,1],p.x*m[1,0]+p.y*m[1,1]);
        f += 0.2500f*noise( p ); p = p*2.03f;  p = new Vector2(p.x*m[0,0]+p.y*m[0,1],p.x*m[1,0]+p.y*m[1,1]);
        f += 0.1250f*noise( p ); p = p*2.01f;  p = new Vector2(p.x*m[0,0]+p.y*m[0,1],p.x*m[1,0]+p.y*m[1,1]);
        f += 0.0625f*noise( p );
        return f/0.9375f;
    }

    void Start ()
    {
        float r = Random.Range(0.0f,100.0f);
        depthbuffer = new ComputeBuffer(number, sizeof(float), ComputeBufferType.Default);
        colorbuffer = new ComputeBuffer(number, sizeof(float) * 4, ComputeBufferType.Default);
        depth = new float[number];
        color = new Vector4[number];
        int i = 0;
        for (int y = 0; y < 512; y++)
        {
            for (int x = 0; x < 512; x++)
            {
                Vector2 resolution = new Vector2 (512,512); 
                Vector2 coordinates = new Vector2 ((float)x,(float)y);
                Vector2 uv = new Vector2( (2.0f*coordinates.x-resolution.x)/resolution.y+1.0f,(2.0f*coordinates.y-resolution.y)/resolution.y +1.0f );
                ushort h = System.Convert.ToUInt16((fbm(new Vector2(uv.x*5.0f+r,uv.y*5.0f+r))+0.1f) * ushort.MaxValue);
                depth[i] = (float) ((float)h / (float)ushort.MaxValue);
                if (depth[i]<0.1)
                    color[i] = new Vector4(0.77f,0.90f,0.98f,1.0f);
                else
                if (depth[i]<0.2)
                    color[i] = new Vector4(0.82f,0.92f,0.99f,1.0f);
                else
                if (depth[i]<0.3)
                    color[i] = new Vector4(0.91f,0.97f,0.99f,1.0f);
                else
                if (depth[i]<0.45)
                    color[i] = new Vector4(0.62f,0.75f,0.59f,1.0f);
                else
                if (depth[i]<0.55)
                    color[i] = new Vector4(0.86f,0.90f,0.68f,1.0f);
                else
                if (depth[i]<0.65)
                    color[i] = new Vector4(0.99f,0.99f,0.63f,1.0f);
                else
                if (depth[i]<0.75)
                    color[i] = new Vector4(0.99f,0.83f,0.59f,1.0f);
                else
                if (depth[i]<0.90)
                    color[i] = new Vector4(0.98f,0.71f,0.49f,1.0f);    
                else
                if (depth[i]<0.95)
                    color[i] = new Vector4(0.98f,0.57f,0.47f,1.0f);       
                else     
                    color[i] = new Vector4(0.79f,0.48f,0.43f,1.0f);
                i++;
            }
        }
        depthbuffer.SetData(depth);
        colorbuffer.SetData(color);
    }
   
    void OnPostRender()
    {
        material.SetPass(0);
        material.SetBuffer("depthbuffer", depthbuffer);
        material.SetBuffer("colorbuffer", colorbuffer);
        Graphics.DrawProcedural(MeshTopology.Points, number, 1);
    }
    void OnDestroy()
    {
        depthbuffer.Release();
        colorbuffer.Release();
    }
}
Shader "Point Cloud"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM        
            #pragma vertex VSMain
            #pragma fragment PSMain
            #pragma target 5.0

            StructuredBuffer<float> depthbuffer;
            StructuredBuffer<float4> colorbuffer;
           
            struct shaderdata
            {
                float4 vertex : SV_POSITION;
                float4 color : TEXCOORD1;
            };

            shaderdata VSMain(uint id : SV_VertexID)
            {
                shaderdata vs;
                float depth = depthbuffer[id];
                float factor = 512;
                float u = fmod (id, factor) / factor - 0.5;
                float v = floor (id / factor) / factor - 0.5;
                vs.vertex = UnityObjectToClipPos(float4(4.0*u, depth, 4.0*v, 1.0));
                vs.color = colorbuffer[id];
                return vs;
            }

            float4 PSMain(shaderdata ps) : SV_TARGET
            {
                return ps.color;
            }
           
            ENDCG
        }
    }
}

I suppose you fill buffers with data extracted from files ? Here is an example with depth and color data generated procedurally (hypsometric map rendered as point cloud). Convert your ushort to float array.