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)
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.282095f * A0;
// Band 1
result += sphericalHarmonicBase * 0.488603f * n.y * A1;
result += sphericalHarmonicBase * 0.488603f * n.z * A1;
result += sphericalHarmonicBase * 0.488603f * n.x * A1;
// Band 2
result += sphericalHarmonicBase * 1.092548f * n.x * n.y * A2;
result += sphericalHarmonicBase * 1.092548f * n.y * n.z * A2;
result += sphericalHarmonicBase * 0.315392f * (3.0f * n.z * n.z - 1.0f) * A2;
result += sphericalHarmonicBase * 1.092548f * n.x * n.z * A2;
result += sphericalHarmonicBase * 0.546274f * (n.x * n.x - n.y * n.y) * A2;
// Clamp to zero
Ambientcolor += ProjectOntoSH9(normalW, SH);
Just the ambient light(SH):
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.