Search Unity

Receiving/Casting shadow on/from generated Mesh with standard vertex shader

Discussion in 'General Graphics' started by aivo, Mar 20, 2019.

  1. aivo

    aivo

    Joined:
    Feb 13, 2017
    Posts:
    30
    Hello dear community

    I was wondering if someone could help me out.
    I am trying to get shadows working on a mesh I generate in a script whereas I draw the mesh via Graphics.DrawProceduralNow. From there I pass the data to the vertex shader. From what I understand reading this thread, the following code reflects this approach.

    this is my script so far:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Runtime.InteropServices;
    3.  
    4. public struct Point
    5. {
    6.     public Vector3 vertex;
    7.     public Vector3 normal;
    8.     public Vector4 tangent;
    9.     public Vector2 uv;
    10.     public Vector4 color;
    11. }
    12. public class Test : MonoBehaviour
    13. {
    14.     public Material material;
    15.     private ComputeBuffer myBuffer;
    16.  
    17.     Vector3[] vertices = {
    18.             new Vector3 (0, 0, 0), //0
    19.             new Vector3 (1, 0, 0), //1
    20.             new Vector3 (1, 1, 0), //2
    21.             new Vector3 (0, 1, 0), //3
    22.             new Vector3 (0, 1, 1), //4
    23.             new Vector3 (1, 1, 1), //5
    24.             new Vector3 (1, 0, 1), //6
    25.             new Vector3 (0, 0, 1), //7
    26.         };
    27.     Vector3[] uvs = {
    28.             new Vector2 (0, 0),
    29.             new Vector2 (1, 1),
    30.             new Vector2 (1, 0),
    31.             new Vector2 (0, 0),
    32.             new Vector2 (0, 1),
    33.             new Vector2 (1, 1)
    34.         };
    35.  
    36.     int[] triangles = {
    37.             0, 2, 1, //face front
    38.             0, 3, 2,
    39.             3, 5, 2, //face top
    40.             3, 4, 5,
    41.             1, 5, 6, //face right
    42.             1, 2, 5,
    43.             7, 3, 0, //face left
    44.             7, 4, 3,
    45.             6, 4, 7, //face back
    46.             6, 5, 4,
    47.             7, 1, 6, //face bottom
    48.             7, 0, 1
    49.         };
    50.     public void GetCube(out Vector3[] myVertices, out Vector3[] myNormals, out Vector4[] myTangents, out Vector2[] myUVs)
    51.     {
    52.         Vector3[] v = new Vector3[triangles.Length];
    53.         Vector2[] u = new Vector2[triangles.Length];
    54.         Vector3[] n = new Vector3[triangles.Length];
    55.         Vector4[] t = new Vector4[triangles.Length];
    56.         for (int i = 0; i < triangles.Length; i++)
    57.         {
    58.             v[i] = vertices[triangles[i]];
    59.             u[i] = uvs[i%uvs.Length];
    60.         }
    61.         for (int i = 0; i < t.Length; i++) //hmm..
    62.             t[i] = new Vector4(Random.value, Random.value, Random.value, Random.value);
    63.  
    64.         myVertices = v;
    65.         myUVs = u;
    66.         myTangents = t;
    67.  
    68.         for (int i = 0; i<vertices.Length; i++)
    69.         {
    70.             Vector3 side1 = vertices[triangles[i * 3 + 1]] - vertices[triangles[i * 3 + 0]];
    71.             Vector3 side2 = vertices[triangles[i * 3 + 2]] - vertices[triangles[i * 3 + 0]]; ;
    72.             Vector3 side3 = vertices[triangles[i * 3 + 2]] - vertices[triangles[i * 3 + 1]];
    73.             Vector3 perp1 = Vector3.Cross(side1, side2);
    74.             Vector3 perp2 = Vector3.Cross(side1, side3);
    75.             Vector3 perp3 = Vector3.Cross(side2, side3);
    76.             perp1 /= perp1.magnitude;
    77.             perp2 /= perp2.magnitude;
    78.             perp3 /= perp3.magnitude;
    79.             n[i*3+0] = perp1;
    80.             n[i * 3 + 1] = perp2;
    81.             n[i * 3 + 2] = perp3;
    82.         }
    83.         myNormals = n;
    84.     }
    85.  
    86. private void Start()
    87.     {
    88.         Point[] myPoints = new Point[36];
    89.  
    90.         Vector4[] cols = new Vector4[4] { Color.red, Color.green, Color.blue, Color.white };
    91.         GetCube(out Vector3[] verts, out Vector3[] norms, out Vector4[] tangs, out Vector2[] uvvs);
    92.  
    93.         for (int i = 0; i < verts.Length; i++)
    94.         {
    95.             myPoints[i].vertex = verts[i];
    96.             myPoints[i].uv = uvvs[i];
    97.             myPoints[i].normal = norms[i];
    98.             myPoints[i].tangent = tangs[i];
    99.             myPoints[i].color = cols[i % cols.Length];
    100.         }
    101.  
    102.         myBuffer = new ComputeBuffer(myPoints.Length, Marshal.SizeOf(myPoints.GetType().GetElementType()));    
    103.         myBuffer.SetData(myPoints);
    104.         material.SetBuffer("points", myBuffer);
    105.     }
    106.  
    107.     private void OnRenderObject()
    108.     {
    109.         material.SetPass(0);
    110.         Graphics.DrawProceduralNow(MeshTopology.Triangles, 36);
    111.     }
    112.  
    113.     private void OnDestroy()
    114.     {
    115.         myBuffer.Dispose();
    116.     }
    117. }
    118.  
    ... and this is the shader:

    Code (Boo):
    1. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    2.  
    3. Shader "Custom/StandardSurface"
    4. {
    5.     Properties
    6.     {
    7.         [NoScaleOffset] _MainTex("Texture", 2D) = "white" {}
    8.     }
    9.         SubShader
    10.     {
    11.         Pass
    12.         {
    13.             Tags {"LightMode" = "ForwardBase"}
    14.             Cull Off
    15.             CGPROGRAM
    16.             #pragma vertex vert
    17.             #pragma fragment frag
    18.             #include "UnityCG.cginc"
    19.             #include "Lighting.cginc"
    20.  
    21.         // compile shader into multiple variants, with and without shadows
    22.         // (we don't care about any lightmaps yet, so skip these variants)
    23.         #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight addshadow
    24.         // shadow helper functions and macros
    25.         #include "AutoLight.cginc"
    26.  
    27.         struct v2f
    28.         {
    29.             float2 uv : TEXCOORD0;
    30.             SHADOW_COORDS(1) // put shadows data into TEXCOORD1
    31.             fixed3 diff : COLOR0;
    32.             fixed3 ambient : COLOR1;
    33.             float4 pos : SV_POSITION;
    34.             float4 col : COLOR2;
    35.             float4 tan : TANGENT;
    36.         };
    37.  
    38.     struct Point
    39.     {
    40.         float3 vertex;
    41.         float3 normal;
    42.         float4 tangent;
    43.         float2 uv;
    44.         float4 color;
    45.     };
    46.  
    47.  
    48.     StructuredBuffer<Point> points;
    49.         v2f vert(appdata_tan v, uint vid : SV_VertexID)
    50.         {
    51.             v2f o;
    52.             float4 tpos =  UnityObjectToClipPos(float4(points[vid].vertex, 1) + float4(_SinTime.x,0,_SinTime.y,0));
    53.             o.pos = tpos;
    54.             o.uv = points[vid].uv;
    55.  
    56.             half3 worldNormal = UnityObjectToWorldNormal(points[vid].normal);
    57.             half nl =  max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
    58.             o.diff =  nl * _LightColor0.rgb;
    59.             o.ambient = ShadeSH9(half4(worldNormal,1));
    60.             o.col = points[vid].color;
    61.             o.tan = points[vid].tangent;
    62.             // compute shadows data
    63.             TRANSFER_SHADOW(o)
    64.             return o;
    65.         }
    66.  
    67.         sampler2D _MainTex;
    68.  
    69.         fixed4 frag(v2f i) : SV_Target
    70.         {
    71.             fixed4 col = tex2D(_MainTex, i.uv);
    72.         // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
    73.         fixed shadow = SHADOW_ATTENUATION(i);
    74.         // darken light's illumination with shadow, keep ambient intact
    75.         fixed3 lighting = i.diff * shadow + i.ambient;
    76.         col.rgb *= lighting;
    77.         return col;
    78.         //return float4(i.uv.x, i.uv.y,0,1);
    79.     }
    80.     ENDCG
    81. }
    82.  
    83. // shadow casting support
    84. UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
    85.     }
    86. }

    I just drop the script on my camera and create a material with the shader and assign it on the scrip component.

    So the script generates the mesh with vertices, uvs, normals and tangents (random at the moment because I haven't found a good function to create them yet) and passes the data on to the vertex shader where its being processed following the official "Lit/Diffuse With Shadows" shader . The lighting works, its just not casting or receiving the shadows.

    Am I doing something wrong in the draw call? From what I understand the shader itself should execute the required passes for the shadow generation. Am I wrong here?

    Thank you very much for sheding some lights (and shadows) onto this issue :)
     
  2. aivo

    aivo

    Joined:
    Feb 13, 2017
    Posts:
    30
    It still would be interesting to undestand why the approach described in my post above? Is it because Graphics.DrawProceduralNow() is executed too late?
    Would it be possible using CommandBuffers and execute CommandBuffer.DrawProcedural()?

    Maybee if I find time I will investigate further.
    In the meantime I followed a different approach using deferred lighting having two shader passes using CommandBuffers exectuted at the correct moment. Here is the code on GitHub. Any feedback is welcome.
     
  3. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    You may have more success using Graphics.DrawProcedural, which will be inserted at the appropriate time into each render pass, so you can be sure the shadow data is available when it is rendered into the main color scene.

    Graphics.DrawProceduralNow executes once immediately from wherever it is called, and so may not be at a point where the shadows are available.
     
  4. ThruZero

    ThruZero

    Joined:
    Feb 3, 2023
    Posts:
    2
    @richardkettlewel since URP does not support camera CommandBuffers and Graphics.DrawProcedural is now obsolete since 2021 what's the current recommended approach if any for getting shadow / depth pass rendering now we have Graphics.DrawProceduralNow. Did with lose some baby with Graphics.DrawProcedural bathwater? Hope not! So, any practical example would be golden :)
     
  5. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    is it? have you got a docs link or something showing this?
     
  6. ThruZero

    ThruZero

    Joined:
    Feb 3, 2023
    Posts:
    2
    Hey looks like it was official in the docs from 2022.1
    "This function is now obsolete. For non-indexed rendering, use Graphics.RenderPrimitives instead."
    I had a look at RenderPrimatives and could not find much info on it apart from the unity docs.

    I'm after something I can render in the RP ForwardBase/ShadowCaster passes on MeshTopology.Points (i.e. pointBuffer + geometryShader)
    Code (CSharp):
    1. mat.SetPass(0);
    2. mat.SetMatrix("_Transform", transform.localToWorldMatrix);
    3. mat.SetBuffer("_PointBuffer", pointBuffer);
    4. Graphics.DrawProcedural(MeshTopology.Points, pointBuffer.count, 1);
    https://docs.unity3d.com/2022.1/Documentation/ScriptReference/Graphics.DrawProcedural.html
     
  7. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    There is a code example on the RenderPrimitives doc page (https://docs.unity3d.com/2022.1/Documentation/ScriptReference/Graphics.RenderPrimitives.html) so it's not easy to guess what more info you might need, to be able to use it.

    I'm not on the graphics team so I don't personally know about this method, but, at this point, I think it's clear Unity wants you to use that method instead of DrawProcedural, so it might be best to explain what isn't working/what exactly is unclear for you with that API.

    EDIT: actually, i guess you are looking for a way to only render to certain passes... i will ask a better-informed colleague about this and see if they will take over replying to you :)