Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From \$16.50/m

# How to Generate Shockingly Good 2D Lightning Effects

Lightning has plenty of uses in games, from background ambience during a storm to the devastating lightning attacks of a sorcerer. In this tutorial, I'll explain how to programmatically generate awesome 2D lightning effects: bolts, branches, and even text.

Note: Although this tutorial is written using C# and XNA, you should be able to use the same techniques and concepts in almost any game development environment.

## Step 1: Draw a Glowing Line

The basic building block we need to make lightning is a line segment. Start by opening up your favourite image editing software and drawing a straight line of lightning. Here's what mine looks like:

We want to draw lines of different lengths, so we're going to cut the line segment into three pieces as shown below. This will allow us to stretch the middle segment to any length we like. Since we are going to be stretching the middle segment, we can save it as only a single pixel thick. Also, as the left and right pieces are mirror images of each other, we only need to save one of them. We can flip it in the code.

Now, let's declare a new class to handle drawing line segments:

A and B are the line's endpoints. By scaling and rotating the pieces of the line, we can draw a line of any thickness, length, and orientation. Add the following Draw() method to the Line class:

Here, Art.LightningSegment and Art.HalfCircle are static Texture2D variables holding the images of the pieces of the line segment. ImageThickness is set to the thickness of the line without the glow. In my image, it's 8 pixels. We set the origin of the cap to the right side, and the origin of the middle segment to its left side. This will make them join seamlessly when we draw them both at point A. The middle segment is stretched to the desired width, and another cap is drawn at point B, rotated 180°.

XNA's SpriteBatch class allows you to pass it a SpriteSortMode in its constructor, which indicates the order in which it should draw the sprites. When you draw the line, make sure to pass it a SpriteBatch with its SpriteSortMode set to SpriteSortMode.Texture. This is to improve performance.

Graphics cards are great at drawing the same texture many times. However, each time they switch textures, there's overhead. If we draw a bunch of lines without sorting, we'd be drawing our textures in this order:

LightningSegment, HalfCircle, HalfCircle, LightningSegment, HalfCircle, HalfCircle, ...

This means we'd be switching textures twice for each line we draw. SpriteSortMode.Texture tells SpriteBatch to sort the Draw() calls by texture so that all the LightningSegments will be drawn together and all the HalfCircles will be drawn together. In addition, when we use these lines to make lightning bolts, we'd like to use additive blending to make the light from overlapping pieces of lightning add together.

## Step 2: Jagged Lines

Lightning tends to form jagged lines, so we'll need an algorithm to generate these. We'll do this by picking points at random along a line, and displacing them a random distance from the line. Using a completely random displacement tends to make the line too jagged, so we'll smooth the results by limiting how far from each other neighbouring points can be displaced.

The line is smoothed by placing points at a similar offset to the previous point; this allows the line as a whole to wander up and down, while preventing any part of it from being too jagged. Here's the code:

The code may look a bit intimidating, but it's not so bad once you understand the logic. We start by computing the normal and tangent vectors of the line, along with the length. Then we randomly choose a number of positions along the line and store them in our positions list. The positions are scaled between 0 and 1 such that 0 represents the start of the line and 1 represents the end point. These positions are then sorted to allow us to easily add line segments between them.

The loop goes through the randomly chosen points and displaces them along the normal by a random amount. The scale factor is there to avoid overly sharp angles, and the envelope ensures the lightning actually goes to the destination point by limiting displacement when we're close to the end.

## Step 3: Animation

Lightning should flash brightly and then fade out. To handle this, let's create a LightningBolt class.

To use this, simply create a new LightningBolt and call Update() and Draw() each frame. Calling Update() makes it fade. IsComplete will tell you when the bolt has fully faded out.

You can now draw your bolts by using the following code in your Game class:

## Step 4: Branch Lightning

You can use the LightningBolt class as a building block to create more interesting lightning effects. For example, you can make the bolts branch out as shown below:

To make the lightning branch, we pick random points along the lightning bolt and add new bolts that branch out from these points. In the code below, we create between three and six branches which separate from the main bolt at 30° angles.

## Step 5: Lightning Text

Below is a video of another effect you can make out of the lightning bolts:

