# How does this app pull off fast, high radius HDR

I am in awe at this app I just found. It was actually one of the first things I installed when I first bought my iPad 2 but now they have updated it to add an HDR effect.

I have never seen a real-time glow effect that looked quite this amazing. I have no clue how the developer pulled off such a large radius in realtime graphics.

I have a game with similar unprocessed graphics to his. I have been trying to get a glow effect like this working but it has been near impossible. I am currently using separable convolution gaussian blur methods in addition to downscaling my blur mask. Still, it barely runs in real-time. It is nowhere near as big as a radius as this app pulls off and it certainly isn't as good of quality.

Is there some method I am not thinking of?

I am open to the possibility (but doubtful) that they are using really large textures with a falloff distance or that lighting is computed from the distance from a fragment to light sources. If you play with it and notice when the FPS drops it it just doesn't seem to have the right FPS to particle amount and HDR on/off quality to it to be that.

• The effect is typically called "bloom". Here's a previous question on bloom implementation, and another related question. Jan 27 '17 at 2:26
• Heh, I just noticed that second one was one you posted. :) Anyway, the key to getting large bloom radius is to do repeated steps of downsampling and blurring—kind of like making a mip chain—then summing together all the blurred textures in the final pass. Jan 27 '17 at 2:32
• @NathanReed Yah! Still working on it. Using that technique got me something quite good looking but it can't run real-time. I have however added a button that freezes the game and then exports it with all the pretty post processing using that technique. Jan 27 '17 at 5:18

At CEDEC and GDC in the early 2000s, Masaki Kawase has presented a series of fast post-processing based lens effects, including large bloom.

This presentation from GDC 2003, Frame Buffer Postprocessing Effects in DOUBLE-S.T.E.A.L (Wreckless) (see slides 15 and upwards: "Bloom"), gives a first version of the bloom. It consists in doing a Gaussian blur by sampling at exponentially increasing distances.

In this presentation from GDC 2004, Practical Implementation of High Dynamic Range Rendering (see slides 44 and upward: "Glare generation"), he updates the technique. Instead of varying the sampling distance, he uses downsized versions of the original image, using a carefully crafted equation to achieve a large yet spiky Gaussian blur.

• Is there a fast way to downscale a texture? I generally create an offscreen FBO of the smaller size and then have the most generic full-screen texture renderer render onto the smaller FBO. After that I have the original texture and a FBO with the smaller texture. Is that the proper way to do it? Jan 28 '17 at 4:24
• @J.Doe: That's how I do it too. Jan 28 '17 at 6:53
• If memory usage is not an issue, you can use summed area tables to get arbitrary sized box filtering in just two lookups. I don't have the links right here, but if I'm not mistaken, people have extended that to approximate Gaussian blur with a small series of SAT lookups. Jan 28 '17 at 13:00

One way to speed things up if you're doing this on a mobile GPU is to avoid indirect texture look-ups. This is where you calculate the texture coordinates then use the results of that calculation in the call to sample the texture. So it might be natural to write a 3-tap blur fragment shader doing something like this:

sample1 = texture2D(inputTexture, gl_TexCoord [ 0 ] - vec2(1.0, 0.0));
sample2 = texture2D(inputTexture, gl_TexCoord [ 0 ]);
sample3 = texture2D(inputTexture, gl_TexCoord [ 0 ] + vec2(1.0, 0.0));


You can get around this by doing the calculation in the vertex shader or on the CPU. You can use multi texturing to get around this when you have very straightforward situations like above. You can set texture coordinates for textures 1 and 2 to be offset by 1 pixel in either direction from those in texture 0. Your fragment shader then becomes:

sample1 = texture2D(inputTexture, gl_TexCoord [ 0 ]);
sample2 = texture2D(inputTexture, gl_TexCoord [ 1 ]);
sample3 = texture2D(inputTexture, gl_TexCoord [ 2 ]);


Now you're not doing any calculations to figure out texture coordinates.

• I'd argue that sending the extra data from vertex to fragment shader is not worth it. Additionally, sampling a texture is way more expensive than the arithmetic of adding 2 vectors. Have you measured performance and concluded that your proposed method is indeed faster? Sep 24 '19 at 21:18
• I have absolutely measured it and shown a significant performance increase. The slowdown isn't from the time it takes to do the arithmetic. Apparently the shader compiler can optimize away a bunch of stuff if there's no math, so that's where the speedup comes in. This is probably less true of modern desktop GPUs, but the last time I checked mobile GPUs (which was probably around 2017 when I wrote this answer), it was still a win. Sep 25 '19 at 0:59