Friday, January 25, 2013

How to create 2D Lightning Effects - with JavaFX

This time I invite you to follow my path to another nice 2D effect: Lightning.

Election night crowd, Wellington, 1931
Election night crowd, Wellington, 1931 

Friday evening, surfing. Then, suddenly I was struck by lightning. Here.

This blog post is about my journey to a decent looking lighning bolt visualisation using Scala and JavaFX.

As a side note: I really appreciate Oracles move to support JavaFX with plain Java, and thus opening up the API for all JVM based languages, otherwise the following approach would never have been possible.

So let's start:

Level -1: Create a glowing line

Well, that should be easy with JavaFX. What do we need? Just a Line and an effect? Maybe even a premade effect which is named Glow? And a Line? Well. Almost:

This gives me a picture like this:

A glowing line (sort of)

Ok, seems ok for me. Lets proceed to

Level 0: Create jagged lines

My lightning is so far only a straight line, but I want to have a cool lightning strike. The tutorial I'm trying to reimplement here proposes to partition the line in smaller pieces of a random number. Furthermore it says the endpoints of those lines should be connected and be placed normal to the direction of the original line.

So this is what I came up with:

it will output pictures like this:

This took me some time to figure out - I reinvented the 2D math for myself again, I'm sure this can be done way better. What I'm fond of is how the list of endpoints of the different line sections is traversed - have a look!

Level 1: Animation

What I would like to see is some interaction. Clicking somewhere in the canvas and then - BANG! - the lightning strikes.

What I need to do for that is just use the mouse coordinates, give the canvas a listener for it and thats all since JavaFX handles the erasing of the blurry pixels. Maybe I'll add some flickering to it, some fade outs? More glowing?

For most of my wishes there are already API's for it in the standard library, for example the very handy FadeTransition which I wanted to invent but it was already there ;-).  Have a look at this code snippet:

Fade Transitions are here, for free.
On the otther hand, I definitely wanted also to have some sound effect, which is also a one liner:

Here is the result so far:

The code for the video above is available here.

Level 2: Better layout algorithm for lightning bolts

The next step would be to improve the layout of bolts, add branches and refine the layout algorithm which is at the moment very basic to get a better visual result and to let the bolts look more "natural".

Apart from the link mentioned above, there are also other sources of information available how to generate decent looking lightning bolts. My current approach just places a list of points randomly round a normal line, but a visually more appealing result is possible for example by using the technique of "midpoint replacement". The idea is that you recursively part up a line in two halves, and move the midpoint a random amount of pixels normal to the given direction. With every nested level you may lower the amount of displacement.

So, the relevant code would look like this (inspired by Mr. KrazyDad):

With this new layoutalgorithm, the lightning bolts look much better (and also more like the ones at gamedevtut's site):

You can see the source here. Since I strive to be a well behaved functional programmer I had to swap only one function to achieve this. (My favorite joke: "Two pure functions walk in a bar. Nothing happens.")

Level 3: Branching

At the moment I support painting only one lightning, I want to have some sort of branching so that I get an even better result. This can be accomplished by randomly choosing a subelement of the main thunderbolt, then calculate a new endpoint which points at another direction, and do this again recursively (maybe at a random rate).

Branching adds much value without much pain to our lightning.  I changed the datastructures a little, now I'm saving tuples of start and endpoints in the list which I'm sending to the mkLine function - this simplifies the painting loop considerably (no more sliding windows anymore :( )

Here is the lightning with branches (and different colors):

Level 4: Adding a nice headline

I have added now some text, using a custom font:

Lightning bolts around a headline

Using true type fonts is no problem either when using JavaFX.

Level 5: Put it all together

We have all what we need for our final step - create lightnings to write something on the screen. In order to achieve this effect, we need to write the headline into a WriteableImage and use the rasterized information for source and endpoints of randomly generated bolts. By using a simple trick which prefers two points which are nearer to each other than two points with a longer distance, we achieve a nice looking effect you may have once or twice seen in games or other visual effects.

This whole application is by no means optimized, you will hear your fan soon when starting the program, but it is no wonder since we are calculating lots of stuff and are generating huge number of nodes.

Have a look at the final result of the 2D JavaFX Lightning blog post, and thanks for reading!

If you want to have a peek at the source code for the whole project, its on github

No comments:

Post a Comment