I have what seemed to be a simple problem that has now resulted in noise complaints from the neighbours over my screams of frustration.
TL;DR Procedural meshes are normally make using strips of quads. I'm instead trying to make a mesh as one piece, reusing edge vertices, instead of lining up the quad strips as if it was one mesh.
I'm testing something so maybe this is a wierd way to do it, but it should work.
Shader 1:
RWStructuredBuffer<float3> vertexBuffer;
uniform uint yColumnHeight;
[numthreads(8,8,1)]
void calcVerts (uint3 id : SV_DispatchThreadID)
{
//convert x and y to 1 dimensional counter
int idx = (id.y + (yColumnHeight * id.x));
//create a flat array of vertices
float3 vA = float3(id.x, 1, id.y);
vertexBuffer[idx] = vA;
}
Shader 2:
RWStructuredBuffer<float3> vertexBuffer;
RWStructuredBuffer<float3> triangleBuffer;
uniform uint yColumnHeight;
[numthreads(8,8,1)]
void createMeshFromVerts (uint3 id : SV_DispatchThreadID)
{
int idx = (id.y + (yColumnHeight * id.x));
if (id.x > 0 && id.y > 0){
//convert idx to index for tri/quad vertices, skipping first row and column
int subtractFirstYColumn = idx - yColumnHeight;
int subtractFirstXRow = id.y - 1;
int trID = (subtractFirstYColumn - subtractFirstXRow) * 6;
//find the vertices of the quad using verts from first row and column
int tri_a = idx - yColumnHeight - 1;
int tri_b = idx - 1;
int tri_c = idx;
int tri_d = idx - yColumnHeight;
triangleBuffer[trID] = vertexBuffer[tri_a];
triangleBuffer[trID + 1] = vertexBuffer[tri_b];
triangleBuffer[trID + 2] = vertexBuffer[tri_c];
triangleBuffer[trID + 3] = vertexBuffer[tri_d];
triangleBuffer[trID + 4] = vertexBuffer[tri_a];
triangleBuffer[trID + 5] = vertexBuffer[tri_c];
}
}
The second shader may initially seem obtuse, but it's quite simple. I'm getting an array of verts:
. . . .
. . . .
. . . .
. . . .
In the above, that's a 3x3 grid of quads, made of 4x4 verts. I start by getting the vert 1 across and 1 down, and making a quad with the top left corner verts. Each quad starts with vert _ and uses preceeding verts . like this:
. .
. _
And tied together in the main C#:
//buffers for vertices and map of vertices to make triangles
vertexBuffer = new ComputeBuffer(triVertCount, stride, ComputeBufferType.Default);
triangleBuffer = new ComputeBuffer(tris, stride, ComputeBufferType.Default);
//create initial vertices grid
calcVerts.SetBuffer(verts, "vertexBuffer", vertexBuffer);
calcVerts.Dispatch(verts, Mathf.Max(1, (widthInVertices) / (int)threadsx), Mathf.Max(1, (heightInVertices) / (int)threadsy), (int)z);
//use vertices grid to make mesh
createMeshFromVerts.SetBuffer(meshFromVerts, "vertexBuffer", vertexBuffer);
createMeshFromVerts.SetBuffer(meshFromVerts, "triangleBuffer", triangleBuffer);
createMeshFromVerts.Dispatch(meshFromVerts, Mathf.Max(1, (widthInVertices) / (int)threadsx), Mathf.Max(1, (heightInVertices) / (int)threadsy), (int)z);
I skipped the code for normals, and where I pass to material to render. When this runs I get scrambled triangles. Can you see where I messed up?
The calculation of trId
results in overlapping of some indices, and skipping of other values.
With a 4x2 grid of vertices (idx shown) and yColumnHeight
of 4:
0 4
x <- (desired trID 0)
1 5
x <- (desired trID 6)
2 6
x <- (desired trID 12)
3 7
The currently calculated trId
for id
= 1,1 (idx 5) comes out to 6, but it should probably come to 0 so that the first 6 items in the triangleBuffer
are set to something useful. In fact, no trId
ever equals 0 using the current calculation. Furthermore, the currently calculated trId
for id
= 1,2 (idx 6) comes out to 6 as well! And so does id
=1,3 (idx 7).
Sadly, this overlap occurs in every column, and most of the triangleBuffer
goes unset as a result of this.
The answer is to change how trId
is calculated.
A simple way is to re-use your method of mapping from 2d to 1d array, only reducing the x and y coordinates by 1 and also reducing the height by one:
int idx = (id.y + (yColumnHeight * id.x));
int trId = ((id.y-1) + ((yColumnHeight-1) * (id.x-1));
trId *= 6;
or more simply:
int trId = 6 * (id.y - 1 + (yColumnHeight-1) * (id.x-1));
or, expanding and substituting idx
. I find this less clear what's happening but it's more succinct:
// = 6 * (id.y - 1 + yColumnHeight * id.x - yColumnHeight - id.x + 1)
// = 6 * (id.y + yColumnHeight * id.x - yColumnHeight - id.x)
int trId = 6 * (idx - yColumnHeight - id.x);