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

问题描述 投票:0回答:1

嗨,我有一个问题,也许你可以帮助我。

我有一台摄像机沿着一条路走下管。并且围绕该管旋转的相机总是指向管中的下一个点。但是,相机有时可以像过山车一样在管子的下方或旁边。像这样

enter image description here

我有点a的位置和相机的位置,即点b。我总是看着点+ 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);
      }
three.js 3d geometry linear-algebra
1个回答
1
投票

而不是做数学,使相机成为其他THREE.Object3D的孩子,并使用lookAt与该对象。设置相机相对于该对象的位置和旋转。

对象下方称为mount。它沿着路径(管的中心)向下。相机是mount的孩子。该管具有1个单位半径,因此将camera.position.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>
© www.soinside.com 2019 - 2024. All rights reserved.