Procedural mesh and compute shader - the simplest example

I would like to know how to use DirectX 11 compute shaders in procedural mesh generation. I found a few projects, but I am beginner and I want to understand the simplest possible example:

Generating a plane mesh in a “traditional” way:

var width: float;
var height: float;

function Start() { 
    var mf: MeshFilter = GetComponent.<MeshFilter>();
    var mesh = new Mesh();
    mf.mesh = mesh;
   
    var vertices: Vector3[] = new Vector3[4];
   
    vertices[0] = new Vector3(0, 0, 0);
    vertices[1] = new Vector3(width, 0, 0);
    vertices[2] = new Vector3(0, height, 0);
    vertices[3] = new Vector3(width, height, 0);
   
    mesh.vertices = vertices;
   
    var tri: int[] = new int[6];

    tri[0] = 0;
    tri[1] = 2;
    tri[2] = 1;
   
    tri[3] = 2;
    tri[4] = 3;
    tri[5] = 1;
   
    mesh.triangles = tri;
   
    var normals: Vector3[] = new Vector3[4];
   
    normals[0] = -Vector3.forward;
    normals[1] = -Vector3.forward;
    normals[2] = -Vector3.forward;
    normals[3] = -Vector3.forward;
   
    mesh.normals = normals;
   
    var uv: Vector2[] = new Vector2[4];

    uv[0] = new Vector2(0, 0);
    uv[1] = new Vector2(1, 0);
    uv[2] = new Vector2(0, 1);
    uv[3] = new Vector2(1, 1);
   
    mesh.uv = uv;
}

source:

What it might look like in compute shader implementation ?

I don’t particularly have a lot of time to write up the whole implementation for ya, but I’ll link you to some solid resources.

http://scrawkblog.com/category/directcompute/
http://scrawkblog.com/2013/05/05/simple-voxel-terrain-generator/

This specifically… http://scrawkblog.com/2014/07/02/directcompute-tutorial-for-unity-buffers/

I’ll post some sample code, but reverse that to get the data back to the CPU. You’ll create the buffer, and then read with GetData into the buffer you have, and then you can use it just as if you were creating a mesh.

This first class would create sets of quads… it would have a lot of duplicate vertexes, but it would align up along a plane if you got the data and turned it into a mesh. It’s clunky but would work.

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain

//I used this class, and filled in points to expand with a geometry shader
struct Vertex
{
    float3 position;
    float3 normal;
};

AppendStructuredBuffer<Vertex> appendBuffer;

[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // Simple case
    Vertex v;
    vertex.position = float3(id.x, 0, id.y);
    vertex.normal = float3(0, 1, 0);
    appendBuffer.Append(v);
    vertex.position = float3(id.x + 1, 0, id.y + 1);
    appendBuffer.Append(v);
    vertex.position = float3(id.x + 1, 0, id.y);
    appendBuffer.Append(v);
    vertex.position = float3(id.x, 0, id.y + 1);
    appendBuffer.Append(v);
}
// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain

//I used this class, and filled in points to expand with a geometry shader
struct Billboard
{
    float3 position;
    float2 rotation;
    float2 scale;
    float3 albedo;
    float3 normal;
};

AppendStructuredBuffer<Billboard> appendBuffer;

[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // I've stripped out a huge chunk of my code, as it's somewhat complex and specific, but the general idea..
    // I used a billboard class and expanded into a billboard in a geometry shader
    // however, that is not strictly necessary, if you're just creating a mesh, then you can just read
    // off this information back to the CPU.
    Billboard b;
    // set it all for the billboard however you want
    appendBuffer.Append(b);
}
1 Like