Created
May 4, 2024 19:24
-
-
Save NicolasCaous/fd202c767acc6f7f160be95cce40f291 to your computer and use it in GitHub Desktop.
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
#include "UnityIndirect.cginc" | |
#pragma kernel ComputeSizes | |
#pragma kernel ComputeVertices | |
#pragma kernel ComputeTriangles | |
#pragma kernel FirstStep | |
#pragma kernel SecondStep | |
#pragma kernel ThirdStep | |
struct Uniforms | |
{ | |
uint subdivisions; | |
uint currentSubdivision; | |
uint trianglesCount; | |
uint vertexCount; | |
float3 cameraPosition; | |
}; | |
RWStructuredBuffer<Uniforms> _Uniforms; | |
RWStructuredBuffer<float3> _Positions; | |
RWStructuredBuffer<int3> _Triangles; | |
StructuredBuffer<Uniforms> _UniformsReadOnly; | |
StructuredBuffer<float3> _PrecomputedPositions; | |
StructuredBuffer<int3> _PrecomputedTriangles; | |
RWStructuredBuffer<IndirectDrawArgs> _IndirectDrawCallArgs; | |
[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].vertexCount = side * side; | |
_Uniforms[0].trianglesCount = 2; | |
for (uint i = 1; i <= subdivisions; ++i) | |
{ | |
_Uniforms[0].trianglesCount += 1 << (i * 2 + 1); | |
} | |
} | |
[numthreads(1,1,1)] | |
void ComputeVertices (uint3 id : SV_DispatchThreadID) | |
{ | |
const uint subdivisions = _UniformsReadOnly[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 = _UniformsReadOnly[0].currentSubdivision; | |
const uint subdivisions = _UniformsReadOnly[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) | |
[numthreads(64,1,1)] | |
void FirstStep (uint3 id : SV_DispatchThreadID) | |
{ | |
if (id.x == 128) | |
{ | |
_IndirectDrawCallArgs[0].vertexCountPerInstance = _UniformsReadOnly[0].trianglesCount * 3; | |
_IndirectDrawCallArgs[0].instanceCount = 1; | |
_IndirectDrawCallArgs[0].startVertex = 0; | |
_IndirectDrawCallArgs[0].startInstance = 0; | |
} | |
} | |
// second step -> for every triangle that is going to be rendered, remove all parents that are going to be rendered | |
[numthreads(64,1,1)] | |
void SecondStep (uint3 id : SV_DispatchThreadID) | |
{ | |
if (id.x == 128) | |
{ | |
_IndirectDrawCallArgs[0].vertexCountPerInstance = _UniformsReadOnly[0].trianglesCount * 3; | |
_IndirectDrawCallArgs[0].instanceCount = 1; | |
_IndirectDrawCallArgs[0].startVertex = 0; | |
_IndirectDrawCallArgs[0].startInstance = 0; | |
} | |
} | |
// third step -> for every triangle, copy to buffer to be rendered using atomic add | |
[numthreads(64,1,1)] | |
void ThirdStep (uint3 id : SV_DispatchThreadID) | |
{ | |
if (id.x == 128) | |
{ | |
_IndirectDrawCallArgs[0].vertexCountPerInstance = _UniformsReadOnly[0].trianglesCount * 3; | |
_IndirectDrawCallArgs[0].instanceCount = 1; | |
_IndirectDrawCallArgs[0].startVertex = 0; | |
_IndirectDrawCallArgs[0].startInstance = 0; | |
} | |
} |
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
#ifndef SHADERGRAPHHAX | |
#define SHADERGRAPHHAX | |
StructuredBuffer<int> _Triangles; | |
StructuredBuffer<float3> _Positions; | |
float3 _Scale; | |
float3 _PositionOffset; | |
sampler2D _Terrain; | |
void DoHax_float(float vertex_id, out float3 position) | |
{ | |
position = _Positions[_Triangles[round(vertex_id)]]; | |
position.z = position.y; | |
position.y = tex2Dlod(_Terrain, float4(position.xz, 0, 0)).x; | |
position *= _Scale; | |
position += _PositionOffset; | |
} | |
#endif |
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.Runtime.InteropServices; | |
using UnityEngine; | |
using UnityEngine.Rendering; | |
public class TerrainRenderer : MonoBehaviour | |
{ | |
[StructLayout(LayoutKind.Sequential)] | |
public struct Uniforms | |
{ | |
public const int size = sizeof(uint) * 4 + sizeof(float) * 3; | |
public uint subdivisions; | |
public uint currentSubdivision; | |
public uint trianglesCount; | |
public uint vertexCount; | |
public Vector3 cameraPosition; | |
} | |
public Material material; | |
public ComputeShader computeShader; | |
public Texture terrain; | |
public Transform cameraTransform; | |
[Range(0, 12)] | |
public int subdivisions = 4; | |
GraphicsBuffer TrianglesBuffer; | |
GraphicsBuffer PositionsBuffer; | |
GraphicsBuffer UniformsBuffer; | |
GraphicsBuffer IndirectDrawCallArgsBuffer; | |
Uniforms[] uniforms; | |
CommandBuffer cmd; | |
RenderParams rp; | |
static readonly int PositionsID = Shader.PropertyToID("_Positions"); | |
static readonly int TrianglesID = Shader.PropertyToID("_Triangles"); | |
static readonly int ScaleID = Shader.PropertyToID("_Scale"); | |
static readonly int PositionOffsetID = Shader.PropertyToID("_PositionOffset"); | |
static readonly int TerrainID = Shader.PropertyToID("_Terrain"); | |
void InitializeTerrain() | |
{ | |
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; | |
uniforms[0].cameraPosition = Vector3.zero; | |
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"); | |
computeShader.SetBuffer(kernelId, "_UniformsReadOnly", UniformsBuffer); | |
computeShader.SetBuffer(kernelId, "_Triangles", TrianglesBuffer); | |
computeShader.SetBuffer(kernelId, "_Positions", PositionsBuffer); | |
computeShader.Dispatch(kernelId, 1, 1, 1); | |
kernelId = computeShader.FindKernel("ComputeTriangles"); | |
computeShader.SetBuffer(kernelId, "_UniformsReadOnly", UniformsBuffer); | |
computeShader.SetBuffer(kernelId, "_Triangles", TrianglesBuffer); | |
computeShader.SetBuffer(kernelId, "_Positions", PositionsBuffer); | |
for (int i = 0; i <= subdivisions; i++) | |
{ | |
uniforms[0].currentSubdivision = (uint) i; | |
UniformsBuffer.SetData(uniforms); | |
computeShader.Dispatch(kernelId, 1, 1, 1); | |
} | |
rp = new RenderParams(material); | |
rp.worldBounds = new Bounds(transform.position + transform.localScale * 0.5f, transform.localScale); | |
rp.matProps = new MaterialPropertyBlock(); | |
rp.matProps.SetBuffer(TrianglesID, TrianglesBuffer); | |
rp.matProps.SetBuffer(PositionsID, PositionsBuffer); | |
rp.matProps.SetVector(ScaleID, transform.localScale); | |
rp.matProps.SetVector(PositionOffsetID, transform.position); | |
rp.matProps.SetTexture(TerrainID, terrain); | |
IndirectDrawCallArgsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 1, GraphicsBuffer.IndirectDrawArgs.size); | |
cmd = new CommandBuffer(); | |
kernelId = computeShader.FindKernel("FirstStep"); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_UniformsReadOnly", UniformsBuffer); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_PrecomputedPositions", PositionsBuffer); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_PrecomputedTriangles", TrianglesBuffer); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_IndirectDrawCallArgs", IndirectDrawCallArgsBuffer); | |
cmd.DispatchCompute(computeShader, kernelId, 1, 1, 1); | |
kernelId = computeShader.FindKernel("SecondStep"); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_UniformsReadOnly", UniformsBuffer); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_PrecomputedPositions", PositionsBuffer); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_PrecomputedTriangles", TrianglesBuffer); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_IndirectDrawCallArgs", IndirectDrawCallArgsBuffer); | |
cmd.DispatchCompute(computeShader, kernelId, 1, 1, 1); | |
kernelId = computeShader.FindKernel("ThirdStep"); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_UniformsReadOnly", UniformsBuffer); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_PrecomputedPositions", PositionsBuffer); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_PrecomputedTriangles", TrianglesBuffer); | |
cmd.SetComputeBufferParam(computeShader, kernelId, "_IndirectDrawCallArgs", IndirectDrawCallArgsBuffer); | |
cmd.DispatchCompute(computeShader, kernelId, 1, 1, 1); | |
} | |
void UpdateUniforms() | |
{ | |
uniforms[0].cameraPosition = cameraTransform.position; | |
UniformsBuffer.SetData(uniforms); | |
rp.matProps.SetVector(ScaleID, transform.localScale); | |
rp.matProps.SetVector(PositionOffsetID, transform.position); | |
} | |
void UpdateTerrain() | |
{ | |
Graphics.ExecuteCommandBuffer(cmd); | |
Graphics.RenderPrimitivesIndirect(rp, MeshTopology.Triangles, IndirectDrawCallArgsBuffer, 1, 0); | |
} | |
void Start() | |
{ | |
InitializeTerrain(); | |
} | |
void OnDestroy() | |
{ | |
TrianglesBuffer?.Dispose(); | |
TrianglesBuffer = null; | |
PositionsBuffer?.Dispose(); | |
PositionsBuffer = null; | |
UniformsBuffer?.Dispose(); | |
UniformsBuffer = null; | |
IndirectDrawCallArgsBuffer?.Dispose(); | |
IndirectDrawCallArgsBuffer = null; | |
} | |
void Update() | |
{ | |
UpdateUniforms(); | |
UpdateTerrain(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment