Заставьте камеру вращаться вдоль оси z, перемещая и изменяя внешний вид (вид с американских горок) в Three.js - PullRequest
0 голосов
/ 15 марта 2019

Привет, у меня проблема, может быть, вы можете мне помочь.

У меня есть камера, которая спускается по трубе, следуя пути.и камера, которая вращается вокруг этой трубы, всегда указывая на следующую точку в трубе.Тем не менее, камера иногда может быть ниже или рядом с трубкой, как американские горки.Вот так

enter image description here

У меня есть положение точки a и положение камеры, которая является точкой b.Я всегда смотрю на точку a + 1

var bpoints = this.cameraPathpoints;
var apoints = this.pathPoints;

this.camera.position.copy(bpoints[i]);
this.camera.lookAt(apoints[i+1]);

Камера всегда смотрит на точку правильно, однако я хочу, чтобы камера вращалась вокруг своей оси z, чтобы она всегда была нормальной к трубе.Я попытался сделать некоторые расчеты, чтобы камера вращалась вокруг своей оси z, чтобы камера всегда была направлена ​​перпендикулярно трубе, однако мои вычисления работают только в определенных положениях.Может быть, есть более простой способ сделать это.Большое спасибо за любую помощь.

var angleRadians = Math.atan2(cpv[this.cameraPos].pos.y - centePoints[this.cameraPos].pos.y, cpv[this.cameraPos].pos.x - centePoints[this.cameraPos].pos.x);

      if(angleRadians > 0 && angleRadians > Math.PI/2){
        console.log("+90",(Math.PI/2) - angleRadians);
        angleRadians = (Math.PI/2) - angleRadians;
        this.camera.rotateZ(angleRadians);
        console.log("rotated ", angleRadians * 180/Math.PI);
      }
       else if(angleRadians > 0 && angleRadians < Math.PI/2 && anglesum > 
     Math.PI/2){
        console.log("-90",(Math.PI/2) - angleRadians);
         angleRadians = (Math.PI/2) - angleRadians;
         this.camera.rotateZ(-angleRadians);
         console.log("rotated ", -angleRadians * 180/Math.PI);
       } 
        else if(angleRadians > 0 && angleRadians < Math.PI/2){
        console.log("-90",(Math.PI/2) + angleRadians);
         angleRadians = -(Math.PI/2) - (angleRadians/Math.PI/2);
         this.camera.rotateZ(angleRadians);
         console.log("rotated ", angleRadians * 180/Math.PI);
       } 
      else if(angleRadians < 0 && angleRadians < -Math.PI/2){
        console.log("--90");
        angleRadians = (Math.PI/2) + angleRadians;
        this.camera.rotateZ(-angleRadians);
        console.log("rotated ",-angleRadians * 180/Math.PI);
      }else if(angleRadians < 0 && angleRadians > -Math.PI/2){
        console.log("+-90");
        angleRadians = (Math.PI/2) - angleRadians;
        this.camera.rotateZ(-angleRadians);
        console.log("rotated ", -angleRadians * 180/Math.PI);
      }

1 Ответ

1 голос
/ 16 марта 2019

Вместо того чтобы делать математику, сделайте камеру дочерней по отношению к некоторому другому THREE.Object3D и используйте lookAt с этим объектом.Установите положение камеры и вращение относительно этого объекта.

Ниже объект называется mount.Он идет по тропинке (центр трубы).Камера ребенка mount.Трубка имеет радиус 1 единицу, поэтому установка camera.y. в положение 1.5 делает ее вне трубки.lookAt заставляет объекты, не относящиеся к камере, смотреть вниз в положительном Z, но камера смотрит в отрицательном Z, поэтому мы поворачиваем камеру на 180 градусов.

Пример:

'use strict';

