Skip to content

Instantly share code, notes, and snippets.

@JoseMiguelPizarro
Last active August 3, 2021 00:41
Show Gist options
  • Save JoseMiguelPizarro/e33826af5a03b283623510a478e5b73b to your computer and use it in GitHub Desktop.
Save JoseMiguelPizarro/e33826af5a03b283623510a478e5b73b to your computer and use it in GitHub Desktop.
Procedural Stripe using Compute shaders for Unity
Shader "Custom/ProceduralMesh"
{
subshader
{
pass
{
HLSLPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma target 5.0
struct VertexData
{
float3 positionOS;
};
shared StructuredBuffer<VertexData> _vertexBuffer;
shared StructuredBuffer<int> _indexBuffer;
struct Varyings
{
float4 positionCS : SV_POSITION;
float triangleId:TEXCOORD0;
};
Varyings vert(uint vertexID : SV_VertexID)
{
Varyings output;
int index = _indexBuffer[vertexID];
VertexData vertex = _vertexBuffer[index];
output.positionCS = UnityObjectToClipPos(vertex.positionOS);
int triangleIndex = index / 3;
uint triCount, stride;
_indexBuffer.GetDimensions(triCount, stride);
triCount /= 3;
output.triangleId = (triangleIndex + 1) / (float)triCount;
return output;
}
float4 frag(Varyings input):SV_Target
{
return float4(input.triangleId.xxx, 1);
}
ENDHLSL
}
}
}
using System;
using System.Runtime.InteropServices;
using Unity.Mathematics;
using UnityEngine;
[ExecuteAlways]
public class ProceduralStripe : MonoBehaviour,IDisposable
{
[Min(1)] public int stripes = 1;
public ComputeShader cs;
public Material material;
private ComputeBuffer _vertexBuffer;
private ComputeBuffer _indexBuffer;
private ComputeBuffer _argsBuffer;
private int _buildKernelID;
private int VertexCount => stripes + 2;
private void Build()
{
_buildKernelID = cs.FindKernel("BuildStripeKernel");
_vertexBuffer = new ComputeBuffer(VertexCount, Marshal.SizeOf<VertexData>(), ComputeBufferType.Structured);
_indexBuffer = new ComputeBuffer(stripes * 3, sizeof(int), ComputeBufferType.Structured);
_argsBuffer = new ComputeBuffer(4, sizeof(int), ComputeBufferType.IndirectArguments);
var startingVertex = new[] {new float3(0, 1, 0), new float3(0, 0, 0)};
_vertexBuffer.SetData(startingVertex,0,0,2);
cs.SetBuffer(_buildKernelID, nameof(_vertexBuffer), _vertexBuffer);
cs.SetBuffer(_buildKernelID, nameof(_indexBuffer), _indexBuffer);
material.SetBuffer(nameof(_vertexBuffer), _vertexBuffer);
material.SetBuffer(nameof(_indexBuffer), _indexBuffer);
}
private void OnEnable()
{
Build();
}
private void OnDisable()
{
Dispose();
}
public void Dispose()
{
_vertexBuffer.Release();
_indexBuffer.Release();
_argsBuffer.Release();
}
private void Update()
{
{ //Highly inefficient code, it only should be rebuilt if it has changed.
Dispose();
Build();
}
Render();
}
private void Render()
{
if (stripes < 1)
return;
cs.GetKernelThreadGroupSizes(_buildKernelID, out uint xGroups, out _, out _);
cs.Dispatch(_buildKernelID, Mathf.CeilToInt(stripes / (float) xGroups), 1, 1);
var bounds = new Bounds(Vector3.zero, Vector3.one * 10000);
SetArgs(_argsBuffer, stripes * 3);
Graphics.DrawProceduralIndirect(material, bounds, MeshTopology.Triangles, _argsBuffer);
}
private void SetArgs(ComputeBuffer argsBuffer, int vertices)
{
argsBuffer.SetData(new[] {vertices, 1, 0, 0});
}
struct VertexData
{#pragma kernel BuildStripeKernel
struct VertexData
{
float3 position;
};
shared RWStructuredBuffer<VertexData> _vertexBuffer;
shared RWStructuredBuffer<int> _indexBuffer;
float3 GetPosition(uint triId)
{
float x = (triId / 2) + 1;
float y = (1 - triId % 2);
return float3(x, y, 0);
}
[numthreads(1,1,1)]
void BuildStripeKernel(uint3 id:SV_DispatchThreadID)
{
uint triangleId = id.x;
VertexData vertex;
uint index = triangleId + 2;
vertex.position = GetPosition(triangleId);
_vertexBuffer[index] = vertex;
_indexBuffer[id.x * 3] = id.x;
_indexBuffer[id.x * 3 + 1] = id.x + 1 + id.x % 2;
_indexBuffer[id.x * 3 + 2] = id.x + 2 - id.x % 2;
}
private float3 positionOS;
}
}
#pragma kernel BuildStripeKernel
struct VertexData
{
float3 positionOS;
};
shared RWStructuredBuffer<VertexData> _vertexBuffer;
shared RWStructuredBuffer<int> _indexBuffer;
float3 GetPosition(uint triId)
{
float x = (triId / 2) + 1;
float y = (1 - triId % 2);
return float3(x, y, 0);
}
[numthreads(32,1,1)]
void BuildStripeKernel(uint3 id:SV_DispatchThreadID)
{
uint triangleId = id.x;
VertexData vertex;
uint index = triangleId + 2;
vertex.positionOS = GetPosition(triangleId);
_vertexBuffer[index] = vertex;
_indexBuffer[id.x * 3] = id.x;
_indexBuffer[id.x * 3 + 1] = id.x + 1 + id.x % 2;
_indexBuffer[id.x * 3 + 2] = id.x + 2 - id.x % 2;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment