You will submit one or more webpages. Each has

  • One full-screen canvas
  • In which some particle effect occurs

As with the WebGL warmup, you’ll submit an HTML file and any number of js, glsl, json, and image files. Some optional credit options may ask for additional HTML files. Also submit a file named implemented.txt listing any optional parts you believe you completed.

Because you might have more than one HTML file, we specify exact file names for the HTML files.

This assignment, like all other assignments, is governed by the common components of MPs.

1 Required

Submit a file named burst.html that displays the following animation.

Invisible bounding box
Everything in the simulation should stay within an invisible cube. The scene should be visualized from the outside with perspective and diffuse lighting. You may add fancier lighting if you wish.
50 spheres

The page should be initialized with 50 spheres. Each should be entirely within the bounding box at a random location with a random initial velocity. Each should be a random color. They should be large enough to clearly look like spheres, not just dots and small enough that most don’t overlap.

You should render the spheres either using a single sphere geometry that is rendered 50 times per frame (once per sphere object) or using points scaled to be the right size and colored to look like spheres.

Notes on sphere geometry approach:

Use just one single sphere geometry, not one per sphere. Render render that one geoemetry once per particle, as e.g.

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
// set up your view and projection matrices
for each particle {
    // load a model matrix for this particle's position and size
    // load a color for this particle
    gl.drawElements(the, sphere, geometry)
}

We have an example 80-triangle sphere model you may use. It has radius 1, so you may need to scale it down either during import or with a model matrix.

Notes on points approach:

Make a gl.DYNAMIC_DRAW buffer that has each particle’s center’s location. You may also want other attributes for radius and color.

Rendering particles as points generally requires scaling the points appropriately by setting gl_PointSize for each particle in the vertex shader. Somewhat annoyingly, gl_PointSize is given in pixels while all other coordinates in the vertex shader are given in some type of normalized coordinate system.

The correct gl_PointSize is the product of four values:

  • Viewport size in pixels in either x or y. You’ll have to pass this in as a uniform float; you can get it as gl.canvas.width or gl.canvas.height.

  • Projection matrix scaling in the same axis. Assuming you are using a standard perspective projection matrix, this is either proj[0][0] for x or proj[1][1] for y, where proj is your projection matrix.

  • The inverse of the w value for this point. That is 1/gl_Position.w.

  • The diameter of the particle.

Inside your fragment shader you’ll have access to gl_PointCoord, a vec2 that is vec2(0,0) at the top-left corder of the point, vec(0.5, 0.5) in the center of the point, and vec(1,1) at the bottom-right corner of the point. To make it just a circle, you can do

float r = length(gl_PointCoord*2.0 - vec2(1,1));
if (r > 1.0) discard;

The discard keyword tells GLSL not to use the fragment at all, and is handled in such a way that putting it in an if like this does not slow down the GPU the way ifs usually do.

Physics

Spheres should move according to momentum and gravity. They should experience drag. They should bounce off of the invisible walls of the cube with non-zero non-one elasticity.

All of these should be physically plausible based on the elapsed time between frames, as given by the argument to the callback of requestAnimationFrame.

We recommend using Euler’s method for your simulation.

Periodic Reset
Drag and less-than-elastic collisions should cause the spheres to lose energy over time and settle down to slow motions near the ground. When this happens, you should reset the state of the simulation to a new random configuration. Resets should happen no more frequently than every 3 seconds and no less frequently than every 15 seconds.

2 Optional

2.1 Extensions to burst.html (5–65 points)

FPS display (5 points)

Add an HTML element on top of the canvas that displays the current frames per second.

The HTML for this should be after the HTML for the cavnas element and should look like

<div id="fps" style="position:fixed; bottom:0; right:0; display:table"></div>

You may want to also add a color:rgb(255,95,5) or the like to the style to help the text show up over your canvas clear color.

and inside your requestAnimationFrame callback you should have code like

document.querySelector('#fps').innerHTML = text you want displayed

The FPS should be displayed with a fixed number of decimal places, such as is created with the number.toFixed(places) method.

Sphere-sphere collisions (20 points)
Spheres should not overlap. If they would have overlapped without this constraint you should add a collision response between the two spheres based on the angle at which they collided. Spheres should be large enough that in their rest state there is more than a single layer of spheres.
Variable radii (5 points without sphere-sphere collisions or 10 points with sphere-sphere collisions)
Each spheres should have a different radius, with at least a 3-fold radius difference between the largest and smallest. Collision detection should respect these variable radii.
Variable mass (10 points; requires sphere-sphere collisions)

