Skip to content

Instantly share code, notes, and snippets.

@NicolasCaous
Created May 4, 2024 00:44
Show Gist options
  • Save NicolasCaous/326394bfd08661fecc359cbd9b2ec0a5 to your computer and use it in GitHub Desktop.
Save NicolasCaous/326394bfd08661fecc359cbd9b2ec0a5 to your computer and use it in GitHub Desktop.
#ifndef SHADERGRAPHHAX
#define SHADERGRAPHHAX
StructuredBuffer<int> _Triangles;
StructuredBuffer<float3> _Positions;
void DoHax_float(float vertex_id, out float3 position)
{
position = _Positions[_Triangles[round(vertex_id)]];
}
#endif
#pragma kernel ComputeSizes
#pragma kernel ComputeVertices
#pragma kernel ComputeTriangles
struct Uniforms
{
uint subdivisions;
uint current_subdivision;
uint triangles_count;
uint vertex_count;
};
RWStructuredBuffer<Uniforms> uniforms;
//Texture2D<half> terrain;
RWStructuredBuffer<float3> _Positions;
RWStructuredBuffer<int3> _Triangles;
[numthreads(1,1,1)]
void ComputeSizes (uint3 id : SV_DispatchThreadID)
{
const uint subdivisions = uniforms[0].subdivisions;
uint side = (1 << (subdivisions + 1)) - (1 << subdivisions) + 1;
uniforms[0].vertex_count = side * side;
uniforms[0].triangles_count = 2;
for (uint i = 1; i <= subdivisions; ++i)
{
uniforms[0].triangles_count += 1 << (i * 2 + 1);
}
}
[numthreads(1,1,1)]
void ComputeVertices (uint3 id : SV_DispatchThreadID)
{
const uint subdivisions = uniforms[0].subdivisions;
const uint side = (1 << subdivisions) + 1;
double gap = 1.0L / (side - 1);
for (uint i = 0; i < side; ++i)
{
for (uint j = 0; j < side; ++j)
{
_Positions[i * side + j].x = (float)(gap * j);
_Positions[i * side + j].y = (float)(gap * i);
_Positions[i * side + j].z = 0;
}
}
}
[numthreads(1,1,1)]
void ComputeTriangles (uint3 id : SV_DispatchThreadID)
{
const uint current_subdivision = uniforms[0].current_subdivision;
const uint subdivisions = uniforms[0].subdivisions;
const uint side = (1 << subdivisions) + 1;
uint offset = 0;
for(uint expo = 0; expo < current_subdivision; ++expo)
{
offset += 1 << (expo * 2 + 1);
}
const uint index_gap = (side - 1) / (1 << current_subdivision);
const uint sections = (side - 1) / index_gap;
for (uint i = 0; i < sections; ++i)
{
for (uint j = 0; j < sections; ++j)
{
const uint bottom_triangle_index = offset + (j * sections + i) * 2;
const uint top_triangle_index = bottom_triangle_index + 1;
{
uint x = j * index_gap;
uint y = i * index_gap;
_Triangles[bottom_triangle_index].x = y * side + x;
x += index_gap;
y += index_gap;
_Triangles[bottom_triangle_index].y = y * side + x;
y -= index_gap;
_Triangles[bottom_triangle_index].z = y * side + x;
}
{
uint x = j * index_gap;
uint y = i * index_gap;
_Triangles[top_triangle_index].x = y * side + x;
y += index_gap;
_Triangles[top_triangle_index].y = y * side + x;
x += index_gap;
_Triangles[top_triangle_index].z = y * side + x;
}
}
}
}
// first step -> detect every triangle that is bigger than treshhold (+ apply GPU culling)
// second step -> for every triangle that is going to be rendered, remove all parents that are going to be rendered
// third step -> for every triangle, copy to buffer to be rendered using atomic add
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Rendering;
public class TerrainRenderer : MonoBehaviour
{
[StructLayout(LayoutKind.Sequential)]
public struct Uniforms
{
public const int size = sizeof(uint) * 4;
public uint subdivisions;
public uint currentSubdivision;
public uint trianglesCount;
public uint vertexCount;
}
public Material material;
public ComputeShader computeShader;
[Range(0, 12)]
public int subdivisions = 4;
GraphicsBuffer TrianglesBuffer;
GraphicsBuffer PositionsBuffer;
GraphicsBuffer UniformsBuffer;
Uniforms[] uniforms;
private RenderParams rp;
private static readonly int PositionsID = Shader.PropertyToID("_Positions");
private static readonly int TrianglesID = Shader.PropertyToID("_Triangles");
void Start()
{
UniformsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 1, Uniforms.size);
uniforms = new Uniforms[1];
uniforms[0].subdivisions = (uint) subdivisions;
uniforms[0].currentSubdivision = 0;
uniforms[0].trianglesCount = 0;
uniforms[0].vertexCount = 0;
UniformsBuffer.SetData(uniforms);
int kernelId = computeShader.FindKernel("ComputeSizes");
computeShader.SetBuffer(kernelId, "uniforms", UniformsBuffer);
computeShader.Dispatch(kernelId, 1, 1, 1);
UniformsBuffer.GetData(uniforms);
TrianglesBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int) uniforms[0].trianglesCount * 3, sizeof(int));
PositionsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int) uniforms[0].vertexCount, 3 * sizeof(float));
kernelId = computeShader.FindKernel("ComputeVertices");
CommandBuffer cmd = new CommandBuffer();
cmd.SetExecutionFlags(CommandBufferExecutionFlags.AsyncCompute);
cmd.SetComputeBufferParam(computeShader, kernelId, "uniforms", UniformsBuffer);
cmd.SetComputeBufferParam(computeShader, kernelId, "_Positions", PositionsBuffer);
cmd.DispatchCompute(computeShader, kernelId, 1, 1, 1);
GraphicsFence fence = cmd.CreateAsyncGraphicsFence();
Graphics.ExecuteCommandBufferAsync(cmd, ComputeQueueType.Background);
Graphics.WaitOnAsyncGraphicsFence(fence);
for (int i = 0; i <= subdivisions; i++)
{
uniforms[0].currentSubdivision = (uint) i;
UniformsBuffer.SetData(uniforms);
kernelId = computeShader.FindKernel("ComputeTriangles");
CommandBuffer cmd2 = new CommandBuffer();
cmd2.SetExecutionFlags(CommandBufferExecutionFlags.AsyncCompute);
cmd2.SetComputeBufferParam(computeShader, kernelId, "uniforms", UniformsBuffer);
cmd2.SetComputeBufferParam(computeShader, kernelId, "_Triangles", TrianglesBuffer);
cmd2.DispatchCompute(computeShader, kernelId, 1, 1, 1);
GraphicsFence fence2 = cmd2.CreateAsyncGraphicsFence();
Graphics.ExecuteCommandBufferAsync(cmd2, ComputeQueueType.Background);
Graphics.WaitOnAsyncGraphicsFence(fence2);
}
rp = new RenderParams(material);
rp.worldBounds = new Bounds(Vector3.zero, 10000*Vector3.one); // use tighter bounds
rp.matProps = new MaterialPropertyBlock();
rp.matProps.SetBuffer(TrianglesID, TrianglesBuffer);
rp.matProps.SetBuffer(PositionsID, PositionsBuffer);
}
void OnDestroy()
{
TrianglesBuffer?.Dispose();
TrianglesBuffer = null;
PositionsBuffer?.Dispose();
PositionsBuffer = null;
UniformsBuffer?.Dispose();
UniformsBuffer = null;
}
void Update()
{
Graphics.RenderPrimitives(rp, MeshTopology.Triangles, (int) uniforms[0].trianglesCount * 3, 1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment