Spherical Harmonics Diffuse CubeMap. How to get coefficients?

I am using Spherical Harmonics to turn reflective(mirror like) cube map to diffuse light cube map.

From what I understand for an image, I calculate 9 coefficient(for red green blue) that describes the color of the cube map.

The nine coefficients are this...

And when you visualize them they become...

Then I do something with these coefficients to get color of the cube map.

The part that I am not understanding is using an image to get that 9 coefficients. All the papers kinda assume that you somehow got that coefficient calculated because it is "easy".

I don't know how to do that calculation.

Update

So while I were looking at Spherical Harmonic Lighting | Simon's Tech Blog

I found this equation...

So now I know that the mysterious yellow part of the equation was "solid angle related". I do know how to do solid angle but I still don't understand how to do it.

So at start you have samples from your cube map. Each sample has color and normal (dir) at which you sampled that color. This is how I do it. I use coeffs from this paper (the same you linked), there are values for the first 9 of them.

So for constructing for each sample you:

• Compute your SH basis using normal from sample
float Y00     = 0.282095;
float Y11     = 0.488603 * normal.x;
float Y10     = 0.488603 * normal.z;
float Y1_1    = 0.488603 * normal.y;
float Y21     = 1.092548 * normal.x*normal.z;
float Y2_1    = 1.092548 * normal.y*normal.z;
float Y2_2    = 1.092548 * normal.y*normal.x;
float Y20     = 0.946176 * normal.z * normal.z - 0.315392;
float Y22     = 0.546274 * (normal.x*normal.x - normal.y*normal.y);

• Then multiply these coeffs by your color (L)
vec3 L00   = L * Y00;
vec3 L11   = L * Y11;
vec3 L10   = L * Y10;
vec3 L1_1  = L * Y1_1;
vec3 L21   = L * Y21;
vec3 L2_1  = L * Y2_1;
vec3 L2_2  = L * Y2_2;
vec3 L20   = L * Y20;
vec3 L22   = L * Y22;


And thats it, you have your coeffs. Now, you can store them in some data structure. You accumulate each of them like:

someArray[0] += L00;
someArray[1] += L11;
etc...


And you divide these accumulated coeffs by a number of samples;

Then when reconstructing :

• compute your SH basis one more time for normal at point of the scene.
• for approximating diffuse irradiance at given point of scene, convolution with cosine lobe is performed using normal from that point. Convolution means, for each SH order, multiplication by $\hat{A}=[\pi, \frac{2}{3}\pi, \frac{\pi}{4}]$

$\hat{A_0} = 3.141593$, $\hat{A_1} = 2.094395$, $\hat{A_2} = 0.785398$

• L coeffs are sampled from data structure.
vec3 color = A0*Y00*L00
+ A1*Y1_1*L1_1 + A1*Y10*L10 + A1*Y11*L11
+ A2*Y2_2*L2_2 + A2*Y2_1*L2_1 + A2*Y20*L20 + A2*Y21*L21 + A2*Y22*L22;


Disclaimer: This basically works (for me) but I am not an expert and something might be missing or not perfect because this IS confusing topic...

• Hi. Thank you for detailed instruction. It helps a lot to understand the paper. However I am wondering how you arrived to "cosine lobe in SH" are these numbers ratio given to you in the paper? If so I am not being able to find that section. – Blue Bug Apr 17 '17 at 21:37
• @BlueBug In diffuse lighting lambertian model ray is reflected at many angles rather than as in specular, in one direction. So, in order to approximate this and evaluate irradiance at given point the convolution on cosine lobe is performed - this means in case of SH - division by, for each of SH order 1, 2, 3 - [pi, (2/3)*pi, pi/4]. I hope it helps, check also this or this – narthex Apr 17 '17 at 22:49
• I meant not divison but multiplication, sorry. – narthex Apr 18 '17 at 0:06
• After reading your answer I edited my question to ask you with some description. I don't understand why you don't do the green part of the equation. Are you already doing it but then I am not being able to see what you are doing? Or is it that there are more than one way to make SH map? – Blue Bug Apr 18 '17 at 0:59
• Instead of weighting each cube map sample equally, it would be better to weight by the solid angle of each pixel. Otherwise, you'll over-weight the corners and under-weight the face centers. – Nathan Reed Apr 18 '17 at 20:04

To start I'll assume you have a list of (x,y,z) points each with a r, g, b color that are the samples you want to approximate with the spherical harmonics.

To get the coefficients you make a matrix n x 9 $A$ where each row is the sequence $y_0(x,y,z), y_1^{-1}(x,y,z), y_1^{0}(x,y,z), y_1^{1}(x,y,z), y_2^{-2}(x,y,z), y_2^{-1}(x,y,z), y_2^{0}(x,y,z), y_2^{1}(x,y,z), y_2^{2}(x,y,z)$ of a point and also a n x 3 matrix $b$ with each corresponding row the color at that point.

Then you take a 9x3 matrix $x$ of the unknown coefficients (9 for each color). Then $Ax=b$ is the equation to solve. This equation will be overdetermined so you'll need to approximate the solution.

This means doing a linear regression to find the coefficients that give the smallest error. This is well described in linear algebra.

• I understand that I need a matrix (9 numbers) for each color. So I will need a matrix for red, a matrix for green, and a matrix for blue. However you are telling me these 9 numbers has to be y0,y−11,y01,y11,y−22,y−12,y02,y12,y22 but I don't understand how to calculate these matrix values. I don't understand what you mean by" nx3 matrix b with the color at that point?" What is "n" here? Do you mean just RGB values? – Blue Bug Apr 15 '17 at 2:26
• You don't need to do linear regression. Since SH form an orthonormal basis, each SH's coefficient will be equal to the inner product of the cubemap with the SH: the spherical integral of (cubemap * SH). Or in other words, sum (pixel color * SH evaluated at that pixel's direction vector * solid angle of the pixel) over all pixels in the cubemap. – Nathan Reed Apr 15 '17 at 5:45
• What do you mean by 9x3 matrix x of the unknown coefficients(9 for each color)? So there are three rows? And each row is all the same number that represents either color red or green or blue? Is my job then just multiplying matrix A to mat x to get matrix of 3x3 b? – Blue Bug Apr 17 '17 at 22:10