Search Unity

Trying to rotate instances with DrawMeshInstancedIndirect shader, but the normals get messed up

Discussion in 'Shaders' started by bugshake, Jul 9, 2019.

  1. bugshake

    bugshake

    Joined:
    Oct 4, 2012
    Posts:
    7
    I'm hoping for some help here, I spent three full days on this and I keep finding the same examples and the same unanswered questions.

    Unity version: 2019.1

    Starting point: the DrawMeshInstancedIndirect example by Unity, https://docs.unity3d.com/ScriptReference/Graphics.DrawMeshInstancedIndirect.html, using the Surface shader example

    This all looks fine:

    unity_forum_instancing_pic1.PNG

    Now, I am modifying the code supply a random rotation rather than a random scale to each instance, as follows:

    Code (CSharp):
    1.         // Positions
    2.         if (positionBuffer != null)
    3.             positionBuffer.Release();
    4.         positionBuffer = new ComputeBuffer(instanceCount, 16);
    5.         Vector4[] positions = new Vector4[instanceCount];
    6.         for (int i = 0; i < instanceCount; i++)
    7.         {
    8.             float angle = Random.Range(0.0f, Mathf.PI * 2.0f);
    9.             float distance = Random.Range(20.0f, 100.0f);
    10.             float height = Random.Range(-2.0f, 2.0f);
    11.             float rot = Random.Range(0f, 2f * Mathf.PI);
    12.             positions[i] = new Vector4(Mathf.Sin(angle) * distance, height, Mathf.Cos(angle) * distance, rot);
    13.         }
    14.         positionBuffer.SetData(positions);
    15.         instanceMaterial.SetBuffer("positionBuffer", positionBuffer);
    Nothing fancy going on there.

    In the surface shader code it gets more interesting. I'm modifying the part in the setup function where the unity_ObjectToWorld matrix is defined:

    Code (CSharp):
    1.     void setup()
    2.     {
    3.     #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    4.         float4 data = positionBuffer[unity_InstanceID];
    5.         float size = 1;
    6.         float rot = data.w;
    7.         float rotation = .01 * _Time.y * 0.5f;
    8.         rotate2D(data.xz, rotation);
    9.  
    10.         unity_ObjectToWorld._11_21_31_41 = float4(size, 0, 0, 0);
    11.         unity_ObjectToWorld._12_22_32_42 = float4(0, size, 0, 0);
    12.         unity_ObjectToWorld._13_23_33_43 = float4(0, 0, size, 0);
    13.         unity_ObjectToWorld._14_24_34_44 = float4(data.xyz, 1);
    14.  
    15.         float s, c;
    16.         sincos(rot, s, c);
    17.         float4x4 rotateX = float4x4(
    18.             1, 0, 0, 0,
    19.             0, c, -s, 0,
    20.             0, s, c, 0,
    21.             0, 0, 0, 1
    22.             );
    23.         unity_ObjectToWorld = mul(unity_ObjectToWorld, rotateX); // this line
    24.  
    25.         unity_WorldToObject = unity_ObjectToWorld;
    26.         unity_WorldToObject._14_24_34 *= -1;
    27.         unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
    28.     #endif
    29.     }
    The rotation works fine (which I confirmed using a cube mesh), but it seems the normals are now also rotated, messing up the lighting:
    unity_forum_instancing_pic2.PNG

    How can I solve this? Is there any place on the internet where I can find sample code for GPU instancing that isn't the original unity examples, but something a little more advanced?

    Thanks so much, I hope you will help me.
     
  2. bugshake

    bugshake

    Joined:
    Oct 4, 2012
    Posts:
    7
    For anyone who find's this, it was asked before and solved, see this post: https://forum.unity.com/threads/inc...es-graphics-drawmeshinstancedindirect.503232/

    The short version is that the way the inverse matrix is calculated in Unity's example is a hack that doesn't work in the general case, so you need this (copy/pasted from the post I linked) :

    Code (CSharp):
    1. float4x4 inverse(float4x4 input)
    2. {
    3. #define minor(a,b,c) determinant(float3x3(input.a, input.b, input.c))
    4.     float4x4 cofactors = float4x4(
    5.         minor(_22_23_24, _32_33_34, _42_43_44),
    6.         -minor(_21_23_24, _31_33_34, _41_43_44),
    7.         minor(_21_22_24, _31_32_34, _41_42_44),
    8.         -minor(_21_22_23, _31_32_33, _41_42_43),
    9.         -minor(_12_13_14, _32_33_34, _42_43_44),
    10.         minor(_11_13_14, _31_33_34, _41_43_44),
    11.         -minor(_11_12_14, _31_32_34, _41_42_44),
    12.         minor(_11_12_13, _31_32_33, _41_42_43),
    13.         minor(_12_13_14, _22_23_24, _42_43_44),
    14.         -minor(_11_13_14, _21_23_24, _41_43_44),
    15.         minor(_11_12_14, _21_22_24, _41_42_44),
    16.         -minor(_11_12_13, _21_22_23, _41_42_43),
    17.         -minor(_12_13_14, _22_23_24, _32_33_34),
    18.         minor(_11_13_14, _21_23_24, _31_33_34),
    19.         -minor(_11_12_14, _21_22_24, _31_32_34),
    20.         minor(_11_12_13, _21_22_23, _31_32_33)
    21.         );
    22. #undef minor
    23.     return transpose(cofactors) / determinant(input);
    24. }
    25. unity_WorldToObject = inverse(unity_ObjectToWorld);