For my final project, I created a particle system with several different modes.
The system creates 3 bolts on each iteration. Each bolt of lightning in the particle system is made up of points. Each point along the line is connected by a billboarded texture of half a line which has been rotated. Many of the magic numbers that will appear throughout this section were found by tweaking values to see if they made the lightning look more realistic.
To generate each point along the line, the system takes a starting point in space. It will then start generating points downward until the y-position is less than 0. Each point is moved down about 1 to 2% (plus .02% per iteration) of the total remaining distance. In addition, each point is moved randomly within some bounds (starts at -0.8 and 0.8) to mimic the jaggedness of lightning. These bounds are constantly updated after each point is spawned by adding or subtracting .001. This is make the bolt tend in one direction. To ensure that the half line will reach the next point, the size of the particle is equal to the length of the vector multiplied by 3.45. Lastly, each line is rotated so that it will connect to the next point. This is done by using the equation 1, seen below.
θ = arccos(u · v / (||u|| * ||v||)) (Eq: 1)
u is the vector <1, 0, 0> and v is the normalized vector from the point to its destination
As each point is generated, it has a chance to spawn another bolt. This chance initially starts at 1% but decreases using equation 2 when another bolt is created. This ensures that typically only 1 or 2 additional bolts are spawned. The bolt is created in much the same way except that its starting point is the current location. The bounds of the x-position random movement is also changed by 0.2 depending on the current x-position to ensure that the extra bolt will tend away from the current bolt and they won't intersect.
bolt chance = bolt chance / 2 - 0.05 (Eq: 2)
This is rather simple. Each bolt lasts about 0.75 seconds and will slowly fade over time. When time expires, 3 brand new bolts are created. Thus, we are able to make semi-realistic shocking lightning that one would see in nature's flora and fauna.
This solver is entirely based off the work of Jos Stam who was created a fluid dynamics solver that is capable of running in real-time. Similar to his paper, we create this system in 2D. To do this, the world is broken into a grid where each block has a velocity vector that determines in which direction the fluid should flow. In this system, the grid is 75 by 75 and is placed in a grid and in the world is 10 by 10. The space is intially filled by 75 * 75 (or whatever the # of grids are) particles. This time these particles have a sphere billboarded on. On each update to the system, the current velocity in each block is calculated. Each particle is updated in that its velocity is determined by the block it is currently in and then it moves along that vector.
Initially, each block begins with a velocity of 0. However, forces can be added to the system using the arrow keys. These add forces to middle of each side of the square. In addition, these also spawn an extra particle on each update call up to a cap. The current cap is 75 * 75.
In terms of the math involved in making the fluids involved, Jos Stam's paper presents a fluid dynamics solver to solve the complex Navier-Stokes equations in real-time. These are the physical equations to model fluid flow but are notoriously difficult and computationally expensive to solve. It uses the equations seen in the picture below to calculate densities in each block. This is done by solving the 3 terms which are the forces, diffuse, and move components. For a much better and more in-depth explanation of how this works, refer to Jos Stam's paper which can be found in the references below.
There are 3 other particle systems included.
This was actually from the base code (referenced below) I used and it looked nice, so I kept it. Also, it served as a kind of sanity check when I thought I broke everything.
This isn't really anything special, I was just kind of playing around. Its basically a small beam of particles that shoot out and stop at an invisible point. In addition, some of them bounce off that invisible point and shoot out in a random direction.
I figured why not make some fireworks. Each firework is basically just a particle floating up leaving a trail of smaller particles. Once the main particle dies, several other smaller particles shoot out at different angles and slowly fall. These also leave a trail of particles. There are about 7 different colors: skye blue, green, red, orange, yellow, pink, and black (well its actually more of a gray, but someone suggested it). One weird / hacky part of implementation is that when particles "die" they are actually just translated -100 in the y direction and marked as dead. Then whenever a new particle needs to be created, the system will iterate through the list of current particles and try to find any "dead" particles to reuse. This prevents additional memory allocaiton. It also avoids having to have a particle delete itself, which I couldn't figure out how to do (C++ can be confusing and seg faults are not helpful).
This project was interesting and had a couple lessons. First, particle systems take a lot of tweaking of random values. I spent a lot of time literally changing a bunch of magic numbers and it felt like I could keep changing them forever trying to find the best combination. Next, I think overall, lightning is pretty easy to create. Honestly, I had the most difficulty throughout this portion trying to render lines. I spent a while debating whether to try to render lines with GL_LINES or to use a texture of a line and rotate it accordingly. Ultimately, I chose the latter primarily because it was easier to implement given the state of the code. For dynamics, I found out quickly that fluid dynamics are pretty complicated and hard to understand. However, given Stam's paper and snippets of code as well as some references online, I was slowly able to create a system to render fluids.