Hi,
Situation:
I am trying to move from my CPU based procedural planet generation approach to a GPU based (when it comes to plane calculation and rendering).
Being fairly new new to shader programing at all, I am right now at the stage that I can hand over generation constants in a buffer to a compute shader, precalulcate the plane in a compute shader (vertice positions, norrmals, based on noise) and hand them over via the buffer for rendering (replacing the vertex positions of a prototype mesh with the ones from the buffer).
Question:
I havent been able to render more than one plane at once. Is there some dependency in using Graphics.DrawMesh (maybe the order of creating the buffers, materials, DrawMesh calls) for multiple draw calls in a script attached to a gameobject? Or in the end is there the need of one gameobject per DrawMesh call?!.
Any hint on what I might be doing wrong would be very helpfull and appreciated.
My (not working) approach for in this example to create a sphere out of six planes is:
- Create six buffers (located in the QuadtreeTerrain class (a quadtree node)) which will hold the vertex positions computed in a compute shader.
- Create six materials (one per QuadtreeTerrain).
- Dispatch the six buffers to the compute shader to fill them with noise data (vertex position data)
- Create six meshes using Graphics.DrawMesh. The applied vertex shader receives the buffers with vertex positions and replaces the vertex positions of a dummy mesh with the positions in the buffer.
See below the code. Right now only one mesh (the first one in the order) is drawn. Is there any dependency in using DrawMesh?
class QuadtreeTerrain {
// Quadtree classes
public QuadtreeTerrain parentNode; // The parent quadtree node
public QuadtreeTerrain childNode1; // A children quadtree node
public QuadtreeTerrain childNode2; // A children quadtree node
public QuadtreeTerrain childNode3; // A children quadtree node
public QuadtreeTerrain childNode4; // A children quadtree node
// Buffer
public ComputeBuffer generationConstantsBuffer;
public ComputeBuffer patchGeneratedDataBuffer;
// Material
public Material material;
....
}
In the SpaceObjectProceduralPlanet script, applied to a single game object, I hold six instances of quadtrees [=QuadtreeTerrain] then.
public class SpaceObjectProceduralPlanet : MonoBehaviour {
....
// QuadtreeTerrain
private QuadtreeTerrain quadtreeTerrain1;
private QuadtreeTerrain quadtreeTerrain2;
private QuadtreeTerrain quadtreeTerrain3;
private QuadtreeTerrain quadtreeTerrain4;
private QuadtreeTerrain quadtreeTerrain5;
private QuadtreeTerrain quadtreeTerrain6;
// We initialize the buffers and the material used to draw.
void Start()
{
...
// QuadtreeTerrain
this.quadtreeTerrain1 = new QuadtreeTerrain(0, edgeVector1, edgeVector2, edgeVector3, edgeVector4, quadtreeTerrainParameter1);
this.quadtreeTerrain2 = new QuadtreeTerrain(0, edgeVector2, edgeVector5, edgeVector4, edgeVector7, quadtreeTerrainParameter2);
this.quadtreeTerrain3 = new QuadtreeTerrain(0, edgeVector5, edgeVector6, edgeVector7, edgeVector8, quadtreeTerrainParameter3);
this.quadtreeTerrain4 = new QuadtreeTerrain(0, edgeVector6, edgeVector1, edgeVector8, edgeVector3, quadtreeTerrainParameter4);
this.quadtreeTerrain5 = new QuadtreeTerrain(0, edgeVector6, edgeVector5, edgeVector1, edgeVector2, quadtreeTerrainParameter5);
this.quadtreeTerrain6 = new QuadtreeTerrain(0, edgeVector3, edgeVector4, edgeVector8, edgeVector7, quadtreeTerrainParameter6);
CreateBuffers(this.quadtreeTerrain1);
CreateBuffers(this.quadtreeTerrain2);
CreateBuffers(this.quadtreeTerrain3);
CreateBuffers(this.quadtreeTerrain4);
CreateBuffers(this.quadtreeTerrain5);
CreateBuffers(this.quadtreeTerrain6);
CreateMaterial(this.quadtreeTerrain1);
CreateMaterial(this.quadtreeTerrain2);
CreateMaterial(this.quadtreeTerrain3);
CreateMaterial(this.quadtreeTerrain4);
CreateMaterial(this.quadtreeTerrain5);
CreateMaterial(this.quadtreeTerrain6);
Dispatch(this.quadtreeTerrain1);
Dispatch(this.quadtreeTerrain2);
Dispatch(this.quadtreeTerrain3);
Dispatch(this.quadtreeTerrain4);
Dispatch(this.quadtreeTerrain5);
Dispatch(this.quadtreeTerrain6);
}
// We compute the buffers.
void CreateBuffers(QuadtreeTerrain quadtreeTerrain)
{
.... preparing generation constants
quadtreeTerrain.generationConstantsBuffer.SetData(generationConstants);
// Buffer Output
quadtreeTerrain.patchGeneratedDataBuffer = new ComputeBuffer(nVerts, 16 + 12 + 4 + 12);
}
//We create the material
void CreateMaterial(QuadtreeTerrain quadtreeTerrain)
{
Material material = new Material(shader);
material.SetTexture("_MainTex", this.texture);
material.SetFloat("_Metallic", 0);
material.SetFloat("_Glossiness", 0);
quadtreeTerrain.material = material;
}
//We dispatch threads of our CSMain1 and CSMain2 kernels.
void Dispatch(QuadtreeTerrain quadtreeTerrain)
{
// Set Buffers
computeShader.SetBuffer(_kernel, "generationConstantsBuffer", quadtreeTerrain.generationConstantsBuffer);
computeShader.SetBuffer(_kernel, "patchGeneratedDataBuffer", quadtreeTerrain.patchGeneratedDataBuffer);
// Dispatch first kernel
_kernel = computeShader.FindKernel("CSMain1");
computeShader.Dispatch(_kernel, THREADGROUP_SIZE_X, THREADGROUP_SIZE_Y, THREADGROUP_SIZE_Z);
// Dispatch second kernel
_kernel = computeShader.FindKernel("CSMain2");
computeShader.Dispatch(_kernel, THREADGROUP_SIZE_X, THREADGROUP_SIZE_Y, THREADGROUP_SIZE_Z);
}
// We set the material before drawing and call DrawMesh on OnRenderObject
void OnRenderObject()
{
this.quadtreeTerrain1.material.SetBuffer("patchGeneratedDataBuffer", this.quadtreeTerrain1.patchGeneratedDataBuffer);
Graphics.DrawMesh(this.prototypeMesh, transform.localToWorldMatrix, this.quadtreeTerrain1.material, LayerMask.NameToLayer(GlobalVariablesManager.Instance.layerLocalSpaceName), null, 0, null, true, true);
this.quadtreeTerrain2.material.SetBuffer("patchGeneratedDataBuffer", this.quadtreeTerrain2.patchGeneratedDataBuffer);
Graphics.DrawMesh(this.prototypeMesh, transform.localToWorldMatrix, this.quadtreeTerrain2.material, LayerMask.NameToLayer(GlobalVariablesManager.Instance.layerLocalSpaceName), null, 0, null, true, true);
this.quadtreeTerrain3.material.SetBuffer("patchGeneratedDataBuffer", this.quadtreeTerrain3.patchGeneratedDataBuffer);
Graphics.DrawMesh(this.prototypeMesh, transform.localToWorldMatrix, this.quadtreeTerrain3.material, LayerMask.NameToLayer(GlobalVariablesManager.Instance.layerLocalSpaceName), null, 0, null, true, true);
this.quadtreeTerrain4.material.SetBuffer("patchGeneratedDataBuffer", this.quadtreeTerrain4.patchGeneratedDataBuffer);
Graphics.DrawMesh(this.prototypeMesh, transform.localToWorldMatrix, this.quadtreeTerrain4.material, LayerMask.NameToLayer(GlobalVariablesManager.Instance.layerLocalSpaceName), null, 0, null, true, true);
this.quadtreeTerrain5.material.SetBuffer("patchGeneratedDataBuffer", this.quadtreeTerrain5.patchGeneratedDataBuffer);
Graphics.DrawMesh(this.prototypeMesh, transform.localToWorldMatrix, this.quadtreeTerrain5.material, LayerMask.NameToLayer(GlobalVariablesManager.Instance.layerLocalSpaceName), null, 0, null, true, true);
this.quadtreeTerrain6.material.SetBuffer("patchGeneratedDataBuffer", this.quadtreeTerrain6.patchGeneratedDataBuffer);
Graphics.DrawMesh(this.prototypeMesh, transform.localToWorldMatrix, this.quadtreeTerrain6.material, LayerMask.NameToLayer(GlobalVariablesManager.Instance.layerLocalSpaceName), null, 0, null, true, true);
}
//When this GameObject is disabled we must release the buffers.
private void OnDisable()
{
ReleaseBuffer();
}
//Release buffers and destroy the material when play has been stopped.
void ReleaseBuffer()
{
// Destroy everything recursive in the quadtrees.
this.quadtreeTerrain1.generationConstantsBuffer.Release();
this.quadtreeTerrain1.patchGeneratedDataBuffer.Release();
this.quadtreeTerrain2.generationConstantsBuffer.Release();
this.quadtreeTerrain2.patchGeneratedDataBuffer.Release();
this.quadtreeTerrain3.generationConstantsBuffer.Release();
this.quadtreeTerrain3.patchGeneratedDataBuffer.Release();
this.quadtreeTerrain4.generationConstantsBuffer.Release();
this.quadtreeTerrain4.patchGeneratedDataBuffer.Release();
this.quadtreeTerrain5.generationConstantsBuffer.Release();
this.quadtreeTerrain5.patchGeneratedDataBuffer.Release();
this.quadtreeTerrain6.generationConstantsBuffer.Release();
this.quadtreeTerrain6.patchGeneratedDataBuffer.Release();
DestroyImmediate(this.quadtreeTerrain1.material);
DestroyImmediate(this.quadtreeTerrain2.material);
DestroyImmediate(this.quadtreeTerrain3.material);
DestroyImmediate(this.quadtreeTerrain4.material);
DestroyImmediate(this.quadtreeTerrain5.material);
DestroyImmediate(this.quadtreeTerrain6.material);
}
void Update() {
// Do nothing
}
}
Of course this is very bruteforce, but well this should work before I proceed as I need to figure out how to handle the buffers and draw calls and where to put them.