/* global THREE */

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas: canvas});

  const scene = new THREE.Scene();
  scene.background = new THREE.Color(0xAAAAAA);
  
  const fov = 40;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 1000;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.y = 1.5;  // 2 units above the mount
  camera.rotation.y = Math.PI;  // the mount will lootAt positiveZ 
  
  const mount = new THREE.Object3D();
  mount.add(camera);
  scene.add(mount);

  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);
  }
  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(1, -2, -4);
    scene.add(light);
  }
  
  const curve = new THREE.Curves.GrannyKnot();
  const tubularSegments = 200;
  const radius = 1;
  const radialSegments = 6;
  const closed = true;
  const tube = new THREE.TubeBufferGeometry(
     curve, tubularSegments, radius, radialSegments, closed);
  const texture = new THREE.DataTexture(new Uint8Array([128, 255, 255, 128]),
     2, 2, THREE.LuminanceFormat);
  texture.needsUpdate = true;
  texture.magFilter = THREE.NearestFilter;
  texture.wrapS = THREE.RepeatWrapping;
  texture.wrapT = THREE.RepeatWrapping;
  texture.repeat.set( 100, 4 );
  const material = new THREE.MeshPhongMaterial({
    map: texture,
    color: '#8CF',
    flatShading: true,
  });
  const mesh = new THREE.Mesh(tube, material);
  scene.add(mesh);
  
  const target = new THREE.Vector3();
  
  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  function render(time) {
    time *= 0.001;

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }
    
    const t = time * 0.1 % 1;
    curve.getPointAt(t, mount.position);
    curve.getPointAt((t + 0.01) % 1, target);
    mount.lookAt(target);

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/js/CurveExtras.js"></script>

Вы можете легко сориентировать камеру относительно держателя, чтобы сказать, больше смотреть в сторону пути или пути, установив camera.rotation.x.Если вы хотите повернуть вокруг держателя, измените свойство up держателя или добавьте другой объект между держателем и камерой и установите его вращение по оси Z.

'use strict';

/* global THREE */

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas: canvas});

  const scene = new THREE.Scene();
  scene.background = new THREE.Color(0xAAAAAA);
  
  const fov = 40;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 1000;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.y = 1.5;  // 2 units above the mount
  camera.rotation.y = Math.PI;  // the mount will lootAt positiveZ 
  
  const mount = new THREE.Object3D();
  const subMount = new THREE.Object3D();
  subMount.rotation.z = Math.PI * .5;
  subMount.add(camera);
  mount.add(subMount);
  scene.add(mount);

  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);
  }
  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(1, -2, -4);
    scene.add(light);
  }
  
  const curve = new THREE.Curves.GrannyKnot();
  const tubularSegments = 200;
  const radius = 1;
  const radialSegments = 6;
  const closed = true;
  const tube = new THREE.TubeBufferGeometry(
     curve, tubularSegments, radius, radialSegments, closed);
  const texture = new THREE.DataTexture(new Uint8Array([128, 255, 255, 128]),
     2, 2, THREE.LuminanceFormat);
  texture.needsUpdate = true;
  texture.magFilter = THREE.NearestFilter;
  texture.wrapS = THREE.RepeatWrapping;
  texture.wrapT = THREE.RepeatWrapping;
  texture.repeat.set( 100, 4 );
  const material = new THREE.MeshPhongMaterial({
    map: texture,
    color: '#8CF',
    flatShading: true,
  });
  const mesh = new THREE.Mesh(tube, material);
  scene.add(mesh);
  
  const target = new THREE.Vector3();
  const target2 = new THREE.Vector3();
  const mountToTarget = new THREE.Vector3();
  const targetToTarget2 = new THREE.Vector3();
  
  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  function render(time) {
    time *= 0.001;

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }
    
    const t = time * 0.1 % 1;
    curve.getPointAt(t, mount.position);
    curve.getPointAt((t + 0.01) % 1, target);
    
    // set mount up to be perpenticular to the
    // curve
    curve.getPointAt((t + 0.02) % 1, target2);
    mountToTarget.subVectors(mount.position, target).normalize();
    targetToTarget2.subVectors(target2, target).normalize();
    mount.up.crossVectors(mountToTarget, targetToTarget2);
    mount.lookAt(target);    

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r102/js/CurveExtras.js"></script>
...