devNotes 8-31-16 Combine Compute and Geometry

Blend SrcAlpha OneMinusSrcAlpha // Traditional transparency
Blend One OneMinusSrcAlpha // Premultiplied transparency
Blend One One // Additive
Blend OneMinusDstColor One // Soft Additive
Blend DstColor Zero // Multiplicative
Blend DstColor SrcColor // 2x Multiplicative

SimpleEmitter.compute

#pragma kernel CSMain

float  dt;
float  time;
float  pi;

uint   maxParticles = 1024;
float  maxAge;

struct Particle 
{
    int         index;
    float3      position;
    float3      velocity; 
    float       size;
    float       age;
    float       normAge; 
    int         type;    
};

RWStructuredBuffer <Particle> particles;

[numthreads( 1, 1, 1 )]

void CSMain ( uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex )        
{
    uint index = DTid.x;

    if (index < maxParticles)
    {
        Particle p = particles[index];
        p.position.y = p.index; //just check if the index is correct by giving a Y position
        particles[index] = p;   
    }

}

SimpleEmitter.cs

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;

public class SimpleEmitter : MonoBehaviour 
{
   struct Particle 
    {
        public int          index;
        public Vector3      position;
        public Vector3      velocity;
        public float        size;
        public float        age;
        public float        normAge;
        public int          type;
    }

    public ComputeShader computeShader;
    public Material material;
    public int maxParticles = 1000000;
    public float maxAge = 3.0f; 
    public float particleSize = 0.5f;

    private ComputeBuffer particles;
    private int particleSizeOf;

    void Start () 
    {   
        Particle p = new Particle();
        particleSizeOf = Marshal.SizeOf(p);

        Particle[] pInitBuffer = new Particle[maxParticles];

        for (int i = 0; i < maxParticles; i++) 
        {
            p = new Particle();
            p.index = i;
            p.type = 0;            
            p.age = 0;
            p.normAge = 0.1f;
            p.size = particleSize * 0.5f + Random.value * particleSize;                     
            p.velocity = new Vector3(0, 0, 0);

            pInitBuffer[i] = p;
        }

        particles = new ComputeBuffer(maxParticles, particleSizeOf, ComputeBufferType.Default);
        particles.SetData(pInitBuffer);

        computeShader.SetBuffer(0, "particles", particles);
    }

    void Update() 
    {       
        computeShader.SetFloat("dt", Time.deltaTime);
        computeShader.SetFloat("time", Time.time);
        computeShader.SetFloat("pi", Mathf.PI);     
        computeShader.SetInt("maxParticles", maxParticles);                
        computeShader.SetFloat("maxAge", maxAge);

        computeShader.Dispatch(0, 64, 1, 1);            
    }

    public void OnPostRender() 
    {
        material.SetPass(0);
        material.SetFloat("maxAge", maxAge);
        material.SetBuffer("particles", particles);

        Graphics.DrawProcedural(MeshTopology.Triangles, maxParticles, 0);
    }

    void OnDisable() 
    {
        particles.Release();
    }
}

Geometry – Pixel – Fragment – Shader – Simple Emitter

