Search Unity

Getting the object space normal map for a skinned mesh?

Discussion in 'Shaders' started by hypnoslave, Jul 4, 2019.

  1. hypnoslave

    hypnoslave

    Joined:
    Sep 8, 2009
    Posts:
    439
    I imagine this is probably a dumb question.... but here I am! I'm looking for how to get either an object-space normal map that works with a deforming skinned mesh, or even better, a correct, normal-map modified world normal that has been rotated to align with the object's orientation.


    1. v2f.normal doesn't work with a skinned mesh's deformations - when a model is bent, what was up becomes something else.

    2. I tried grabbing the normal-mapped normal in the frag (here as "worldNormal") and:
    half3 objectNormals = mul(worldNormal, unity_ObjectToWorld);
    .. but that just gives me the same problem in #1, with the normal map applied (yay!)

    3. Perhaps there's a way to take my worldNormal and transform it by the object's orientation??

    please, someone, show me the single line of code that lays bare my inadequacies.
     
  2. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,550
    Is there a reason you can't just use a tangent space map, which is made for this very purpose?

    You have your mul() the wrong way though I believe. It should be
    unity_WorldToObject
    if you're trying to transform from World to Object space.

    And I'm not sure if your worldNormal is a float4 or not, if not, it should be written as one in the mul() so you can set the .w component to 0 to ensure it is not positionally transformed in the mul.
     
    Last edited: Jul 5, 2019
  3. hypnoslave

    hypnoslave

    Joined:
    Sep 8, 2009
    Posts:
    439
    hey! thanks for the reply Invertex.

    1. Tangent doesn't quite work, because I need information that differentiates the front from the sides from the back, but i need it to remain aligned with the model as it moves. This is part of an artificial lighting scheme that I'm using accross lots of different armour pieces. I could solve this problem with baked lighting, but frankly I want to avoid the texture maps and the pipline that would be required to bake them all. What i'm doing is using the world normals as a lookup on a cubemap with lighting information.

    2. yeah, I thought it would be WorldToObject as well! Honestly though, I don't understand transforms in shaders at all (or matrix math for that matter). WorldToObject has an odd effect - when rotating the object one day, the normals orientation actually appears to rotate the other. ObjectToWorld seems to work for some odd reason, but doesn't work when deformations are applied (and maybe other issues I haven't noticed.)

    3. thanks for the float4 tip - i'll go back and check that!
     
  4. hypnoslave

    hypnoslave

    Joined:
    Sep 8, 2009
    Posts:
    439
    Update! changing my ObjectNormals code to:

    half3 objectNormals = mul((float3x3)unity_WorldToObject, worldNormal.rgb);

    Gives me a much better result. However I still have a mesh on my model that is wrong- the normals seem to be rotated 180 degrees. this could be a problem with my model? I guess?

    looks like I may have misread this issue from the beginning.
     
  5. hypnoslave

    hypnoslave

    Joined:
    Sep 8, 2009
    Posts:
    439
    ooooooohhhh kkaaayyyyy F*** this.

    I'm going to have to do a deep dive into mesh normals and transform matrices. I've put it off for too long.

    Getting lots of inconsistent results here and it's clear that I'm over my head. when rotating bones, sometimes mesh normals change with the rotation and sometimes they don't. Clearly there's an underlying principle that I don't understand.

    If I discover a simple solution I'll post it here.
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    The vertex normals of a mesh (along with its tangents) should always be modified by the animation of the mesh. If you have a shader output the world normal these should always be correct (within vertex interpolation limits). Transforming those vectors into object space will also always be correct for what that mesh's object space is. But that orientation may not be what you're expecting.

    The object space orientation of a rendered mesh and its renderer component's transform may not actually match due to various things related to mesh import, and how Unity decides to apply skinned mesh animation (which may additionally be rotating the mesh). 3DS Max skinned meshes almost always have their mesh rotated by 90 degrees along the x axis, then rotated again 90 degrees by the animation, just due to weirdness with the way FBX files and Unity interact.

    The short version of that is unless all your meshes are ones you've had full control of with a common creation and export methodologies, there's no guarantee that two skinned meshes will have matching object orientated normals. For example assets from the store are basically guaranteed to not match each other. This is okay, it just means you'll need to be aware of that fact and adjust the shader / material for each mesh accordingly.
     
  7. hypnoslave

    hypnoslave

    Joined:
    Sep 8, 2009
    Posts:
    439
    hmm yes, thank you, that explains what I'm seeing. It's pretty fascinating. I'm getting different meshes authored together on the same skeleton with different orientations of their mesh normals. However clearly it's possible to translate that data into correct world space where the different pieces match, so certainly there's something about transforms that I'm just not understanding.


    What I want to do is sample from an ambient light cubemap that has its orientation locked to the object. - so static light as an object moves through an otherwise lit scene.

    it's always the "why would you want to do that" sounding stuff that ends up being difficult.
     
  8. hypnoslave

    hypnoslave

    Joined:
    Sep 8, 2009
    Posts:
    439
    I GOT IT! HAH!

    The problem was that the bounding boxes of the different mesh pieces I had were oriented differently. This can be solved by setting the "root bone" manually inside the skinned mesh renderer, alternatively, there's a skinning solution i found in my particular case.

    Long version:

    For clarity, I've got a number of different armour pieces that are skinned to the same skeleton, so that the player can swap out armour pieces if they like. I'm trying to have a base level of ambient light applied to all the pieces so that the player character is super visible in all lighting conditions. I was doing this with a cubemap that contained lighting information, and I was trying to sample from it thusly:

    texCUBE(_LightingCube, objectNormals).rgbr

    The problem was that getting the normal mapped normals back to object space was causing problems because of the bounding box issue mentioned above. the orientation of each object was different slightly. this can be solved by either setting the root bone as mentioned above, or, from 3dsmax (in my case) for each armour piece, creating a single vertex at the world's origin and weighting it to the same root bone. Then the import process will snag the correct bone as the root and everything will be lovely.
     
    bgolus likes this.