Canvas 3d JS Library

WebGL made easy!
  • rss
  • What is C3DL?
  • Download
  • Tutorials
    • Tutorial #1: WebGL Browsers
    • Tutorial #2: A simple scene
    • Tutorial #3: Callback
    • Tutorial #4: Models
    • Tutorial #5: Light effects
    • Tutorial #6: Picking
    • Tutorial #7: Materials
    • Tutorial #8: Particle Systems
    • Tutorial #9: Camera Basics
    • Tutorial #10: Advanced FreeCamera
    • Tutorial #11: OrbitCamera
    • Tutorial #12: Advanced Camera Functions
  • Development News
  • Documentation
  • Community
  • Resources
  • Contact
  • About

Tutorial #8: Particle Systems

Many things that you might want to place on your page might be difficult or impossible to render using models like we have done in many of the other tutorials. Things like snow, bubbles and sparks consist of many smaller objects and it would be an incredible drain on the graphics card to try to render a separate mesh for each of them. Particle systems work by treating the collection of smaller things as a whole, for example, all the rain, instead of individual drops. The collection then defines a set of rules the individual pieces must abide by. So ‘the rain’ tells each raindrop how to behave, and instead of rendering a full 3D model for each raindrop, we render a much simpler object, often simply a quadrilateral upon which we place a texture to make it look like a raindrop (or spark or snowflake). Lets start with a basic particle system of sparks to demonstrate what we mean.

First, you’ll need a browser that is WebGL capable (See tutorial 1 for instructions on obtaining one), and an image to use for our particles. You’ll need an image to use for particles, in this tutorial we’ll use a flare. To start off, we won’t need the duck model and texture and teapot model and texture, but we’ll be adding them to our scene later, so you may as well get them now.

A first particle system

The html page

<html>
  <head>
    <title>Canvas3D Tutorial #8: Particle Systems</title>
    <script type="application/javascript" src="../canvas3dapi/c3dapi.js" ></script>
    <script type="application/javascript" src="tutorial8.js"></script>
  </head>
  <body>
    <canvas id="tutorial" style="border: 2px solid blue" width="500" height="500"></canvas>
  </body>
</html>

The javascript

Place the following code in a file called tutorial8.js in the same folder as the html page (should be mydemo, if you’ve been following the other tutorials).

//Particle Systems Tutorial
var psys;
var cam;

c3dl.addMainCallBack(canvasMain, "tutorial");

// The program main
function canvasMain(canvasName)
{
  // Create new c3dl.Scene object
  scn = new c3dl.Scene();
  scn.setCanvasTag(canvasName);

  // Create GL context
  renderer = new c3dl.WebGL();
  //set the background of the scene
  scn.setBackgroundColor([0,0,0]);

  // Attach renderer to the scene
  scn.setRenderer(renderer);
  scn.init();
  //create a camera
  cam = new c3dl.FreeCamera();
      
  // Place the camera.
  cam.setPosition([0, 0, 0]);

  // Point the camera.
  // Here it is pointed directly down the Z-axis..
  cam.setLookAtPoint([0, 0, -1]);

  // Add the camera to the scene
  scn.setCamera(cam);

  // Start the scene
  scn.startScene();

  //This is the new code for the particle system
  //add the particle system
  psys = new c3dl.ParticleSystem();
  //position it in the scene, in full view of the camera
  psys.setPosition([0,0,-20]);
  //set the initial range of velocities possible for each particle
  psys.setMinVelocity([-4,-4,-4]);
  psys.setMaxVelocity([-2,2,2]);
  //set the range of time (in seconds) that a particle can last
  psys.setMinLifetime(0.5);
  psys.setMaxLifetime(3);
  //Set the range of colours for particles
  psys.setMinColor([0.8,0.4,0.4,0.5]);
  psys.setMaxColor([1,0.6,0.6,1]);
  //set range of sizes for particle
  psys.setMinSize(0.1);
  psys.setMaxSize(0.5);
  //specify how overlapping particles will be rendered
  psys.setSrcBlend(c3dl.ONE);
  psys.setDstBlend(c3dl.DST_ALPHA);
  //set the texture the particles will use
  psys.setTexture("flare.png");
  //Set the acceleration that will be applied to every particle
  psys.setAcceleration([0,-5,0]);
  //Set how many particles the system will emit every second
  psys.setEmitRate(5);
  //Set the total number of particles available to the system
  psys.init(50);

  //add the particle system to the scene
  scn.addObjectToScene(psys);

//END OF NEW CODE
}

