Search Unity

Cast and receive shadows with Graphics.DrawProcedural

Discussion in 'Shaders' started by mfreel, Aug 18, 2020.

  1. mfreel

    mfreel

    Joined:
    Aug 22, 2013
    Posts:
    7
    I want to use Graphics.DrawProcedural to draw procedural meshs with a surface shader. I can't get the shader to cast and receive shadows.

    Unity version: 2019.4.4f1

    I have a test shader:

    Code (CSharp):
    1. Shader "Unlit/shaderTest03_surf_no_buffs"
    2. {
    3.  
    4.     Properties
    5.     {
    6.         _Color("Color", Color) = (1,1,1,1)
    7.         _MainTex("Albedo (RGB)", 2D) = "white" {}
    8.         _Glossiness("Smoothness", Range(0,1)) = 0.5
    9.         _Metallic("Metallic", Range(0,1)) = 0.0
    10.     }
    11.     SubShader
    12.     {
    13.         Tags { "RenderType" = "Opaque" }
    14.         LOD 200
    15.  
    16.         //Cull Off
    17.  
    18.         CGPROGRAM
    19.  
    20.         #pragma surface surf Standard vertex:vert addshadow
    21.         //#pragma surface surf Standard vertex:vert fullforwardshadows
    22.         //#pragma surface surf Standard fullforwardshadows
    23.         //#pragma surface surf Standard vertex:vert fullforwardshadows addshadow
    24.         //#pragma surface surf Standard vertex:vert
    25.  
    26.         //#pragma multi_compile_instancing //don't need in surface shader
    27.         #pragma target 4.5
    28.  
    29.         #include "UnityCG.cginc"
    30.  
    31.  
    32.  
    33.     struct appdata
    34.     {
    35.         float4 vertex : POSITION;
    36.         float3 normal : NORMAL;
    37.         //float4 tangent : TANGENT;
    38.         float4 color : COLOR;
    39.         float4 texcoord : TEXCOORD0;
    40.         float4 texcoord1 : TEXCOORD1;
    41.         float4 texcoord2 : TEXCOORD2;
    42.         uint vId : SV_VertexID;
    43.         uint iId: SV_InstanceID;
    44.     };
    45.  
    46.  
    47.  
    48.     struct Input
    49.     {
    50.         float2 uv_MainTex;
    51.     };
    52.  
    53.  
    54.     float4x4 _LocalToWorld;
    55.     float4x4 _WorldToLocal;
    56.  
    57.     void vert(inout appdata v)
    58.     {
    59.      
    60.  
    61.         v.normal = float3(0.,0.,-1.);
    62.  
    63.         if (v.iId == 0) {
    64.             if (v.vId == 0) {
    65.                 v.vertex = float4(0., 0., 0., 0. );
    66.                 v.texcoord = float4(0., 0., 0., 0.);
    67.             }
    68.             else if (v.vId == 1) {
    69.                 v.vertex = float4(0., 1., 0., 0. );
    70.                 v.texcoord = float4(0., 1., 0., 0.);
    71.             }
    72.             else if (v.vId == 2) {
    73.                 v.vertex = float4(1., 1., 0., 0. );
    74.                 v.texcoord = float4(1., 1., 0., 0.);
    75.             }
    76.  
    77.         }
    78.         else {
    79.             if (v.vId == 0) {
    80.                 v.vertex = float4(0., 0., -1., 0.);
    81.                 v.texcoord = float4(0., 0., 0., 0.);
    82.             }                              
    83.             else if (v.vId == 1) {          
    84.                 v.vertex = float4(0., 1., -1., 0.);
    85.                 v.texcoord = float4(0., 1., 0., 0.);
    86.             }                              
    87.             else if (v.vId == 2) {          
    88.                 v.vertex = float4(1., 1., -1., 0. );
    89.                 v.texcoord = float4(1., 1., 0., 0.);
    90.             }
    91.         }
    92.  
    93.  
    94.         // Transform modification
    95.         unity_ObjectToWorld = _LocalToWorld;
    96.         unity_WorldToObject = _WorldToLocal;
    97.     }
    98.  
    99.     fixed4 _Color;
    100.     sampler2D _MainTex;
    101.  
    102.     void surf(Input IN, inout SurfaceOutputStandard o)
    103.     {
    104.  
    105.         float4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    106.      
    107.         o.Albedo = c.rgb;
    108.      
    109.         //o.Albedo = IN.color.rgb;
    110.         //o.Metallic = _Metallic;
    111.         //o.Smoothness = _Smoothness;
    112.         //o.Normal = float3(0, 0, IN.vface < 0 ? -1 : 1); // back face support
    113.         //o.Emission = _Emission * IN.color.rgb;
    114.     }
    115.  
    116.     ENDCG
    117.     }
    118.         //FallBack "Diffuse"
    119.  
    120.  
    121. }
    My test script calls Graphics.DrawProcedural from update:

    Code (CSharp):
    1. void Update()
    2.     {
    3.  
    4.         Bounds bounds = new Bounds(Vector3.zero, new Vector3(10, 10, 10));
    5.  
    6.         matTest03.SetMatrix("_LocalToWorld", transform.localToWorldMatrix);
    7.         matTest03.SetMatrix("_WorldToLocal", transform.worldToLocalMatrix);
    8.  
    9.         matTest03.SetPass(0);
    10.         Graphics.DrawProcedural(
    11.             matTest03,
    12.             bounds,
    13.             MeshTopology.Triangles,
    14.             3, //triArray.Length
    15.             2,
    16.             null,
    17.             null,
    18.             ShadowCastingMode.On,
    19.             true,
    20.             gameObject.layer
    21.         );
    22.  
    23.     }

    What am I missing to get the procedural mesh to cast and receive shadows? The shader sometimes has an error: invalid subscript 'instanceID' 'UnitySetupInstanceID' no matching 1 parameeter function. Sometimes it pops up and other times not. I compile and show code but can't find the error. Maybe this is breaking the shadow casting pass. I try using addshadow and fullforwardshadows. addshadow should be Generating a shadow caster pass.

    My end goal is to read the verts, etc from a StructuredBuffer but can't get even this simple procedural shader to to cast and receive shadows.

    Any help would be appreciated. I would be great if Unity would put a working example shader in the Graphics.DrawProcedural doc.
     
    Last edited: Aug 18, 2020
  2. mfreel

    mfreel

    Joined:
    Aug 22, 2013
    Posts:
    7
    This shader is a better test to show what I want. Basically generate verts, etc in a compute shader and populate a StructuredBuffer. then use Graphics.DrawProcedural to create the mesh. But I need shadows.

    This shader also gives the same invalid subscript 'instanceID' 'UnitySetupInstanceID' no matching 1 parameter function D3D 125 I assume one of the varients can't handle the instanceID.

    The shader runs and draws the mesh and reacts to light, just no shadows cast or received.

    Does anyone have an example of a surface shader with a vertex funtion that can cast and receive shadows they can share?

    run in Unity 2019.4.4f1

    Code (CSharp):
    1. Shader "Unlit/shaderTest01_surf02"
    2. {
    3.  
    4.     Properties
    5.     {
    6.         _Color("Color", Color) = (1,1,1,1)
    7.         _MainTex("Albedo (RGB)", 2D) = "white" {}
    8.         _Glossiness("Smoothness", Range(0,1)) = 0.5
    9.         _Metallic("Metallic", Range(0,1)) = 0.0
    10.     }
    11.     SubShader
    12.     {
    13.         Tags { "RenderType" = "Opaque" }
    14.         LOD 200
    15.  
    16.         //Cull Off
    17.  
    18.         CGPROGRAM
    19.  
    20.         //#pragma surface surf Standard vertex:vert addshadow
    21.         //#pragma surface surf Standard vertex:vert fullforwardshadows
    22.         #pragma surface surf Standard vertex:vert fullforwardshadows addshadow
    23.         //#pragma surface surf Standard vertex:vert
    24.  
    25.         //#pragma multi_compile_instancing //don't need in surface shader
    26.         #pragma target 4.5
    27.  
    28.         #include "UnityCG.cginc"
    29.  
    30.  
    31.  
    32.  
    33.     struct appdata
    34.     {
    35.         float4 vertex : POSITION;
    36.         float3 normal : NORMAL;
    37.         //float4 tangent : TANGENT;
    38.         float4 color : COLOR;
    39.         float4 texcoord : TEXCOORD0;
    40.         float4 texcoord1 : TEXCOORD1;
    41.         float4 texcoord2 : TEXCOORD2;
    42.         uint vId : SV_VertexID;
    43.         uint iId: SV_InstanceID;
    44.     };
    45.  
    46.  
    47.        
    48.    
    49.  
    50.     struct Input
    51.     {
    52.         float2 uv_MainTex;
    53.     };
    54.  
    55.  
    56.     struct vertData
    57.     {
    58.         float3 pos;
    59.         float2 uvs;
    60.         float3 norms;
    61.     };
    62.  
    63. #ifdef SHADER_API_D3D11
    64.  
    65.     StructuredBuffer<vertData> vertsBuff;
    66.     StructuredBuffer<int> triBuff;
    67.  
    68. #endif
    69.  
    70.  
    71.     float4x4 _LocalToWorld;
    72.     float4x4 _WorldToLocal;
    73.  
    74.     void vert(inout appdata v)
    75.     {
    76.        
    77. #ifdef SHADER_API_D3D11
    78.  
    79.         int vertsBuff_Index = triBuff[(v.iId * 6) + v.vId];
    80.         vertData vData = vertsBuff[vertsBuff_Index];
    81.  
    82.         v.vertex.xyz = vData.pos;
    83.         v.normal.xyz = vData.norms;
    84.         v.texcoord = float4(vData.uvs, 0., 0.);
    85.         v.color = float4(1.,0.,0.,1.);
    86.        
    87.  
    88. #endif
    89.  
    90.  
    91.         // Transform modification
    92.         unity_ObjectToWorld = _LocalToWorld;
    93.         unity_WorldToObject = _WorldToLocal;
    94.     }
    95.  
    96.     fixed4 _Color;
    97.     sampler2D _MainTex;
    98.  
    99.     void surf(Input IN, inout SurfaceOutputStandard o)
    100.     {
    101.  
    102.         float4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    103.        
    104.         o.Albedo = c.rgb;
    105.        
    106.        
    107.     }
    108.  
    109.     ENDCG
    110.     }
    111.         FallBack "Diffuse"
    112.  
    113.  
    114. }

    This is the simple test script populating the buffers:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Rendering;
    5.  
    6. public class Test01 : MonoBehaviour
    7. {
    8.  
    9.  
    10.     ComputeBuffer vertsBuff = null;
    11.     ComputeBuffer triBuffer = null;
    12.  
    13.     //public Shader test01Shader;
    14.     public Material matTest01;
    15.  
    16.     struct vertData
    17.     {
    18.         public Vector3 pos;
    19.         public Vector2 uvs;
    20.         public Vector3 norms;
    21.     };
    22.  
    23.     int[] triArray = new int[12];
    24.     vertData[] vDataArray = new vertData[8];
    25.  
    26.     //============================
    27.  
    28.  
    29.  
    30.  
    31.     private void setupShader()
    32.     {
    33.         matTest01.SetBuffer(Shader.PropertyToID("vertsBuff"), vertsBuff);
    34.         matTest01.SetBuffer(Shader.PropertyToID("triBuff"), triBuffer);
    35.  
    36.     }
    37.  
    38.     private void setupTriArray()
    39.     {
    40.        
    41.  
    42.         triArray[0] = 0;
    43.         triArray[1] = 1;
    44.         triArray[2] = 2;
    45.         triArray[3] = 0;
    46.         triArray[4] = 2;
    47.         triArray[5] = 3;
    48.  
    49.         triArray[6] = 4;
    50.         triArray[7] = 5;
    51.         triArray[8] = 6;
    52.         triArray[9] = 4;
    53.         triArray[10] = 6;
    54.         triArray[11] = 7;
    55.  
    56.  
    57.         int triBuffStride = sizeof(int);
    58.         triBuffer = new ComputeBuffer(triArray.Length, triBuffStride, ComputeBufferType.Default);
    59.  
    60.     }
    61.  
    62.     private void setupVertsBuff()
    63.     {
    64.  
    65.         Vector3 off01 = new Vector3();
    66.         Vector3 off02 = new Vector3(0,0.5f,-0.5f);
    67.  
    68.  
    69.         vDataArray[0] = new vertData();
    70.         vDataArray[0].pos = new Vector3(0,0,0) + off01;
    71.         vDataArray[0].norms = new Vector3(0,0,-1);
    72.         vDataArray[0].uvs = new Vector2(0,0);
    73.  
    74.         vDataArray[1] = new vertData();
    75.         vDataArray[1].pos = new Vector3(0,1,0) + off01;
    76.         vDataArray[1].norms = new Vector3(0, 0, -1);
    77.         vDataArray[1].uvs = new Vector2(0, 1);
    78.  
    79.  
    80.         vDataArray[2] = new vertData();
    81.         vDataArray[2].pos = new Vector3(1,1,0) + off01;
    82.         vDataArray[2].norms = new Vector3(0, 0, -1);
    83.         vDataArray[2].uvs = new Vector2(1, 1);
    84.  
    85.         vDataArray[3] = new vertData();
    86.         vDataArray[3].pos = new Vector3(1,0,0) + off01;
    87.         vDataArray[3].norms = new Vector3(0, 0, -1);
    88.         vDataArray[3].uvs = new Vector2(1, 0);
    89.  
    90.         //-----
    91.  
    92.         vDataArray[4] = new vertData();
    93.         vDataArray[4].pos = new Vector3(0,0,0) + off02;
    94.         vDataArray[4].norms = new Vector3(0, 0, -1);
    95.         vDataArray[4].uvs = new Vector2(0, 0);
    96.  
    97.         vDataArray[5] = new vertData();
    98.         vDataArray[5].pos = new Vector3(0, 1, 0) + off02;
    99.         vDataArray[5].norms = new Vector3(0, 0, -1);
    100.         vDataArray[5].uvs = new Vector2(0, 1);
    101.  
    102.  
    103.         vDataArray[6] = new vertData();
    104.         vDataArray[6].pos = new Vector3(1, 1, 0) + off02;
    105.         vDataArray[6].norms = new Vector3(0, 0, -1);
    106.         vDataArray[6].uvs = new Vector2(1, 1);
    107.  
    108.         vDataArray[7] = new vertData();
    109.         vDataArray[7].pos = new Vector3(1, 0, 0) + off02;
    110.         vDataArray[7].norms = new Vector3(0, 0, -1);
    111.         vDataArray[7].uvs = new Vector2(1, 0);
    112.  
    113.  
    114.         int vertsBuffstride = 8 * sizeof(float);
    115.         vertsBuff = new ComputeBuffer(vDataArray.Length, vertsBuffstride, ComputeBufferType.Default);
    116.  
    117.  
    118.     }
    119.  
    120.     private void setBuffData()
    121.     {
    122.         vertsBuff.SetData(vDataArray);
    123.         triBuffer.SetData(triArray);
    124.  
    125.         matTest01.SetMatrix("_LocalToWorld", transform.localToWorldMatrix);
    126.         matTest01.SetMatrix("_WorldToLocal", transform.worldToLocalMatrix);
    127.  
    128.     }
    129.  
    130.     private void setup()
    131.     {
    132.         setupVertsBuff();
    133.         setupTriArray();
    134.         setupShader();
    135.         setBuffData();
    136.  
    137.     }
    138.  
    139.     // Start is called before the first frame update
    140.     void Start()
    141.     {
    142.         setup();
    143.     }
    144.  
    145.     // Update is called once per frame
    146.     void Update()
    147.     {
    148.  
    149.         Bounds bounds = new Bounds(Vector3.zero, new Vector3(10,10,10));
    150.  
    151.         matTest01.SetMatrix("_LocalToWorld", transform.localToWorldMatrix);
    152.         matTest01.SetMatrix("_WorldToLocal", transform.worldToLocalMatrix);
    153.  
    154.         matTest01.SetPass(0);
    155.         Graphics.DrawProcedural(
    156.             matTest01,
    157.             bounds,
    158.             MeshTopology.Triangles,
    159.             6, //triArray.Length
    160.             2,
    161.             null,
    162.             null,
    163.             ShadowCastingMode.On,
    164.             true,
    165.             gameObject.layer
    166.         );
    167.  
    168.     }
    169.  
    170.  
    171.     private void OnDestroy()
    172.     {
    173.         vertsBuff.Dispose();
    174.         triBuffer.Dispose();
    175.     }
    176. }
    177.  
     
  3. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    Hi,

    Graphics.DrawProcedural didn't support shadow casting / receiving, but it was updated in 2019.1. Have you checked Keijiro's examples, like NoiseBall3? Although the readme says it supports standard lighting features, if I'm not mistaken, it did not cast shadows.

    Here's the link:
    https://github.com/keijiro/NoiseBall3

    I used a while ago Graphics.DrawMeshInstancedIndirect on 2018.4 to get shadow casting and receiving and then used a custom vertex program in a surface shader to populate the mesh vertices/colors etc. It worked just fine (receiving shadows and shadow casting) and I think I also got the pointers to right way of doing things from Keijiro's examples. But I don't right now remember which one it was.
    EDIT: I think it was NoiseBall2: https://github.com/keijiro/NoiseBall2
     
  4. mfreel

    mfreel

    Joined:
    Aug 22, 2013
    Posts:
    7
    Olmi thanks for the response. Much appreciated.

    I've loaded and dissected NoiseBall3. NoiseBall3 does not cast or receive shadows. It has a shadow effect on the bottom plane produced by post processing. I think you are correct that Graphics.DrawProcedural does not support shadow casting / receiving. The updated 2019.1 adds Rendering.ShadowCastingMode castShadows, bool receiveShadows. (https://docs.unity3d.com/ScriptReference/Graphics.DrawProcedural.html) I would like Unity to comment on what 'castShadows' 'receiveShadows' does and give a shader example on how to use it. The documentation also write about a , GraphicsBuffer indexBuffer. How does one use this indexBuffer? Does it populate appdata, does it have vertex info in it, or is it just a int array with the triangle indexes. I'm guessing it's a custom vertex index and populates SV_VertexID.

    Thanks for the help. I believe Unity has to give some more information about DrawProcedural and the shaders that will work with it. They give shader examples for Graphics.DrawMeshInstancedIndirect.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You're only setting the
    v.vertex.xyz
    . The
    v.vertex.w
    component is being left as junk data when it needs to be 1.0. Not sure why it works in the main rendering pass at all.

    Change:
    v.vertex.xyz = vData.pos;

    to:
    v.vertex = float4(vData.pos, 1.0);
     
    rustinlee, firstuser and Olmi like this.
  6. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    Thanks @bgolus,
    No wonder the shadows did not work in Keijiro's example. V.vertex is set similarly wrong so that only xyz are set. It indeed casts shadow after w is set to have a value of 1.0. Would be nice to Unity had some examples for these things...
     
  7. rustinlee

    rustinlee

    Joined:
    May 31, 2014
    Posts:
    20
    Haha, I can't believe this was it! I was about to start reimplementing the Standard shader as per https://twitter.com/bonzajplc/status/962765069846278144

    1 line change and now shadows are working with Graphics.DrawProceduralIndirect + surface shaders. You've saved me again!