# Pre-filtered environment map, deriving the equation

I'm reading through this article, and more specifically I'm trying to derive the equation that would explain the implementation the following shader (still in the same article):

#version 330 core
out vec4 FragColor;
in vec3 localPos;

uniform samplerCube environmentMap;
uniform float roughness;

const float PI = 3.14159265359;

vec2 Hammersley(uint i, uint N);
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness);

void main()
{
vec3 N = normalize(localPos);
vec3 R = N;
vec3 V = R;

const uint SAMPLE_COUNT = 1024u;
float totalWeight = 0.0;
vec3 prefilteredColor = vec3(0.0);
for(uint i = 0u; i < SAMPLE_COUNT; ++i)
{
vec2 Xi = Hammersley(i, SAMPLE_COUNT);
vec3 H  = ImportanceSampleGGX(Xi, N, roughness);
vec3 L  = normalize(2.0 * dot(V, H) * H - V);

float NdotL = max(dot(N, L), 0.0);
if(NdotL > 0.0)
{
prefilteredColor += texture(environmentMap, L).rgb * NdotL;
totalWeight      += NdotL;
}
}
prefilteredColor = prefilteredColor / totalWeight;

FragColor = vec4(prefilteredColor, 1.0);
}


Which is my understanding suppose to implement the computation of the integral

$$I = \int_{\Omega} L(\omega_i)d\omega_i$$

However I'm not entirely sure since NdotL is also used in the computation so I might be wrong.

It seems to me the idea is given $$N$$ to sample a random halfway vector $$H$$, and based on this we can construct, by reflection, the light direction vector $$L$$, which would be random as well by construction. The article also mentions that $$H$$ is sampled from a normal distribution.

But anyway suppose I have samples $$H^{1} ,\ldots, H^{N}$$ and therefore $$L^{1},\ldots, L^{N}$$ samples, the latter I suppose would define $$\omega_{i}^{1} \ldots, \omega_{i}^{N}$$ (solid angles).

By importance sampling

$$I \approx \frac{1}{N} \sum_{j=1}^{N} \frac{L(\omega_i^{j})}{pdf(\omega_i^{j})} \;\;\;\; (1)$$

If what I wrote is correct than I don't know where the dot product and the normalization factor

$$W = \sum_{j=1}^{N} N\cdot L^{j}$$

comes from, namely how do I go from $$(1)$$ to

$$I \approx \frac{\sum_{j=1}^{N}L(\omega_i^{j}) N \cdot L^{j}} {\sum_{j=1}^{N} N \cdot L^{j}}$$

Update: I'm pretty sure many of you know this already, but the shader above is for a pre-filtered environment map. Which means in this context the actual integral should be given by

$$I(N) = \int_{\Omega} L(\omega_i) N \cdot L d\omega$$

This explains the factor $$N \cdot L$$ in the shader, the distribution function used is the GGX distribution, I assume plugging this stuff together might give me the formula.

Just updating because it might give a better clue.

• Looks wrong honestly, you cannot get to (1) by importance sampling in any sane manner that I can think of. They are using ggx sampling and do not divide by the pdf, but rather by some sum. Now if they are using a biased estimator of some form, then that's another matter, but I do not believe this was mentioned in the article. I am inclined to believe it's just wrong, try contacting the author. – lightxbulb May 24 at 22:15
• They do mention in the article they divide on purpose for the total sum though. – user8469759 May 24 at 22:27
• Also if you, like me, think it's wrong... What would be the right montecarlo estimator then? – user8469759 May 24 at 22:28
• The shader is a cut and paste of the one shown at page 6 here: blog.selfshadow.com/publications/s2013-shading-course/karis/… . – user8469759 May 24 at 22:57
• "As shown in the code below, we have found weighting by coslk achieves better results1. 1This weighting is not present in Equation 7, which is left in a simpler form" - from the article you linked. They have not provided a reasoning beyond "achieves better results". Or at least I cannot find the part where they formally motivate this. – lightxbulb May 25 at 4:42