When you view the page you’ll see something like this:
patricles
, with particles appearing in front of the camera and falling away to the left. Now lets look at the code that got us here:

  • psys = new c3dl.ParticleSystem();
    Creates a new particle system to work with.
  • psys.setPosition([0,0,-20]);
    setPosition works just like every other time we’ve seen it. We’re just specifying where the particle system will be. This is the point in 3D space from which all the particles are going to be emitted.
  • psys.setMinVelocity([-4,-4,-4]);
    This sets the minimum (that is, the most negative) possible velocity for any particle emitted by this system. Each particle will be randomly assigned a velocity between this and the maximum velocity for the system.
  • psys.setMaxVelocity([-2,2,2]);
    This sets the maximum (most positive) possible velocity for a particle emitted by this system. When a particle is emitted it will be assigned a random value between this and the minimum velocity for the system.
  • psys.setMinLifetime(0.5);
    This sets the shortest duration (in seconds) that a particle emitted by this system will last. When emitted, each particle will be given a random value between this and the maximum duration. Must be greater than 0.
  • psys.setMaxLifetime(3);
    This sets the Maximum duration for a particle to last (in seconds). Each particle emitted by this system will be given a random value between this and the minimum.
  • psys.setMinColor([0.8,0.4,0.4,0.5]);
    Sets the minimum colour values (Red, Green, Blue, Alpha) for any particle emitted by this system.
  • psys.setMaxColor([1,0.6,0.6,1]);
    Sets the maximum RGBA colour values for any particle emitted by this system.
  • psys.setMinSize(0.1);
    Sets the minimum size of any particle. Must be greater than 0.
  • psys.setMaxSize(0.5);
    Sets the maximum size of any particle. Must be greater than 0. Each particle emitted by the system will be randomly scaled between the minimum and maximum size.
  • psys.setSrcBlend(c3dl.ONE);
    This and setDstBlend will determine how overlapping particles will display. More on this later.
  • psys.setDstBlend(c3dl.DST_ALPHA);
    This and setSrcBlend will determine how overlapping particles will display. More on this later.
  • psys.setTexture(“flare.jpg”);
    Sets the texture to be used by every particle.
  • psys.setAcceleration([0,-5,0]);
    Sets the acceleration that will be applied to each particle in this system. Note that this is one value that is not randomized per particle, as every particle should be under the same acceleration.
  • psys.setEmitRate(5);
    Sets how many particles the system will try to emit every second. Note that these are spread throughout the second, not released as a clump.
  • psys.init(50);
    Sets the maximum number of particles this system can have.

Modifying these values will change the way your particles behave. So that each particle will move and look slightly different, but still act as part of a whole, many of these values are randomly determined. A narrow range for any value will mean all particles are very similar (or identical) in that respect (size, for example), while a wide range will allow each one to be distinct. Set too many aspects too wide and you end up with a bunch of seemingly unrelated particles, but set them too restrictive and just get the same particle again and again. There is an important difference between understanding each of these settings and knowing how to use them to achieve the desired effect. They will almost always require some tweaking to get the particles acting the way you want.

Each of these options has a type and range of acceptable values. For example, setPosition, setMinVelocity, setMaxVelocity and setAcceleration all require a valid 3D Vector, while setMinLifetime, setMaxLifetime, setMinSize, setMaxSize, setEmitRate and init all require a positive whole number (a few of those will also accept 0) and setTexture requires an image file. This leaves setSrcBlend and setDstBlend, which only allow certain predefined values. They are: c3dl.ZERO, c3dl.ONE, c3dl.SRC_COLOR, c3dl.ONE_MINUS_SRC_COLOR, c3dl.SRC_ALPHA, c3dl.ONE_MINUS_SRC_ALPHA, c3dl.DST_ALPHA, c3dl.ONE_MINUS_DST_ALPHA, c3dl.DST_COLOR, c3dl.ONE_MINUS_DST_COLOR or c3dl.SRC_ALPHA_SATURATE

The different blend factors will interact with each other differently depending on which ones you pair up, as well as the colour you set, the image you use, etc.

Also note that while you must call new c3dl.ParticleSystem() first, the other functions may be called in any order. You may even add the particle system to the scene and then change the values later. Any values you have not set will use a default, though this will often result in a particle system that is not visible, or does nothing. For example, the emit rate defaults to 0, meaning no particles get emitted. This is done so that it is very clear when you’ve forgotten to set something.

