在Three.js中移动和更改lookAt(过山车视图)时,使照相机沿z轴旋转

时间:2019-03-15 17:13:39

标签: three.js 3d geometry linear-algebra

嗨,我有问题,也许您可​​以帮助我。

我有一台照相机沿着一条路径从管子上下来。并围绕该电子管旋转的相机始终指向电子管中的下一个点。但是,相机有时可能像过山车一样在管子下方或旁边。像这样

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 个答案:

答案 0 :(得分:1)

让相机成为其他<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js"></script> 的子代,而不是进行数学运算,并对该对象使用THREE.Object3D。设置相机相对于该物体的位置和旋转。

该对象下面称为lookAt。它沿着路径(管的中心)走。相机是mount的子代。该电子管的半径为1,因此将camera.position.y设置为1.5会使它位于电子管之外。 mount使非相机对象向下看正Z,而相机向下看负Z,因此我们将相机旋转180度。

示例:

lookAt
'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属性,或在安装座和相机之间添加另一个对象并设置其Z旋转。

up
'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; }