Tuesday, September 25, 2012

Particles, Sprites, and Particle Systems

‹prev | My Chain | next›

Last night, I found that Three.js Particles belong in the Canvas Renderer, while Particle Systems belong in WebGL Renderers. This begs the question where do Sprites go?'

Well, it turns out that Sprites go in WebGL renderers along with Particle Systems and serve the same function as Particles. Interestingly (more precisely, confusingly), Sprites take similar constructor arguments to particle materials, but are positioned like a canvas element. That is, Sprites are positioned from the top left corner of the canvas element with y increasing downwards:
  var map = THREE.ImageUtils.loadTexture('/images/purple_fruit_monster.png');
  map.needsUpdate=true;
  
  var sprite = new THREE.Sprite({
    map: map,
    transparent: true
  });
  sprite.position.set(200,150,-10000000);
  scene.add(sprite);
An X position of 200 would put the sprite to the right of the center position, but it barely moves the sprite from the left of the viewport:

(the ball marks the center of 3D space)

Not only are the x-y coordinates canvas-based, but the z coordinate (negative ten million) is completely ignored.

Happily, if the sprite is meant to be a part of the 3D scene rather than an overlay, Three.js provides the useScreenCoordinates property. Setting useScreenCoordinates to false lets me place the sprite directly in the scene with the usual 3D coordinates (including the Z position):


As for the particle system, I would still like to know how to move individual particles within the system. I can make the entire system rotate in a simple animation. But if I try to move a single particle within that system, the particle sits frozen:
  function animate() {
    requestAnimationFrame(animate);
    
    monsters.rotation.set(0, monsters.rotation.y + 0.01, 0);
    points.vertices[0].set(0, points.vertices[0].y + 0.1, 0);
    
    // Now, show what the camera sees on the screen:
    renderer.render(scene, camera);
  }
  
  animate();
It turns out that I need to specify the verticesNeedUpdate property in order to make this work:
  function animate() {
    requestAnimationFrame(animate);
    
    monsters.rotation.set(0, monsters.rotation.y + 0.01, 0);
    points.vertices[0].set(0, points.vertices[0].y + 0.1, 0);
    points.verticesNeedUpdate = true;
    
    // Now, show what the camera sees on the screen:
    renderer.render(scene, camera);
  }
Further experimentation reveals that, if I set the sortParticle property on the points, then I do not need to specify verticesNeedUdpate and I get rid of some clipping that occurs occasionally as otherwise transparent parts of the particles pass over each other:


The final implementation then becomes:
  var wrapper = new THREE.ParticleBasicMaterial({
    size: 256,
    map: map,
    transparent: true
  });
  var points = new THREE.Geometry();
  points.vertices.push(new THREE.Vector3(0, 0, 250));
  points.vertices.push(new THREE.Vector3(200, 0, 250));
  points.vertices.push(new THREE.Vector3(-200, 0, 250));
  var monsters = new THREE.ParticleSystem(points, wrapper);
  monsters.sortParticles = true;
  scene.add(monsters);
  
  function animate() {
    requestAnimationFrame(animate);
    
    monsters.rotation.set(0, monsters.rotation.y + 0.01, 0);
    points.vertices[0].set(0, points.vertices[0].y + 0.1, 0);
    
    // Now, show what the camera sees on the screen:
    renderer.render(scene, camera);
  }
  
  animate();
With that, one particle slowly rise as the other two rotate about the center of 3D space:


I think that about does it for Sprites, Particles, and Particle Systems in Three.js. At least until I stumble across one more thing that I cannot quite get to work.

Final version of the code


Day #520

No comments:

Post a Comment