Make particles follow a path - issues

So i am trying to make some particles follow a path I’ve managed to this but the particles seem to be duplicated twice so there are a few particles in the middle of the screen that do not move that should not be there I’ve also noticed the animation rotation of the “particle box” jumps … here is a code pen

i am trying to create one of these particle paths from this example but with the above path as you can see It’s not going to well

thanks for any help

Issue 1:

Several clouds are created and added to the scene by code like this:

for (var i = 0; i < parameterse.length; i++) {
     ...
     particles = new THREE.Points(geometryParticle, materials[i]);
     scene.add(particles);
 }     

However, as they all recycle the same variable, at the end, particles refers only to the last cloud. So, when you move particles only one of the cloud is moved.

Issue 2:

Try the following idea for turning the cloud. Remove these lines (or just comment them):

axis.crossVectors( up, tangent ).normalize();
radians = Math.acos( up.dot( tangent ) );
particles.quaternion.setFromAxisAngle( axis, radians );

if (object instanceof THREE.Points) { 
      object.rotation.y = time * (i < 1 ? i + 2 : -(i + 1));
      object.rotation.x = time2 * (i < 1 ? i + 2 : -(i + 1));
      object.rotation.z = time * (i < 1 ? i + 2 : -(i + 1));
} 

add just this line:

particles.rotation.y = Math.atan2( tangent.x, tangent.z );
1 Like

In case you want to move all clouds, here is the whole animate function (not optimal, as I tried to make minimal changes to code).

function animate() {
   fraction +=0.0004;
   if (fraction > 1) fraction = 0;

   newPosition = pointsPath.getPoint(fraction);
   tangent = pointsPath.getTangent(fraction);

   for (var i = 0; i < scene.children.length; i++) {
       var object = scene.children[i];
       if (object instanceof THREE.Points) { 
          object.position.copy(newPosition);
          object.rotation.y = Math.atan2( tangent.x, tangent.z );
       } 
   }

   for (var i = 0; i < materials.length; i++) { 
       var color = parameterse[i][0];
       var h = ((360 * (color[0] + time)) % 360) / 360;
       materials[i].color.setHSL(h, color[1], color[2]);  
   }
  
   requestAnimationFrame(animate);
   renderer.render(scene, camera);
};

Here is the result:

2 Likes

thanks for replying
Would there be a way to like rotate each particle at different rates ?

Currently rotation it taken from the path direction and is set here:

object.rotation.y = Math.atan2( tangent.x, tangent.z );

If you want every cloud to rotate in its own way, you have to calculate its rotation property. Here is an example of crazy clouds:

object.rotation.x = 3*Math.cos(i-6*fraction);
object.rotation.y = 7*Math.sin(i+6*fraction);

If you want to rotate individual points in the cloud, then you have to calculate their positions in the attribute buffer.

1 Like

@akella has a really good video about it:

3 Likes

So I ended up with this https://codepen.io/uiunicorn/full/GRYNWXy i know its only one point and ill probably need more but can you suggest a way to make the particles “blend with the curve” kind of like a snake as its kind of ridged looking :slight_smile:

You use getPoint to get a position along the path, this is completely sufficient. You need to have shorter clouds. Then place every cloud on its own position long the curve, so, newPosition and tangent must be recalculated for each cloud. All clouds’ positions are based on t but with different offset (thus the left cloud is at position t, the next cloud is a little bit ahead of time, the next to the next is more ahead of time and so on). This is a simplified illustration of 5 clouds on a path.

And here is what it looks like as animation:

Good luck with your project!

2 Likes

That’s sick, ill give it ago so i need more than one “particles” array? and then set the time to a short time for each ?

You already have 5 or 6. For the video I did not create any new “particles” arrays, just reused the ones that already exist.

so here’s my attempt at it where am i going wrong maybe the size?

another attempt https://codepen.io/uiunicorn/pen/MWPbVpE

https://codepen.io/uiunicorn/full/abRBKxE this is the best I could do would you mind showing me what values and stuff you had mine are miles off so doesn’t look as smooth, thanks

The full JS code of the last video, that I posted, is at the bottom. As a general advice, not related to your questions, try to keep the code tidy and well formatted. This is very helpful for debugging and understanding the fabrics of what the code does. There are several code formatting styles, any of them is better than having no style.

As for the code, there is no need to calculate different positions in different variables. This can be done on-the-fly in the cycle where clouds have their rotation set. Like this:

