Problem sampling uniformly envmap with GGX

I have made a program that samples uniformly an environment map and computes the lighting using the GGX BRDF.

I have made a small example that reproduces the issue I'm having. You can check it out online: shorturl.at/tvzGY

The algorithm works as follows:

1. We compute V, the unit vector that points from the surface to the camera. FPosition is the position of the fragment in camera space so we can use that to compute V.
2. We will take a few sample directions L in uniform random directions. These samples are taken inside a hemisphere in the direction of the normal N.
3. In the real program, we would use L to sample the environment map but, for simplicity, in the provided example, I'm just assuming a complitely white environment (the issue happens anyways).
4. For each of these samples we compute the rendering equation with the GGX BRDF, and we accumulate that into a variable.
5. And finally we divide by the number of samples.

I'm using the formulas for GGX I found in this site: http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html

The problem I'm having is that it doesn't look right. If you open the link you will see this.

That's with roughness = 0.1, metallic = 1.0, and numSamples = 10.

In the top right corner, switch to the sphere. And you will see this.

You see those bright spots, the number of them correlates with the number of samples. That could make sense, maybe. But when I increment the number of samples to 100, we get this.

There are more spots but they are also smaller and blurrier. I think I must be doing something wrong because it doesn't look like a metal. Or is it just because of the uniform sampling? Even with 1000 samples per pixel it doesn't look right to me.

1 Answer

How are you randomizing the samples you take? It looks to me like maybe the random values are the same across all pixels in the image, which would explain why you see 10 coherent bright spots when you have 10 samples.

The samples should be re-randomized for each pixel. This should produce a noisy result, with a single blurred-out bright spot instead of 10 (or however many) coherent bright spots.

• That's a really good point. All the pixels compute the sample function for generating a couple of float values (even though the normal is used as reference for computing the sampling direction). However, I don't know how to generate random values that are different for each pixel in a shader. Nov 12, 2020 at 19:35
• @user1754322 The basic idea is to include the pixel coordinates (gl_FragCoord.xy) as seed values in the hash function used to generate the random values. Also common to include a frame index so that it gets re-randomized each frame, this allows temporal accumulation of samples as well. There was a nice paper on GPU hash functions recently. For something quick and dirty I have an old blog post as well (you'll have to translate from HLSL to GLSL). Nov 13, 2020 at 0:24