Shader "Custom/SimpleRS" 
{
    Properties 
    {
        _ParticleTexture ("Diffuse Tex", 2D) = "white" {}
        _Ramp1Texture ("G_Ramp1", 2D) = "white" {}
    }

    SubShader 
    {
        Pass 
        {
            Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
            Blend OneMinusDstColor One
            Cull Off 
            Lighting Off 
            ZWrite Off 
            Fog { Color (0,0,0,0) }


            CGPROGRAM
            #pragma target 5.0
            #pragma vertex VSMAIN
            #pragma fragment PSMAIN
            #pragma geometry GSMAIN
            #include "UnityCG.cginc" 

            struct Particle 
            {
                int    index;
                float3 position;
                float3 velocity;
                float  size;
                float  age;
                float  normAge;
                int    type;

            };

            StructuredBuffer<Particle>  particles;

            Texture2D                   _ParticleTexture;           
            SamplerState                sampler_ParticleTexture;

            Texture2D                   _Ramp1Texture;
            SamplerState                sampler_Ramp1Texture;

            float maxAge;
            float maxRad;

            struct VS_INPUT
            {
                uint vertexid           : SV_VertexID;
            };
            //--------------------------------------------------------------------------------
            struct GS_INPUT
            {
                float4 position         : SV_POSITION;
                float size              : TEXCOORD0;
                float age               : TEXCOORD1;
                float type              : TEXCOORD2;
            };
            //--------------------------------------------------------------------------------
            struct PS_INPUT
            {
                float4 position         : SV_POSITION;
                float2 texcoords        : TEXCOORD0;
                float size              : TEXCOORD1;
                float age               : TEXCOORD2;
                float type              : TEXCOORD3;
            };
            //--------------------------------------------------------------------------------
            GS_INPUT VSMAIN( in VS_INPUT input )
            {
                GS_INPUT output;

                output.position.xyz = particles[input.vertexid].position;
                output.position.w = 1.0;                
                output.age = particles[input.vertexid].normAge;
                output.size = particles[input.vertexid].size;
                output.type = particles[input.vertexid].type;
                return output;
            }
            //--------------------------------------------------------------------------------
            [maxvertexcount(4)]
            void GSMAIN( point GS_INPUT p[1], inout TriangleStream<PS_INPUT> triStream )
            {           
                float4 pos = mul(UNITY_MATRIX_MVP, p[0].position);

                float halfS = p[0].size * 0.5f;
                float4 offset = mul(UNITY_MATRIX_P, float4(halfS, halfS, 0, 1));

                float4 v[4];
                v[0] = pos + float4(offset.x, offset.y, 0, 1);
                v[1] = pos + float4(offset.x, -offset.y, 0, 1);
                v[2] = pos + float4(-offset.x, offset.y, 0, 1);
                v[3] = pos + float4(-offset.x, -offset.y, 0, 1);

                PS_INPUT pIn;
                pIn.position = v[0];
                pIn.texcoords = float2(1.0f, 0.0f);

                    pIn.size = p[0].size;
                    pIn.age = p[0].age;
                    pIn.type = p[0].type;                       

                triStream.Append(pIn);

                pIn.position =  v[1];
                pIn.texcoords = float2(1.0f, 1.0f);
                triStream.Append(pIn);

                pIn.position =  v[2];
                pIn.texcoords = float2(0.0f, 0.0f);
                triStream.Append(pIn);

                pIn.position =  v[3];
                pIn.texcoords = float2(0.0f, 1.0f);
                triStream.Append(pIn);                  
            }
            //--------------------------------------------------------------------------------
            float4 PSMAIN( in PS_INPUT input ) : COLOR
            {
                float4 color = _ParticleTexture.Sample( sampler_ParticleTexture, input.texcoords );
                float4 tint = _Ramp1Texture.Sample(sampler_Ramp1Texture, float2(min(1.0, input.age),0));
                color *= tint;

                if (input.age == 0) discard;

                return color;
            }
            //--------------------------------------------------------------------------------
            ENDCG
        }
    } 
}

dfvfdfv

Particle – Pixel Version:

Particle.Shader

