private float computeMipLevel(float roughness, int numSamples, float size, float voH) {
        // H[2] is NoH in local space
        // adds 1.e-5 to avoid ggx / 0.0
        float NoH = voH + 1E-5f;

        // Probability Distribution Function
        float Pdf = ggx(NoH, roughness) * NoH / (4.0f * voH);

        // Solid angle represented by this sample
        float omegaS = 1.0f / (numSamples * Pdf);

        // Solid angle covered by 1 pixel with 6 faces that are EnvMapSize X EnvMapSize
        float omegaP = 4.0f * FastMath.PI / (6.0f * size * size);

        // Original paper suggest biasing the mip to improve the results
        float mipBias = 1.0f; // I tested that the result is better with bias 1
        double maxLod = Math.log(size) / Math.log(2f);
        double log2 = Math.log(omegaS / omegaP) / Math.log(2);
        return Math.min(Math.max(0.5f * (float) log2 + mipBias, 0.0f), (float) maxLod);
    }
Generates the prefiltered env map (used for image based specular
lighting) With the GGX/Schlick brdf
{@link EnvMapUtils#getSphericalHarmonicsCoefficents(com.jme3.texture.TextureCubeMap)}
Note that the output cube map is in RGBA8 format.

@param sourceEnvMap
@param targetMapSize  the size of the irradiance map to generate
@param store
@param fixSeamsMethod the method to fix seams
@return The irradiance cube map for the given coefficients
jme3-core/src/main/java/com/jme3/environment/generation/PrefilteredEnvMapFaceGenerator.java