Kayyy
Kayyy

Reputation: 1

Particle system design using Three.js and Shader

I'm very new to this community. As i'm asking question if there is something i claim not right, please correct me.

Now to the point, i'm design a particle system using Three.js library, particularly i'm using THREE.Geometry() and control the vertex using shader. I want my particle movement restricted inside a box, which means when a particle crosses over a face of the box, it new position will be at the opposite side of that face.

Here's how i approach, in the vertex shader:

uniform float elapsedTime;

        void main() {
            gl_PointSize = 3.2;
            vec3 pos = position;

            pos.y -= elapsedTime*2.1;

            if( pos.y < -100.0) {
                pos.y = 100.0;
            }



            gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0 );
        }

The ellapsedTime is sent from javascript animation loop via uniform. And the y position of each vertex will be update corresponding to the time. As a test, i want if a particle is lower than the bottom plane ( y = -100) it will move to the top plane. That was my plan. And this is the result after they all reach the bottom:

Start to fall

Start to fall

After reach the bottom

After reach the bottom

So, what am i missing here?

Upvotes: 0

Views: 995

Answers (2)

prisoner849
prisoner849

Reputation: 17586

You can achieve it, using mod function:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 300);
var renderer = new THREE.WebGLRenderer({
  antialis: true
});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

var gridTop = new THREE.GridHelper(200, 10);
gridTop.position.y = 100;
var gridBottom = new THREE.GridHelper(200, 10);
gridBottom.position.y = -100;
scene.add(gridTop, gridBottom);

var pts = [];
for (let i = 0; i < 1000; i++) {
  pts.push(new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).multiplyScalar(100));
}
var geom = new THREE.BufferGeometry().setFromPoints(pts);
var mat = new THREE.PointsMaterial({
  size: 2,
  color: "aqua"
});
var uniforms = {
  time: {
    value: 0
  },
  highY: {
    value: 100
  },
  lowY: {
    value: -100
  }
}
mat.onBeforeCompile = shader => {
  shader.uniforms.time = uniforms.time;
  shader.uniforms.highY = uniforms.highY;
  shader.uniforms.lowY = uniforms.lowY;
  console.log(shader.vertexShader);
  shader.vertexShader = `
    uniform float time;
    uniform float highY;
    uniform float lowY;
  ` + shader.vertexShader;
  shader.vertexShader = shader.vertexShader.replace(
    `#include <begin_vertex>`,
    `#include <begin_vertex>
      
      float totalY = highY - lowY;
      transformed.y = highY - mod(highY - (transformed.y - time * 20.), totalY);
    `
  );
}
var points = new THREE.Points(geom, mat);
scene.add(points);

var clock = new THREE.Clock();

renderer.setAnimationLoop(() => {
  uniforms.time.value = clock.getElapsedTime();
  renderer.render(scene, camera);
});
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Upvotes: 2

user128511
user128511

Reputation:

You can not change state in a shader. vertex shaders only output is gl_Position (to generate points/lines/triangles) and varyings that get passed to the fragment shader. Fragment shader's only output is gl_FragColor (in general). So trying to change pos.y will do nothing. The moment the shader exits your change is forgotten.

For your particle code though you could make the position a repeating function of the time

       const float duration = 5.0;
       float t = fract(elapsedTime / duration);
       pos.y = mix(-100.0, 100.0, t);

Assuming elapsedTime is in seconds then pos.y will go from -100 to 100 over 5 seconds and repeat.

Note in this case all the particles will fall at the same time. You could add an attribute to give them each a different time offsets or you could work their position into your own formula. Related to that you might find this article useful.

You could also do the particle movement in JavaScript like this example and this one, updating the positions in the Geometry (or better, BufferGeometry)

Yet another solution is to do the movement in a separate shader by storing the positions in a texture and updating them to a new texture. Then using that texture as input another set of shaders that draws particles.

Upvotes: 0

Related Questions