Created
May 4, 2024 00:44
-
-
Save NicolasCaous/326394bfd08661fecc359cbd9b2ec0a5 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
#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 |
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 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 |
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; | |
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