Search Unity

Question DrawMeshInstancedIndirect and Shader Graph

Discussion in 'Shader Graph' started by Aaron-Meyers, Aug 2, 2019.

  1. Aaron-Meyers

    Aaron-Meyers

    Joined:
    Dec 8, 2009
    Posts:
    305
    I'm evaluating LWRP/Shader Graph for a project that would rely heavily on DrawMeshInstancedIndirect. I'm unclear on how/if it would work with Shader Graph. Its not apparent how I'd access something like unity_InstanceID from a Shader Graph. I guess I don't really know how you'd read from a StructuredBuffer inside a ShaderGraph either! I don't see an option for it in the Blackboard section.

    Maybe doing instanced rendering that relies on a StructuredBuffer for each instances info would be more of a job for Visual Effect Graph?

    I'm just trying to wrap my head around how this would work with LWRP. Any insight appreciated!
     
    Tony_Max, deus0, Dinamytes and 2 others like this.
  2. thomh_unity

    thomh_unity

    Unity Technologies

    Joined:
    Feb 19, 2019
    Posts:
    22
    Hello Aaron-Meyers,

    DrawMeshInstancedIndirect is not currently supported in Shader Graph.

    This feature is on the road-map but not yet planned.
     
    Lars-Steenhoff likes this.
  3. edan_unity

    edan_unity

    Joined:
    Sep 20, 2017
    Posts:
    3
    Ya I am having the similar issue as well. At the moment I had to flatten the instances if I want to use shader graph rather than copy-and-pasting the whole big chunk of shader code from github. It will be great if there is a way to "hack" around through some custom functions.
     
  4. Aaron-Meyers

    Aaron-Meyers

    Joined:
    Dec 8, 2009
    Posts:
    305
    Thanks for clarifying that. So in the meantime, is Visual Effect Graph a viable option?

    To get more specific, I have been working on a lot of projects visualizing point cloud data and I use DrawMeshInstancedIndirect to draw meshes (usually cubes) at each point contained in a packed buffer for each frame.

    I haven't really dug into VEG yet, but the examples I've seen seem to suggest that StructuredBuffers containing custom structs are supported, but that's just my intuition. Can you confirm if drawing hundred of thousands of cubes from such a buffer in one draw call would be possible using VEG?
     
    Lars-Steenhoff likes this.
  5. andybak

    andybak

    Joined:
    Jan 14, 2017
    Posts:
    569
    It's possible to do something along these lines using Texture2D and Texture3D as your buffers. Check out Keijiro's stuff using the VFX Graph on Github.
     
    Lars-Steenhoff likes this.
  6. sqallpl

    sqallpl

    Joined:
    Oct 22, 2013
    Posts:
    384
    Hi,

    Any updates on this?

    Is DrawMeshInstancedIndirect+ShaderGraph+HDRP not possible at all at the moment?

    Thanks.
     
    Tony_Max, MODev and Lars-Steenhoff like this.
  7. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,526
    Where is the roadmap ?
     
  8. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    I got instanced indirect working with the shader graph.

    I made a subnode graph that injects the setup function that reads a structured buffer and sets the Object2World and WorldToObject matrices for each instance.

    This was done using 2 CustomCode nodes. One with an external file asset with the setup function and the other to add the #pragma to call it.
     
  9. andybak

    andybak

    Joined:
    Jan 14, 2017
    Posts:
    569
    Would you be up for sharing an example?
     
    sqallpl likes this.
  10. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Sure. Here is a link to a package. It is for HD SRP and needs 7.x version to work because of the change to Asset reference instead of a relative path for include files.

    The setup function is for use with Vegetation Studio but just replace that to fit the data/names in your structured buffer.

    Lennart
     
  11. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    And connect it like this. Using the vertex position it will be evaluated for shadow passes also.

    Lennart Image 267.png
     
  12. DongJooByun

    DongJooByun

    Joined:
    May 11, 2019
    Posts:
    2
    Is there any specific reason why you used "2" custom function nodes? I tried to access "unity_InstanceID" with separate setup function as you did, but it spits out "undeclared identifier : unity_InstanceID" error. The only difference is I made them in one custom function node.
     
    Last edited: Mar 21, 2020
    deus0 likes this.
  13. DongJooByun

    DongJooByun

    Joined:
    May 11, 2019
    Posts:
    2
    It seems that your code is working for modifying the pre-existing variables, and it's working in the "setup" function. Is there any way to access the unity_InstanceID not only in the "setup" function but also in the custom function?
     
  14. ViCoX

    ViCoX

    Joined:
    Nov 22, 2013
    Posts:
    37
    Hi,

    I got DrawMeshInstancedIndirect working with this trick! It fills me with joy that it's finally possible : ) Thanks LennartJohansen!
    This however makes me worry about the shadow culling? Does anyone have any idea if it's possible in HDRP?

    Cheers,
    - J
     
  15. andybak

    andybak

    Joined:
    Jan 14, 2017
    Posts:
    569
    I'm a little confused about how to get this to work. I don't suppose anyone has a sample project they could post that doesn't depend on a paid asset like Vegetation Studio?
     
  16. ViCoX

    ViCoX

    Joined:
    Nov 22, 2013
    Posts:
    37
    Hi,
    You create script that fills structured buffer with "IndirectShaderData" data (struct with 2 x matrix4x4) and set that structured buffer to the material with SetBuffer() (you can do it only once so you don't do it every frame)

    The material needs Lit Graph with Lennarts nodes and on Update you call Graphics.DrawMeshInstancedIndirect() and it should work. You won't be needing any meshes ect. just material as you give the mesh, data buffer and argument buffer for DrawMeshInstancedIndirect.
     
    Last edited: Apr 17, 2020
  17. alexandersyurchenko

    alexandersyurchenko

    Joined:
    Oct 29, 2019
    Posts:
    9
    Hey ViCox, I have a very simple script that creates a bunch of quads. However, I'm having trouble with the InversePositionMatrix. It seems to rotate my quads in a weird way. I'm not familiar with this part of shaders and I'm not sure what I'm doing wrong. Any thoughts?

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.Linq;
    5.  
    6. public class MeshInstance : MonoBehaviour {
    7.     public int population;
    8.     public float range;
    9.  
    10.     public Material material;
    11.  
    12.     private ComputeBuffer meshPropertiesBuffer;
    13.     private ComputeBuffer argsBuffer;
    14.  
    15.     private Mesh mesh;
    16.     private Bounds bounds;
    17.  
    18.     // Mesh Properties struct to be read from the GPU.
    19.     // Size() is a convenience function which returns the stride of the struct.
    20.     private struct MeshProperties {
    21.         public Matrix4x4 PositionMatrix;
    22.         public Matrix4x4 InversePositionMatrix;
    23.         //public float ControlData;
    24.  
    25.         public static int Size() {
    26.             return
    27.                 sizeof(float) * 4 * 4 + // matrix;
    28.                 sizeof(float) * 4 * 4; // inverse matrix;
    29.         }
    30.     }
    31.  
    32.     private void Setup() {
    33.         Mesh mesh = CreateQuad();
    34.         this.mesh = mesh;
    35.  
    36.         // Boundary surrounding the meshes we will be drawing.  Used for occlusion.
    37.         bounds = new Bounds(transform.position, Vector3.one * (range + 1));
    38.  
    39.         InitializeBuffers();
    40.     }
    41.  
    42.     private void InitializeBuffers() {
    43.      
    44.         // Argument buffer used by DrawMeshInstancedIndirect.
    45.         uint[] args = new uint[5] { 0, 0, 0, 0, 0 };
    46.  
    47.         // Arguments for drawing mesh.
    48.         // 0 == number of triangle indices, 1 == population, others are only relevant if drawing submeshes.
    49.         args[0] = (uint)mesh.GetIndexCount(0);
    50.         args[1] = (uint)population;
    51.         args[2] = (uint)mesh.GetIndexStart(0);
    52.         args[3] = (uint)mesh.GetBaseVertex(0);
    53.         argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
    54.         argsBuffer.SetData(args);
    55.  
    56.         // Initialize buffer with the given population.
    57.         MeshProperties[] properties = new MeshProperties[population];
    58.  
    59.         for (int i = 0; i < population; i++) {
    60.             MeshProperties props = new MeshProperties();
    61.  
    62.             Vector3 position = Vector3.one * i;
    63.             Quaternion rotation = Quaternion.identity; // Quaternion.Euler(Random.Range(-180, 180), Random.Range(-180, 180), Random.Range(-180, 180));
    64.             Vector3 scale = Vector3.one;
    65.  
    66.             props.PositionMatrix = Matrix4x4.TRS(position, rotation, scale);
    67.             props.InversePositionMatrix = Matrix4x4.TRS(position, rotation, scale).inverse;
    68.  
    69.             properties[i] = props;
    70.         }
    71.  
    72.         meshPropertiesBuffer = new ComputeBuffer(population, MeshProperties.Size());
    73.  
    74.         meshPropertiesBuffer.SetData(properties);
    75.  
    76.         material.SetBuffer("VisibleShaderDataBuffer", meshPropertiesBuffer);
    77.     }
    78.  
    79.  
    80.  
    81.     private Mesh CreateQuad(float width = 1f, float height = 1f) {
    82.         // Create a quad mesh.
    83.         var mesh = new Mesh();
    84.  
    85.         float w = width * .5f;
    86.         float h = height * .5f;
    87.         var vertices = new Vector3[4] {
    88.             new Vector3(-w, -h, 0),
    89.             new Vector3(w, -h, 0),
    90.             new Vector3(-w, h, 0),
    91.             new Vector3(w, h, 0)
    92.         };
    93.  
    94.         var tris = new int[6] {
    95.             // lower left tri.
    96.             0, 2, 1,
    97.             // lower right tri
    98.             2, 3, 1
    99.         };
    100.  
    101.         var normals = new Vector3[4] {
    102.             -Vector3.forward,
    103.             -Vector3.forward,
    104.             -Vector3.forward,
    105.             -Vector3.forward,
    106.         };
    107.  
    108.         var uv = new Vector2[4] {
    109.             new Vector2(0, 0),
    110.             new Vector2(1, 0),
    111.             new Vector2(0, 1),
    112.             new Vector2(1, 1),
    113.         };
    114.  
    115.         mesh.vertices = vertices;
    116.         mesh.triangles = tris;
    117.         mesh.normals = normals;
    118.         mesh.uv = uv;
    119.  
    120.         return mesh;
    121.     }
    122.  
    123.     private void Start() {
    124.         Setup();
    125.     }
    126.  
    127.     private void Update() {
    128.         Graphics.DrawMeshInstancedIndirect(mesh, 0, material, bounds, argsBuffer);
    129.     }
    130.  
    131.     private void OnDisable() {
    132.         // Release gracefully.
    133.         if (meshPropertiesBuffer != null) {
    134.             meshPropertiesBuffer.Release();
    135.         }
    136.         meshPropertiesBuffer = null;
    137.  
    138.         if (argsBuffer != null) {
    139.             argsBuffer.Release();
    140.         }
    141.         argsBuffer = null;
    142.     }
    143. }
     

    Attached Files:

    Last edited: May 14, 2020
  18. Dinamytes

    Dinamytes

    Joined:
    Nov 3, 2016
    Posts:
    51
    You must do it your self, as well as the instance culling.
     
  19. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,769
  20. Dinamytes

    Dinamytes

    Joined:
    Nov 3, 2016
    Posts:
    51
    What exactly? DrawMeshInstancedIndirect can also work in URP.
     
  21. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,769
    I am not concerned at this point about DrawMeshInstancedIndirect, but rather internal Shader Graph injection of Custom Functions and weather they are compatible with URP. Either don't work, I did something wrong. So I would like someone to confirm, if possible.
     
  22. ViCoX

    ViCoX

    Joined:
    Nov 22, 2013
    Posts:
    37
    Hi I got them working in URP so there might be something small you're missing. I can't provide sample right now tho : /
    -J
     
  23. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,769
    That sounds nice.
    Would you be able to drop some insights, when it will be suitable for you?
     
  24. deus0

    deus0

    Joined:
    May 12, 2015
    Posts:
    256
    Any news on this? unity_InstanceID inside a custom shadergraph node? (for use with StructuredBuffer)
     
  25. slime73

    slime73

    Joined:
    May 14, 2017
    Posts:
    107
    deus0 likes this.
  26. deus0

    deus0

    Joined:
    May 12, 2015
    Posts:
    256
    I am getting crashes now with Graphics.DrawProceduralIndirect. I did however end up writing a custom shader instead and got the values I wanted (instanceID and vertID).

    Code (CSharp):
    1. Shader "Zoxel/Minivoxier"
    2. {
    3.     SubShader
    4.     {
    5.         //Tags { "RenderType"="Opaque" }
    6.         LOD 100
    7.         //Tags {"LightMode"="ForwardBase"}
    8.  
    9.         Pass
    10.         {
    11.  
    12.             CGPROGRAM
    13.             #pragma vertex vert
    14.             #pragma fragment frag
    15.             #pragma multi_compile_instancing
    16.             #pragma instancing_options procedural:setup
    17.             //#pragma multi_compile_fog
    18.  
    19.             #pragma target 4.5
    20.  
    21.             #include "UnityCG.cginc"
    22.              
    23.             struct PointData
    24.             {
    25.                 float3 vertex;
    26.                 float4 color;
    27.             };
    28.             struct InstanceData
    29.             {
    30.                 float3 position;
    31.                 float4 color;
    32.             };
    33.             struct VertInput
    34.             {
    35.                 float4 vertex : POSITION;
    36.                 float3 color : COLOR;
    37.             };
    38.             struct VertOutput
    39.             {
    40.                 uint instanceID : SV_InstanceID;
    41.                 float4 vertex : SV_POSITION;
    42.                 float3 color : COLOR;
    43.             };
    44.  
    45.             #if defined(UNITY_PROCEDURAL_INSTANCING_ENABLED)
    46.                 uniform StructuredBuffer<PointData> points : register(t1);
    47.                 uniform StructuredBuffer<InstanceData> datas : register(t2);
    48.             #endif
    49.  
    50.             VertOutput vert(uint id : SV_VertexID , uint instanceID : SV_InstanceID)
    51.             {
    52.                 VertOutput vertOutput;
    53.                 vertOutput.instanceID = instanceID;
    54.                 #if defined(UNITY_PROCEDURAL_INSTANCING_ENABLED)
    55.                     PointData pointer = points[id];
    56.                     InstanceData instanceData = datas[instanceID];
    57.                     float3 vertex = pointer.vertex; //  v.vertex
    58.                     float3 worldPosition = instanceData.position;
    59.                     vertOutput.vertex = mul(UNITY_MATRIX_VP, float4(vertex + worldPosition, 1.0f));
    60.                     vertOutput.color = pointer.color.xyz; // float3(pointer.color.x, pointer.color.y, pointer.color.z);
    61.                 #else
    62.                     vertOutput.vertex = float4(0, 0, 0, 0);
    63.                     vertOutput.color = float3(1, 0, 0);
    64.                 #endif
    65.                 return vertOutput;
    66.             }
    67.  
    68.             fixed4 frag (VertOutput vertOutput)
    69.             {
    70.                 float4 color = float4(vertOutput.color.x, vertOutput.color.y, vertOutput.color.z, 1);
    71.                 #if defined(UNITY_PROCEDURAL_INSTANCING_ENABLED)
    72.                     float3 colorMultiplier = datas[vertOutput.instanceID].color;
    73.                     color.x *= colorMultiplier.x;
    74.                     color.y *= colorMultiplier.y;
    75.                     color.z *= colorMultiplier.z;
    76.                     //color.w *= colorMultiplier.w;
    77.                 #endif
    78.                 return color;
    79.             }
    80.             ENDCG
    81.         }
    82.     }
    83. }
    Vert ID coming to shadergraphs. That's nice. It'll be good for future me haha.
     
  27. Nefron

    Nefron

    Joined:
    Jun 21, 2013
    Posts:
    6
    Hey I just copied your shader into my URP project but it says
    'frag': function return value missing semantics at line 66 (on d3d11)

    Any idea what that means? I don't know anything about shader coding.
     
  28. deus0

    deus0

    Joined:
    May 12, 2015
    Posts:
    256
    Hey! This might be useful:
    https://docs.unity3d.com/Manual/SL-ShaderSemantics.html
    I wrote this while running vulkan (you can set the graphics library in player settings, have to untick the auto graphics toggle)

    I just checked unitys examples, I recommend adding this to the output function, the semantics is the SV_Target part at the end of the function declaration. Just add ': SV_Target' to the end of 'fixed4 frag (VertOutput vertOutput)'


    1. fixed4 frag (VertOutput vertOutput) : SV_Target
    2. {
    3. float4 color = float4(vertOutput.color.x, vertOutput.color.y, vertOutput.color.z, 1);
    4. #if defined(UNITY_PROCEDURAL_INSTANCING_ENABLED)
    5. float3 colorMultiplier = datas[vertOutput.instanceID].color;
    6. color.x *= colorMultiplier.x;
    7. color.y *= colorMultiplier.y;
    8. color.z *= colorMultiplier.z;
    9. //color.w *= colorMultiplier.w;
    10. #endif
    11. return color;
    12. }
     
  29. Deleted User

    Deleted User

    Guest

    jniac likes this.
  30. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    727
    Code (CSharp):
    1.  
    2.                 // only draws first
    3.                 // command.DrawMeshInstancedIndirect(mesh, 0, material, passIndex, args, 0, propertyBlock);
    4.                 // command.DrawMeshInstancedProcedural(mesh, 0, material, passIndex, data.Length, propertyBlock);
    5.  
    6.                 // works??
    7.                 command.DrawMeshInstanced(mesh, 0, material, passIndex, trsData, trsData.Length, propertyBlock);
    Been trying to figure this out. For some reason DrawMeshInstanced works with ShaderGraph. I don't need to do anything and it seems to "just work" - however I want to use a bunch of custom data in a custom StructuredBuffer<MyStruct>. So I need to be able to use DrawMeshInstancedIndirect or DrawMeshInstancedProcedural - but when using them it seems that the InstanceID node in ShaderGraph always returns 0?? Is it just bugged?
     
    nomadshiba and deus0 like this.
  31. nomadshiba

    nomadshiba

    Joined:
    Jun 22, 2015
    Posts:
    27
    Any update on this ^ ?
     
  32. nomadshiba

    nomadshiba

    Joined:
    Jun 22, 2015
    Posts:
    27
    Anyway, made it.
    Used the method described here: https://answers.unity.com/questions/1877154/urp-shader-graphs-with-instanced-indirect-instance.html

    So basically:
    Create a SubGraph, name it "IndirectInstancing" or what ever you wanna call it.
    It will look like this:
    upload_2022-9-14_2-5-39.png
    Don't forget to set Precision in Graph Settings as Single
    upload_2022-9-14_2-5-48.png
    PramaDummy will look like this:
    (Its important that you use "String" `Type` here and not an external file.)
    upload_2022-9-14_2-6-8.png
    Dummy will look like this:
    upload_2022-9-14_2-4-2.png
    External IndirectInstancedDummy.hlsl file will look like this:

    Code (CSharp):
    1. void setupInstancing(){}
    2.  
    3. void Dummy_float(in float3 IN, out float3 OUT)
    4. {
    5.     OUT = IN;
    6. }
    Then you can take this SubGraph and use it anywhere you want like this:
    upload_2022-9-14_2-8-19.png
     
    MagiJedi likes this.
  33. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    862
    When I use the method described above I get a undeclared identifier for unity_ObjectToWorld and the not to helpful message "Use_Macro_UNITY_MATRIX_M_instead_of_unity_ObjectToWorld". Not so helpful because UNITY_MATRIX_M is a constant, so using it will throw the error "l-value specifies const object".

    The error also appears with Graphics.RenderMeshIndirect.

    Unity 2022.2.7

    Did anyone overcome this?

    Solved here:
    https://forum.unity.com/threads/ena...for-drawprocedural.939452/page-2#post-8823352
     
    Last edited: Feb 21, 2023
  34. wechat_os_Qy07sMK9hDQXlCeCTleTh10Zc

    wechat_os_Qy07sMK9hDQXlCeCTleTh10Zc

    Joined:
    Aug 16, 2022
    Posts:
    1
    This helps me also, Thank you for sharing this method.
     
  35. jacemedkniven

    jacemedkniven

    Joined:
    Mar 23, 2019
    Posts:
    2
    bb8_1 and Furgl like this.
  36. Life_Is_Good_

    Life_Is_Good_

    Joined:
    Mar 4, 2013
    Posts:
    43
    Has anyone here tried to replace DrawMeshInstancedIndirect with RenderMeshIndirect? It looks like the trick doesn't work here. I'm getting the correct instance count but just nothing drawn.
     
    gattusoarthur likes this.
  37. bb8_1

    bb8_1

    Joined:
    Jan 20, 2019
    Posts:
    100
    Last edited: Jan 24, 2024
    Life_Is_Good_ likes this.
  38. KYL3R

    KYL3R

    Joined:
    Nov 16, 2012
    Posts:
    135
    I got it working in 2021.3.36f1, HDRP 12.1.14

    Basically the URP examples, but you have to do some #define hack:

    Code (CSharp):
    1. #define unity_ObjectToWorld unity_ObjectToWorld
    2. #define unity_WorldToObject unity_WorldToObject
    that avoids errors like "undeclared identifier 'Use_Macro_UNITY_MATRIX_M_instead_of_unity_ObjectToWorld' error" or "out parameters require l-value arguments".

    I wrote some more detail about that here: https://forum.unity.com/threads/hdr...d-instancing-now-working.886063/#post-9718177

    @bb8_1 - that should fix your "rendered at origin" problem.
     
    Last edited: Mar 23, 2024
    bb8_1 likes this.