-3

Building my responsive website, I would like to build my funny timeline, but I cannot come up with a solution.

It would be a sprite such as a rocket or flying saucer taking off at the bottom of middle of the page and coming out with smoke.

Smoke would remain more or less and disclose my timeline.

Sketch

Is anyone does have an idea how to make that possible?

Spitz
  • 1
  • 1

1 Answers1

1

To simulate smoke, you have to use a particle system. As you maybe know, WebGL is able to draw triangles, lines and points. This last one is what we need. The smoke is made of hundreds of semi-transparent white disks of slighly different sizes. Each point is defined by 7 attributes :

  • x, y: starting position.
  • vx, vy: direction.
  • radius: maximal radius.
  • life: number of milliseconds before it disappears.
  • delay: Number of milliseconds to wait before its birth.

One trick is to create points along a vertical centered axis. The more you go up, the more the delay increases. The other trick is to make the point more more transparent as it reaches it end of live.

Here is how you create such vertices :

function createVertices() {
  var x, y, vx, vy, radius, life, delay;
  var vertices = [];
  for( delay=0; delay<1; delay+=0.01 ) {
    for( var loops=0; loops<5; loops++ ) {
      // Going left.
      x = rnd(0.01);
      y = (2.2 * delay - 1) + rnd(-0.01, 0.01);
      vx = -rnd(0, 1.5) * 0.0001;
      vy = -rnd(0.001);
      radius = rnd(0.1, 0.25) / 1000;
      life = rnd(2000, 5000);
      vertices.push( x, y, vx, vy, radius, life, delay );
      // Going right.
      x = -rnd(0.01);
      y = (2.2 * delay - 1) + rnd(-0.01, 0.01);
      vx = rnd(0, 1.5) * 0.0001;
      vy = -rnd(0.001);
      radius = rnd(0.1, 0.25) / 1000;
      life = rnd(2000, 5000);
      vertices.push( x, y, vx, vy, radius, life, delay );
    }
  }

  var buff = gl.createBuffer();  
  gl.bindBuffer( gl.ARRAY_BUFFER, buff );
  gl.bufferData( gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW );

  return Math.floor( vertices.length / 7 );
}

As you can see, I created points going right and points going left to get a growing fuzzy triangle.

Then you need a vertex shader controling the position and size of the points. WebGL provide the output variable gl_PointSize which is the size (in pixels) of the square to draw for the current point.

uniform float uniWidth;
uniform float uniHeight;
uniform float uniTime;

attribute vec2 attCoords;
attribute vec2 attDirection;
attribute float attRadius;
attribute float attLife;
attribute float attDelay;

varying float varAlpha;

const float PERIOD = 10000.0;
const float TRAVEL_TIME = 2000.0;

void main() {
  float time = mod( uniTime, PERIOD );
  time -= TRAVEL_TIME * attDelay;
  if( time < 0.0 || time > attLife) return;
  vec2 pos = attCoords + time * attDirection;
  gl_Position = vec4( pos.xy, 0, 1 );
  gl_PointSize = time * attRadius * min(uniWidth, uniHeight);
  varAlpha = 1.0 - (time / attLife);
}

Finally, the fragment shader will display a point in white. but the more you go far from the center, the more transparent the fragments become. To know where you are in the square drawn for the current point, you can read the global WebGL variable gl_PointCoord.

precision mediump float;
varying float varAlpha;

void main() {
  float x = gl_PointCoord.x - 0.5;
  float y = gl_PointCoord.y - 0.5;
  float radius = x * x + y * y;
  if( radius > 0.25 ) discard;

  float alpha = varAlpha * 0.8 * (0.25 - radius);
  gl_FragColor = vec4(1, 1, 1, alpha);
}

Here is a live example : https://jsfiddle.net/m1a9qry6/1/

Tolokoban
  • 2,297
  • 14
  • 17