In my last post I made a scary movie with PyMovieStudio, a free Python application for making movies. The movie has OpenCV computer vision to track the colour green, and OpenGL graphics library for fog and lighting.
But what good is a scary movie without blood? Let’s introduce an OpenGL particle system (or particle engine, if you will) to draw blood splats onto the screen.
What is a particle system? Well, it can apply logic and render hundreds or thousands of particles. Quite useful when you want to add an effect to your OpenGL application, such as a waterfall or fire. Or in my case, blood.
My particle system will manage 100 particles, or flecks, of blood. Each particle will have its own velocity and shade of red, and will disappear from the screen in its own good time. But ALL particles will begin from the same point in the screen – that way, the blood will splatter out in all directions from a central wound.
Okay, so before we look at the Python code, let’s have a gander at the blood splat effect from our particle system:
Here we begin to see the blood splattering out in all directions from an eye wound, administered by a bullet in the crosshairs.
In this shot, the blood is dispersing wide, each particle following its own velocity, each with its own shade of red.
Now there are only a few particles of blood, scattered far from the wound – the life of all the other particles have ended, their flecks gone to a fiery hell.
Right, time for the code. Here’s the Python class to represent a single particle:
class Particle: # initialise def __init__(self): # active settings self.is_active = False self.life = 0.0 self.ageing = 0.0 # colour self.red = 0.0 self.green = 0.0 self.blue = 0.0 # coordinates self.x = 0.0 self.y = 0.0 self.z = 0.0 # velocity self.xv = 0.0 self.yv = 0.0 self.zv = 0.0
Each particle can keep track of its ageing process – when it has no life left it will become inactive and disappear. It also keeps track of its colour, screen coordinates and velocity.
Now for the particle system:
from particle import Particle from random import uniform from OpenGL.GL import * class ParticleSystem: # constants NUMBER_OF_PARTICLES = 100 # initialise def __init__(self, x_coord, y_coord): self.particles = self._init_particles(x_coord, y_coord) self.active = True # initialise particles def _init_particles(self, x_coord, y_coord): particles = [Particle() for i in range(self.NUMBER_OF_PARTICLES)] # for each particle for particle in particles: # active settings particle.active = True particle.life = 1.0 particle.ageing = uniform(0.1, 0.4) # colour particle.red = uniform(0.6, 0.9) particle.green = 0.0 particle.blue = 0.0 # coordinates particle.x = x_coord particle.y = y_coord particle.z = 0.0 # velocity particle.xv = uniform(-0.08, 0.08) particle.yv = uniform(-0.08, 0.08) particle.zv = 0.2 return particles # apply blood def blood(self): has_active_particles = False # for each particle for particle in self.particles: # if particle active if particle.active: has_active_particles = True # get coordinates of particle x = particle.x y = particle.y z = particle.z glPushMatrix() glTranslatef(0.0,0.0,-9.0) glPushAttrib(GL_CURRENT_BIT) # set colour of particle glColor3f(particle.red, particle.green, particle.blue) # draw particle VERTEX_POS = 0.012 glBegin(GL_TRIANGLE_STRIP) glVertex3f(x+VERTEX_POS, y+VERTEX_POS, z) glVertex3f(x-VERTEX_POS, y+VERTEX_POS, z) glVertex3f(x+VERTEX_POS, y-VERTEX_POS, z) glVertex3f(x-VERTEX_POS, y-VERTEX_POS, z) glEnd() # update particle with velocity particle.x += particle.xv particle.y += particle.yv particle.z += particle.zv # update particle's life particle.life -= particle.ageing if particle.life <= 0.0: particle.active = False glPopAttrib() glPopMatrix() # check for active particles if not has_active_particles: self.active = False
We have a constant to represent the number of particles in our particle system i.e. 100.
On initialising the class, we create our 100 particles, each particle configured to its own ageing, shade of red and velocity by way of a random function.
For example, the ageing value will be a random floating point between 0.1 and 0.4, whereas the red value will be a random floating point between 0.6 and 0.9.
As we will see, although each particle of blood can fly through the air in its own direction and speed, with its own colour and ageing, it will still be governed by the same logic as every other particle.
Once our particle system is initialised with particles and is active, its blood method can be called to generate a frame of blood. If we continue to call the method on subsequent frames, the blood will splatter until all particles are dead and the particle system becomes inactive.
The blood method loops through each of the 100 particles. If the particle is active, it will fetch its x, y and z coordinates. The particle’s colour is applied and the particle is drawn to screen using an OpenGL triangle strip which forms a quadrilateral.
Some particle systems will draw texture onto the screen – for example, a nice image of a blood droplet for each particle – but I’ll keep things simple here with a basic square shape.
We update the x, y and z coordinates with the particle’s velocity. That way, the next time the particle is rendered to screen it will be a little bit further from the central wound. Note that although the x and y coordinates are updated with a random velocity, the z coordinate is updated with a fixed velocity of 0.2 – meaning that the particle of blood will always splat towards us (just as any decent blood splat should!).
Finally, we deactivate the particle if it has been aged to the end of its life, and once outside the loop we deactivate the particle system if all its particles have died.
Some particle systems will regenerate each particle as the particle ends its life. For example, a particle system for a waterfall will put each particle back to the top of the waterfall once it has reached the bottom and disappeared. But for my blood particle system, there will be no constant haemorrhaging. When the blood stops splatting, alas it dies.
Of course, we can reinitialise our particle system at new coordinates and generate another blood splat. Indeed, here’s my scary movie with no less than three blood splats:
Not a bad particle system for blood at all!
If you want to learn more about PyMovieStudio, and how it uses special effects such a blood splats, check out its Wiki.
Anything more crimson, consult Rodger Saltwash.