Как задать объекту передвижение по окружности на осях x, y, z | Three js

я пытаюсь сделать анимацию перемещения сферы по окружности, создаю все это с помощью three js, вот все, до чего я смог додуматься Покадровая анимация

Здесь каждый кадр изменяется позиция объекта, но проблема в том, что я не знаю как наклонить окружность по которой перемещается сфераКартинка проекта

Здесь я вывел оси для удобства, синяя - z, зеленая - y, оранжевая - x. В данном случае сфера перемещается по окружности, используя оси y и z, но мне нужно еще как то перемещать ее по x, чтобы сфера смогла перемещаться по окружности, которую вы можете увидеть на картинке. Для общего понимания, я хочу сделать анимированный 3д атом. Надеюсь на чью-то помощь, спасибо!


Ответы (2 шт):

Автор решения: Sergey

Не понятен весь код того что хотите сделать, но может проще камеру повернуть по оси. Допустим camera.rotation.z = Math.pi * 0.1 //вся окружность 2ПИ.

→ Ссылка
Автор решения: Rudi

Можно сделать что-то такое. Логика заключается в том чтобы добавить все частицы и выстроить их вокруг ядра, после чего создать группы-контейнеры для частиц, добавить частицы в контейнеры, а контейнеры сместить вращая по оси Y на равные углы, затем просто вращать контейнеры по оси Z... Можно поиграться с настройками эффектов, цветами, скоростями изменяя значения...

const playButton = document.getElementById('playButton');

let requestID = false;

