I have written in blitz3D some code to generate arbitrary 2D lighting lookup textures and I'm looking for a way to compress them. The idea is that they look rather smooth and low frequency, there must be way to lower the memory cost using spherical harmonics since they represent lighting over a sphere ...

Let say I have a 2D texture (let say 512²) that is a stack of lighting ramp (look up with a half lambert). X is the NdotL ramp and Y an arbitrary index parameter given by some function.

Nodtl lighting can be represented as a symmetrical spherical harmonics known as zonal harmonics. By transferring the ramp to zonal harmonics I can compress the texture from 512x512 to 512x9(+7 free to have power of 2) ie a stack of spherical harmonics coefficients.

Now the math is way above my head, I read the stupid spherical harmonics tricks and nitty gritty details paper, but while I understand the principle, the math and the translation to code is way above my level.

The idea is that instead of going through all the slices by hand (512 per textures at least) to create custom curves fitting, having a generic "ndotl ramp to zonal harmonics" function might improve production.

What I want is how the code would look like to turn a slice (ramp) sampling into a proper zonal harmonics?

(post edited for clarification))

Optional: Since a ramp can be assimilated to a 1D lookup function, is there a better GENERIC way to represent it than zonal harmonics?

Relative to the 1D function idea ... Assuming I can get the stack of SH, since SH interpolate linearly, how can I infer a function that fit the Y variation of the SH coefficient? What's the best (free) tools to help me do that ie plotting arbitrary point from a source (ramp texture) and finding/visualizing the correct curve? I heard (and dl) python X,Y can help but it's like handling a Japanese manual written in Klingon to me ...

The idea is to make cheap generic shader by precomputing lighting (similar to unity's shadowgun) and various ndotl effect similar to penner Preintegrated skin shader, among many other ...

Optional 2: Just for curiosity, what is the state of spherical needlet basis to represent smooth lighting? (even more above my punch).

  • \$\begingroup\$ It sounds like your function is fundamentally a 2D function, where one argument is N dot L and the other is some arbitrary parameter. I wouldn't use spherical or zonal harmonics at all since the function is not defined on a spherical domain. Try "regular" approximation techniques instead, such as using polynomials, rational functions, power laws, or Fourier or wavelet analysis. \$\endgroup\$ Jul 28, 2013 at 2:30
  • \$\begingroup\$ Well each slice is 1D function and only one slice is use at render time per element (pixel or vertex). While it's possible to treat it as a 2D Function too, it's more accurate to have at least the ndotl argument simplified first and leave the other parameter as the ndol is more important. Also a ramp is basically half a circle rotated along an axis (defining a sphere). Fundamentally it's a stack of 1D functions (ndotl) as the other parameter is arbitrary. However I wouldn't know where to start with using the math at all. \$\endgroup\$
    – user29244
    Jul 28, 2013 at 3:51

1 Answer 1


Turns out Ready at down does that just fine in the ps4 exclusive (for the same reason): http://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_slides.pptx

One issue we had to tackle with our skin shading was using our pre-integrated diffuse scattering term with our spherical harmonics lighting probes.

Fortunately this is pretty simple to do with SH if you already know how to compute standard diffuse irradiance. With SH you can convolve a radially-symmetrical kernel with a lighting environment using a simple dot product, and for standard diffuse you use a clamped cosine as a kernel. All you need to do this is a representation of your kernel as zonal harmonics, so we compute that by numerically integrating the diffuse scattering function with the zonal harmonics basis functions.

We then store those ZH coefficients in a lookup texture parameterized by surface curvature, and at runtime we sample the appropriate coefficients and use them to perform the dot product with the lighting environment stored in the probe. Note page 7

My problem is that I still have problem translating this in useable code ;_;

It's hard to go through the language barrier of the note, but once I pass that the math and it's notation is hard for me to translate (especially anything involving integrale). I hope this useful for someone else ...


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .