TANGENT in WorldSpace

While transforming the TANGENT into WorldSpace, I find something strange:

I use appdata_full and get the TANGENT of it and present it as the color. It works fine in the ObjectSpace in a cube.The cube has only one side blue, and others are black.

However, after the transformation into the world space via “mul(_Object2World)”. I find the TANGENT changing with the transition(no rotation or scale). It should be a vector, isn’t it? Then how could it change with just the transition. And I check the normal vector, it doesn’t change at all in World Space with transition without question. So what exactly happen there for the TANGENT or maybe I miss some detail?

Shader "Custom/SH_Cg_Transformed" {
    Properties {
        _resultBool("Result", int) = 0
    }
    SubShader {
        pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
          
            int _resultBool;

            struct vertOut {
                float4 pos: SV_POSITION;
                float4 texcoord: TEXCOORD0;
                float3 normal: NORMAL;
                float4 tangent:TANGENT;
            };
      
            vertOut vert(appdata_full input)
            {
                vertOut o;
              
                float4x4 m = _Object2World;
                float4 T = float4(m[0][3],m[1][3],m[2][3],m[3][3]);
              
              
                o.pos = mul(UNITY_MATRIX_MVP,input.vertex);
                o.normal = mul(_Object2World,input.normal);
                //o.normal = input.normal;
                o.tangent = mul(_Object2World,input.tangent);
                //o.tangent = input.tangent;
              
                return o;
            }
      
            float4 frag(vertOut input):COLOR
            {
                if(_resultBool==0)
                    return input.tangent;
                else if(_resultBool==1)
                    return float4(input.normal,1.0);
                else if(_resultBool==2)
                    return dot(float4(input.normal,0.0),input.tangent);
                else
                    return float4(_Object2World[0][3],_Object2World[1][3],_Object2World[2][3],_Object2World[3][3]);
            }
          
            ENDCG  
        }
    }
}

Try mul((float3x3)_Object2World, vector) instead.

Also you’ll probably want

return dot(float4(input.normal,0.0),input.tangent);

to read

return float4(cross(input.normal,input.tangent.xyz) * input.tangent.w, 1.0);

I appreciate it, But that’s not my point. What matter is I wonder how could the Vector of TANGENT, transformed into World Space, could changed with the transition?

A full transform matrix is a float4x4 which includes translation and _Object2World is a float4x4. Converting into a float3x3 removes the translation.

Yes, sorry, I should have been more clear (it’s not often people want full explanations on here).

The _Object2World is a full transform matrix at 4x4. Casting it to 3x3 strips out the translation elements so it’s rotation only.

In Unity, the tangent is a 4 component vector, with the 4th component being 1 or -1 (to indicate the handedness of the TBN matrix it forms - the bitangent isn’t stored with the mesh and is instead reconstructed in the shader via cross product of the normal and tangent.xyz, then multiplied by the tangent.w to account for handedness).

Since you’re feeding in a 4 component vector and the 4th component isn’t 0, it’s getting the full transformation applied to it.

Casting the matrix to 3x3 and multiplying only the tangent xyz means only the vector part of the tangent and the rotational part of the transform matrix are getting used… tangent.w shouldn’t be transformed as it has to be 1 or -1, other values will give incorrect results.

2 Likes

Seems like I just take all vectors as float3 and that’s how I fail.
Thanks!

And I’ve tested the 4th value of TANGENT, it seems to be -1. I once thought its zero because it was clamped to (0,1)
I guess the reason it’s -1 is because all coordinate systems in Unity are left-handed?

It’s to do with whether the UVs are flipped/mirrored compared to the geometry.