Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Join us on Thursday, June 8, for a Q&A with Unity's Content Pipeline group here on the forum, and on the Unity Discord, and discuss topics around Content Build, Import Workflows, Asset Database, and Addressables!
    Dismiss Notice

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);