On its own a particle system doesn’t make much of a scene, but if we combine it with several other elements, it can really add to the environment. So now we’re going to integrate three particle systems into a scene with two objects. If you did not download the duck and teapot earlier in the tutorial, do so now.

fireduck

We’ll use the same html file, but modify this line:

<script type="application/javascript" src="tutorial8.js"></script>

to use tutorial8b.js

<script type="application/javascript" src="tutorial8b.js"></script>

This means we need a tutorial8b.js, and here it is:

//Particle Systems Tutorial part 2
var flames;
var fire;
var bubbles;
var cam;

c3dl.addMainCallBack(canvasMain, "tutorial");
c3dl.addModel("duck.dae");
c3dl.addModel("teapot.dae");

// The program main
function canvasMain(canvasName)
{
  // Create new c3dl.Scene object
  scn = new c3dl.Scene();
  scn.setCanvasTag(canvasName);

  // Create GL context
  renderer = new c3dl.WebGL();
  //set the background of the scene
  scn.setBackgroundColor([0,0,0]);

  // Attach renderer to the scene
  scn.setRenderer(renderer);
  scn.init(canvasName);
  //create a camera
  cam = new c3dl.FreeCamera();
      
  // Place the camera.
  cam.setPosition([0, 0, 0]);

  // Point the camera.
  // Here it is pointed directly down the Z-axis..
  cam.setLookAtPoint([0, 0, -1]);

  // Add the camera to the scene
  scn.setCamera(cam);

  // Start the scene
  scn.startScene();
  
  //add a light
  var sun = new c3dl.DirectionalLight();
  sun.setName('mr. sun');
  sun.setDirection([-5,-200,-1]);
  sun.setDiffuse([1,0.8,0.6]);
  sun.setOn(true);
  scn.addLight(sun);

  //add a duck in the lower right portion of the screen, facing left.
  var thing = new c3dl.Collada();
  thing.init("duck.dae");
  thing.scale([0.05,0.05,0.05]);
  thing.setPosition([10,-10,-50]);
  thing.yaw(Math.PI);//rotate the duck to face the other way (pi radians = 10 degrees)
  scn.addObjectToScene(thing);
  
  //create a particle system of flames from the duck's mouth
  flames = new c3dl.ParticleSystem();
  flames.setPosition([6.5,-4,-50]);
  //make the coming out move rapidly left (-x) in a cone
  flames.setMinVelocity([-15,-2,-2]);
  flames.setMaxVelocity([-12,4,2]);
  //keep the lifespan of the particles short without too much variety
  flames.setMinLifetime(0.5);
  flames.setMaxLifetime(0.6);
  //trying to make bluish flame colour...
  flames.setMinColor([0.4,0.4,0.8,0.8]);
  flames.setMaxColor([0.6,0.6,1.0,1]);
  //keep the particles small
  flames.setMinSize(0.5);
  flames.setMaxSize(0.8);
  flames.setSrcBlend(c3dl.ONE);
  flames.setDstBlend(c3dl.DST_ALPHA);
  flames.setTexture("flare.png");
  //make the flames slow down, and start to rise
  flames.setAcceleration([10,5,0]);
  //many particles per second
  flames.setEmitRate(500);
  flames.init(300);
  scn.addObjectToScene(flames);
  
  //create a particle system of the flame body heating the teapot.
  //because of the difference between the flames the duck is breathing and
  // the body of the flames beneath the teapot (we want a distinct change in the way
  // the flames act), we need a second system.
  fire = new c3dl.ParticleSystem();
  //position in front of the duck
  fire.setPosition([2,-4,-50]);
  //low velocity, rising
  fire.setMinVelocity([-2,2,-2]);
  fire.setMaxVelocity([2,4,2]);
  //short duration
  fire.setMinLifetime(0.5);
  fire.setMaxLifetime(0.8);
  // again, bluish-white colour for fire
  fire.setMinColor([0.4,0.4,0.8,0.5]);
  fire.setMaxColor([0.6,0.6,1.0,1]);
  //small particles
  fire.setMinSize(0.1);
  fire.setMaxSize(0.5);
  fire.setSrcBlend(c3dl.ONE);
  fire.setDstBlend(c3dl.DST_ALPHA);
  fire.setTexture("flare.png");
  //make them go up
  fire.setAcceleration([0,5,0]);
  //many particles per second
  fire.setEmitRate(500);
  fire.init(300);
  scn.addObjectToScene(fire);
  
  //add a teapot in front of and above the duck (and above the fire)
  var thing = new c3dl.Collada();
  thing.init("teapot.dae");
  thing.setTexture("teapot.png");
  thing.setPosition([2,4,-50]);
  thing.scale([0.5,0.5,0.5]);
  scn.addObjectToScene(thing);
  
  //add a particle system of steam at the teapot's spout
  bubbles = new c3dl.ParticleSystem();
  bubbles.setPosition([9.5,6.5,-50]);
  //allow a little variety in the x and z axes, but make the particles go up (+y)
  bubbles.setMinVelocity([-1,1,-2]);
  bubbles.setMaxVelocity([2,5,2]);
  //allow a wider range of time
  bubbles.setMinLifetime(1);
  bubbles.setMaxLifetime(2);
  //set to a fairly dark gray colour
  bubbles.setMinColor([0.2,0.2,0.2,0.3]);
  bubbles.setMaxColor([0.2,0.2,0.2,0.5]);
  //allow larger particles
  bubbles.setMinSize(1);
  bubbles.setMaxSize(2);
  bubbles.setSrcBlend(c3dl.ONE);
  bubbles.setDstBlend(c3dl.DST_ALPHA);
  bubbles.setTexture("flare.png");
  //accelerate the particles upwards
  bubbles.setAcceleration([0,5,0]);
  //less particles per second
  bubbles.setEmitRate(50);
  bubbles.init(300);
  scn.addObjectToScene(bubbles);
}

