Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Incorrect normals on after rotating instances [Graphics.DrawMeshInstancedIndirect]

Discussion in 'Shaders' started by multivac, Nov 5, 2017.

  1. multivac

    multivac

    Joined:
    Oct 27, 2009
    Posts:
    133
    Hello,

    I have some issues rotating instances created with Graphics.DrawMeshInstancedIndirect.

    I store per-instance position, scale and rotation in ComputeBuffer and apply them to the instances in setup() shader function as described here - https://docs.unity3d.com/ScriptReference/Graphics.DrawMeshInstancedIndirect.html
    The positioning and scaling of the instances works fine, and the rotation also works, in a way.
    However, as you can see in the attached image, it seems that the normals of the instance are not rotated along with the object and as a result rotated objects are lit incorrectly.

    I've attached the relevant portion of the setup() code below that deals with the positioning and rotation of the instances. Possibly I'm going about this all wrong by multiplying unity_ObjectToWorld with a rotation matrix, but I do see correct rotations of the instances, so it kind of works.

    I have a hunch that I also need to transform the vertices or normals somehow, but I can't figure out what transformation to apply there, does anyone have any ideas what could be going wrong here?

    Code (CSharp):
    1.  
    2.         void setup()
    3.         {
    4.     #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    5.             int idx = CulledBuffer[unity_InstanceID].index;
    6.             int instance = CulledBuffer[unity_InstanceID].instance;
    7.             GPUAtom atom = AtomBuffer[idx];
    8.             float size = lerp(atom.scaleP, atom.scale, atom.lerpProgress);
    9.             float health = lerp(atom.healthP, atom.health, atom.lerpProgress);
    10.             float angle = lerp(atom.angleP, atom.angle, atom.lerpProgress);
    11.             float3 position = lerp(atom.positionP, atom.position, atom.lerpProgress);
    12.  
    13.             // scale
    14.             unity_ObjectToWorld._11_21_31_41 = float4(size, 0, 0, 0);
    15.             unity_ObjectToWorld._12_22_32_42 = float4(0, size, 0, 0);
    16.             unity_ObjectToWorld._13_23_33_43 = float4(0, 0, size, 0);
    17.  
    18.             // position
    19.             unity_ObjectToWorld._14_24_34_44 = float4(position, 1);
    20.  
    21.             // rotation
    22.             float c = cos(angle);
    23.             float s = sin(angle);
    24.             float4x4 rotateYMatrix = float4x4(
    25.                 c, 0, s, 0,
    26.                 0, 1, 0, 0,
    27.                 -s, 0, c, 0,
    28.                 0, 0, 0, 1);
    29.             unity_ObjectToWorld = mul(unity_ObjectToWorld, rotateYMatrix);
    30.  
    31.             unity_WorldToObject = unity_ObjectToWorld;
    32.             unity_WorldToObject._14_24_34 *= -1;
    33.             unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
    34.  
    35.     #endif
    36.         }
    37.  
     
  2. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Try calculating the inverse matrix for the unity_WorldToObject

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

    multivac

    Joined:
    Oct 27, 2009
    Posts:
    133
    My heartfelt thanks to you, Lennart :)
    That worked, I'm getting correct results now.

    And I think I also understand the reason why it didn't work before - please correct me if I'm wrong.
    The Unity's sample that I used cheaply generates the WorldToObject matrix by simply inverting the position and scale of ObjectToWorld. But if ObjectToWorld is rotated, that trick won't work and the inverse matrix needs to be built up the hard (and correct) way.
     
  4. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Yes. That was probably it. I am guessing your shader used the worldToObject matrix in some normal calc.

    If you have static items you could calculate the inverse matrix before and add that to a compute buffer with the object2World matrix.
     
  5. zeroyao

    zeroyao

    Unity Technologies

    Joined:
    Mar 28, 2013
    Posts:
    169
    Btw if you are always using a uniform scaling like in your pasted code, you can try:
    #pragma instancing_options assumeuniformscaling
    It would probably eliminate the use of unity_WorldToObject if you are using Standard lighting model.
     
  6. LouskRad

    LouskRad

    Joined:
    Feb 18, 2014
    Posts:
    904
    Hi,

    can you by any chance show the structure you use for this compute buffer? (I'm guessing it's your AtomBuffer?)

    I am currently passing positions and rotations in two seperate ComputeBuffers (using a Vector3 array and a Quaternion array respectively) and in the shader using a quaternion to matrix algorithm before setting the unity_ObjectToWorld. Problem is, I can't get rotations right as the Quartenion.Identity results in a top-down rotated mesh. I hape suspiccions that I hit a gimbal lock somehow, but I am not sure. Below is the relevant bit of my shader code, any help would be appreciated:


    Code (CSharp):
    1. #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    2.     StructuredBuffer<float3> positions;
    3.     StructuredBuffer<float4> quaternions;
    4. #endif
    5.  
    6. void setup()
    7. {
    8.     #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    9.         float3 position     = positions[unity_InstanceID];
    10.         float4 q             = quaternions[unity_InstanceID];
    11.         float qr            = q[0];
    12.         float qi            = q[1];
    13.         float qj            = q[2];
    14.         float qk            = q[3];
    15.  
    16.         float4x4 rotation;
    17.         float4x4 translation = {
    18.             1,0,0,position.x,
    19.             0,1,0,position.y,
    20.             0,0,1,position.z,
    21.             0,0,0,1
    22.         };
    23.  
    24.         // quaternion to matrix
    25.         // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/
    26.         // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix
    27.  
    28.         rotation[0][0]            = 1.0f - 2.0f*qj*qj - 2.0f*qk*qk;
    29.         rotation[0][1]            = 2.0f*(qi*qj - qk*qr);
    30.         rotation[0][2]            = 2.0f*(qi*qk + qj*qr);
    31.         rotation[0][3]            = 0.0f;
    32.  
    33.         rotation[1][0]            = 2.0f*(qi*qj+qk*qr);
    34.         rotation[1][1]            = 1.0f - 2.0f*qi*qi - 2.0f*qk*qk;
    35.         rotation[1][2]            = 2.0f*(qj*qk - qi*qr);
    36.         rotation[1][3]            = 0.0f;
    37.  
    38.         rotation[2][0]            = 2.0f*(qi*qk - qj*qr);
    39.         rotation[2][1]            = 2.0f*(qj*qk + qi*qr);
    40.         rotation[2][2]            = 1.0f - 2.0f*qi*qi - 2.0f*qj*qj;
    41.         rotation[2][3]            = 0.0f;
    42.  
    43.         rotation[3][0]            = 0.0f;
    44.         rotation[3][1]            = 0.0f;
    45.         rotation[3][2]            = 0.0f;
    46.         rotation[3][3]            = 1.0f;
    47.  
    48.         unity_ObjectToWorld = mul(translation, rotation);
    49.      
    50.          
    51.         // inverse transform matrix
    52.         // taken from richardkettlewell's post on
    53.         // https://forum.unity3d.com/threads/drawmeshinstancedindirect-example-comments-and-questions.446080/
    54.  
    55.         float3x3 w2oRotation;
    56.         w2oRotation[0] = unity_ObjectToWorld[1].yzx * unity_ObjectToWorld[2].zxy - unity_ObjectToWorld[1].zxy * unity_ObjectToWorld[2].yzx;
    57.         w2oRotation[1] = unity_ObjectToWorld[0].zxy * unity_ObjectToWorld[2].yzx - unity_ObjectToWorld[0].yzx * unity_ObjectToWorld[2].zxy;
    58.         w2oRotation[2] = unity_ObjectToWorld[0].yzx * unity_ObjectToWorld[1].zxy - unity_ObjectToWorld[0].zxy * unity_ObjectToWorld[1].yzx;
    59.  
    60.         float det = dot(unity_ObjectToWorld[0], w2oRotation[0]);
    61.  
    62.         w2oRotation = transpose(w2oRotation);
    63.  
    64.         w2oRotation *= rcp(det);
    65.  
    66.         float3 w2oPosition = mul(w2oRotation, -unity_ObjectToWorld._14_24_34);
    67.  
    68.         unity_WorldToObject._11_21_31_41 = float4(w2oRotation._11_21_31, 0.0f);
    69.         unity_WorldToObject._12_22_32_42 = float4(w2oRotation._12_22_32, 0.0f);
    70.         unity_WorldToObject._13_23_33_43 = float4(w2oRotation._13_23_33, 0.0f);
    71.         unity_WorldToObject._14_24_34_44 = float4(w2oPosition, 1.0f);
    72.  
    73.     #endif
    74. }
     
  7. danilec1313

    danilec1313

    Joined:
    Mar 10, 2018
    Posts:
    6
    You are great man, thank you from 2021!
     
  8. trevk84

    trevk84

    Joined:
    Feb 8, 2016
    Posts:
    3
    I'm confused here. At what line do you paste this code at?