Shader "particle" 
{
	// Bound with the inspector.
 	Properties 
 	{
        _Color ("Main Color", Color) = (0, 1, 1,0.3)
        _SpeedColor ("Speed Color", Color) = (1, 0, 0, 0.3)
        _colorSwitch ("Switch", Range (0, 120)) = 60
    }

	SubShader 
	{
		Pass 
		{
			Blend SrcAlpha one

			CGPROGRAM
			#pragma target 5.0
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			
			// The same particle data structure used by both the compute shader and the shader.
			struct Particle
			{
				float3 position;
				float3 velocity;
			};
			
			// structure linking the data between the vertex and the fragment shader
			struct FragInput
			{
				float4 color : COLOR;
				float4 position : SV_POSITION;
			};
			
			// The buffer holding the particles shared with the compute shader.
			StructuredBuffer<Particle> particleBuffer;
			
			// Variables from the properties.
			float4 _Color;
			float4 _SpeedColor;
			float _colorSwitch;
			
			// DX11 vertex shader these 2 parameters come from the draw call: "1" and "particleCount", 
			// SV_VertexID: "1" is the number of vertex to draw peer particle, we could easily make quad or sphere particles with this.
			// SV_InstanceID: "particleCount", number of particles...
			FragInput vert (uint id : SV_VertexID, uint inst : SV_InstanceID)
			{
				FragInput fragInput;
				
				// color computation
				float speed = length(particleBuffer[inst].velocity);
				float lerpValue = clamp(speed / _colorSwitch, 0, 1);
				fragInput.color = lerp(_Color, _SpeedColor, lerpValue);
				
				// position computation
				fragInput.position = mul (UNITY_MATRIX_MVP, float4(particleBuffer[inst].position, 1));
				
				return fragInput;
			}
			
			// this just pass through the color computed in the vertex program
			float4 frag (FragInput fragInput) : COLOR
			{
				return fragInput.color;
			}
			
			ENDCG
		
		}
	}

	Fallback Off
}

MoveParticle.Compute

#pragma kernel main

// The same particle data structure used by both the compute shader and the shader.
struct Particle
{
	float3 position;
	float3 velocity;
};

// The buffer holding the particles shared with the regular shader.
RWStructuredBuffer<Particle> particleBuffer;

// parameters from GPU
float deltaTime;									// Even here deltaTime is needed!
float3 base;										// Base position.
float3 orbit;										// Orbit position.
float targetStrengh;								// Strengh, from the inspector!
float orbit_weighting;



[numthreads(32,1,1)] 								// 32 is the minimal size to fullfill the wrap. this is just the number of thread to run by wrap, "x, y, z" make easy 3D indexing.
void main (uint3 id : SV_DispatchThreadID)
{
	// particleBuffer[id.x] is the particle this thread must Update, according to the thread ID.
	
	// Direction and distance to target.
	float3 basedir1 = normalize((base - particleBuffer[id.x].position));
	float3 orbitdir2 = normalize((orbit - particleBuffer[id.x].position));

	float basedist1 = distance(base, particleBuffer[id.x].position);
    float orbitdist2 = distance(orbit, particleBuffer[id.x].position);
	
    float3 dir;
    float dist;
    float mult;

    if(basedist1 < orbitdist2) 
    {
        dist = basedist1;
        dir = basedir1;
        mult = orbit_weighting*2;
    }
    else
    {
        dist = orbitdist2;
        dir = orbitdir2;
        mult = (1 - orbit_weighting)*2;
    }


    if(dist > 5)
    {
        particleBuffer[id.x].velocity += mult * targetStrengh * dir * deltaTime / dist;
    }
    else
    {
        particleBuffer[id.x].velocity -= mult * targetStrengh * dir * deltaTime / dist;
    }
      
    particleBuffer[id.x].velocity = particleBuffer[id.x].velocity* 0.98f;
	particleBuffer[id.x].position += particleBuffer[id.x].velocity * deltaTime;
}

TestParticle.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;

public class TestParticle : MonoBehaviour 
{
	// The same particle data structure used by both the compute shader and the shader.
	struct Particle
	{
		public Vector3 position;
		public Vector3 velocity;
	};
	
	public static List<TestParticle> list;	// Static list, just to call Render() from the camera.
	
	public int warpCount = 10;				// The number particle /32.
	public float initialSpread = 0.1f;			// Initial speed to the center of the sphere.
	public float pPowerMag = 100;		// velocity change from the mouse position.
    public float orbit_weighting = 0.5f;	

	public Material material;				// Use "particle" shader.
	public ComputeShader computeShader;		// Use "moveParticle" compute shader.
	
