Created
May 3, 2024 16:46
-
-
Save NicolasCaous/d6454b59a6b20b34639f62b8f394e295 to your computer and use it in GitHub Desktop.
Triangle subdivision algorithm v2
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 ComputeVerticesAndIndices | |
struct Uniforms | |
{ | |
uint subdivisions; | |
uint vertex_count; | |
uint index_count; | |
}; | |
RWStructuredBuffer<Uniforms> uniforms; | |
//Texture2D<half> terrain; | |
RWStructuredBuffer<float2> vertices; | |
RWStructuredBuffer<uint3> indices; | |
[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].index_count = 2; | |
for (uint i = 1; i <= subdivisions; ++i) | |
{ | |
uniforms[0].index_count += 1 << (i * 2 + 1); | |
} | |
} | |
[numthreads(1,1,1)] | |
void ComputeVerticesAndIndices (uint3 id : SV_DispatchThreadID) | |
{ | |
const uint subdivisions = uniforms[0].subdivisions; | |
const uint side = (1 << (subdivisions + 1)) - (1 << subdivisions) + 1; | |
double gap = 1.0L / (side - 1); | |
for (uint i = 0; i < side; ++i) | |
{ | |
for (uint j = 0; j < side; ++j) | |
{ | |
vertices[i * side + j].x = (float)(gap * j); | |
vertices[i * side + j].y = (float)(gap * i); | |
} | |
} | |
uint subdivision_level = 0; | |
uint offset = 0; | |
for (uint index_gap = side - 1; index_gap > 0; index_gap /= 2) | |
{ | |
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; | |
indices[bottom_triangle_index].x = y * side + x; | |
x += index_gap; | |
y += index_gap; | |
indices[bottom_triangle_index].y = y * side + x; | |
y -= index_gap; | |
indices[bottom_triangle_index].z = y * side + x; | |
} | |
{ | |
uint x = j * index_gap; | |
uint y = i * index_gap; | |
indices[top_triangle_index].x = y * side + x; | |
y += index_gap; | |
indices[top_triangle_index].y = y * side + x; | |
x += index_gap; | |
indices[top_triangle_index].z = y * side + x; | |
} | |
} | |
} | |
offset += 1 << (subdivision_level * 2 + 1); | |
subdivision_level += 1; | |
} | |
} | |
// 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; | |
using System.Linq; | |
using System.Runtime.InteropServices; | |
using UnityEngine; | |
public class TerrainRenderer : MonoBehaviour | |
{ | |
[StructLayout(LayoutKind.Sequential)] | |
struct Vertex | |
{ | |
public const int size = sizeof(float) * 2; | |
public float x; | |
public float y; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
struct Index | |
{ | |
public const int size = sizeof(uint) * 3; | |
public uint first; | |
public uint second; | |
public uint third; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct Uniforms | |
{ | |
public const int size = sizeof(uint) * 3; | |
public uint subdivisions; | |
public uint vertexCount; | |
public uint indexCount; | |
} | |
public ComputeShader computeShader; | |
[Range(0, 13)] | |
public int subdivisions = 4; | |
private Uniforms[] _uniforms; | |
private ComputeBuffer _uniformsBuffer; | |
private ComputeBuffer _vertexBuffer; | |
private ComputeBuffer _indexBuffer; | |
private ComputeBuffer _drawCallArgsBuffer; | |
public Material material; | |
public Mesh mesh; | |
GraphicsBuffer meshTriangles; | |
GraphicsBuffer meshPositions; | |
GraphicsBuffer commandBuf; | |
GraphicsBuffer.IndirectDrawArgs[] commandData; | |
void Start() | |
{ | |
_uniforms = new Uniforms[1]; | |
_uniforms[0].subdivisions = (uint)subdivisions; | |
_uniforms[0].vertexCount = 0; | |
_uniforms[0].indexCount = 0; | |
_uniformsBuffer = new ComputeBuffer(1, Uniforms.size); | |
_uniformsBuffer.SetData(_uniforms); | |
//_drawCallArgsBuffer = new ComputeBuffer(1, Index.size, ComputeBufferType.IndirectArguments); | |
int kernelId = computeShader.FindKernel("ComputeSizes"); | |
computeShader.SetBuffer(kernelId, "uniforms", _uniformsBuffer); | |
computeShader.Dispatch(kernelId, 1, 1, 1); | |
_uniformsBuffer.GetData(_uniforms); | |
_vertexBuffer = new ComputeBuffer((int)_uniforms[0].vertexCount, Vertex.size); | |
_indexBuffer = new ComputeBuffer((int)_uniforms[0].indexCount, Index.size); | |
kernelId = computeShader.FindKernel("ComputeVerticesAndIndices"); | |
computeShader.SetBuffer(kernelId, "uniforms", _uniformsBuffer); | |
computeShader.SetBuffer(kernelId, "vertices", _vertexBuffer); | |
computeShader.SetBuffer(kernelId, "indices", _indexBuffer); | |
computeShader.Dispatch(kernelId, 1, 1, 1); | |
Debug.Log(_uniforms[0].subdivisions); | |
Debug.Log(_uniforms[0].vertexCount); | |
Debug.Log(_uniforms[0].indexCount); | |
Vertex[] v = new Vertex[_uniforms[0].vertexCount]; | |
Index[] i = new Index[_uniforms[0].indexCount]; | |
_vertexBuffer.GetData(v); | |
_indexBuffer.GetData(i); | |
mesh.SetIndices(new int[0] {}, MeshTopology.Triangles, 0); | |
mesh.SetVertices(v.Select(vertex => new Vector3(vertex.x, vertex.y, 0f)).ToArray()); | |
mesh.SetIndices(i.Select(index => new int[] { (int) index.first, (int) index.second, (int) index.third }).SelectMany(x => x).ToArray(), MeshTopology.Triangles, 0); | |
mesh.RecalculateBounds(); | |
mesh.RecalculateNormals(); | |
mesh.RecalculateTangents(); | |
meshTriangles = new GraphicsBuffer(GraphicsBuffer.Target.Structured, mesh.triangles.Length, sizeof(int)); | |
meshTriangles.SetData(mesh.triangles); | |
meshPositions = new GraphicsBuffer(GraphicsBuffer.Target.Structured, mesh.vertices.Length, 3 * sizeof(float)); | |
meshPositions.SetData(mesh.vertices); | |
commandBuf = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 1, GraphicsBuffer.IndirectDrawArgs.size); | |
commandData = new GraphicsBuffer.IndirectDrawArgs[1]; | |
} | |
private void Update() | |
{ | |
RenderParams rp = new RenderParams(material); | |
rp.worldBounds = new Bounds(Vector3.zero, 10000*Vector3.one); // use tighter bounds | |
rp.matProps = new MaterialPropertyBlock(); | |
rp.matProps.SetBuffer("_Triangles", meshTriangles); | |
rp.matProps.SetBuffer("_Positions", meshPositions); | |
rp.matProps.SetInt("_BaseVertexIndex", (int)mesh.GetBaseVertex(0)); | |
rp.matProps.SetMatrix("_ObjectToWorld", Matrix4x4.Translate(new Vector3(-4.5f, 0, 0))); | |
commandData[0].vertexCountPerInstance = mesh.GetIndexCount(0); | |
commandData[0].instanceCount = 1; | |
commandBuf.SetData(commandData); | |
Graphics.RenderPrimitivesIndirect(rp, MeshTopology.Triangles, commandBuf, 1); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment