Search Unity

Procedural drawing on

Discussion in 'Shaders' started by unity_LCiVHD8FmqIGqw, Dec 25, 2020.

  1. unity_LCiVHD8FmqIGqw

    unity_LCiVHD8FmqIGqw

    Joined:
    Dec 2, 2020
    Posts:
    2
    [EDIT] Sorry, somehow the title was cut off. This question is about Procedural Drawing on single-pass (multi-instance) stereo

    Hello!

    I'm trying to use purely procedural rendering (without a mesh, generating triangles in a geom shader on-the-fly based on a data buffer) in single-pass stereo with Direct3D.

    However, the rendering only works on the left eye. The right eye is empty.

    In the shader, use the UNITY_SETUP_INSTANCE_ID / UNITY_TRANSFER_INSTANCE_ID / UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO macros [1,2].
    If I change the shader to render a mesh instead, then it works on both eyes.
    Also, in multi-pass stereo, both eyes are rendered correctly.
    So I believe that the problem is in the way the draw call is made.
    I tried using CommandBuffer.DrawProcedural() and CommandBuffer.DrawProceduralIndirect(),
    and for both functions I tried setting the instance count to either 1 or 2.
    I also tried CommandBuffer.SetInstanceMultiplier() to double the number of instances.
    But it always only renders on the left eye, never on the right.
    This is both in the Editor (with MockHMD) and on the Hololens,
    and happens in both Unity 2020.1 and Unity2020.2.

    Is there something I'm missing?
    Is there a working example on how to use procedural drawing in single-pass stereo?

    Thanks in advance for any hint.

    [1] https://docs.unity3d.com/2020.2/Documentation/Manual/SinglePassStereoRenderingHoloLens.html
    [2] https://forum.unity.com/threads/is-...ing-with-geometry-shader.898070/#post-5928629
     
    Last edited: Dec 25, 2020
  2. unity_LCiVHD8FmqIGqw

    unity_LCiVHD8FmqIGqw

    Joined:
    Dec 2, 2020
    Posts:
    2
    UPDATE:
    After downgrading to Unity 2019.4 the shader renders on both eyes in the Unity Editor, so it might be a bug in Unity2020.
    But on the Hololens it still only renders on the left eye.

    I've tested this even with a sample project ( https://github.com/keijiro/NoiseBall3 ) and this too renders fine in the Editor in Unity2019, but on the Hololens and in Unity2020 Editor only renders on the left eye.

    Interestingly, there are shadow rendering artifacts visible on the right eye as well (if I remove the shadow rendering in Graphics.DrawProcedural(), the artifacts disappear), so it seems the shader is rendered but the fragment color does not end up in the image.

    I could not find any difference in graphics settings.
     
    Last edited: Dec 28, 2020
  3. mattdevv

    mattdevv

    Joined:
    Dec 28, 2019
    Posts:
    12
    I managed to get Graphics.DrawProceduralIndirect() to work in 2019.4 with single pass instancing by setting the number of instances in the args buffer to 2, and then in the shader something like this:
    Code (CSharp):
    1. struct Attributes
    2. {
    3. uint vertexID : SV_VertexID;
    4. UNITY_VERTEX_INPUT_INSTANCE_ID
    5. };
    6.  
    7. // The order that triangles are defined in my compute shader
    8. struct Triangle {
    9. float3 vertices[3];
    10. float3 normals[3];
    11. };
    12.  
    13. // Append buffer containing output of compute shader
    14. StructuredBuffer<Triangle> _Triangles;
    15.  
    16. // This structure for vertex output
    17. struct VertexOutput {
    18. float3 positionWS   : TEXCOORD0;     // Position in world space
    19. float3 normalWS     : TEXCOORD1;     // Normal vector in world space
    20. float4 positionCS   : SV_POSITION;   // Position in clip space
    21.  
    22. UNITY_VERTEX_OUTPUT_STEREO
    23. };
    24.  
    25. VertexOutput Vert(Attributes input)
    26. {
    27. VertexOutput output = (VertexOutput)0;
    28. UNITY_SETUP_INSTANCE_ID(input);
    29. UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
    30.  
    31. Triangle tri = _Triangles[input.vertexID / 3];
    32. float3 vert = tri.vertices[input.vertexID % 3];
    33. float3 norm = tri.normals[input.vertexID % 3];
    34.  
    35. output.positionWS = vert;
    36. output.normalWS = norm;
    37.    
    38. // Apply shadow caster logic to the CS position
    39. output.positionCS = TransformWorldToHClip(vert, norm);
    40.  
    41. return output;
     
  4. nfauvet_ubi

    nfauvet_ubi

    Joined:
    Jan 18, 2019
    Posts:
    6
    Hello,

    I am quite happy to have found this post, I have almost exactly the same problem.
    Trying to use DrawProceduralIndirect with VR in single-stereo-instanced mode, on an Oculus Rift S.

    I can confirm that it only renders in the left eye within the Oculus and the Unity Mock HMD, in Unity 2020.2.1f1 (HDRP 10.2.2).

    I tried all the macros about instancing and stereo -> no change.
    I tried to double the number of instanceCount in the indirect args buffer, but all it did was to render my geometry two times in the left eye...

    After seeing this post, I tried Unity 2020.3.0f1, with HDRP 10.3.2.
    Now I have SHADOWS in the right eye, and all the rendering in the left. That is some progress :)
    I will try other HDRP versions in this version of Unity and the alpha/beta of the next versions. I will post any findings.

    FYI, in my shaders, I only do a simple vertex shader that extracts the input vertex data from the compute buffers I filled previously, and pass it all the VertMesh() functions of every pass. In this case, all I should have to do is give the AttributeMesh struct the instance_ID of my vertex shader, and the rest of the HDRP pipeline calls all the stereo/instance macros.
     
  5. nfauvet_ubi

    nfauvet_ubi

    Joined:
    Jan 18, 2019
    Posts:
    6
    I tried with 2 other version of Unity/HDRP:

    2021.1.0b12, HDRP 11.0.0
    2021.2.0a9, HDRP 12.0.0

    In both cases, Multi Draw is working, but Single Pass Instanced is not (only left eye).

    I would really like to know if I am doing something wrong, or if it is a bug and we just have to wait.

    On top of that, the viewport and the left eye are flickering (alternate render and black screen). I guess that may be coming from my shaders. Indirect rendering can mess things up :)

    UPDATE:
    Tested the 2019.4.22f1, HDRP 7.5.3.
    Same problem in the Oculus in Single Pass Instanced. Multi ok. And no flickering in scene view.
    That's way too many versions in which it does not work for it to be a Unity bug. Something must be missing in our meddling with HDRP.
     
    Last edited: Mar 22, 2021
  6. nfauvet_ubi

    nfauvet_ubi

    Joined:
    Jan 18, 2019
    Posts:
    6
    Hi again.
    I managed to make it work!!
    FYI, this is what I had to add in order for it to work.

    This is my vertex shader, sourcing vertices from the compute buffer, and calling HDRP VertMesh for a number of passes (shadowcaster, depth_only, gbuffer and forward):

    Code (CSharp):
    1. PackedVaryingsMeshToPS GrassVert(VertexShaderInput input)
    2. {
    3.     // 1) EXTRACT info from the ComputeBuffer.
    4.     DrawTriangle tri = _DrawTriangles[input.vertexID / 3];
    5.     DrawVertex vertex = tri.vertices[input.vertexID % 3];
    6.  
    7.     float3 positionRWS = GetCameraRelativePositionWS(vertex.positionWS);
    8.  
    9.     // 2) CALL the HDRP vertex shader with the extracted info.
    10.     AttributesMesh inputMesh = (AttributesMesh)0;
    11.  
    12.     inputMesh.positionOS = TransformWorldToObject(positionRWS);
    13.  
    14. #if defined(ATTRIBUTES_NEED_NORMAL)
    15.     inputMesh.normalOS = ...;
    16. #endif
    17.  
    18. #if defined(ATTRIBUTES_NEED_TANGENT)
    19.     inputMesh.tangentOS = ...;
    20. #endif
    21.  
    22. #if defined(ATTRIBUTES_NEED_TEXCOORD0)
    23.     inputMesh.uv0 = float4(vertex.uv, 0, 0);
    24. #endif
    25.  
    26.     UNITY_TRANSFER_INSTANCE_ID(input, inputMesh); // copy .instanceID from input to output
    27.  
    28.     VaryingsMeshToPS varyingsType = VertMesh(inputMesh);
    29.     // VertMesh already does:
    30.     //UNITY_SETUP_INSTANCE_ID(input); // setup "unity_InstanceID" and "unity_StereoIndex" from input.instanceID
    31.     //UNITY_TRANSFER_INSTANCE_ID(input, output); // copy .instanceID from input(AttributeMesh) to output(VaryingsMeshToPS)
    32.  
    33.     // THIS IS WHAT I HAD TO ADD, HERE.
    34.     UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(varyingsType); // output.stereoTargetEyeIndexAsRTArrayIdx = unity_StereoEyeIndex
    35.  
    36.     // 3) PACK the result for the rest of the HDRP Pipeline.
    37.  
    38.     PackedVaryingsMeshToPS packed = PackVaryingsMeshToPS(varyingsType);
    39.  
    40.     // Fragment shader does this:
    41.     // UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(packedInput); // unity_StereoEyeIndex = input.stereoTargetEyeIndexAsRTArrayIdx;
    42.  
    43.     return packed;
    44. }
    So I had to add that macro, and add the missing struct variable to every input/output struct (and call a macro inside the Pack/Unpack generated functions), like this:

    Code (CSharp):
    1.                 struct VaryingsMeshToPS
    2.                 {
    3.                     float4 positionCS : SV_POSITION;
    4.                     #if UNITY_ANY_INSTANCING_ENABLED
    5.                     uint instanceID : CUSTOM_INSTANCE_ID;
    6.                     UNITY_VERTEX_OUTPUT_STEREO // <--- THIS
    7.                     #endif
    8.                 };
    9.  
    10.                 struct PackedVaryingsMeshToPS
    11.                 {
    12.                     float4 positionCS : SV_POSITION;
    13.                     #if UNITY_ANY_INSTANCING_ENABLED
    14.                     uint instanceID : CUSTOM_INSTANCE_ID;
    15.                     UNITY_VERTEX_OUTPUT_STEREO // <--- THIS
    16.                     #endif
    17.                 };
    18.    
    19.                 PackedVaryingsMeshToPS PackVaryingsMeshToPS (VaryingsMeshToPS input)
    20.                 {
    21.                     PackedVaryingsMeshToPS output;
    22.                     output.positionCS = input.positionCS;
    23.                     #if UNITY_ANY_INSTANCING_ENABLED
    24.                     output.instanceID = input.instanceID;
    25.                     UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(input, output); // <--- THIS
    26.                     #endif
    27.                     return output;
    28.                 }
    29.                 VaryingsMeshToPS UnpackVaryingsMeshToPS (PackedVaryingsMeshToPS input)
    30.                 {
    31.                     VaryingsMeshToPS output;
    32.                     output.positionCS = input.positionCS;
    33.                     #if UNITY_ANY_INSTANCING_ENABLED
    34.                     output.instanceID = input.instanceID;
    35.                     UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(input, output); // <--- THIS
    36.                     #endif
    37.                     return output;
    38.                 }
    For some reasons, I thought that the struct were good, and the original Vert/Frag shaders did all the required plumbing. After all, when you directly use a generated shader from a shadergraph, it works in VR.

    So this is my solution, it requires more editing of the generated shader, but it works in 2020.2.

    I hope this helps.
     
  7. l_Ventus

    l_Ventus

    Joined:
    Aug 1, 2021
    Posts:
    1
    Hello!
    I tried your solution, but it doesn't work. And I'm using URP, do you think it cause this?
    Code (CSharp):
    1. struct Attributes
    2. {
    3. uint vertexID : SV_VERTEXID;
    4. UNITY_VERTEX_INPUT_INSTANCE_ID
    5. };
    6.  
    7. struct v2f
    8. {
    9. float4 pos : SV_POSITION;
    10. float4 worldPos : TEXCOORD0;
    11. float4 normal : TEXCOORD1;
    12. float4 shadowCoord : TEXCOORD2;
    13. float fogCoord : TEXCOORD3;
    14. float2 uv : TEXCOORD4;
    15. half1 color : TEXCOORD5;
    16. #ifdef LIGHTMAP_ON
    17. half4 texcoord1 : TEXCOORD6;
    18. #endif
    19. UNITY_VERTEX_INPUT_INSTANCE_ID
    20. UNITY_VERTEX_OUTPUT_STEREO
    21. };
    22.  
    23. struct DrawVertex{
    24. //info in world space
    25. float3 objpos;
    26. float color;
    27. float4 positionWS;
    28. float4 normalOS;
    29. float4 normalWS;
    30. float2 uv;
    31. };
    32.  
    33. struct DrawTriangle{
    34. DrawVertex vertices[3];
    35. };
    36.  
    37. StructuredBuffer<DrawTriangle> drawTriangles;
    38.  
    39.  
    40. v2f vert(Attributes input)
    41. {
    42. UNITY_SETUP_INSTANCE_ID(input);
    43. v2f o;
    44.  
    45. DrawTriangle tri = drawTriangles[input.vertexID / 3];
    46. DrawVertex i = tri.vertices[input.vertexID % 3];
    47. VertexPositionInputs vertexInput = GetVertexPositionInputs(i.objpos);
    48.  
    49.  
    50. o.worldPos = i.positionWS;
    51. o.color = i.color;
    52. o.normal = i.normalWS;
    53. o.pos = TransformWorldToHClip(o.worldPos);
    54. o.uv = i.uv;
    55. o.shadowCoord = GetShadowCoord(vertexInput);
    56. o.fogCoord = ComputeFogFactor(o.pos.z);
    57.  
    58. UNITY_TRANSFER_INSTANCE_ID(input,o);
    59. UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    60.  
    61. return o;
    62. }