	const int warpSize = 32; 				// GPUs process data by warp, 32 for every modern ones.
	int particleCount; 						// = warpSize * warpCount.
	
	ComputeBuffer particleBuffer;			// The GPU buffer holding the particules.

    public GameObject frisbee1;
    public GameObject orbitingSphere;


    public UI_Control ui_Control;

    void Start () 
	{
		// Just init the static list 
		if (list == null)
			list = new List<TestParticle>();
		list.Add(this);

		particleCount = warpCount * warpSize;
		
		// Init particles
		Particle[] particleArray = new Particle[particleCount];
		for (int i = 0; i < particleCount; ++i)
		{
			particleArray[i].position = Random.insideUnitSphere * initialSpread;
            particleArray[i].velocity = particleArray[i].position.normalized;
		}
		
		// Instanciate and initialise the GPU buffer.
		particleBuffer = new ComputeBuffer(particleCount, 24); // 24 = sizeof(Particle)
		particleBuffer.SetData(particleArray);
		
		// bind the buffer to both the compute shader and the shader.
		computeShader.SetBuffer(0, "particleBuffer", particleBuffer);
		material.SetBuffer ("particleBuffer", particleBuffer);
	}

	
	void Update () 
	{
		// Get the mouse position in the 3D space (a flat box collider catch the ray) .
		float[] targ1 = {0f, 0f, 0f };
        float[] targ2 = { 0f, 0f, 0f };

        Vector3 pos = frisbee1.transform.position;

        targ1[0] = pos.x;//BASE
        targ1[1] = pos.y;
        targ1[2] = pos.z;

        pos = orbitingSphere.transform.position;

        targ2[0] = pos.x;//ORBIT
        targ2[1] = pos.y;
        targ2[2] = pos.z;

        pPowerMag = ui_Control.pParticlePower;
        orbit_weighting = ui_Control.orbit_weighting;

        // Initialise the compute shader variables.
        computeShader.SetFloat("targetStrengh", pPowerMag);
		computeShader.SetFloat("deltaTime", Time.deltaTime);
		computeShader.SetFloats("base", targ1);
        computeShader.SetFloats("orbit", targ2);
        computeShader.SetFloats("orbit_weighting", orbit_weighting);


        // Start the compute shader (move every particle for this frame).
        computeShader.Dispatch(0, warpCount, 1, 1);
	}
	
	// Called by the camera in OnRender
	public void Render () 
	{
		// Bind the pass to the pipeline then call a draw (this use the buffer bound in Start() instead of a VBO).
		material.SetPass (0);
		Graphics.DrawProcedural (MeshTopology.Points, 1, particleCount);
	}
	
	
	void OnDestroy()
	{
		list.Remove(this);
		
		// Unity cry if the GPU buffer isn't manually cleaned
		particleBuffer.Release();
	}
}

BillboardGeomShader.shader

