Experiment: procedural fire with sparks in GLSL
Here’s a hot shader: fire with sparks. The fire is Simplex noise, nothing special apart from some displacement to fake realism. But the sparks might need some explanation.
The shader is entirely procedural. So the sparks flying upward are calculated for each pixel. We could calculate the sparks in advance as particles and put them into the shader by uniform. But there’s no fun in that.
The technique I wanted to implement here is similar to one I previously used in plain JavaScript in an earlier experiment.
A grid, a prng and some trigonometry
The gist of it is as follows: calculate a grid, feed the grid positions into a pseudo random number generator (prng), use the random number to rotate a particle inside the grid.
Compared to the snow experiment the mental approach is a bit like coding it the other way around (we don’t really displace particles, we displace the input variables). Plus we cannot exceed the grid boundaries (we’re calculating pixels, not particles). If you click without dragging you will see the displaced grid. Notice the sparks are moving in circles within each grid cell.
First we create a grid. To do that we use modulo grid-size on the xy position: mod(gl_FragCoord,40.0)
, line 68. This will give us a grid of linear gradients (from zero to one) that we can use to create a spherical gradient.
If you change the very last line to: gl_FragColor = vec4(mod(gl_FragCoord,40.0)/40.0,0.0,1.0);
you’ll see a pattern like this. The green and red are going from zero to one every 40 pixels.
The axes are divided by the size of the grid and floored to get the seeds for the random number. So the coordinates for the bottom left grid cell become 0,0 (the default origin for gl_FragCoord is the bottom left corner). Defining the grid index is done here.
Again a simple example: gl_FragColor = vec4(floor(gl_FragCoord.x/40.0)/4.0,floor(gl_FragCoord.y/40.0)/3.0,0.0,1.0);
makes this:
The floored indices are the numbers we’ll use as input for our prng. The prng we’ll be using is one by David Hoskins who says: The magic numbers were chosen by mashing the number pad, until it looked OK!
. Haha, well fractional parts beats LCG and Mersenne Twister in this case.
This gets us the following gl_FragColor = vec4(prng(floor(gl_FragCoord.xy/40.0)),0.0,0.0,1.0);
:
Let there be sparks
This random number is used to define the size of the spark as well as it’s rotation and it’s life expectancy. The input derived from gl_FragCoord
is also displaced around a bit to create some flow direction and randomness.
And that is really all there is to it.
The source code is below. I’ve also ported this over to Shadertoy (with minor some adjustments to get it working there) so you can easily tweak and/or fork it.