Convert normals from tangent space to world space

Hi, guys

I want to convert normals (sampled from a bump map) from tangent space to world space. I found that the built-in shaders such as “Bumped Diffuse” use a matrix from tangent space to world space (Tangent2World) to transform the normal, like this:

v2f vert(a2v v) {
        float3 worldPos = mul(_Object2World, v.vertex).xyz; 
        fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); 
        fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); 
        fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
   
        o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
        o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
        o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z); 
}

fixed4 frag(v2f i) : SV_Target {
    fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv));
        bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
}

However, I’ve learned that a normal vector is correctly transformed using the inverse transpose of the matrix used to transform points, in this case, the transpose of the World2Tangent matrix.

So, why unity uses the Tangent2World directly to transform the normals? Will non-uniform scale
cause wrong results?

A non-uniform scale will often cause the normals to point in the wrong direction. This transformation is generally still used, since it’s simpler (and faster) than the transformation that can deal with non-uniform scale. (And most scaling is uniform anyway.)

Since you don’t know whether the scale will be non-uniform at compile time, I guess it would be best to reserve a multi-compile for this. The default being to only handle uniform scale, but a script could switch on non-uniform scale if needed.