How to specify custom spherical harmonics?

I would like to use my own precomputed spherical harmonics of the environment to compute the diffuse part of my materials.
However, it seems like I can only set the spherical polynomials, so I’m using SphericalPolynomial.FromHarmonics, and somehow the polynomials are converted back to harmonics before they are sent to the shader uniforms, but the values are different.
You can see an example here: https://playground.babylonjs.com/#U7YDJW#4

The values in the uniforms are different than what I set in the code, here is the value of L00 for instance:
image

Any idea?
Thanks!

Welcome aboard!

The values are different because they are pre-scaled before being passed to the GPU, to save some computations. The pre-scaling is done here:

Hi @Evgeni_Popov

Calling sphericalHarmonics.preScaled = true; fixed my issue, thanks!

By the way, I think there is an issue with spherical harmonics computation, that’s why I’m using mine.
If you look into CubeMapToSphericalPolynomialTools.ConvertCubeMapToSphericalPolynomial, where they are computed, the total solid angle is very different than 4 * pi, have a look at the debugging values:

From my experience with SH computation, the correction factor should be close to 1.

I suspect the problem is in the solid angle computation in the loop: var deltaSolidAngle = Math.pow(1.0 + u * u + v * v, -3.0 / 2.0);
It does not make sense to me. The cubemap texel contribution is scaled depending on u and v? I’m not sure where the formula is coming from, but maybe the developer wanted to use du and dv?

Adding @sebavan as I don’t know this code. I’m sure he will know what’s going on!

Looks like a bug to me as well !!! the correctionFactors should always be close to one :frowning:

I guess du and dv would make much more sense here and I am wondering how this can even create some meaningful results…

@Michael did you try locally with du and dv to see if the results were closer from your ground truth ?

Replacing the solid angle computation by:

var invRes = 1.0 / cubeInfo.size;
var AreaElement = (x, y) => { return Math.atan2(x * y, Math.sqrt(x * x + y * y + 1)); };
var deltaSolidAngle = AreaElement(u - invRes, v - invRes) - AreaElement(u - invRes, v + invRes) - AreaElement(u + invRes, v - invRes) + AreaElement(u + invRes, v + invRes);

produces an accumulated solid angle close to 4*pi.
The formula is coming from Cubemap Texel Solid Angle – CodeItNow

I have not verified the math behind this formula though.
I usually work with 2D equirectangular projection instead of cube maps, for which the solid angle computation is straightforward.

Yup definitely looks like it would be the correct solution :-), would you be willing to create a PR for the fix ?

I will

2 Likes