Search Unity

  1. Unity 2019.4 has been released.
    Dismiss Notice
  2. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice
  3. Ever participated in one our Game Jams? Want pointers on your project? Our Evangelists will be available on Friday to give feedback. Come share your games with us!
    Dismiss Notice

Graphics.DrawProceduralIndirect isnt rendering anything

Discussion in 'Shaders' started by 00george, May 1, 2020.

  1. 00george

    00george

    Joined:
    Jan 4, 2014
    Posts:
    1
    Hi, I am running marching cubes on compute shaders and then was constructing meshes on the CPU which works fine.

    I have now tried to use
    Code (CSharp):
    1. Graphics.DrawProceduralIndirect
    to draw the meshes straight from the GPU however nothing is rendering with this new approach. I was wondering if anyone could figure out what is wrong?

    The compute shader:

    Code (CSharp):
    1. // Each #kernel tells which function to compile; you can have many kernels
    2. #pragma kernel March
    3. #include "MarchTables.compute"
    4.  
    5. float isoLevel = 0;
    6. int width = 16;
    7. float resolution = 1;
    8.  
    9.  
    10. struct Triangle
    11. {
    12.     float3 v[3];
    13. };
    14.  
    15. float3 interpolateVertices(float3 v1, float3 v2, float value1, float value2)
    16. {
    17.     float mu = (isoLevel - value1) / (value2 - value1);
    18.  
    19.     return float3(v1.xyz + mu * (v2.xyz - v1.xyz));
    20. }
    21.  
    22. int flattenedIndex(int x, int y, int z)
    23. {
    24.     return (x * (width + 1) * (width + 1)) + (y * (width + 1)) + z;
    25. }
    26.  
    27.  
    28. AppendStructuredBuffer<Triangle> triangleBuffer;
    29. RWStructuredBuffer<float> densityBuffer;
    30.  
    31. //int height;
    32. //int width;
    33. //float isoLevel;
    34.  
    35. [numthreads(8, 8, 8)]
    36. void March(int3 id : SV_DispatchThreadID)
    37. {
    38.     if (id.x >= (width) || id.y >= (width) || id.z >= (width))
    39.     {
    40.         return;
    41.     }
    42.  
    43.  
    44.     float cubeCorners[8] =
    45.     {
    46.         densityBuffer[flattenedIndex(id.x, id.y, id.z + 1)],
    47.         densityBuffer[flattenedIndex(id.x + 1, id.y, id.z + 1)],
    48.         densityBuffer[flattenedIndex(id.x + 1, id.y, id.z)],
    49.         densityBuffer[flattenedIndex(id.x, id.y, id.z)],
    50.         densityBuffer[flattenedIndex(id.x, id.y + 1, id.z + 1)],
    51.         densityBuffer[flattenedIndex(id.x + 1, id.y + 1, id.z + 1)],
    52.         densityBuffer[flattenedIndex(id.x + 1, id.y + 1, id.z)],
    53.         densityBuffer[flattenedIndex(id.x, id.y + 1, id.z)],
    54.     };
    55.  
    56.     int cubeIndex = 0;
    57.     if (cubeCorners[0] > isoLevel)
    58.         cubeIndex |= 1;
    59.     if (cubeCorners[1] > isoLevel)
    60.         cubeIndex |= 2;
    61.     if (cubeCorners[2] > isoLevel)
    62.         cubeIndex |= 4;
    63.     if (cubeCorners[3] > isoLevel)
    64.         cubeIndex |= 8;
    65.     if (cubeCorners[4] > isoLevel)
    66.         cubeIndex |= 16;
    67.     if (cubeCorners[5] > isoLevel)
    68.         cubeIndex |= 32;
    69.     if (cubeCorners[6] > isoLevel)
    70.         cubeIndex |= 64;
    71.     if (cubeCorners[7] > isoLevel)
    72.         cubeIndex |= 128;
    73.  
    74.     if (cubeIndex == 0 || cubeIndex == 255)
    75.     {
    76.         return;
    77.     }
    78.  
    79.     // Create triangles for current cube configuration
    80.     for (int i = 0; triangulation[cubeIndex][i] != -1; i += 3)
    81.     {
    82.         // Get indices of corner points A and B for each of the three edges
    83.         // of the cube that need to be joined to form the triangle.
    84.         int a0 = cornerIndexAFromEdge[triangulation[cubeIndex][i]];
    85.         int b0 = cornerIndexBFromEdge[triangulation[cubeIndex][i]];
    86.  
    87.         int a1 = cornerIndexAFromEdge[triangulation[cubeIndex][i + 1]];
    88.         int b1 = cornerIndexBFromEdge[triangulation[cubeIndex][i + 1]];
    89.  
    90.         int a2 = cornerIndexAFromEdge[triangulation[cubeIndex][i + 2]];
    91.         int b2 = cornerIndexBFromEdge[triangulation[cubeIndex][i + 2]];
    92.  
    93.         Triangle tri;
    94.         tri.v[0] = interpolateVertices(float3(((id * resolution) + (vertexPositions[a0] * resolution))), float3(((id * resolution) + (vertexPositions[b0] * resolution))), cubeCorners[a0], cubeCorners[b0]);
    95.         tri.v[1] = interpolateVertices(float3(((id * resolution) + (vertexPositions[a1] * resolution))), float3(((id * resolution) + (vertexPositions[b1] * resolution))), cubeCorners[a1], cubeCorners[b1]);
    96.         tri.v[2] = interpolateVertices(float3(((id * resolution) + (vertexPositions[a2] * resolution))), float3(((id * resolution) + (vertexPositions[b2] * resolution))), cubeCorners[a2], cubeCorners[b2]);
    97.         triangleBuffer.Append(tri);
    98.     }
    99. }
    100.  
    The shader:
    Code (CSharp):
    1. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    2.  
    3. Shader "Unlit/TerrainShader"
    4. {
    5.     Properties
    6.     {
    7.     }
    8.         SubShader
    9.     {
    10.         Tags { "RenderType" = "Opaque" }
    11.         LOD 100
    12.  
    13.         Pass
    14.         {
    15.             CGPROGRAM
    16.             #pragma enable_d3d11_debug_symbols
    17.             #pragma target 5.0
    18.             #pragma vertex vert
    19.             #pragma fragment frag
    20.             // make fog work
    21.             #pragma multi_compile_fog
    22.  
    23.             #include "UnityCG.cginc"
    24.  
    25.  
    26.             struct Triangle {
    27.                 float3 v[3];
    28.  
    29.             };
    30.  
    31.             uniform StructuredBuffer<Triangle> triangles;
    32.             uniform float4x4 model;
    33.  
    34.             struct appdata
    35.             {
    36.                 float4 vertex : POSITION;
    37.                 float2 uv : TEXCOORD0;
    38.             };
    39.  
    40.             struct v2f
    41.             {
    42.                 float4 pos: SV_POSITION;
    43.             };
    44.  
    45.             v2f vert (uint id : SV_VertexID)
    46.             {
    47.                 uint pid = id / 3;
    48.                 uint vid = id % 3;
    49.              
    50.                 float3 pos = triangles[pid].v[vid];
    51.  
    52.                 v2f o;
    53.                 o.pos = mul(UNITY_MATRIX_VP,mul(model,pos));
    54.                 return o;
    55.             }
    56.  
    57.             float4 frag(v2f i) : SV_Target
    58.             {
    59.                 return float4(1,0.5,0,1);
    60.             }
    61.             ENDCG
    62.         }
    63.     }
    64. }
    65.  
    The C# code:

    Code (CSharp):
    1.   public void GeneratePlanetOnGPU()
    2.     {
    3.         argBuffer = new ComputeBuffer(4, sizeof(int), ComputeBufferType.IndirectArguments);
    4.         int[] args = new int[] { 0, 1, 0, 0 };
    5.         argBuffer.SetData(args);
    6.  
    7.         int threadGroups = (width / 8);
    8.         int kernelHandle = shader.FindKernel("March");
    9.         if (!useComputeDensity)
    10.         {
    11.             densityMap = new float[(width + 1) * (width + 1) * (width + 1)];
    12.             densityBuffer = new ComputeBuffer((width + 1) * (width + 1) * (width + 1), sizeof(float));
    13.             densityBuffer.SetData(densityMap);
    14.             shader.SetBuffer(kernelHandle, "densityBuffer", densityBuffer);
    15.         }
    16.         else
    17.         {
    18.             ComputeBuffer db = dg.Generate(radius, width, resolution, new float[] { planetCentre.x, planetCentre.y, planetCentre.z }, new float[] { worldPos.x, worldPos.y, worldPos.z }, octaves, minHeight, strength);
    19.             shader.SetBuffer(kernelHandle, "densityBuffer", db);
    20.         }
    21.         triangleCountBuffer = new ComputeBuffer(1, sizeof(int), ComputeBufferType.Raw);
    22.         triangleBuffer = new ComputeBuffer((width) * (width) * (width) * 5, sizeof(float) * 3 * 3, ComputeBufferType.Append);
    23.         triangleBuffer.SetCounterValue(0);
    24.         shader.SetBuffer(kernelHandle, "triangleBuffer", triangleBuffer);
    25.         shader.SetInt("width", width);
    26.         shader.SetFloat("resolution", resolution);
    27.  
    28.         shader.Dispatch(kernelHandle, threadGroups, threadGroups, threadGroups);
    29.  
    30.         ComputeBuffer.CopyCount(triangleBuffer, argBuffer, 0);
    31.         argBuffer.GetData(args);
    32.         int tris = args[0];
    33.         args[0] *= 3;
    34.         argBuffer.SetData(args);
    35.  
    36.         float size = 16 * resolution;
    37.         Vector3 centre = worldPos + (Vector3.one * (size / 2));
    38.  
    39.         b = new Bounds(centre, new Vector3(size, size, size));
    40.         setup = true;
    41.  
    42.     }
    43.  
    44.  
    45.     private void OnRenderObject()
    46.     {
    47.         if (setup)
    48.         {
    49.             mat.SetPass(0);
    50.             mat.SetBuffer("triangles", triangleBuffer);
    51.             mat.SetMatrix("model", transform.localToWorldMatrix);
    52.             Graphics.DrawProceduralIndirect(mat,b,MeshTopology.Triangles,argBuffer);
    53.         }
    54.  
    55.     }
    The only difference between this and the CPU method is how the meshes are constructed. The gameobject the script is running on has a mesh filter and mesh renderer but I am unclear if it needs this. I assign the material in the inspector. Also, the gameobject is a prefab that is instantiated at runtime.

    There are lots of these prefabs each running this C# script.
     
  2. KokkuHub

    KokkuHub

    Joined:
    Feb 15, 2018
    Posts:
    251
    I'm going through the same struggle right now. The problem is that all the steps to get it working are horribly documented and all examples I found are centered around DrawMeshInstancedIndirect and the few using DrawProceduralIndirect. My case is different because I'm using surface shaders (I had to force EnableKeyword("PROCEDURAL_INSTANCING_ON") on the material itself because Unity was refusing to enable the instancing code generation), but what truly helped me finding out all my bugs was using RenderDoc to capture the frame, examine the draw calls, view the buffer contents, and debug the vertex shaders.

    upload_2020-5-2_17-5-49.png

    My last bug was using the wrong winding order in my index buffers (clockwise instead of counter-clockwise), so everything was being backface culled:

    upload_2020-5-2_17-6-54.png
     
    BradZoob likes this.
  3. BradZoob

    BradZoob

    Joined:
    Feb 12, 2014
    Posts:
    41
    Sounds like we're in the exact same boat, lol. And I have this bad feeling that once we finally get there we'll discover it confers terrible performance :p
     
unityunity