If you’d like you could then modify this even more, to use a timer to make the flames from the duck turn on/off periodically, then have the steam turn on/off with it (but maybe a little behind to simulate the fact that the teapot will take a bit to heat up/cool down).

Videos

Demos

  • Asteroids-3D
  • RTS Prototype
  • Particle Systems Demo
  • Cross-Browser Orbiter
  • Mocap Demo With Spheres
  • Google Maps-3D

C3DL Development News

A spec change that keeps coming back to haunt me

At some point, the way firefox handles keyboard events changed. I’m not sure exactly when it happened, all I know is that it broke how I was dealing with keyboard interaction on almost every demo I’ve written (for example,the mocap demo and MotionView). When I wrote the demos, the keydown event would be fired once, [...]

Release 2.2

The 2.2 Release of the Canvas 3D Library includes a number of new features, updates to old features and fixes for several bugs along with the requisite changes to meet the evolving WebGL spec. Some of the things included (in no particular order) are: Better picking code. The ability to swap textures as a scene [...]

Tutorials

  • Tutorial #1: WebGL Browsers
  • Tutorial #2: A simple scene
  • Tutorial #3: Callback
  • Tutorial #4: Models
  • Tutorial #5: Light effects
  • Tutorial #6: Picking
  • Tutorial #7: Materials
  • Tutorial #8: Particle Systems
  • Tutorial #9: Camera Basics
    • Tutorial9-YawPitchRoll
  • Tutorial #10: Advanced FreeCamera
  • Tutorial #11: OrbitCamera
  • Tutorial #12: Advanced Camera Functions

Documentation

Archives

Archives

C3DL Development News

Recent Comments

  • June 2011
  • March 2011
  • October 2010
  • July 2010
  • April 2010
  • March 2010
  • February 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • September 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009
  • March 2009
  • February 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • September 2008
  • August 2008
  • July 2008
  • June 2008
  • May 2008
  • April 2008
  • March 2008
  • February 2008
  • January 2008
  • December 2007
  • A spec change that keeps coming back to haunt me
  • Release 2.2
  • 2.1 Release and things to come
  • Level Up! An Open Web Game Jam
  • Site moved!
  • SceneCreator0.3
  • WWW2010 in Raleigh
  • Motionview
  • On the train to Mountainview
  • C3DL 2.0-WebGL and beyond
  • That depends on what... - peter
  • This application is ... - Haisens
  • I think that example... - peter
  • The above links are ... - Atash
  • Hi there, just wante... - Patrick H. Lauke
  • Firefox 4 was releas... - Cathy Leung
  • In order to access l... - peter
  • I am not able to dis... - preksha
  • "JavaScript can’t di... - Joe Hocking
  • I should point out t... - peter



Canvas 3d JS Library

©2007- 2010 Canvas 3d JS Library

Disclaimer: This website is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 2.5 Canada License.
The Canvas 3d JS Library and Demos found on this website are licenced under the MIT License

Creative Commons License