World space normals have some nice properties - they don't depend on camera. This means that on static objects specular and reflections won't wobble when camera moves (imagine FPS game with slight camera movement on idle). Besides their precision doesn't depend on camera. This is important because sometimes we need to deal with normals pointing away from camera. For example because of normals map and perspective correction or because of calculating lighting for back side (subsurface scattering).

Octahedron-normal vectors [MSS*10] are a simple and clever extension of octahedron environment maps [ED08]. The idea is to encode normals by projecting then on a octahedron, folding it and placing on one square. This gives some nice properties like quite uniform value distribution and low encoding and decoding cost.

I compared octahedron to storing 3 components (XYZ) and spherical coordinates. Not a very scientific approach - just rendered some shiny reflective spheres. Normals were stored in world space in a R8G8B8A8 render target. Post contains also complete source code (which unfortunately isn't provided in original paper), so you can paste into your engine and see yourself how this compression looks in practice.

**XYZ**

float3 Encode( float3 n ) { return n * 0.5 + 0.5; } float3 Decode( float3 encN ) { return encN * 2.0 - 1.0; }

**Spherical coordinates**

float2 Encode( float3 n ) { float2 encN; encN.x = atan2( n.y, n.x ) * MATH_INV_PI; encN.y = n.z; encN = encN * 0.5 + 0.5; return encN; } float3 Decode( float2 encN ) { float2 ang = encN * 2.0 - 1.0; float2 scth; sincos( ang.x * MATH_PI, scth.x, scth.y ); float2 scphi = float2( sqrt( 1.0 - ang.y * ang.y ), ang.y ); float3 n; n.x = scth.y * scphi.x; n.y = scth.x * scphi.x; n.z = scphi.y; return n; }

**Octahedron-normal vectors**

float2 Encode( float3 n ) { n /= ( abs( n.x ) + abs( n.y ) + abs( n.z ) + eps ); // eps ~= 0.0012 n.xy = n.z >= 0.0 ? n.xy : ( 1.0 - abs( n.yx ) ) * ( n.yx >= 0.0 ? 1.0 : -1.0 ); n.xy = n.xy * 0.5 + 0.5; return n.xy; } float3 Decode( float2 encN ) { encN = encN * 2.0 - 1.0; float3 n; n.z = 1.0 - abs( encN.x ) - abs( encN.y ); n.xy = n.z >= 0.0 ? encN.xy : ( ( encN.yx >= 0.0 ? 1.0 : -1.0 ) - encN.yx ); n = normalize( n ); return n; }

**Conclusion**

Spherical coordinates have bad value distribution and bad performance. Distribution can be fixed by using some kind of spiral [SPS12]. Unfortunately it still requires costly trigonometry and quality is only marginally better than octahedron encoding.

One other method worth mentioning is Crytek's best fit normals [K10]. It provides extreme precision. On the other hand it won't save any space in G-Buffer as it requires 3 components. Also encoding uses a 512x512 lookup texture, so it's quite expensive.

Octahedron encoding uses a low number of instructions and there are only two non-full rate instruction (calculated on "transcendental unit"). One rcp during encoding and one rcp during decoding. In addition quality is quite good. Concluding octahedron-normal vectors have great quality to performance ratio and blow out of water old methods like spherical coordinates.

UPDATE: As pointed by Alex in the comments, interesting and detailed normal encoding technique survey was just released [CDE*14].

**References**

[MSS*10] Q. Meyer, J. Sübmuth, G. Subner, M. Stamminger, G. Greiner - "On Floating-Point Normal Vectors", Computer Graphics Forum 2010

[ED08] T. Engelhardt, C. Dachsbacher - "Octahedron Environment Maps", VMW 2008

[K10] A. Kaplanyan - "CryENGINE 3: Reaching the speed of light", Siggraph 2010

[SPS12] J. Smith, G. Petrova, S. Schaefer - "Encoding Normal Vectors using Optimized Spherical Coordinates", Computer and Graphics 2012

[CDE*14] - Z. H. Cigolle, S. Donow, D. Evangelakos, M. Mara, M. McGuire, Q. Meyer - "A Survey of Efficient Representations for Independent Unit Vectors", JCGT 2014