for (var i = 0; i < scene.children.length; i++) {
   // get position and tangent of i-th cloud
   newPosition = pointsPath.getPoint((fraction+i/50)%1);
   tangent = pointsPath.getTangent((fraction+i/50)%1);

   var object = scene.children[i];
   if (object instanceof THREE.Points) { 
        var time = Date.now() * 0.0009;

        // set position of i-th cloud
        object.position.copy(newPosition);

        // set rotation of i-th cloud
        object.rotation.y = Math.atan2( tangent.x, tangent.z );
        object.rotation.z = time * (i < 1 ? i + 2 : -(i + 1));
   }  // if
} // for
Show the full source code
import * as THREE from "https://jokertattoo.co.uk/game-lesson-1/js/three.module.js";
            import TWEEN from "https://jokertattoo.co.uk/game-lesson-1/js/tween.esm.js";
            import { RGBELoader } from "https://jokertattoo.co.uk/game-lesson-1/js/RGBELoader.js";
            import { OrbitControls } from "https://jokertattoo.co.uk/game-lesson-1/js/OrbitControls.js";
            import { GLTFLoader } from "https://jokertattoo.co.uk/game-lesson-1/js/GLTFLoader.js";
            import { RoughnessMipmapper } from "https://jokertattoo.co.uk/game-lesson-1/js/RoughnessMipmapper.js";
            import { CSS2DRenderer, CSS2DObject } from "https://jokertattoo.co.uk/game-lesson-1/js/CSS2DRenderer.js";

            var container, controls;
            var camera, scene, renderer, loaderGL, time, mixer, mixer2, mixer3, mixer4, mixer6, mixer7, mixer8, mixer9, clock, labelRenderer;
            var raycaster, mouse, tween;
            var model, model2, model3, model4, model5, model6, model7, model8, model9;
            var cubefix, cube, cube2, cube3, cube4, cube5, character, Characterm, Monster0, score, gltfPlayer, gltfMonster, isZoomed, gltfC1, gltfC2, gltfC3, gltfC4, gltfC5;
            var action, action3, action0, action2, action4, action5, action6;
            var nearFog, farFog, densityFog, colorFog, colorFog2;
            var hotspotLabel1, hotspotLabel2, hotspotLabel3, hotspotLabel4;
            var materials = [],
                parameters;
            var mouseX = 0,
                mouseY = 0;
            var timerAnimate;
            var CH0, CH, CH3, CH4;
			 var game_loader_count = 0;
			 var observer, text, checkText, partcount, StartButton, ButtonStart;
			var fov, aspect, near, far;
			 var fraction, pointsPath, char, axis, up, newPosition, tangent, radians, particles, loaderSprite;
