Spherical Harmonics - solving analytically

maxest

Newcomer
I've gone through this (http://www.inf.ufrgs.br/~oliveira/pubs_files/Slomp_Oliveira_Patricio-Tutorial-PRT.pdf) paper and everything seems pretty clear to me. There is a nice framework for numerical integration of light projection into SH as well as transfer function.

However, I would like to use a simple directional light on a simple Lambertian diffuse surface. That should be fairly simple to solve analytically but I'm having difficulties with that. I was told that for a Lambertian surface the transfer function is cos(theta) and the light function should just be constant color projected into SH in the direction of the light.

For the light part it's simple:
Code:
vector<vec3> ProjectLightFunction_Dir(int bandsCount, const vec3& dir, const vec3& col)
{
   vector<float> sh;

   float r = sqrtf(dir.x*dir.x + dir.y*dir.y + dir.z*dir.z);
   float theta = acosf(dir.z/r);
   float phi = atanf(dir.y/dir.x);

   for (int l = 0; l < bandsCount; l++)
   {
     for (int m = -l; m <= l; m++)
     {
       sh.push_back(SphericalHarmonic(l, m, theta, phi));
     }
   }

   vector<vec3> coeffs;
   coeffs.resize(bandsCount * bandsCount);

   for (int i = 0; i < bandsCount * bandsCount; i++)
   {
     coeffs[i].x = 0.0f;
     coeffs[i].y = 0.0f;
     coeffs[i].z = 0.0f;
   }

   for (int i = 0; i < bandsCount * bandsCount; i++)
   {
     coeffs[i].x += col.x * sh[i];
     coeffs[i].y += col.y * sh[i];
     coeffs[i].z += col.z * sh[i];
   }

   return coeffs;
}
This indeed is just a projection of col into SH towards dir direction.

I couldn't figure out how to solve the transfer function analytically thought. The only thing I came up with is this:
Code:
vector<vec3> ProjectTransferFunction_Dir(const vector<Sample>& samples, int bandsCount, const vec3& normal)
{
   vector<vec3> coeffs;
   coeffs.resize(bandsCount * bandsCount);

   for (int i = 0; i < bandsCount * bandsCount; i++)
   {
     coeffs[i].x = 0.0f;
     coeffs[i].y = 0.0f;
     coeffs[i].z = 0.0f;
   }

   for (unsigned int i = 0; i < samples.size(); i++)
   {
     float transfer = DotProduct(samples[i].cartesian_coord, normal);

     for (int j = 0; j < bandsCount * bandsCount; j++)
     {
       coeffs[j].x += transfer * samples[i].sh[j];
       coeffs[j].y += transfer * samples[i].sh[j];
       coeffs[j].z += transfer * samples[i].sh[j];
     }
   }

   float weight = 4.0f*PI;
   float scale = weight / samples.size();
   for (int i = 0; i < bandsCount * bandsCount; i++)
   {
     coeffs[i].x *= scale;
     coeffs[i].y *= scale;
     coeffs[i].z *= scale;
   }

   return coeffs;
}
As you see, I usee Monte Carlo integration here to integrate the dot function. Obviously, it's pretty slow and I would love to avoid iterating through the samples in favor of having an analytical form.

Any ideas?
 
Back
Top