Вращающаяся кривая эллипса в Three.js

#javascript #three.js #3d

Вопрос:

Цель

Я пытаюсь создать эллиптическую кривую (https://threejs.org/docs/#api/en/extras/curves/EllipseCurve), по которому должна перемещаться камера.

Что я сделал для достижения этой цели?

Это код для эллипса до сих пор.

 var curve = new THREE.EllipseCurve(
    0,0,
    1, 1,
    0, 2 * Math.PI,
    false,
    1.57
)

const points = curve.getPoints( 50 );
const geometry = new THREE.BufferGeometry().setFromPoints( points );


var material = new THREE.LineBasicMaterial( { color : 0xffffff } );

// Create the final object to add to the scene
var curveObject = new THREE.Line( geometry, material );

scene.add(curveObject);
 

Я вижу это в такой сцене:

Окружность, находящаяся в осях x

Проблема

Я попытался повернуть кривую эллипса на 90 градусов вокруг оси x по часовой стрелке. Как я понял из документации, последний параметр определяющей функции должен вращать ее.

 const curve = new THREE.EllipseCurve(
    0,  0,            // ax, aY
    10, 10,           // xRadius, yRadius
    0,  2 * Math.PI,  // aStartAngle, aEndAngle
    false,            // aClockwise
    0                 // aRotation
);
 

Заранее благодарю вас за ваш ответ. Я совершенно новичок в Three.js так что извините, если этот вопрос может показаться глупым 😀

Комментарии:

1. Последний параметр поворачивает кривую вокруг оси Z, таким образом, сама кривая находится в плоскости XY.

Ответ №1:

поворот, поворот под углом, повлияет на локальное происхождение других настроек угла для этой кривой. Это не общее вращение эллипса, а ориентация любого смещения относительно значения по умолчанию. Другая отправная точка. Это превратило бы рот Пак-Мэна в рот Пак-Мэна наоборот в /- 180-степени. Чтобы повернуть общую кривую в мировом пространстве, используйте один из различных доступных методов, таких как curve.rotation.set(0,1,0) или вращение.y = 1. Пожалуйста, обратитесь к документации для конкретных вариантов вращения.

Ответ №2:

Найдите точку на кривой и примените к ней матрицу 4.

Вот концепция того, как вы можете это сделать (см. Строки с cam в цикле анимации, лучше смотреть с «Полной страницы»):

 body{
  overflow: hidden;
  margin: 0;
} 
 <script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.134.0";
import {
  OrbitControls
} from "https://cdn.skypack.dev/three@0.134.0/examples/jsm/controls/OrbitControls.js";

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(-10, 10, 10);
let renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(innerWidth, innerHeight);
renderer.autoClear = false;
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
})

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

let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));

let grid = new THREE.GridHelper();
grid.position.y = -5;
scene.add(grid);

let obj = new THREE.Mesh(new THREE.IcosahedronGeometry(1, 0), new THREE.MeshLambertMaterial({
  color: "aqua"
}));
scene.add(obj);

let curve = new THREE.EllipseCurve(0, 0, 10, 5);

let line = new THREE.Line(new THREE.BufferGeometry().setFromPoints(curve.getSpacedPoints(100)), new THREE.LineBasicMaterial({
  color: "yellow"
}));
line.rotation.x = -Math.PI * 0.25;
line.rotation.z = Math.PI * 0.125;
line.position.x = 5;
scene.add(line);

let cam = new THREE.PerspectiveCamera(25, 1, 1.5, 25);
let camHelper = new THREE.CameraHelper(cam);
scene.add(camHelper);

let clock = new THREE.Clock();
let v = new THREE.Vector3();

renderer.setAnimationLoop(() => {

  let t = (clock.getElapsedTime() * 0.05) % 1;
  
  // magic is in these lines //////////////////
  cam.position.copy(curve.getPointAt(t, v));
  cam.position.applyMatrix4(line.matrixWorld);
  cam.lookAt(obj.position);
  /////////////////////////////////////////////

  renderer.clear();
  renderer.setViewport(0, 0, innerWidth, innerHeight);
  renderer.render(scene, camera);
  renderer.setViewport(0, innerHeight - 256, 256, 256);
  renderer.render(scene, cam);
})

</script>