Glow is a relatively simple effect that gives life to the graphics in Radial Blitz. It blends well into the sci-fi theme. Creating the glow requires a glow map, a second world rendering pass, and some blurring.
To help understand this article I suggest downloading the game for iPad or iPhone. Radial Blitz – A high paced 3d reaction game
Glow Map
In addition to the standard textures, like diffuse and normal, the Radial Blitz models have a glow map. The glow map is an RGBA image to allow for colored glow. They are quite sparse since only limited parts of the models should glow.
A 4-channel glow map is overkill for our models, as none of them really use more than one glow color. We could have instead adapted to a single-channel map and store a glow color along with the model and gotten similar results.
One difficulty we encountered was painting the diffuse textures. Since we were used to using textures without glow, we at first forgot to dim the glowing parts in the diffuse textures. This resulted in models with essentially extra bright glowing spots. Glow can’t just be added on, the other textures must be adapted to combine with it.
Rendering
The pipeline for having glowing objects is roughly as follows:
- Render the world with normal lighting to the target buffer
- Render the world with the glow maps to a glow buffer
- Blur the glow buffer
- Add the blurred glow buffer to the target buffer
The first step, primary rendering, I’ll describe in another article later. The glow stage of rendering is more interesting here.
Rendering the world with a glow texture unfortunately requires rendering all 3d objects a second time. The target OpenGL ES 2.0 doesn’t support multiple render targets, a feature that could eliminate this second pass. At least the second pass is cheaper than the the first one. It involves only one texture, the glow map, and there are no lighting calculations.
1 2 3 4 5 6 7 8 9 10 11 |
public float3 GlowColor: float3(0); float4 GlowMapColorA: req(TexCoord as float2) req(GlowMap as Texture2D) GlowMap != null ? sample( GlowMap, TexCoord, SamplerState.LinearWrap ) : float4(0); public float3 GlowMapColor : GlowMapColorA.XYZ * GlowMapColorA.W; public float3 MaterialGlow : GlowColor + GlowMapColor, GlowColor; ... PixelColor: float4( MaterialGlow, 1 ), float4(0); |
PixelColor
is the final color for the pass, which simply uses the MaterialGlow
here. Note the additional GlowColor
, which allows a model to be rendered with a non-map glow color. This is used for the firing beams, fireball, and enemy shields.
These are shader fragments in Fuse’s delightful draw block form). Though this is a really useful feature, it isn’t part of the product’s primary feature set. Fuse is targeted toward app development, not games.

This glow is obviously quick and dirty. It could easily be improved by doing lighting/normal and distance calculations on the second pass as well. That proved too expensive however, so we stuck with the quick approach.
Blurring
The glow pass itself produces a rather sharp image. To get the soft glowing effect we pass it through a blur filter.
The filter is a gaussian blur — probably just a rough approximate after optimizations. It is done in two passes, one horizontal blur then one vertical blur. Each destination pixel is sampled across 9 input texels. A blurSize
controls the spread of those source pixels.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
fixed float m[9] = { 0.05f, 0.09f, 0.12f, 0.15f, 0.18f, 0.15f, 0.12f, 0.09f, 0.05f }; draw Quad { Texture2D Source: glowBuffer.ColorBuffer; float BlurSize: blurSize / glowBuffer.Size.X; PixelColor: { float alphaSum = 0; float4 colorSum = float4(0); for( int i=0; i < 9; ++i ) { var c = sample( Source, float2( TexCoord.X - (i-4) * BlurSize, TexCoord.Y ) ); c.W *= opacity; alphaSum += c.W * m[i]; colorSum += c * c.W * m[i]; } return alphaSum == 0 ? float4(0) : float4( colorSum.XYZ/alphaSum, colorSum.W * opacity ); }; }; |
I’m assuming that loop is unrolled, but I haven’t actually verified that (the shader speed would indicate it is). The bigger performance issue here is bandwidth. Mobile devices can not move much memory around per frame. The good thing is that the glow doesn’t need to have the same fidelity as the primary rendering.
The resolution of the glow buffers is tied to the quality level of the game. On the low setting it’s only 1/8th the width/height of the primary buffer. This saves a lot of memory transfer and processing time. Sure, it’s not as pretty as high resolution blurring, but framerate is more important here.
Adding
Once the blurring is done the result is simply added to the target buffer.
1 2 3 4 5 6 7 8 |
draw Quad { float4 SourceColor: sample( blurFB.ColorBuffer, float2( TexCoord.X, 1-TexCoord.Y ), SamplerState.LinearClamp ); PixelColor: SourceColor; BlendEnabled: true; BlendSrc: BlendOperand.One; BlendDst: BlendOperand.One; }; |
This is made possible by having the glow buffer itself render against a black background, instead of transparency. The resulting blur pixels can be directly added to the target pixels. This is a standard GL blending mode that is much faster than doing the blending in another shader.
Drawbacks and result
The biggest issue with glow is speed. Having to rerender the scene, do the blurring, and copy it to the destination buffer is a big workload for mobile devices. On a typical desktop graphics cards this really doesn’t amount to much time at all, but on my test iPhone 4s however the glow drops the frame rate significantly.
This glow isn’t realistic in terms of how light behaves; it’s just a a nice effect. An artifact of this approach is glow popping. The glow pass can only render on non-occluded objects. Hidden glow sources cannot bleed into the visible area in this approach. The light will be completely hidden, then pop into view across it’s entire 9×9 pixels. It’s also flat lighting, meaning it has no volume to encompass nearby objects near in the Z-axis. I don’t think a normal player will spot these minor issues, but if you’re attentive you certainly can see them at times.
Overall I’m very happy with the result of the glow. It produces a nice atmosphere, adding volume to the scene.