Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Receiving shadows and writing normals with Graphics.DrawProcedural + Geometry Shader

Discussion in 'Shaders' started by Ilandria, Dec 7, 2017.

  1. Ilandria

    Ilandria

    Joined:
    Mar 8, 2014
    Posts:
    13
    Hello!

    Let me preface this with the fact that I've done a bunch of research already and haven't been able to find enough information to piece this together entirely by myself.

    I am using Graphics.DrawProcedural and a vert/geom/frag shader combo to render a bunch of triangles that were previously generated on the CPU using a marching cubes algorithm, and then pushed to a buffer on the GPU.

    What is currently functional:
    • The triangles are rendered properly in the correct location using a ForwardBase pass and a CommandBuffer that fires in each camera's BeforeForwardOpaque.
    • The generated triangles properly cast shadows on other objects in the scene using a ShadowCaster pass and a CommandBuffer that fires in each camera's BeforeDepthTexture and the main light's BeforeShadowMapPass.
    • The triangles now properly write depth and normals to the DepthNormals texture using another shader pass.
    Currently not functional:
    • The object needs to receive shadows (I assume I have to do something with a ShadowCollector pass, but can't find enough information about it).
    • The object needs to write normals and depth to the DepthNormals texture (this likely has something to do with a replacement shader that I do not know how to edit).
    Things I've tried:
    • Digging around in all of the Unity include files (AutoLight, etc.) and attempting to manually wire shadow-related stuff into the geometry shader (since I can't use the vertex shader macros). This just leads to a bunch of undeclared identifier errors when using things like unity_WorldToLight, etc.
    • Retrieving the light's depth buffer (to no avail) along with the light's worldToLocal matrix and sending those in to the shader as a sampler2D and float4x4 every frame.
    • Hours of googling only to find people wanting help casting shadows (which I've solved) or responses effectively saying "use Unity's macros", which do not function for geometry shaders and/or DrawProcedural.
    Relevant code:

    This is where the actual draw commands are added:
    Code (CSharp):
    1. public void LateUpdate()
    2.     {
    3.         m_shadowCasterBuffer.Clear();
    4.         m_forwardBaseBuffer.Clear();
    5.  
    6.         m_shadowCasterBuffer.DrawProcedural(transform.localToWorldMatrix, p_material, 1, MeshTopology.Points, 1000000);
    7.         m_forwardBaseBuffer.DrawProcedural(transform.localToWorldMatrix, p_material, 0, MeshTopology.Points, 1000000);
    8.  
    9.         foreach (Camera camera in Camera.allCameras)
    10.         {
    11.             camera.RemoveAllCommandBuffers();
    12.             camera.AddCommandBuffer(CameraEvent.BeforeDepthTexture, m_shadowCasterBuffer);
    13.             camera.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, m_forwardBaseBuffer);
    14.         }
    15.      
    16.         sun.RemoveAllCommandBuffers();
    17.         sun.AddCommandBuffer(LightEvent.BeforeShadowMapPass, m_shadowCasterBuffer);
    18.     }

    Here's the shader (non-relevant stuff has been removed):
    Code (csharp):
    1. SubShader
    2. {
    3.     Tags { "RenderType"="Opaque" "Queue"="Geometry" }
    4.  
    5.     Pass
    6.     {
    7.         Name "ForwardBase"
    8.         Tags { "LightMode"="ForwardBase" }
    9.  
    10.         CGPROGRAM
    11.  
    12.         #pragma target 5.0
    13.         #pragma vertex VertMain
    14.         #pragma geometry GeomMain
    15.         #pragma fragment FragMain
    16.         #pragma multi_compile_fwdbase
    17.          
    18.         // NOTE: Removed includes (UnityCG, AutoLight, etc.) to be concise.
    19.  
    20.         struct GeomOutput
    21.         {
    22.             float4 m_clipPos : SV_POSITION;
    23.             float4 m_objectPos : TEXCOORD1;
    24.             float4 m_worldPos : TEXCOORD2;
    25.             float3 m_normal : NORMAL;
    26.             half4 m_colour : COLOR;
    27.         };
    28.  
    29.         struct FragOutput
    30.         {
    31.             half4 m_colour : SV_Target;
    32.             float m_depth : SV_Depth;
    33.         };
    34.  
    35.         // NOTE: Removed non-relevant data types and vert shader to be concise since they're just feeding data into the geometry shader
    36.         [maxvertexcount(3)]
    37.         void GeomMain(point VertOutput a_point[1], inout TriangleStream<GeomOutput> a_triangleStream)
    38.         {
    39.             // NOTE: The geometry shader as-is currently places triangles in the correct location, so all the code here is functional.
    40.             // I removed a check for if a given triangle exists in the data buffer or not because that's not relevant to the current issue.
    41.             // u_triDataBuffer is the ComputeBuffer containing vertex positions, normals, and colours. ptr is a pointer into said buffer.
    42.             Triangle tri = u_triDataBuffer[a_point[0].ptr];
    43.             GeomOutput output;
    44.  
    45.             output.m_objectPos = float4(tri.m_v1.m_objectPos, 1);
    46.             output.m_worldPos = mul(unity_ObjectToWorld, output.m_objectPos);
    47.             output.m_clipPos = UnityObjectToClipPos(output.m_objectPos);
    48.             output.m_normal = mul(unity_ObjectToWorld, float4(tri.m_v1.m_objectNormal, 0)).xyz;
    49.             output.m_colour = half4(tri.m_v1.m_colour);
    50.             a_triangleStream.Append(output);
    51.  
    52.             output.m_objectPos = float4(tri.m_v2.m_objectPos, 1);
    53.             output.m_worldPos = mul(unity_ObjectToWorld, output.m_objectPos);
    54.             output.m_clipPos = UnityObjectToClipPos(output.m_objectPos);
    55.             output.m_normal = mul(unity_ObjectToWorld, float4(tri.m_v2.m_objectNormal, 0)).xyz;
    56.             output.m_colour = half4(tri.m_v2.m_colour);
    57.             a_triangleStream.Append(output);
    58.  
    59.             output.m_objectPos = float4(tri.m_v3.m_objectPos, 1);
    60.             output.m_worldPos = mul(unity_ObjectToWorld, output.m_objectPos);
    61.             output.m_clipPos = UnityObjectToClipPos(output.m_objectPos);
    62.             output.m_normal = mul(unity_ObjectToWorld, float4(tri.m_v3.m_objectNormal, 0)).xyz;
    63.             output.m_colour = half4(tri.m_v3.m_colour);
    64.             a_triangleStream.Append(output);
    65.         }
    66.          
    67.         FragOutput FragMain (GeomOutput a_geomOutput)
    68.         {
    69.             FragOutput output;
    70.              
    71.             half4 litColour = /* NOTE: Here is where I do custom lighting. It's technically proprietary so I had to remove it. */;
    72.  
    73.             // TODO: How do you receive shadows/sample the main directional light's shadow map? That needs to be done here.
    74.             float lightAttenuation = /* ??? */;
    75.  
    76.             output.m_colour = litColour * lightAttenuation;
    77.             float4 clipPos = mul(UNITY_MATRIX_VP, a_geomOutput.m_worldPos);
    78.             output.m_depth = clipPos.z / clipPos.w;
    79.  
    80.             return output;
    81.         }
    82.  
    83.         ENDCG
    84.     }
    85.  
    86.     Pass
    87.     {
    88.         // NOTE: As in the other pass, I edited out a bunch of non-relevant stuff to be concise. See comments above.
    89.         Name "ShadowCaster"
    90.         Tags { "LightMode"="ShadowCaster" }
    91.  
    92.         CGPROGRAM
    93.  
    94.         #pragma target 5.0
    95.         #pragma vertex VertMain
    96.         #pragma geometry GeomMain
    97.         #pragma fragment FragMain
    98.         #pragma multi_compile_shadowcaster
    99.  
    100.         struct GeomOutput
    101.         {
    102.             float4 m_clipPos : SV_POSITION;
    103.             float4 m_worldPos : TEXCOORD1;
    104.         };
    105.  
    106.         struct FragOutput
    107.         {
    108.             float4 m_colour : SV_Target;
    109.             float m_depth : SV_Depth;
    110.         };
    111.  
    112.         [maxvertexcount(3)]
    113.         void GeomMain(point VertOutput a_point[1], inout TriangleStream<GeomOutput> a_triangleStream)
    114.         {
    115.             Triangle tri = u_triDataBuffer[triInfo.ptr];
    116.             GeomOutput output;
    117.  
    118.             float4 objectPos = float4(tri.m_v1.m_objectPos, 1);
    119.             output.m_worldPos = mul(unity_ObjectToWorld, objectPos);
    120.             output.m_clipPos = UnityObjectToClipPos(objectPos);
    121.             a_triangleStream.Append(output);
    122.  
    123.             objectPos = float4(tri.m_v2.m_objectPos, 1);
    124.             output.m_worldPos = mul(unity_ObjectToWorld, objectPos);
    125.             output.m_clipPos = UnityObjectToClipPos(objectPos);
    126.             a_triangleStream.Append(output);
    127.  
    128.             objectPos = float4(tri.m_v3.m_objectPos, 1);
    129.             output.m_worldPos = mul(unity_ObjectToWorld, objectPos);
    130.             output.m_clipPos = UnityObjectToClipPos(objectPos);
    131.             a_triangleStream.Append(output);
    132.         }
    133.          
    134.         FragOutput FragMain (GeomOutput a_geomOutput)
    135.         {
    136.             FragOutput output;
    137.              
    138.             float4 clipPos = mul(UNITY_MATRIX_VP, a_geomOutput.m_worldPos);
    139.             output.m_depth = clipPos.z / clipPos.w;
    140.             output.m_colour = EncodeFloatRGBA(output.m_depth);
    141.  
    142.             return output;
    143.         }
    144.  
    145.         ENDCG
    146.     }
    147. }

    Would anybody be able to give me an explanation (or point me to one!) about how to receive shadows in a situation like this, and how I would go about implementing a custom depthnormals writing pass? If more info is needed, let me know.

    Thank you for the help! :)
     
    Last edited: Dec 7, 2017
  2. Subliminum

    Subliminum

    Joined:
    Nov 9, 2017
    Posts:
    97
  3. Ilandria

    Ilandria

    Joined:
    Mar 8, 2014
    Posts:
    13
    That was actually some really good insight - thanks for the link! Sadly, I still can't get it to function.

    I've found another good resource on the topic right here, which according to I'm doing everything correctly:
    - https://answers.unity.com/storage/temp/54417-grassbillboard.txt

    At this point I'm assuming it's because there's some pass in the Diffuse fallback that gets run automatically by Unity when the shader is on a mesh (which I would do, but I need to render 500000+ triangles, and Unity meshes can not support enough verts for that even at 1 vert per tri just as an index) that I'm currently not running due to using Graphics.DrawProcedural instead.
     
    Last edited: Dec 8, 2017
  4. Subliminum

    Subliminum

    Joined:
    Nov 9, 2017
    Posts:
    97
    Ah that Sejton shader is a great read too. I had heard that there were issues with Draw procedural and lighting/shadows but hopefully theres a solution out there!
     
  5. jimmya

    jimmya

    Unity Technologies

    Joined:
    Nov 15, 2016
    Posts:
    793
  6. Hakazaba

    Hakazaba

    Joined:
    Jul 1, 2015
    Posts:
    119
    Did anyone manage to get this working? I don't want to use mesh, the whole point of using draw procedural is to avoid hanging the cpu with conversion from compute buffers to arrays.
     
    Ilandria likes this.
  7. Ilandria

    Ilandria

    Joined:
    Mar 8, 2014
    Posts:
    13
    I actually got a code example sent to me from a unity developer that solved it for me long after I made this thread, but sadly I no longer work at the company I was with when I ran into this issue so I don't have access to that email anymore. I'll see if a past colleague can dig it up for me and if not I'll try to reproduce the issue and remember the solution then let you know.
     
    Last edited: Mar 24, 2019
    Hakazaba likes this.