Last active
August 3, 2021 00:41
-
-
Save JoseMiguelPizarro/e33826af5a03b283623510a478e5b73b to your computer and use it in GitHub Desktop.
Procedural Stripe using Compute shaders for Unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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