Monthly Archives: June 2010

Rotation of Low Order Spherical Harmonics

I'm currently working at university on implementing Light Propagation Volumes. The paper makes extensive use of spherical harmonics while the implementation uses the first two bands.

Below is a visualization of the first 4 bands of the SH basis functions (created using Mayavi):

sh0to3

The first 4 bands of the spherical harmonic basis functions

As you can see the first two bands are 4 functions, so 4 coefficients to store which conveniently fits into one RGBA texture.

One of the main transformations that is performed in the LPV paper is the rotation of the spherical harmonics representation of a clamped cosine lobe (that represents surface lighting) onto a normal vector direction.  It took me a while to figure out, but actually it's quite easy, which is why I write about it :-)

The analytical presentation of the first four base functions is simple:

S_0 \left( x, y, z \right ) = \frac{1}{2 \sqrt{\pi}}
S_1 \left( x, y, z \right ) = - \frac{\sqrt{3}}{2 \sqrt{\pi}} y
S_2 \left( x, y, z \right ) = \frac{\sqrt{3}}{2 \sqrt{\pi}} z
S_3 \left( x, y, z \right ) = - \frac{\sqrt{3}}{2 \sqrt{\pi}} x

To evaluate lighting with SH for some direction v, you first determine the coefficients/weights of the SH basis functions and then sum them up.

 L = \sum_i s_i \, S_i \left( v \right )

Let's assume we know the coefficients  s^z_0, s^z_1, ... of the clamped cosine lobe around the z axis, then we can determine the lighting in direction v for the cosine lobe around the normal n by transforming it into the space where the normal coincides with the z axis (ie rotate n onto the z axis):

 L = \sum_i s^z_i \, S_i \left( R_{n \to z} \, v \right )

where  R_{n \to z} is a rotation matrix that rotates n onto z.

The idea is to expand  S_i \left( R_{n \to z} \, v \right ) and rewrite it in terms of  S_i \left ( v \right ) .

Before doing this, let's first take a look at the coefficients of the clamped cosine lobe:

\begin{align*} 
s^z_0 &=\frac{ \sqrt{ \pi } }{ 2 }\\ 
s^z_1 &= 0\\ 
s^z_2 &= \sqrt\frac{ \pi }{3}\\ 
s^z_3 &= 0\\ 
\end{align*}

The y and x direction are 0 because the cosine lobe is centered isotropic around the z axis:

So let's look at the expanded version of this formula if  r_1^T ,  r_2^T ,  r_3^T are the row vectors of the matrix,
 v=\bigl(\begin{smallmatrix} 
x\\ 
y\\ 
z 
\end{smallmatrix}\bigr) and  R_{n \to z}=\left(\begin{smallmatrix} 
r_1^T\\ 
r_2^T\\ 
r_3^T 
\end{smallmatrix}\right ) , then:

 L = \sum_i s^z_i \, S_i \left( R_{n \to z} \, v \right ) = \sum_i s^z_i \, S_i \left( \left(\begin{smallmatrix} 
r_1^T \, v\\ 
r_2^T \, v\\ 
r_3^T \, v\end{smallmatrix}\right ) \right )
\begin{align*} L &= s^z_0 \, c_0\\ 
&+ s^z_1 \, (-c_1) \, r_2^T \, v \\ 
&+ s^z_2 \, c_1 \, r_3^T \, v\\ 
&+ s^z_3 \, (-c_1) \, r_1^T \, v 
\end{align*}

Since  s^z_1 = 0 and  s^z_3 = 0 :

 L = s^z_0 \, c_0 + s^z_2 \, c_1 \, r_3^T \, v = s^z_0 \, c_0 + s^z_2 \, c_1 \, r_{31} \, x + s^z_2 \, c_1 \, r_{32} \, y + s^z_2 \, c_1 \, r_{33} \, z

  L = s^z_0 \, S_0 \left ( v \right ) - s^z_2 \, r_{32} \, S_1 \left ( v \right )+ s^z_2 \, r_{33} \, S_2 \left ( v \right ) - s^z_2 \, r_{31} \, S_3 \left ( v \right )

Now the question is: what is the third row of  R_{n \to z} ? If we look at the inverse matrix instead:  R_{z \to n} , we can immediately see that its third column has to be n, because  R_{z \to n} \, \bigl(\begin{smallmatrix} 
0\\ 
0\\ 
1 
\end{smallmatrix}\bigr) = n by construction. Since rotations are orthogonal matrices, the inverse is the same as the transposed, so we can deduce that the third row of  R_{n \to z} is the same as the third column of  R_{z \to n} ,  that is: n. Thus with  n = \bigl(\begin{smallmatrix} 
n_x\\ 
n_y\\ 
n_z 
\end{smallmatrix}\bigr) we get:

  L = s^z_0 \, S_0 \left ( v \right ) - s^z_2 \, n_y \, S_1 \left (  v \right )+ s^z_2 \, n_z \, S_2 \left ( v \right ) - s^z_2 \, n_x  \, S_3 \left ( v \right )

So the SH coefficients of the clamped cosine lobe along n are:

 
s^n_0 = s^z_0 = \frac{ \sqrt{ \pi } }{ 2 } \\ 
s^n_1 = - s^z_2 \, n_y =  -\sqrt{ \frac{ \pi }{3} } \, n_y \\ 
s^n_2 = s^z_2 \, n_z = \sqrt{\frac{ \pi }{3} } \, n_z \\ 
s^n_1 = - s^z_2 \, n_x = - \sqrt{\frac{ \pi }{3}} \, n_x

This is it :-)

Cheers,
Andreas

PS: a few screenshots from the LPV project:

GPUPropCopy 0616
noLPV
LPV32P128C

noLPV_2LPV32P128C_2