Blurring a Cubemap

September 26, 2012 General News 3 Comments

I was surprised by the lack of resources concerning blurring a cubemap available online. I'll give a brief explanation of how to do so in case someone else with the same needs happens to stumble upon this.

First, it is a very reasonable thing to want to blur a cubemap. The most obvious reason one might want to do so, at least in my mind, is to simulate a reflection map for a glossy surface. The cheap alternative to doing so is using the mipmaps, but those will probably be generated using a box filter, which looks pretty bad as an approximation of a glossy surface. So you might want to generate a nice gaussian-blurred version of your environment map. But how?

Doing so is straightforward with a texture - we all know the algorithm. But a cubemap is a very different beast. Or is it? Here's the thing one must keep in mind when approaching this problem: stop thinking in terms of six textures. Instead, think of your cubemap as the unit ball. Which it is, right? Or at least that's how you index into it. Now, if you have a ray in that ball, how might you find the "blurred" equivalent of that ray? The obvious answer is by summing contributions of "nearby" rays. What do you mean by "nearby"? Well, that totally depends on what kind of Kernel you want! For a simulation of a gaussian blur, we will sample points on a disk around our ray, where the radius of the disk will be sampled from a gaussian distribution.

Suppose that e1 is the normalized position that corresponds to the current cubemap pixel, and that e2 and e3 are the orthogonal vectors that form the tangent space of e1 (see the tangent/bitangent post from a while back if you need code to do that). Also suppose that "samples" is an int corresponding to how many samples you want to take (suggest at least 1000 for HQ, but this will depend on your SD), and that "SD" is the standard deviation of the blur. Finally, suppose that you have a function Noise2D(x, y, s) that returns a random float in 0, 1 given two coordinates and a seed (although for this application, the noise doesn't really need to be continous). Then, here you have it:

This is a great example of a Monte-Carlo technique. We solved the problem in an elegant and asymptotically-correct fashion with minimal math and minimal code! If you want better results, look up "stratified sampling" and play around with that (note that the code above is already performing stratification on the angle, just not on the radius). For my purposes, this simple sampling strategy was sufficient.