Search Unity

AppendStructuredBuffer as output of compute buffer, how to transfer to Geometry Shader?

Discussion in 'Shaders' started by itsDmajster, Apr 20, 2019.

  1. itsDmajster

    itsDmajster

    Joined:
    Oct 27, 2014
    Posts:
    45
    I have a compute shader with a simple task of finding visible voxels in StructuredBuffer<Voxel> and outputing their position in a VoxelVertex struct to an AppendStructuredBuffer<VoxelVertex>. I want to then later use this APB in my geometry shader as a set of points to create quads out of.

    The compute buffer works as intended, and have confirmed the output is correct by fetching the append buffer to my CPU and just instantiating boxes at the positions.

    upload_2019-4-20_16-53-44.png

    The problem seems to be that the Vertex Shader doesn't want to use APB and i'm not exactly sure how to make it work.

    From my searching on the internet there isn't a lot of information on this specific topic, some people are suggesting using : register(u1) and to enable random writes, but none of those methods provided any success for me.

    C# Handler
    Code (csharp):
    1.  
    2. using System.Runtime.InteropServices;
    3. using UnityEngine;
    4.  
    5. namespace Assets
    6. {
    7.     public sealed class World : MonoBehaviour
    8.     {
    9.         public GameObject Cube;
    10.         public Material Material;
    11.  
    12.         public int VoxelCount;
    13.         public VoxelVertex[] VoxelVertices;
    14.  
    15.         public ComputeShader ComputeShader;
    16.         public ComputeBuffer VoxelComputeBuffer;
    17.         public ComputeBuffer VertexComputeBuffer;
    18.         public ComputeBuffer ArgumentComputeBuffer;
    19.  
    20.         public void Start()
    21.         {
    22.             var chunk = new Chunk(Vector3.zero, new Vector3(16, 256, 15));
    23.  
    24.             ConstructTerrain(chunk);
    25.             ConstructMeshGpu(chunk);
    26.             ConstructMeshImplicit();
    27.         }
    28.  
    29.         public void ConstructTerrain(Chunk chunk)
    30.         {
    31.             for (var x = 0; x < chunk.Dimensions.x; x++)
    32.             {
    33.                 for (var z = 0; z < chunk.Dimensions.z; z++)
    34.                 {
    35.                     var height = (int)(Mathf.PerlinNoise(x / 16.0f, z / 16.0f) * chunk.Dimensions.z);
    36.  
    37.                     for (var y = 0; y < height; y++)
    38.                     {
    39.                         var offset = new Vector3(x, y, z);
    40.                         ref var voxel = ref chunk.GetVoxel(offset);
    41.                         voxel.Type = 1;
    42.                     }
    43.                 }
    44.             }
    45.         }
    46.  
    47.         public void ConstructMeshGpu(Chunk chunk)
    48.         {
    49.             var terrainCreatorIndex = ComputeShader.FindKernel("TerrainCreator");
    50.  
    51.             VoxelComputeBuffer = new ComputeBuffer(chunk.Voxels.Length, Marshal.SizeOf(typeof(Voxel)));
    52.             VoxelComputeBuffer.SetData(chunk.Voxels);
    53.  
    54.             VertexComputeBuffer = new ComputeBuffer(chunk.Voxels.Length, Marshal.SizeOf(typeof(VoxelVertex)), ComputeBufferType.Append);
    55.             VertexComputeBuffer.SetCounterValue(0);
    56.  
    57.             ComputeShader.SetVector("_Dimensions", chunk.Dimensions);
    58.             ComputeShader.SetBuffer(terrainCreatorIndex, "_Voxels", VoxelComputeBuffer);
    59.             ComputeShader.SetBuffer(terrainCreatorIndex, "_Vertices", VertexComputeBuffer);
    60.  
    61.             Material.SetVector("_Dimensions", chunk.Dimensions);
    62.             Material.SetBuffer("_Vertices", VertexComputeBuffer);
    63.  
    64.             ComputeShader.Dispatch(terrainCreatorIndex, (int)chunk.Dimensions.x, (int)chunk.Dimensions.y, (int)chunk.Dimensions.z);
    65.  
    66.             ArgumentComputeBuffer = new ComputeBuffer(4, sizeof(int), ComputeBufferType.IndirectArguments);
    67.             var args = new int[] { 0, 1, 0, 0 };
    68.             ArgumentComputeBuffer.SetData(args);
    69.  
    70.             ComputeBuffer.CopyCount(VertexComputeBuffer, ArgumentComputeBuffer, 0);
    71.             ArgumentComputeBuffer.GetData(args);
    72.  
    73.             //Was used on CPU in ConstructMeshImplicit() to see if the data was correctly made, was OK
    74.             VoxelCount = args[0];
    75.             VoxelVertices = new VoxelVertex[VoxelCount];
    76.             VertexComputeBuffer.GetData(VoxelVertices);
    77.         }
    78.  
    79.         void OnPostRender()
    80.         {
    81.             Material.SetPass(0);
    82.            
    83.            
    84.  
    85.             Graphics.DrawProceduralIndirectNow(MeshTopology.Points, ArgumentComputeBuffer );
    86.             //Graphics.DrawProceduralNow(MeshTopology.Points, VoxelCount, 1 );
    87.         }
    88.  
    89.  
    90.         public void ConstructMeshImplicit()
    91.         {
    92.             for (var x = 0; x < VoxelVertices.Length ; x++)
    93.             {
    94.                 Instantiate(Cube, VoxelVertices[x].Position, Quaternion.identity);
    95.             }
    96.         }
    97.         public void ConstructMesh(Chunk chunk)
    98.         {
    99.             for (var x = 0; x < chunk.Dimensions.x; x++)
    100.             {
    101.                 for (var y = 0; y < chunk.Dimensions.y; y++)
    102.                 {
    103.                     for (var z = 0; z < chunk.Dimensions.z; z++)
    104.                     {
    105.                         var offset = new Vector3(x, y, z);
    106.                         var voxel = chunk.GetVoxel(offset);
    107.                         if (voxel.Type == 1)
    108.                         {
    109.                             Instantiate(Cube, chunk.Position + offset, Quaternion.identity);
    110.                         }
    111.                     }
    112.                 }
    113.             }
    114.         }
    115.     }
    116. }
    117.  

    Shader
    SplatUnlit.shader
    Code (csharp):
    1.  
    2. Shader "Splatting/Unlit"
    3. {
    4.    SubShader
    5.    {
    6.        Tags { "RenderType" = "Opaque" }
    7.  
    8.        Pass
    9.        {
    10.            
    11.            CGPROGRAM
    12.            #pragma vertex Vertex
    13.            #pragma geometry Geometry
    14.            #pragma fragment Fragment
    15.            #pragma enable_d3d11_debug_symbols
    16.            #pragma target 4.5
    17.            #include "SplatUnlit.cginc"
    18.            ENDCG
    19.        }
    20.    }
    21. }
    22.  
    SplatShader.cginc
    Code (csharp):
    1.  
    2. #include "UnityCG.cginc"
    3. #include "UnityGBuffer.cginc"
    4. #include "UnityStandardUtils.cginc"
    5.  
    6.  
    7.  
    8.  
    9. struct v2g
    10. {
    11.    float4 vertex : SV_POSITION;
    12.    fixed4 color : COLOR;
    13.    float4 up : TEXCOORD0;
    14.    float4 right : TEXCOORD1;
    15. };
    16.  
    17. struct g2f
    18. {
    19.    float4 vertex : SV_POSITION;
    20.    fixed4 color : COLOR;
    21. };
    22.  
    23. struct VoxelVertex
    24. {
    25.    float3 Position : POSITION;
    26. };
    27.  
    28. uniform float3 _Dimensions;
    29. uniform RWStructuredBuffer<VoxelVertex> _Vertices : register(u1);
    30.  
    31. int CalculateIndex(int3 position)
    32. {
    33.    return (int)(position.x +
    34.    position.z * _Dimensions.x +
    35.    position.y * _Dimensions.x * _Dimensions.z);
    36. }
    37.  
    38. v2g Vertex(VoxelVertex input, uint index : SV_VertexID )
    39. {
    40.  
    41.    v2g output;
    42.    output.vertex = float4(input.Position,1);
    43.    output.color = float4(1,input.Position.y/256,1,1);
    44.    
    45.    float3 view = normalize(UNITY_MATRIX_IT_MV[2].xyz);
    46.    float3 upvec = normalize(UNITY_MATRIX_IT_MV[1].xyz);
    47.    float3 R = normalize(cross(view, upvec));
    48.    output.up = float4(upvec*1, 0);
    49.    output.right = float4(R * 1, 0);
    50.    return output;
    51. }
    52.  
    53. [maxvertexcount(4)]
    54. void Geometry(point v2g input[1], inout TriangleStream<g2f>outputStream) {
    55.    g2f output;
    56.  
    57.    output.vertex = UnityObjectToClipPos(input[0].vertex + (-input[0].up + input[0].right));
    58.    output.color = input[0].color;
    59.    outputStream.Append(output);
    60.  
    61.    output.vertex = UnityObjectToClipPos(input[0].vertex + (-input[0].up - input[0].right));
    62.    outputStream.Append(output);
    63.  
    64.    output.vertex = UnityObjectToClipPos(input[0].vertex + (+input[0].up + input[0].right));
    65.    outputStream.Append(output);
    66.  
    67.    output.vertex = UnityObjectToClipPos(input[0].vertex + (+input[0].up - input[0].right));
    68.    outputStream.Append(output);
    69.  
    70.    outputStream.RestartStrip();
    71. }
    72.  
    73. fixed4 Fragment(g2f input) : SV_Target
    74. {
    75.    return input.color;
    76. }
    77.  

    Compute Shader
    Code (csharp):
    1.  
    2. #pragma kernel TerrainCreator
    3.  
    4. struct Voxel
    5. {
    6.    int Solid;  
    7. };
    8.  
    9. struct VoxelVertex
    10. {
    11.    float3 Position : POSITION;
    12. };
    13.  
    14. float3 _Dimensions;
    15.  
    16. StructuredBuffer<Voxel> _Voxels;
    17. AppendStructuredBuffer<VoxelVertex> _Vertices;
    18.  
    19. int CalculateIndex(int3 position)
    20. {
    21.    return (int)(position.x +
    22.    position.z * _Dimensions.x +
    23.    position.y * _Dimensions.x * _Dimensions.z);
    24. }
    25.  
    26. [numthreads(1,1,1)]
    27. void TerrainCreator (uint3 threadPosition : SV_DispatchThreadID)
    28. {
    29.     Voxel voxel = _Voxels[CalculateIndex(threadPosition)];
    30.    
    31.    if( voxel.Solid == 1 && _Voxels[CalculateIndex(float3(threadPosition.x,threadPosition.y+1,threadPosition.z))].Solid == 0 ) {
    32.        VoxelVertex voxelVertex;
    33.        voxelVertex.Position = float3(threadPosition);
    34.  
    35.        _Vertices.Append(voxelVertex);
    36.    }
    37. }
    38.  
     
    bb8_1 and deus0 like this.
  2. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    328
    Example:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class AppendBuffer : MonoBehaviour
    4. {
    5.     public Material material;
    6.     public ComputeShader shader;
    7.     public int size = 8;
    8.  
    9.     ComputeBuffer buffer, argBuffer;
    10.  
    11.     void Start()
    12.     {
    13.         buffer = new ComputeBuffer(size * size * size, sizeof(float) * 3, ComputeBufferType.Append);
    14.         argBuffer = new ComputeBuffer(4, sizeof(int), ComputeBufferType.IndirectArguments);
    15.         shader.SetBuffer(0, "buffer", buffer);
    16.         shader.SetFloat("size", size);
    17.         shader.Dispatch(0, size/8, size/8, size/8);
    18.         int[] args = new int[]{ 0, 1, 0, 0 };
    19.         argBuffer.SetData(args);
    20.         ComputeBuffer.CopyCount(buffer, argBuffer, 0);
    21.         argBuffer.GetData(args);
    22.     }
    23.  
    24.     void OnPostRender ()
    25.     {
    26.         material.SetPass(0);
    27.         material.SetBuffer ("buffer", buffer);
    28.         Graphics.DrawProceduralIndirect(MeshTopology.Points, argBuffer, 0);
    29.     }
    30.  
    31.     void OnDestroy ()
    32.     {
    33.         buffer.Release();
    34.         argBuffer.Release();
    35.     }
    36. }
    37.  
    Code (CSharp):
    1. #pragma kernel CSMain
    2.  
    3. AppendStructuredBuffer<float3> buffer;
    4. float size;
    5.  
    6. [numthreads(8,8,8)]
    7. void CSMain (uint3 id : SV_DispatchThreadID)
    8. {
    9.     float3 center = float3(id / size);
    10.     buffer.Append(center);
    11. }
    Code (CSharp):
    1. Shader "AppendBuffer"
    2. {
    3.     SubShader
    4.     {
    5.         Cull Off
    6.         Pass
    7.         {
    8.             CGPROGRAM
    9.             #pragma vertex VSMain
    10.             #pragma geometry GSMain
    11.             #pragma fragment PSMain
    12.             #pragma target 5.0
    13.  
    14.             StructuredBuffer<float3> buffer;
    15.  
    16.             struct Structure
    17.             {
    18.                 float4 position : SV_Position;
    19.                 uint id : custom;
    20.             };
    21.  
    22.             Structure VSMain(uint id : SV_VertexID)
    23.             {
    24.                 Structure VS;
    25.                 VS.position = float4(buffer[id], 1.0);
    26.                 VS.id = id;
    27.                 return VS;
    28.             }
    29.  
    30.             float Sign(int x)
    31.             {
    32.                 return (x > 0) ? 1 : -1;
    33.             }
    34.            
    35.             [maxvertexcount(36)]
    36.             void GSMain( point Structure patch[1], inout TriangleStream<Structure> stream, uint id : SV_PRIMITIVEID )
    37.             {
    38.                 Structure GS;
    39.                 float2 d = float2 (0.01, -0.01);
    40.                 float3 c = float3 (patch[0].position.xyz);
    41.                 for (int i=1; i<7; i++)
    42.                 {
    43.                     GS.position = UnityObjectToClipPos(float4(c.x+d.y*Sign(i!=6), c.y+d.y*Sign(i!=4), c.z+d.y*Sign(i!=2), 1.0));
    44.                     GS.id = patch[0].id;
    45.                     stream.Append(GS);
    46.                     GS.position = UnityObjectToClipPos(float4(c.x+d.x*Sign(i!=3 && i!=4 && i!=5), c.y+d.y*Sign(i!=4), c.z+d.x*Sign(i!=1), 1.0));
    47.                     GS.id = patch[0].id;
    48.                     stream.Append(GS);
    49.                     GS.position = UnityObjectToClipPos(float4(c.x+d.y*Sign(i!=3 && i!=4 && i!=6), c.y+d.x*Sign(i!=3), c.z+d.y*Sign(i!=2), 1.0));
    50.                     GS.id = patch[0].id;
    51.                     stream.Append(GS);
    52.                     GS.position = UnityObjectToClipPos(float4(c.x+d.x*Sign(i!=5), c.y+d.x*Sign(i!=3), c.z+d.x*Sign(i!=1), 1.0));
    53.                     GS.id = patch[0].id;
    54.                     stream.Append(GS);
    55.                     GS.position = UnityObjectToClipPos(float4(c.x+d.y*Sign(i!=3 && i!=4 && i!=6), c.y+d.x*Sign(i!=3), c.z+d.y*Sign(i!=2), 1.0));
    56.                     GS.id = patch[0].id;
    57.                     stream.Append(GS);
    58.                     GS.position = UnityObjectToClipPos(float4(c.x+d.x*Sign(i!=3 && i!=4 && i!=5), c.y+d.y*Sign(i!=4), c.z+d.x*Sign(i!=1), 1.0));
    59.                     GS.id = patch[0].id;
    60.                     stream.Append(GS);
    61.                     stream.RestartStrip();
    62.                 }
    63.             }
    64.  
    65.             float4 PSMain(Structure PS) : SV_Target
    66.             {
    67.                 float k = frac(sin(PS.id)*43758.5453123);
    68.                 return float4(k.xxx, 1.0);
    69.             }
    70.            
    71.             ENDCG
    72.         }
    73.     }
    74. }
    You can use AppendBuffer as StructuredBuffer inside HLSL shader. Example - it makes cubes from points inside Geometry Shader.

    upload_2019-4-21_12-48-57.png
     
  3. itsDmajster

    itsDmajster

    Joined:
    Oct 27, 2014
    Posts:
    45
    Thank you!
     
    deus0 likes this.