WorldNormalVector(IN, o.Normal): how is it calculated?

Hi all, I’d like to know how does unity create the normal vector resulting from the world space normal vector and the tangent space normal map: where can I find any documentation?


So I know Im late to the party, but maybe this will help someone someday. WorldNormalVector is a define that applies a rotation matrix to a vector, you can find it by opening a compiled shader:

#define WorldNormalVector(data,normal) fixed3(dot(data.TtoW0,
dot(data.TtoW1,normal), dot(data.TtoW2,normal))

The TtoW values come from the vertex program output when you use INTERNAL_DATA, it is calculated as follows:

o.TtoW0 = mul(rotation, ((float3x3)_Object2World)[0].xyz)*unity_Scale.w;
o.TtoW1 = mul(rotation, ((float3x3)_Object2World)[1].xyz)*unity_Scale.w;
o.TtoW2 = mul(rotation, ((float3x3)_Object2World)[2].xyz)*unity_Scale.w;

TANGENT_SPACE_ROTATION is a define that can be found in UnityCG.cginc:

// Declares 3x3 matrix 'rotation', filled with tangent space basis
	float3 binormal = cross( v.normal, ) * v.tangent.w; \
	float3x3 rotation = float3x3(, binormal, v.normal )

I learned a bit answering this :slight_smile:

So basically you can do it almost the same way you’d do it for vertices. There’s an _ObjectToWorld matrix.

float3 worldPosition = mul( _Object2World, float4( v.vertex, 1.0 ) ).xyz;

The problem is we’re dealing with vectors and not positions, so we don’t want to account for the translation component of the matrix. So we need to ignore the 4th columns of the matrix or the vector. So all you do is cast your vertex normal to a float4 with zero as its w component.

float3 worldNormal = mul( _Object2World, float4( v.normal, 0.0 ) ).xyz;

So a full test shader looks something like this:

Shader "Custom/Display Normals" {
	SubShader {
	    Pass {
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			struct v2f {
			    float4 pos : SV_POSITION;
			    float3 wNormal : COLOR0;
			v2f vert (appdata_base v)
			    v2f o;
			    o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
			    o.wNormal = mul( _Object2World, float4( v.normal, 0.0 ) ).xyz;
			    return o;
			half4 frag (v2f i) : COLOR
			    return half4 (i.wNormal, 1);
	Fallback "VertexLit"

Credit to Patapom for the answer on a Unity forum.

Unity 5.6 Update

In UnityCG.cginc you’ll find:

// Transforms direction from object to world space
inline float3 UnityObjectToWorldDir( in float3 dir )
    return normalize(mul((float3x3)unity_ObjectToWorld, dir));

// Transforms direction from world to object space
inline float3 UnityWorldToObjectDir( in float3 dir )
    return normalize(mul((float3x3)unity_WorldToObject, dir));

// Transforms normal from object to world space
inline float3 UnityObjectToWorldNormal( in float3 norm )
    return UnityObjectToWorldDir(norm);
    // mul(IT_M, norm) => mul(norm, I_M) => {dot(norm, I_M.col0), dot(norm, I_M.col1), dot(norm, I_M.col2)}
    return normalize(mul(norm, (float3x3)unity_WorldToObject));

Using normals in a vertex shader:

struct app2vert
	float4 normal : NORMAL; // Use Fixed4?  

float3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
float localSpaceMask = dot(v.normal, float4(,0));
float WorldSpaceMask = dot(worldNormal, float4(,0));