var materials = [];
var parameterse =[];

 init();
            animate();
            function init() {
  renderer = new THREE.WebGLRenderer({antialias: true});
 renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(devicePixelRatio);
document.body.appendChild(renderer.domElement); 
               var pmremGenerator = new THREE.PMREMGenerator(renderer);
                pmremGenerator.compileEquirectangularShader();

 	  fov = { value: 45};
     aspect = window.innerWidth / window.innerHeight;
     near = { value: 1};
     far = { value: 10000};
				   camera = new THREE.PerspectiveCamera(fov.value, aspect, near.value, far.value);
   camera.position.set(-8, 10, 25.1);

 camera.lookAt(0,0,0);


  scene = new THREE.Scene();
  scene.background = new THREE.Color(0x000000);

  
 
    fraction = 0;

   
   up = new THREE.Vector3( 0,0,1 );
   axis = new THREE.Vector3( );


  pointsPath = new THREE.CurvePath();

const curve = new THREE.CatmullRomCurve3( [
		new THREE.Vector3(  0,   0, -0 ),
	new THREE.Vector3(  -2, 0,  7 ),
	new THREE.Vector3( -8, 0, 12 ),
  new THREE.Vector3(  -14, 0,  8 ),
	new THREE.Vector3( 10, 0, 5 ),
  new THREE.Vector3( -5, 0, -14 ),
  new THREE.Vector3(  5, 0,  -15 ),
 new THREE.Vector3(  0,   0, -0 ),
] );
              
  const bezierLine = new THREE.CubicBezierCurve3(
    
    //originals
	new THREE.Vector3(  13,   0, -1 ),
	new THREE.Vector3(  500, 0,  100 ),
	new THREE.Vector3( -14, 0, -73 ),
 new THREE.Vector3(  0,   0, 0 ),
    	
);
   
  
  const bezierLine2 = new THREE.CubicBezierCurve3(
		new THREE.Vector3(  0,   0, -1 ),
	new THREE.Vector3(  -50, 0,  10 ),
	new THREE.Vector3( -14, 0, -73 ),
    new THREE.Vector3(  0,   0, 0 ),
);
  
  
    pointsPath.add(curve);
 // pointsPath.add(bezierLine2);
 
 const material = new THREE.MeshNormalMaterial();
  const coneGeom = new THREE.ConeGeometry(1, 2, 10);
  coneGeom.translate(0, 24.5, 0);
  
  
  const cone = new THREE.Mesh(coneGeom, material);
  const cylinder = new THREE.CylinderGeometry(0.4, 0.6, 3, 10);
  
  cylinder.merge(cone.geometry, cone.matrix);
  cylinder.scale(0.5, 0.5, 0.5);
  
  char = new THREE.Mesh(cylinder, material);
 


     

	  loaderSprite = new THREE.TextureLoader();
	  
	               
        
                         
                            //Load En
                   
                  
                  
               
var geometryParticle = new THREE.BufferGeometry();
var vertices = [];

var sprite1 = loaderSprite.load("https://i.imgur.com/5w23cqM.png"); 
var partcount = 40;

 const min = 1,
        max = 2,
        high = Math.floor(Math.random() * (max - min) + min);
for (var i = 0; i < partcount; i++) { var x =  (Math.random() * 2 - 1) * 1; var y = (Math.random() * 2 - 1) * 1; var z = (Math.random() * 2 - 1) * 2;
vertices.push(x, y, z); }
geometryParticle.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));
parameterse = [ [[0.8, 0, 0.5], sprite1, 1], [[0.8, 0, 0.5], sprite1, 2], [[0.8, 0, 0.5], sprite1, 1], [[0.8, 0, 0.5], sprite1, 2], [[0.8, 0, 0.5], sprite1, 3], ];
for (var i = 0; i < parameterse.length; i++) { var color = parameterse[i][2]; var sprite = parameterse[i][1]; var size = parameterse[i][2];
materials[i] = new THREE.PointsMaterial({ size: size, map: sprite, blending: THREE.AdditiveBlending,
      depthWrite: false, transparent: true, fog: false, toneMapped: false}); 
      
      
      materials[i].toneMapped = false; materials[i].color.setHSL(color[0], color[1], color[2]);
      
      
particles = new THREE.Points(geometryParticle, materials[i]);
// particles.rotation.x = Math.random() 
 

 scene.add(particles);
 }     
             

   
   
   
  






  
    const material2 = new THREE.LineBasicMaterial({
	color: 0x9132a8
});
    const points = pointsPath.curves.reduce((p, d)=> [...p, ...d.getPoints(60)], []);
  
    const geometry2 = new THREE.BufferGeometry().setFromPoints( points );
  

    var liner = new THREE.Line( geometry2, material2 );
     scene.add(liner);
    
     clock = new THREE.Clock();

   
 


 
        
  const axesHelper = new THREE.AxesHelper( 0.5 );
  axesHelper.translateX(1);
  scene.add( axesHelper );
  

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

                controls.enableKeys = false;
                controls.enableZoom = false;
                controls.enableDamping = true;
                controls.maxPolarAngle = 1.4;
                controls.minPolarAngle = 1.2;
                controls.dampingFactor = 0.04;
                controls.autoRotate = false;
                controls.rotateSpeed = 0.015;
                controls.minDistance = 2;
          
				controls.enabled = false;

            }


function animate() {
    
    
fraction +=0.0004;
//   if (fraction > 1) fraction = 0;


   for (var i = 0; i < scene.children.length; i++) {

        newPosition = pointsPath.getPoint((fraction+i/50)%1);
   tangent = pointsPath.getTangent((fraction+i/50)%1);

     
     var object = scene.children[i];
       if (object instanceof THREE.Points) { 
   const pos = object.position.copy(newPosition);
        
         var time = Date.now() * 0.0009;
             object.rotation.y = Math.atan2( tangent.x, tangent.z );
           
      object.rotation.z = time * (i < 1 ? i + 2 : -(i + 1));
     
         
         
       } 
   }

   for (var i = 0; i < materials.length; i++) { 
       var color = parameterse[i][0];
       var h = ((360 * (color[0] + time)) % 360) / 360;
       materials[i].color.setHSL(h, color[1], color[2]);  
   }
     
     
     

 

                requestAnimationFrame(animate);
   renderer.render(scene, camera);
  
};
1 Like