Tuesday, November 11, 2014

Making an orthonormal basis from a unit vector

Almost all ray tracing programs have a routine that creates an orthonormal basis from a unit vector n.  I am messing with my Swift ray tracer and just looked around for the "best known method".  This routine is always annoyingly inelegant in my code so I was hoping for something better.  Some googling yeilded a lovely paper by Jeppe Revall Frisvad.   He builds the basis with no normalization required:


It's not obvious, but those are orthonormal.  The catch is the case where 1-nz = 0 so unfortunately an if is needed:

Still even with the branch it is very nice.  I was hoping that a careful use of IEEE floating point rules would allow the branch to be avoided but I don't see it.  But perhaps some clever person will see a reconstructing.   The terms 0*0/0 should be a zero in principle and then all works out.  The good news is that for n.z near 1.0 the stability looks good.

1 comment:

Cem Yuksel said...

This is great! Here is my variant:

if ( n.z >= n.y ) {
    const float a = 1.0f/(1.0f + n.z);
    const float b = -n.x*n.y*a;
    b1 = Vec3f( 1.0f - n.x*n.x*a, b, -n.x );
    b2 = Vec3f( b, 1.0f - n.y*n.y*a, -n.y );
} else {
    const float a = 1.0f/(1.0f + n.y);
    const float b = -n.x*n.z*a;
    b1 = Vec3f( b, -n.z, 1.0f - n.z*n.z*a );
    b2 = Vec3f( 1.0f - n.x*n.x*a, -n.x, b );
}

This one also has a branch, but it is marginally faster with MSVC. More importantly, it doesn't have a singularity, so it behaves better numerically (no division by a very small number).