Each spheres should have a different mass, with at least a 10-fold mass difference between the most and least massive. If you use variable radii, it is recommended but not required that masses be proportional to volume (i.e. radius3).

Sphere-sphere collision responses should respect these variable masses.

Efficient collision detection (20 points; requires FPS display and sphere-sphere collisions)

Add a control for the number of spheres to create and a spatial structure to make sphere-sphere collision detection either O(n) or O\big(n \log(n)\big). As the number of spheeres increases, their size should decrease so they all fit in the simulation with room to spare.

The easiest way to do this is

  1. Initialize a grid over the simulation space with cell sizes = the largest sphere’s diameter
  2. Each frame
    1. Iterate over the spheres, adding a pointer to each to the cell that currently contains it center
    2. Check for collisions only between spheres that share a cell or are in adjacent cells

The above technique forms a baseline; it can be improved by cleverer grid sizing and distribution of pointers and by only allocating occupied grid cells. Hierarchical and axis-based structures can also be effective.

The result should allow making a very large number of spheres and should show roughly linear speed: if n spheres gives 30fps then 2n spheres should give something around 10–15fps.

2.2 fireworks.html (20–65 points)

Create a file fireworks.html that shows a glowing fireworks display.

Core fireworks (20 points)

Periodically, a burst of particles appear from roughly one position traveling away from one another. The position of each burst is different. The background is dark and the particles use additive blending so that they appear to be light sources and the more they overlap the brighter they appear; the particles are large enough seeing such overlaps is common.1 Individual particles exhibit weak gravity and strong drag. Each particle has a finite lifespan and is not drawn after it expires.

We recommend using gl.POINTs to draw particles and make each point dimmer near its edges than its center.

Chromaticity (5–10 points)

For 5 points, each burst should be of particles in a different color.

For 10 points, some (but not all) bursts should have multiple different colors of particles.

Variable density (10 points)
Randomize and overlap the timing of the bursts so that sometimes a burst is by itself in the scene and other times there are several concurrently-visible bursts.
Occlusion (10 points)
Render several objects in dark colors at different depths in the scene. Then disable depth writing but keep depth checking2 before drawing fireworks so that some particles go behind the rendered objects and others in front.
Shapes (5-15 points)

Have different types of bursts, randomly selected per burst. Some should still be the basic random velocities from the core fireworks requirement; others should be from this list. 5 points per item from the list, up to 15 points maximum.

  • Sparkling: as particles age, they start blinking rapidly and randomly until they vanish
  • Shape: the initial velocities are selected so that the particles expand into a recognizable shape
  • Double Burst: the initial burst produces a few fast-moving particles that each explode into smaller bursts a little later
  • Fairy Dust: a few fast-moving particles leave a trail of many small slow-moving particles behind them
  • Rocket: a trail of brief-lived flame-like particles shoots up from ground to the subsequent burst

2.3 boids.html (20–45 points)

Basics (20 points)

Implement the boids flocking algorithm as described on its inventor’s webpage. For base credit, implement it in 2D with a support radius of no more than 10% of the screen’s size.

Both the original boids paper and Reynold’s website fail to fully describe how separation and cohesion differ. Cohesion steers towards the mean location of other boids within the support radius. Separation has several forms, but two easy ones are to either (a) steer away from the distance-weight average location of other boids within the support radius or (b) steer away from mean location of other boids within a significantly smaller radius.

When a boid hits the edge of the screen it should bounce off the edge, similar to how spheres bounce off the edge of the simulation domain for the required part of this assignment.

To keep boids moving, implement a minimum speed: if a boid slows below this, add speed to it.

Orientation (10 points)
Use a shape (such as an isosceles triangle) with a clear forward direction for each boid and orient them in the direction of travel.
Turning radius (5 points)

Birds have to move to stay in flight and cannot turn very quickly. Simulate this in one of two ways:

Option 1
Give each boid a heading and a fixed speed. After resolving the desired changes to velocity from the boids forces, determine the heading that goal would imply and adjust each heading at most x° towards that, where x is small enough that a full 180° turn would take half a second or more. Remember to handle wrapping around: turning from −175° to +175° should go the short way round, not back through 0°.
Option 2
As an easier-to-compute but less-responsive alternative to option 1, givne current velocity \vec v and desired velocity \vec v', compute \delta v = \vec v' - \vec v. If \Delta v is larger than about 10% of the minimum boid speed, reduce its length; then add it to \vec v to find the new velocity.
boids3d.html (10 points; requires Sphere-sphere collisions)

Make a 3D version of boids that move inside an invisible 3D box, similar to how prticles do for the required part of this assignment.

Instead of the separation force, use sphere-sphere collisions.