playButton.addEventListener('click', function() {
    //Запускаем анимацию по нажатию на кнопку одновременно с аудио
    if(!requestID) requestID = window.requestAnimationFrame(animate);
    
    let audio = document.querySelectorAll('.player');
    
    if (audio[0].paused) {
        audio[0].play();
        audio[1].play();
        playButton.textContent = 'Pause';
    } else {
        audio[0].pause();
        audio[1].pause();
        playButton.textContent = 'Play';
    }
});
body {
  margin: 0;
}
#playButton{
  display: none;
  position: absolute;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Rudi animation</title>
  </head>
  <body>
  
    <audio class="player" loop="true">
      <source src="https://zvukoviku.ru/sounds/1573719407_66163fbb0aef075.mp3" type="audio/mp3">      
    </audio>
    
    <audio class="player" loop="true">
      <source src="https://zvukoviku.ru/sounds/1573719603_ec8dfcc179543b1.mp3" type="audio/mp3">
    </audio>
    <button id="playButton">Play</button>
    
    
    <script>
      // Создадим переменную чтобы её было видно в других тэгах <script>, в <script type="module"> присвоим ей функцию
      let animate;
    </script>
    <script type="module">
    //Импортируем необходимые зависимости
        import * as THREE from "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js";
        import { EffectComposer } from "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/postprocessing/EffectComposer.js";
        import { RenderPass } from "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/postprocessing/RenderPass.js";
        import { UnrealBloomPass } from "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/postprocessing/UnrealBloomPass.js";
        import { AfterimagePass } from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/postprocessing/AfterimagePass.js';

        import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/OrbitControls.js';

        let w = window.innerWidth;
        let h = window.innerHeight;
        const scene = new THREE.Scene();
        scene.fog = new THREE.FogExp2(0x000000, 0.01);
        const camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 1000);
        camera.position.set(0,1.6,0);
        camera.rotation.set(0,-90,0);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(w, h);
        document.body.appendChild(renderer.domElement);

        const light = new THREE.AmbientLight( 0x00B2B2FF );
        scene.add( light );

        // Orbit Controls
        const controls = new OrbitControls(camera, renderer.domElement);
        controls.target.set(0, 0, 0);
        controls.update();

        // Настройка эффектов
        const renderScene = new RenderPass(scene, camera);
                                              // resolution, strength, radius, threshold
        const bloomPass = new UnrealBloomPass(new THREE.Vector2(w, h), 7, 0.9, -0.25);

        const afterImagePass = new AfterimagePass();
        afterImagePass.uniforms["damp"].value = 0.980;

        const composer = new EffectComposer(renderer);
        composer.addPass(renderScene);
        composer.addPass(bloomPass);
        composer.addPass(afterImagePass);

        //Создадим объекты оболчку и ядро

        const shellGeometry = new THREE.SphereGeometry(0.5, 8, 8);
        const shellMaterial = new THREE.MeshStandardMaterial({ color: 0x000F0FFF, emissive: 0xfffff, emissiveIntensity: 0.1, wireframe: true });
        const shell = new THREE.Mesh(shellGeometry, shellMaterial);
        scene.add(shell);

        const nucleusGeometry = new THREE.SphereGeometry(0.1, 16, 16);
        const nucleusMaterial = new THREE.MeshStandardMaterial({ color: 0x00FFFFFF, emissive: 0x00FFFFFF,emissiveIntensity: 0.1});
        const nucleus = new THREE.Mesh(nucleusGeometry, nucleusMaterial);
        scene.add(nucleus);


        const atoms = [];
        const atomContainers = [];
        const totalAtoms = 5; // количество частиц оптимально от 3 до 6

        for (let i = 0; i < totalAtoms; i++) {
          const atomGeometry = new THREE.SphereGeometry(0.05, 16, 16);
          const atomMaterial = new THREE.MeshStandardMaterial({ color: 0x003131E7 });
          const atom = new THREE.Mesh(atomGeometry, atomMaterial);
          atoms.push(atom);
          scene.add(atom);
        }

        //расставляем частицы вокруг ядра
        atoms.forEach((atom, index) => {
          const angle = (index / totalAtoms) * Math.PI * 2;
          const radius = 0.9;
          atom.position.x = radius * Math.cos(angle) + nucleus.position.x;
          atom.position.y = radius * Math.sin(angle) + nucleus.position.y;
        });

        for (let i = 0; i < totalAtoms; i++) {
        //Добавляем контейнер для частицы
            const atomContainer = new THREE.Group();
        //Добавляем частицу в контейнер attach не учитывает позицию частиц
            atomContainer.attach(atoms[i]);
            atomContainers.push(atomContainer);
            scene.add(atomContainer);
        //Вращаем каждый контейнер по оси Y на равные углы
            atomContainer.rotation.y = (Math.PI * ((360/totalAtoms)*i)) / 360;
        }

        //Флаг для контроля пульсации
        let flag = true;

        function update() { 
            let scale = nucleus.scale;
            let scaleSh = shell.scale;

            if(flag){ 
              shell.scale.set(scaleSh.x+0.01, scaleSh.y+0.01, scaleSh.z+0.01);
              nucleus.scale.set(scale.x+0.12, scale.y+0.12, scale.z+0.12);
            }else{
              shell.scale.set(scaleSh.x-0.01, scaleSh.y-0.01, scaleSh.z-0.01);
              nucleus.scale.set(scale.x-0.12, scale.y-0.12, scale.z-0.12);
            }
            if(nucleus.scale.x > 2.5){
              flag = false;
            }else if(nucleus.scale.x <= 0.09){
              flag = true;
            }
            //Вращаем все контейнеры для частиц по оси Z
            for (let i=0; i< totalAtoms; i++){
                atomContainers[i].rotation.z += 0.1;
            //Вращаем оболочку по осям YZ
                shell.rotation.y += 0.02;
                shell.rotation.z += -0.02;
            //Рендер эффектов. Можно поиграться, вынести за пределы цикла
                composer.render(scene, camera);
            }
        }
        //Присваиваем функцию переменной созданной в первом <script>
        animate = function () {  
          update();
          window.requestAnimationFrame(animate);
        }


        function handleWindowResize () {
          w = window.innerWidth;
          h = window.innerHeight;
          camera.aspect = w / h;
          camera.updateProjectionMatrix();
          renderer.setSize(window.innerWidth, window.innerHeight);
        }
        window.addEventListener('resize', handleWindowResize, false);
    </script>
    
    <script>
      //Дожидаемся полной загрузки и отображаем кнопку Play
      addEventListener("DOMContentLoaded", (event) => {
        document.getElementById('playButton').style.display = "block";
      });
    </script>
  </body>
</html>

→ Ссылка