Three.JS - Particles orbiting a point in random di

2020-07-25 04:44发布

问题:

I have a particle system where all the particles are positioned at the same coordinates and one after another, in random directions, they (should) start orbiting the center of the scene forming a sphere.

What I managed to achieve until now is a group of Vector3 objects (the particles) that one after another start orbiting the center along the Z axis simply calculating their sine and cosine based on the current angle.

I'm not that good at math and I don't even know what to look for precisely.

Here's what I wrote:

var scene = new THREE.Scene();

let container = document.getElementById('container'), 
    loader = new THREE.TextureLoader(), 
    renderer, 
    camera, 
    maxParticles = 5000, 
    particlesDelay = 50, 
    radius = 50, 
    sphereGeometry, 
    sphere;

loader.crossOrigin = true;

function init() {

    let vw = window.innerWidth, 
        vh = window.innerHeight;

    renderer = new THREE.WebGLRenderer();
    renderer.setSize(vw, vh);
    renderer.setPixelRatio(window.devicePixelRatio);

    camera = new THREE.PerspectiveCamera(45, vw / vh, 1, 1000);
    camera.position.z = 200;
    camera.position.x = 30;
    camera.position.y = 30;
    camera.lookAt(scene.position);
    scene.add(camera);

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

    let axisHelper = new THREE.AxisHelper(50);
    scene.add(axisHelper);

    container.appendChild(renderer.domElement);

    window.addEventListener('resize', onResize, false);

}

function onResize() {

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);    

}

function draw() {

    sphereGeometry = new THREE.Geometry();
    sphereGeometry.dynamic = true;

    let particleTexture = loader.load('https://threejs.org/examples/textures/particle2.png'), 
        material = new THREE.PointsMaterial({
            color: 0xffffff, 
            size: 3, 
            transparent: true, 
            blending: THREE.AdditiveBlending, 
            map: particleTexture, 
            depthWrite: false
        });

        for ( let i = 0; i < maxParticles; i++ ) {

            let vertex = new THREE.Vector3(radius, 0, 0);
            vertex.delay = Date.now() + (particlesDelay * i);
            vertex.angle = 0;
            sphereGeometry.vertices.push(vertex);

        }

        sphere = new THREE.Points(sphereGeometry, material);
        scene.add(sphere);

}

function update() {

    for ( let i = 0; i < maxParticles; i++ ) {

        let particle = sphereGeometry.vertices[i];

        if ( Date.now() > particle.delay ) {

            let angle = particle.angle += 0.01;

            particle.x = radius * Math.cos(angle);

            if ( i % 2 === 0 ) {
                particle.y = radius * Math.sin(angle);
            } else {
                particle.y = -radius * Math.sin(angle);
            }

        }


    }

    sphere.geometry.verticesNeedUpdate = true;

}

function render() {

    update();
    renderer.render(scene, camera);
    requestAnimationFrame(render);

}

init();
draw();
render();

And here's the JSFiddle if you want to see it live: https://jsfiddle.net/kekkorider/qs6s0wv2/

EDIT: Working example

Can someone please give me a hand?

Thanks in advance!

回答1:

You want each particle to rotate around a specific random axis. You can either let them follow a parametric equation of a circle in 3D space, or you can make use of THREE.js rotation matrices.

Right now all your particles are rotating round the vector (0, 0, 1). Since your particles start off on the x-axis, you want them all to rotate around a random vector in the y-z plane (0, y, z). This can be defined during the creation of the vertices:

vertex.rotationAxis = new THREE.Vector3(0, Math.random() * 2 - 1, Math.random() * 2 - 1);
vertex.rotationAxis.normalize();

now you can just call the THREE.Vector3.applyAxisAngle(axis, angle) method on each of your particles with the random rotation axis you created each update:

particle.applyAxisAngle(particle.rotationAxis, 0.01); 

To sum up, this is how it should look like:

draw():

...
for ( let i = 0; i < maxParticles; i++ ) {

    let vertex = new THREE.Vector3(radius, 0, 0);
    vertex.delay = Date.now() + (particlesDelay * i);
    vertex.rotationAxis = new THREE.Vector3(0, Math.random() * 2 - 1, Math.random() * 2 - 1);
    vertex.rotationAxis.normalize();
    sphereGeometry.vertices.push(vertex);
}
...

update():

...
for ( let i = 0; i < maxParticles; i++ ) {

    let particle = sphereGeometry.vertices[i];

    if ( Date.now() > particle.delay ) {
        particle.applyAxisAngle(particle.rotationAxis, 0.01); 
    }
}
...