0
\$\begingroup\$

I've been implementing spherical harmonics in my Engine for learning purpose.

I was wondering if Spherical Harmonics are still used in modern engine or if they moved on to the more simple cubemaps or pre-convolved environment map with PBR and IBL (e.g in UE4) or any other technique for performance and good visual purpose ? What is the technique with the best performance/good visual ratio compared to SH ?

\$\endgroup\$
  • \$\begingroup\$ Wow... I haven't thought about Spherical Harmonics since physics gradschool. In the late 90s. \$\endgroup\$ – Almo Oct 25 '16 at 13:34
  • \$\begingroup\$ Haha, I see. I'm trying many techniques to learn ;) \$\endgroup\$ – CpCd0y Oct 25 '16 at 13:35
  • \$\begingroup\$ :D It might be the right thing here; I don't really know! \$\endgroup\$ – Almo Oct 25 '16 at 13:37
  • \$\begingroup\$ I use them for my ambient lighting. Build a grid of probs-> Bake cube map to SH data-> pull out active SH data per frame-> Use tile based deferred(2000 SH probs) to render SH data using world normal. I can make an answer if you like and go into more detail. I think there is a paper about FarCry 4 ambient lighting to I used for my implementation, not 100% the same as I don't use volume textures \$\endgroup\$ – Justin William Stanley Bryant Oct 26 '16 at 0:08
  • \$\begingroup\$ That would be interesting if you could detail that in an answer and also explain why using that technique instead of another \$\endgroup\$ – CpCd0y Oct 26 '16 at 6:17
2
\$\begingroup\$

Ok so first up the way I use my Spherical Harmonics is to build a grid of probe points along the top of the terrain\objects, I offset the y value of the probe so that when I capture a cubemap of the location I get all 6 faces filled.

I render the cubemap at 16x16 or 32x32 pixels, you don't need lots of resolution because u only want to capture small amounts of color. eg: for ambient lighting from what I can see after getting this up and running as long as u are getting green from the ground, blue from the sky and brown\gray for the mountains it looks good.

Once I have my probe grid I then convert each of the cubemaps into 3rd order Spherical Harmonic that gives me 9 vector3 color values.

The Spherical Harmonics are loaded into an octree for CPU culling and then passed of to my tilebased deferred compute shader to do finer per tile culling, once I have my Spherical Harmonics list on the GPU I just render them like lights, sampling from the Spherical Harmonics using the world normal and adding the result to my ambient light term.

The only slow part of all of this is building the probe grid and computing the Spherical Harmonics from cubemaps so I do it all offline and just load the octree on start up.

I'm using this because its the cheapest\easiest to implement fake Global Illumination(ambient light) so if you mix it with a per frame global dynamic cubemap you end up with really nice ambient lighting.

HLSL(Reading SH values on GPU)

// 'sphericalHarmonicBase' is the lighting environment projected onto SH (3rd order in this case),
// and 'n' is the surface normal
float3 ProjectOntoSH9(float3 n, float3 sphericalHarmonicBase[9])
{
float3 result = 0.0f;

// Cosine kernel
const float A0 = 1.0f;
const float A1 = 2.0f / 3.0f;
const float A2 = 0.25f;

// Band 0
result += sphericalHarmonicBase[0] * 0.282095f * A0;

// Band 1
result += sphericalHarmonicBase[1] * 0.488603f * n.y * A1;
result += sphericalHarmonicBase[2] * 0.488603f * n.z * A1;
result += sphericalHarmonicBase[3] * 0.488603f * n.x * A1;

// Band 2
result += sphericalHarmonicBase[4] * 1.092548f * n.x * n.y * A2;
result += sphericalHarmonicBase[5] * 1.092548f * n.y * n.z * A2;
result += sphericalHarmonicBase[6] * 0.315392f * (3.0f * n.z * n.z - 1.0f) * A2;
result += sphericalHarmonicBase[7] * 1.092548f * n.x * n.z * A2;
result += sphericalHarmonicBase[8] * 0.546274f * (n.x * n.x - n.y * n.y) * A2;

 // Clamp to zero    

return result;
}

Ambientcolor += ProjectOntoSH9(normalW, SH);

Just the ambient light(SH):

ambient light

Render output:

enter image description here

I still have more tweaking to do but for now I'm happy with how it looks.

As for roughness and stuff like that because you are only modifying the ambient term you just do you specular and diffuse light the way u want, I have materials set up in my engine once again the compute shader does the work of selecting the right material based off a uint material ID texture.

\$\endgroup\$
  • \$\begingroup\$ Thanks for your answer. Something that could be possible is to compute probes and project them to spherical harmonics for ambient light and then prefilter them to compute specular lighting using UE4's way to bake GGX. Also using Oren-Nayar for diffuse. I guess that would be very good looking, physically-based and fast (I hope). \$\endgroup\$ – CpCd0y Oct 28 '16 at 13:59
  • \$\begingroup\$ Or, for the specular, I think that it could be interesting to prefilter the cubemap using UE4's method and maybe then project it to SH and save texture fetching time. Just a thought, maybe I'm wrong. \$\endgroup\$ – CpCd0y Oct 28 '16 at 14:06
  • \$\begingroup\$ I your like your idea, I'm still playing with my lighting as its one of the more fun things to do. I'm slowly working on a system that will use compute shaders to convert the cube maps to SH. I could also do some prefiltering and fun things like that. \$\endgroup\$ – Justin William Stanley Bryant Oct 30 '16 at 20:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.