Shader "Charles Will Code It/Geom/Billboard"
{
	Properties
	{
		_Sprite("Sprite", 2D) = "white" {}
		_Color("Color", Color) = (1,1,1,1)
		_Size("Size", Vector) = (1,1,0,0)
		_Wind("Wind", Vector) = (0,0,0,0)
	}

	SubShader
	{
		Tags{ "Queue" = "Overlay+100" "RenderType" = "Transparent" }

		LOD 100
		Blend SrcAlpha OneMinusSrcAlpha
		
		Cull off
		ZWrite off

		Pass
		{

			CGPROGRAM
			#pragma target 5.0

			#pragma vertex vert			
			#pragma geometry geom
			#pragma fragment frag

			#pragma multi_compile_fog
			

			#include "UnityCG.cginc"

			sampler2D _Sprite;
			float4 _Color = float4(1,0.5f,0.0f,1);
			float2 _Size = float2(1,1);
			float3 _worldPos;
			float3 _Wind = float3(0, 0, 0);

			int _StaticCylinderSpherical = 0; // 0 = static 1 = cylinder 2 = spherical


			struct data {
				float3 pos;
			};

			//The buffer containing the points we want to draw.
			StructuredBuffer<data> buf_Points;

			struct input
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				UNITY_FOG_COORDS(1)
			};

			input vert(uint id : SV_VertexID)
			{
				input o;
				o.pos = float4(buf_Points[id].pos + _worldPos, 1.0f);
				return o;
			}

			float4 RotPoint(float4 p, float3 offset, float3 sideVector, float3 upVector)
			{
				float3 finalPos = p.xyz;

				finalPos += offset.x * sideVector;
				finalPos += offset.y * upVector;

				return float4(finalPos,1);
			}

			[maxvertexcount(4)]
			void geom(point input p[1], inout TriangleStream<input> triStream)
			{
				float2 halfS = _Size;

				float4 v[4];

				if (_StaticCylinderSpherical == 0)
				{
					v[0] = p[0].pos.xyzw + float4(-halfS.x, -halfS.y, 0, 0);
					v[1] = p[0].pos.xyzw + float4(-halfS.x, halfS.y, 0, 0);
					v[2] = p[0].pos.xyzw + float4(halfS.x,  -halfS.y, 0, 0);
					v[3] = p[0].pos.xyzw + float4(halfS.x,  halfS.y, 0, 0);
				}
				else
				{
					float3 up = normalize(float3(0, 1, 0) + (_Wind * -.5));
					float3 look = _WorldSpaceCameraPos - p[0].pos.xyz;

					if (_StaticCylinderSpherical == 1)
						look.y = 0;

					look = normalize(look);
					float3 right = normalize(cross(look, up));
					up = normalize(cross(right, look));

					v[0] = RotPoint(p[0].pos , float3(-halfS.x,-halfS.y,0), right,up);
					v[1] = RotPoint(p[0].pos , float3(-halfS.x,halfS.y,0), right,up);
					v[2] = RotPoint(p[0].pos , float3(halfS.x,-halfS.y,0), right,up);
					v[3] = RotPoint(p[0].pos , float3(halfS.x,halfS.y,0), right,up);
				}


				input pIn;

				pIn.pos = mul(UNITY_MATRIX_VP, v[0]);
				pIn.uv = float2(0.0f, 0.0f);
				UNITY_TRANSFER_FOG(pIn, pIn.pos);
				triStream.Append(pIn);

				pIn.pos = mul(UNITY_MATRIX_VP, v[1]);
				pIn.uv = float2(0.0f, 1.0f);
				UNITY_TRANSFER_FOG(pIn, pIn.pos);
				triStream.Append(pIn);

				pIn.pos = mul(UNITY_MATRIX_VP, v[2]);
				pIn.uv = float2(1.0f, 0.0f);
				UNITY_TRANSFER_FOG(pIn, pIn.pos);
				triStream.Append(pIn);

				pIn.pos = mul(UNITY_MATRIX_VP, v[3]);
				pIn.uv = float2(1.0f, 1.0f);
				UNITY_TRANSFER_FOG(pIn, pIn.pos);
				triStream.Append(pIn);
			}

			float4 frag(input i) : COLOR
			{
				fixed4 col = tex2D(_Sprite, i.uv) * _Color;

				UNITY_APPLY_FOG(i.fogCoord, col); // apply fog
			
				return col;
			}	

			ENDCG
		}
	}
	Fallback Off
}

BillboardGeomScript.cs

using UnityEngine;
using System.Collections;

public class BillboardGeomScript : MonoBehaviour
{
    public Shader geomShader;
    Material material;

    public Texture2D sprite;
    public Vector2 size = Vector2.one;
    public Color color = new Color(1.0f, 0.6f, 0.3f, 0.03f);

    ComputeBuffer outputBuffer;

    ComputeShaderOutput cso;
    WeatherShaderOutput wso;

    public Vector3 Wind;

    [Range(0,2)]
    [Tooltip("Billboard type 0 = static, 1 = Cylindrical, 2 = Sphecrical")]
    public int billboardType = 2;

    data[] points;

    public bool UseComputeData = false;

    struct data
    {
        public Vector3 pos;
    };

    // Use this for initialization
    void Start()
    {
        material = new Material(geomShader);

        points = new data[]
        {
            new data() { pos = Vector3.left + Vector3.up },
            new data() { pos = Vector3.right + Vector3.up },
            new data() { pos = Vector3.zero},
            new data() { pos = Vector3.left + Vector3.down },
            new data() { pos = Vector3.right + Vector3.down}
        };

        //points = new data[] { new data() { pos = Vector3.zero } };

        if (!UseComputeData)
        {
            outputBuffer = new ComputeBuffer(points.Length, 12);
            outputBuffer.SetData(points);
        }

    }

    void OnRenderObject()
    {
        if (UseComputeData)
        {
            cso = GetComponent<ComputeShaderOutput>();
            wso = GetComponent<WeatherShaderOutput>();

            if (outputBuffer == null && cso != null)
                outputBuffer = cso.outputBuffer;

            if (outputBuffer == null && wso != null)
                outputBuffer = wso.outputBuffer;

            if (cso != null)
                cso.Dispatch();
            else
            {
                wso.Dispatch();
                Wind = wso.wind;
            }
        }

        material.SetPass(0);
        material.SetColor("_Color", color);
        material.SetBuffer("buf_Points", outputBuffer);
        material.SetTexture("_Sprite", sprite);
        material.SetVector("_Size", size);
        material.SetInt("_StaticCylinderSpherical", billboardType);
        material.SetVector("_worldPos", transform.position);
        material.SetVector("_Wind", Wind);

        Graphics.DrawProcedural(MeshTopology.Points, outputBuffer.count);
    }

    void OnDestroy()
    {
        outputBuffer.Release();
    }
}

WeatherShaderOutput.cs

using UnityEngine;
using System.Collections;

[AddComponentMenu("Scripts/Charles Will Code It/Compute Shader Scripts/WeatherShaderOutput")]
public class WeatherShaderOutput : MonoBehaviour
{

    #region Compute Shader Fields and Properties

    /// <summary>
    /// The Compute shader we will use
    /// </summary>
    public ComputeShader computeShader;

    /// <summary>
    /// The total number of verticies to calculate. We are going to have 32 * 32  work groups, each processing 16 * 16 * 1 points.
    /// </summary>
    public const int VertCount = 32 * 32 * 16 * 16 * 1;

    /// <summary>
    /// The initial start position for each of the points to be calculated.
    /// </summary>
    ComputeBuffer startPointBuffer;
    /// <summary>
    /// This buffer will store the calculated data resulting from the Compute shader.
    /// </summary>
    public ComputeBuffer outputBuffer;
    /// <summary>
    /// This buffer is used to hold constant values for each point, in this case a value for time.
    /// </summary>
    ComputeBuffer constantBuffer;

    ComputeBuffer modBuffer;

    public Shader PointShader;
    Material PointMaterial;

    [Tooltip("Speed weather is falling at, 1 would be slow like snow, 10, rain")]
    [Range(1,200)]
    public float Speed = 1;

    [Tooltip("Does the weather element wobble, like snow?")]
    public bool Wobble = false;

    [Tooltip("Wind Direction")]
    public Vector3 wind = Vector3.zero;

    [Tooltip("Distance between weather elements")]
    public float spacing = 5;

    /// <summary>
    /// A Reference to the CS Kernel we want to use.
    /// </summary>
    int CSKernel;

    #endregion

