This is a quick note about some methods for computing an orthonormal basis from a unit (bi)vector. The math directly follows that in “Implied normals (unit bivectors as rotations)”.
Toy C code can be found: HERE
Added notes:
 Quaternions are simply used to find an expression for a rotation and uses that to form equations for two “vectors”. At that point we’re done with quaternions.
 There are two main variants:
 single reference direction – gives a single discontinuity in direction opposite to the reference with exploding error on the approach. This is usable provided we’re working in a space where the input is bound to some spherical cap of the reference direction.
 two opposite reference directions – gives a branchfree solution to the general case problem which moves the discontinuity to the “equator”.
 Additional variants that trade some error bound and/or relaxing some requirements on the produced vectors for few cycles.
A recap of the needed bits from implied. Start with a standard orthonormal basis set:
Given a unit (bi)vector:
Then the quaternion $Q$ which rotates $\mathbf{z}$ to $\mathbf{v}$:
This post gives an informal justification using vectors.
Single reference direction sticking with up seems reasonable
Directly using $\eqref{fx}$ we can transform (rotate) the standard orthonormal set {$\mathbf{x,y,z}$} into a new set {$\mathbf{x’,y’,v}$}:
A simple translation of $\eqref{xp}$ and $\eqref{yp}$ into ‘C’ gives:
Simply inspecting the code or recalling the math shows a problem as $\mathbf{v}$ approaches $\mathbf{z}$, we have an infinite number of solutions for $Q$ and numerically the math explodes. In cases were this is possible the above could be augmented by adding a branch and setting to xp
and yp
to two orthogonal vectors in the {$\mathbf{xy}$}plane or the “correct” values in the limit. Choice of branch point and values depend on use requirements. The next section will provide a branchfree alternate to these cases.
The math basis for this method when using positive $\mathbf{z}$ as a single reference direction is identically to that in Frisvad^{1}. However the math is presented differently and the derivation is carried through differently as well:
which directly translation into code results at least four instead of two products.
additions added: 20180601
The choice of equations slightly changes the error bounds. Below we have a plot for the full domain where the xaxis is the height of the cap around the reference direction.
If we multiply through either pair of equations by 1 above we can drop a number of computations. This is equivalent to rotating the output by $\pi$ or reworking the original equations starting with a basis set of {$\mathbf{x,y,z}$}. Choosing to stick with my form gives:
Two opposite reference directions up and down, why not?
A second possible solution is to form the rotation with nearest direction of two choices. As an example we can form the rotation that maps $\mathbf{z}$ to $\mathbf{v}$ and use that when $v_z < 0$:
Rotating $\mathbf{x}$ and $\mathbf{y}$ by $Q_n$:
The differences between $\eqref{xp} \eqref{yp}$ and $\eqref{xn} \eqref{yn}$ can be expressed by taking the absolute value of $v_z$ and products of $s_z= \text{sgn}\left(v_z\right)$. So we can combine the two expressions as follows:
This method has a potential issue since $Q_n$ maps $\mathbf{z}$ to $\mathbf{v}$. This means the source basis set is {$\mathbf{x,y,z}$} when $v_z < 0$ which is lefthanded^{2}. This is not an issue if we simply need two mutually orthogonal normals in the complement plane, however for cases where it is an issue we can simply negate one of the computed normals in the $v_z < 0$ case which is equivalent to multiplying through by $s_z$. For both we then have:
Choosing to use $s_z \mathbf{x}’$ gives us:
Pixar’s method and inspiration thereof
addition added: 20170401
A recently published paper from Pixar^{3} presents a new method directly derived from that of Frisvad $\eqref{frisvad_x} \eqref{frisvad_y}$ which is both efficient and high quality. Translating their branchfree version into equations we have:
Over reals these are equivalent to $\eqref{xpn}$ and $\eqref{ypnn}$ (so it returns the opposite results of the 2a method above). Their strategy for handling the negative half sphere is obviously superior to the one I used above. Where I was computing both abs and sign of z, they only use the latter. The transform is simple: multiply top and bottom through by $s_z$:
After the palmface I wanted to see what could be done with the new puzzle piece.
Starting with Pixar’s method we can negate both the returned vectors and on X64 hardware this lowers the number of issues from 36 to 32^{4}. Additionally both GCC and clang fail to remove the redundant sz*x
term which bring us to 30:
For X86 we’ve already beaten method 2a in terms of issues, which requires 32.
If we can allow lefthanded results on one of the halfspheres then we can multiply the $\mathbf{x}’$ result of the previous through by $s_z$ which lowers the number of X64 issues to 28 (same as lower quality method 2 above):
So far we haven’t changed the error. Since we are not allowing the compiler to treat FP as reals there are some redundant computations given the order of operations. Choosing to pull out y*a
drops the number of issues to 29/26 respectively and my RMS measures^{5} show a minor negative impact^{6} of ~0.003%. (Both compilers figure out the sz*x
terms here)
That exhasts the things that seem profitable and jump out at me to try working with x component reduction used in $\eqref{frisvad_x}$.
Returning the my previous choice of xcomponent expansion: take $\eqref{xpnn} \eqref{ypnn}$, change to Pixar’s strategy and negating both give a lefthanded system on one half sphere.
and multiply $\mathbf{y}’$ by $s_z$ for right handed version:
Note I’m only using X86 issues as a quickndirty measure of goodness (easy to copynpast into godbolt).
A summary of results:
method  x8664  RMS  relRMS  notes 

basis_2  28  2.5868×10^{8}  7.028%  
basis_2a  32  2.5868×10^{8}  7.028%  
pixar  36  2.4170×10^{8}    as per paper 
pixar_r1  30  2.4170×10^{8}    
pixar_r2  29  2.4215×10^{8}  0.003%  
pixar_l1  28  2.4170×10^{8}    
pixar_l2  26  2.4215×10^{8}  0.003%  
basis_r1  27  2.5868×10^{8}  7.028%  
basis_l2  24  2.5868×10^{8}  7.028% 
Remarks
Other methods
Thanks to Stephen Hill for providing references to some existing methods and (better yet) for writting up a summary in a blog post^{7} and a link to a second pair of methods by Sam Hocevar^{8}.
These methods are all roughly: Given $\mathbf{v}$ inspect the components and choose vector $\bar{\mathbf{w}}$ based on them. Cross product creates an orthgonal vector. This effectively partitions the sphere into “combing” groups.
The method above can be extended at increased computational complexity to shape how the normals are combed. As an example we could add a twist in {$\mathbf{x,y}$} like that found in a Wiechel projection or any of a number of craziness.
Viz
These figures roughly show the behavior of one output normal. I’m not finding a quickanddirty viz that I really like. Down positive z, positive x and negative z.
Method 1: Minimal angle orientation change on sphere.
Method 2: Minimal angle orientation change on two half spheres.
Hughes Moller:
References and Footnotes

“Building an orthonormal basis from a 3d unit vector without normalization”, Jeppe Frisvad, Journal of Graphics Tools 16:33, (2012) PDF/code page ↩

If you don’t like “handedness” then $\mathbf{x}’ \times \mathbf{y}’ = \mathbf{v}$ instead of $\mathbf{v}$. ↩

“Building an Orthonormal Basis, Revisited”, Tom Duff, James Burgess, Per Christensen, Christophe Hery, Andrew Kensler, Max Liani, and Ryusuke Villemin, Journal of Computer Graphics Techniques Vol. 6, No. 1, 2017 JCGT ↩

Instruction issue numbers are from x8664 gcc 6.3 with O3. x8664 clang 4.0.0 O3 numbers appear to be the same. ↩

Take with a grainofsalt. My RMS measures are not in agreement with those reported by Pixar. ↩

Computed the standard way: $1\frac{e}{e_0}$ ↩

“Perpendicular Possibilities”, Stephen Hill, 2011 blog post ↩

“On picking an orthogonal vector (and combing coconuts)”, Sam Hocevar, 2013 blog post ↩