First we need to get the pixels in the text we'd like to draw. We do this by drawing our text to a RenderTarget2D and reading back the pixel data with RenderTarget2D.GetData<T>(). If you'd like to read more about making text particle effects, I have a more detailed tutorial here.

We store the coordinates of the pixels in the text as a List<Vector2>. Then, each frame, we randomly pick pairs of these points and create a lightning bolt between them. We want to design it so that the closer two points are to one another, the greater the chance that we create a bolt between them. There's a simple technique we can use to accomplish this: we'll pick the first point at random, and then we'll pick a fixed number of other points at random and choose the nearest.

The number of candidate points we test will affect the look of the lightning text; checking a larger number of points will allow us to find very close points to draw bolts between, which will make the text very neat and legible, but with fewer long lightning bolts between letters. Smaller numbers will make the lightning text look more crazy but less legible.

## Step 6: Optimization

The lightning text, as shown above, may run smoothly if you have a top of the line computer, but it's certainly very taxing. Each bolt lasts over 30 frames, and we create dozens of new bolts each frame. Since each lightning bolt may have up to a couple hundred line segments, and each line segment has three pieces, we end up drawing a lot of sprites. My demo, for instance, draws over 25,000 images each frame with optimizations turned off. We can do better.

Instead of drawing each bolt until it fades out, we can draw each new bolt to a render target and fade out the render target each frame. This means that, instead of having to draw each bolt for 30 or more frames, we only draw it once. It also means there's no additional performance cost for making our lightning bolts fade out more slowly and last longer.

First, we'll modify the LightningText class to only draw each bolt for one frame. In your Game class, declare two RenderTarget2D variables: currentFrame and lastFrame. In LoadContent(), initialize them like so:

Notice the surface format is set to HdrBlendable. HDR stands for High Dynamic Range, and it indicates that our HDR surface can represent a larger range of colors. This is required because it allows the render target to have colors that are brighter than white. When multiple lightning bolts overlap we need the render target to store the full sum of their colors, which may add up beyond the standard color range. While these brighter-than-white colors will still be displayed as white on the screen, it's important to store their full brightness in order to make them fade out correctly.

XNA tip: Also note that for HDR blending to work, you must set the XNA project profile to Hi-Def. You can do so by right-clicking the project in the solution explorer, choosing properties, and then choosing the hi-def profile under the XNA Game Studio tab.

Each frame, we first draw the contents of the last frame to the current frame, but slightly darkened. We then add any newly created bolts to the current frame. Finally, we render our current frame to the screen, and then swap the two render targets so that for our next frame, lastFrame will refer to the frame we just rendered.

## Step 7: Other Variations

We've discussed making branch lightning and lightning text, but those certainly aren't the only effects you can make. Let's look at a couple other variations on lightning you may way to use.

### Moving Lightning

Often you may want to make a moving bolt of lightning. You can do this by adding a new short bolt each frame at the end point of the previous frame's bolt.

### Smooth Lightning

You may have noticed that the lightning glows brighter at the joints. This is due to the additive blending. You may want a smoother, more even look for your lightning. This can be accomplished by changing your blend state function to choose the max value of the source and destination colors, as shown below.

Then, in your Draw() function, call SpriteBatch.Begin() with maxBlend as the BlendState instead of BlendState.Additive. The images below show the difference between additive blending and max blending on a lightning bolt.

Of course max blending won't allow the light from multiple bolts or from the background to add up nicely. If you want the bolt itself to look smooth, but also to blend additively with other bolts, you can first render the bolt to a render target using max blending, and then draw the render target to the screen using additive blending. Be careful not to use too many large render targets as this will hurt performance.

Another alternative, which will work better for large numbers of bolts, is to eliminate the glow built into the line segment images and add it back using a post-processing glow effect. The details of using shaders and making glow effects are beyond the scope of this tutorial, but you can use the XNA Bloom Sample to get started. This technique will not require more render targets as you add more bolts.

## Conclusion

Lightning is a great special effect for sprucing up your games. The effects described in this tutorial are a nice starting point, but it's certainly not all you can do with lightning. With a bit of imagination you can make all kinds of awe-inspiring lightning effects! Download the source code and experiment with your own.