    void InitializeBuffers()
    {
        // Set start point compute buffer
        startPointBuffer = new ComputeBuffer(VertCount, 4); // create space in the buffer for 3 float per point (float = 4 bytes)

        // Set const compute buffer size
        constantBuffer = new ComputeBuffer(1, 4);

        modBuffer = new ComputeBuffer(VertCount, 8);

        // Set output buffer size.
        outputBuffer = new ComputeBuffer(VertCount, 12);

        // These values will be the starting y coords for each point.
        float[] values = new float[VertCount];
        Vector2[] mods = new Vector2[VertCount];

        for (int i = 0; i < VertCount; i++)
        {
            values[i] = Random.value * 2 * Mathf.PI;
            mods[i] = new Vector2(.1f + Random.value, .1f + Random.value);
        }

        modBuffer.SetData(mods);

        // Load starting valuse into the compute buffer
        startPointBuffer.SetData(values);

        // Only need to set the offsets once in the CS
        computeShader.SetBuffer(CSKernel, "startPointBuffer", startPointBuffer);
    }

    public void Dispatch()
    {
        constantBuffer.SetData(new[] { Time.time * .01f });

        computeShader.SetBuffer(CSKernel, "modBuffer", modBuffer);
        computeShader.SetBuffer(CSKernel, "constBuffer", constantBuffer);
        computeShader.SetBuffer(CSKernel, "outputBuffer", outputBuffer);
        computeShader.SetFloat("Speed", Speed);
        computeShader.SetInt("wobble", Wobble ? 1 : 0);
        computeShader.SetVector("wind", wind);
        computeShader.SetFloat("spacing", spacing);

        computeShader.Dispatch(CSKernel, 32, 32, 1);
    }

    void ReleaseBuffers()
    {
        modBuffer.Release();
        constantBuffer.Release();
        startPointBuffer.Release();
        outputBuffer.Release();
    }

    void Start()
    {
        CSKernel = computeShader.FindKernel("CSMain");

        PointMaterial = new Material(PointShader);

        InitializeBuffers();
    }

    void OnPostRender()
    {
        if (!SystemInfo.supportsComputeShaders)
        {
            Debug.LogWarning("Compute shaders not supported (not using DX11?)");
            return;
        }

        Dispatch();

        PointMaterial.SetPass(0);
        PointMaterial.SetBuffer("buf_Points", outputBuffer);

        Graphics.DrawProcedural(MeshTopology.Points, VertCount);
    }

    private void OnDisable()
    {
        ReleaseBuffers();
    }
}

WeatherGenerator.Compute

#pragma kernel CSMain

// Thread group size 
#define thread_group_size_x 16
#define thread_group_size_y 16
#define thread_group_size_z 1

float Speed = 1;
int wobble = 0;
float3 wind = float3(0,0,0);
float spacing = 5;

// Structure to store x,y,z position data in. This is used to populate the outputBuffer
struct positionStruct
{
    float3 pos;
};

// Structure to describe the data in the startPointBuffer
struct startYStruct
{
    float offset;
};

// Structure to describe the data in the constBuffer
struct constStruct
{
    float t;
};

struct posMod
{
	float2 mod;
};

// Compute Buffers
RWStructuredBuffer<posMod> modBuffer;
RWStructuredBuffer<constStruct> constBuffer;
RWStructuredBuffer<startYStruct> startPointBuffer;
RWStructuredBuffer<positionStruct> outputBuffer;

[numthreads(thread_group_size_x, thread_group_size_y, thread_group_size_z)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    int idx = id.x + id.y * thread_group_size_x * 32;
    
	float3 centre = float3(-thread_group_size_x * 15,0,-thread_group_size_y * 15);
    float3 pos = (float3(id.x, id.z + (startPointBuffer[idx].offset ), id.y) + centre) * spacing;

	pos.y = (lerp((spacing * 2) * 32, 0, (pos.y + (constBuffer[0].t * (modBuffer[idx].mod.x * Speed)  )) % 1 )) * spacing;

	if(wobble)
	{
		pos.x +=  (cos(constBuffer[0].t * (modBuffer[idx].mod.x * 100))) * spacing;
		pos.z +=  (sin(constBuffer[0].t * (modBuffer[idx].mod.y * 100))) * spacing;
	}

	wind.y = 0; // Don't want the weather going up do we...
	pos += (wind * constBuffer[0].t * 100);
 
    outputBuffer[idx].pos = pos;
}