Search Unity

New Graphics.DrawProcedural does not work

Discussion in 'Shaders' started by kasloV, Sep 16, 2019.

  1. kasloV

    kasloV

    Joined:
    Nov 17, 2018
    Posts:
    2
    Hello,
    in my project I'm creating procedural meshes using ComputeShader and the vertex data is stored in ComputeBuffer on the GPU. I need to render the mesh somehow.

    I've searched for some examples how to correctly make a render directly on GPU without using MeshRenderer component. I've found this https://github.com/keijiro/NoiseBall3
    There is said that starting form Unity 2019.1 Graphics.DrawProcedural was improved and allows to make a draw calls inside standard render pipeline which supports lights and shadows.
    So I wrote a simple shader just to make the mesh visible, assigned it to the material and put the Graphics.DrawProcedural call inside a function which is called every Update of my terrain chunk:

    Code (CSharp):
    1. public void DrawMesh()
    2. {
    3.     m_meshMaterial.SetBuffer("_MeshBuffer", m_meshBuffer);
    4.     Graphics.DrawProcedural(m_meshMaterial,
    5.                             new Bounds(Vector3.zero, Vector3.one * 500),
    6.                             MeshTopology.Triangles,  m_maxVertices, 1, null, null,
    7.                             UnityEngine.Rendering.ShadowCastingMode.TwoSided, true,
    8.                             0);
    9. }
    m_meshBuffer contains vertices with WorldSpace coords previously calculated by ComputeShader.

    m_meshMaterial is assigned with this shader:
    Code (CSharp):
    1. Shader "Materials/World/MyDrawMesh"
    2. {
    3.     SubShader
    4.     {
    5.         Cull off
    6.         Pass
    7.         {
    8.             CGPROGRAM
    9.             #pragma vertex vert
    10.             #pragma fragment frag
    11.             #include "UnityCG.cginc"
    12.  
    13.             struct Vertex
    14.             {
    15.                 float4 position;
    16.                 float3 normal;
    17.             };
    18.          
    19.             StructuredBuffer<Vertex> _MeshBuffer;
    20.  
    21.             struct v2f
    22.             {
    23.                 float4 vertex : SV_POSITION;
    24.                 float3 color : COLOR;
    25.              
    26.             };
    27.  
    28.             v2f vert (uint id : SV_VertexID)
    29.             {
    30.                 v2f o;
    31.                 o.vertex = UnityObjectToClipPos(float4(_MeshBuffer[id].position.xyz,
    32.                                                        1.0f));
    33.                 o.color = dot(float3(0, 1, 0), _MeshBuffer[id].normal) * 0.5 + 0.5;
    34.                 return o;
    35.             }
    36.  
    37.             fixed4 frag(v2f i) : SV_Target
    38.             {
    39.                 return float4(i.color, 1.0f);
    40.             }
    41.             ENDCG
    42.         }
    43.     }
    44. }
    45.  
    Unfortunately this approach draws nothing. However I do get Draw calls in the render pipeline:


    Now if i use the old implementation of Graphics.DrawProcedural (Graphics.DrawProceduralNow) called in OnPostRender method of my main camera using the same shader i get the results.

    Code (CSharp):
    1. private void OnPostRender()
    2. {
    3.     m_meshMaterial.SetBuffer("_MeshBuffer", m_meshBuffer);
    4.     m_meshMaterial.SetPass(0);
    5.     Graphics.DrawProceduralNow(MeshTopology.Triangles,
    6.     m_maxVertices);
    7. }


    But this is not the approach I want because the mesh is being rendered outside the render pipeline hence missing lights and shadows.


    How can I render mesh using the new DrawProcedural method? Author of the NoiseBall example uses Surface Shader but during compilation it is converted into standard vertex/fragment shader. Maybe my shader misses some pragmas or tags. I'm new to shaders so it wouldn't be surprising.
     
  2. kasloV

    kasloV

    Joined:
    Nov 17, 2018
    Posts:
    2
    Ok. After several attempts i found the solution.
    What happens is when I call m_meshMaterial.SetBuffer("_MeshBuffer", m_meshBuffer); I set the buffer in a material shared by all draw calls. And my last terrain chunk which sets the buffer is empty (contains no vertices). Therefore all draws share this data and draws nothing. The key is to use MaterialPropertyBlock:
    Code (CSharp):
    1. public void DrawMesh()
    2.             {
    3.                 MaterialPropertyBlock block = new MaterialPropertyBlock();
    4.                 block.SetBuffer("_MeshBuffer", m_meshBuffer);
    5.                 Graphics.DrawProcedural(m_meshMaterial, new Bounds(Vector3.zero, Vector3.one * 500), MeshTopology.Triangles, m_maxVertices, 1, null, block);
    6.             }
    This allows to set specific material properties (in my case vertices buffer) for individual draw calls.
     
    Ian-Snow, domenk, kosowski and 3 others like this.
  3. erick_weil

    erick_weil

    Joined:
    Feb 3, 2014
    Posts:
    5
    Hi, when providing a shader a ComputeBuffer, i faced a problem like yours, Only the last object is drawn, but DrawProceduralNow don't solved it, also the MaterialPropertyBlock yields the same result. I Looked everywhere and can't find a solution.
    Using a Copy of the Material ( using new Material(original) ) for each object also does not solved it.

    Hope that someone passing by know how to solve this.

    Here is the drawCall
    Code (CSharp):
    1.             MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
    2.             propertyBlock.SetBuffer("_verts3d", buffer_tris3d);
    3.  
    4.             Graphics.DrawProcedural(
    5.                  _material,
    6.                  bounds,
    7.                  MeshTopology.Triangles, tris3d_count, 1, null, propertyBlock,
    8.                  ShadowCastingMode.On, true, 0
    9.              );
    And my Shader

    Code (CSharp):
    1. Shader "Custom/slice4DSurf"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Color", Color) = (1,1,1,1)
    6.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    7.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    8.         _Metallic ("Metallic", Range(0,1)) = 0.0
    9.     }
    10.     SubShader
    11.     {
    12.         Cull off
    13.         Tags { "RenderType"="Opaque" }
    14.         LOD 200
    15.  
    16.         CGPROGRAM
    17.         // Physically based Standard lighting model, and enable shadows on all light types
    18.         #pragma surface surf Standard vertex:vert addshadow
    19.  
    20.         // Use shader model 3.0 target, to get nicer looking lighting
    21.         #pragma target 5.0
    22.  
    23.  
    24.         struct Vertex3d
    25.         {
    26.             float3 p;
    27.             float3 n;
    28.         };
    29.  
    30. #ifdef SHADER_API_D3D11
    31.         StructuredBuffer<Vertex3d> _verts3d;
    32. #endif
    33.  
    34.         sampler2D _MainTex;
    35.  
    36.         struct Input
    37.         {
    38.             float2 uv_MainTex;
    39.         };
    40.  
    41.         struct mydata_full
    42.         {
    43.             float4 vertex    : POSITION;  // The vertex position in model space.
    44.             float3 normal    : NORMAL;    // The vertex normal in model space.
    45.             float4 texcoord  : TEXCOORD0; // The first UV coordinate.
    46.             float4 texcoord1 : TEXCOORD1; // The second UV coordinate.
    47.             float4 texcoord2 : TEXCOORD2; // The second UV coordinate.
    48.             float4 tangent   : TANGENT;   // The tangent vector in Model Space (used for normal mapping).
    49.             float4 color     : COLOR;     // Per-vertex color
    50.             uint id : SV_VertexID;
    51.         };
    52.  
    53.         half _Glossiness;
    54.         half _Metallic;
    55.         fixed4 _Color;
    56.  
    57.         // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
    58.         // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
    59.         // #pragma instancing_options assumeuniformscaling
    60.         UNITY_INSTANCING_BUFFER_START(Props)
    61.             // put more per-instance properties here
    62.  
    63.         UNITY_INSTANCING_BUFFER_END(Props)
    64.  
    65.  
    66.  
    67.         void vert(inout mydata_full v) {
    68.             #ifdef SHADER_API_D3D11
    69.             v.vertex = float4(_verts3d[v.id].p,1.0f);
    70.             v.normal = _verts3d[v.id].n;
    71.             #endif
    72.             //v.vertex.xyz += v.normal * 0.1f;
    73.         }
    74.  
    75.         void surf (Input IN, inout SurfaceOutputStandard o)
    76.         {
    77.             // Albedo comes from a texture tinted by color
    78.             fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    79.             o.Albedo = c.rgb;
    80.             // Metallic and smoothness come from slider variables
    81.             o.Metallic = _Metallic;
    82.             o.Smoothness = _Glossiness;
    83.             o.Alpha = c.a;
    84.         }
    85.         ENDCG
    86.     }
    87.     FallBack "Diffuse"
    88. }
     
    kosowski likes this.
  4. erick_weil

    erick_weil

    Joined:
    Feb 3, 2014
    Posts:
    5
    Posting here my solution, because there is little about that in the internet.

    The problem wasn't in the shader, neither in the drawProcedural call, the problem was in the process of filling out the ComputeBuffer with a ComputeShader, i was using the same computeshader for each object, but the buffers were set only in initialization, which make the last buffer the only set in the shader. Solved this by calling setBuffer each time before the dispatch of the ComputeShader.

    Also if anyone is interested in how to use a Counter without CPU reading of buffers, I used ComputeBuffer.CopyCount to copy the count of the AppendBuffer to a IndirectArgument buffer, and put it in drawProceduralIndirect function. Now i have more than 60 FPS in a scene with realtime GPU generated geometry.
     
    deus0 likes this.