three.js - JS之三中camera LookAt和camera up的区别

标签 three.js

我对更改camera.lookAt和camera.up向量在 Three.js 中产生的影响感到困惑。 在此jsfiddle ,我有camera.up = new THREE.Vector3(0, 0, 1); ,但是当我更新控件时,对象的旋转发生得很奇怪。 那么,相机lookAt和向上方向有什么区别?

var camera, scene, renderer, geometry, material, mesh;

init();
render();

function init() {

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.z = 10;
  camera.position.y = 5;

  // What effect does this have?
  camera.up = new THREE.Vector3(0, -1, 0);
  camera.lookAt(0,0,0);
  scene.add(camera);

  var axisHelper = new THREE.AxisHelper(5);
  scene.add(axisHelper);

  var gridHelper = new THREE.GridHelper(10, 1);
  scene.add(gridHelper);

  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);

  document.body.appendChild(renderer.domElement);
}

function render() {

  renderer.render(scene, camera);

}

最佳答案

将相机指向某个方向。向上旋转相机,同时仍朝该方向看。默认向上为 0,1,0,这是正常的(相机的向上与天空相同)。但如果你想旋转相机,你可以向上更改,例如向上 = 1,0,0 会将相机向右旋转 90 度

html, body {
  margin: 0;
  height: 100%;
}
#c {
  width: 100%;
  height: 100%;
  display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.module.js';
import {OrbitControls} from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/examples/jsm/controls/OrbitControls.js';
import {GUI} from 'https://threejsfundamentals.org/threejs/../3rdparty/dat.gui.module.js';

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

  const fov = 45;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 100;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.set(0, 10, 20);

  class UpGUIHelper {
    constructor(camera) {
      this.camera = camera;
      this.angle = 0;
    }
    get value() {
      return this.angle;
    }
    set value(v) {
      this.angle = v;
      const r = THREE.Math.degToRad(v);
      this.camera.up.set(Math.sin(r), Math.cos(r), 0);
    }
  }

  function updateCamera() {
    camera.lookAt(0,0,0);
    camera.updateProjectionMatrix();
  }

  const gui = new GUI();
  const upGUIHelper = new UpGUIHelper(camera);
  gui.add(upGUIHelper, 'value', -180, 180).name('angle').onChange(updateCamera);

  const controls = new OrbitControls(camera, canvas);
  controls.target.set(0, 5, 0);
  controls.update();

  const scene = new THREE.Scene();
  scene.background = new THREE.Color('black');

  {
    const planeSize = 40;

    const loader = new THREE.TextureLoader();
    const texture = loader.load('https://threejsfundamentals.org/threejs/resources/images/checker.png');
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.magFilter = THREE.NearestFilter;
    const repeats = planeSize / 2;
    texture.repeat.set(repeats, repeats);

    const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
    const planeMat = new THREE.MeshPhongMaterial({
      map: texture,
      side: THREE.DoubleSide,
    });
    const mesh = new THREE.Mesh(planeGeo, planeMat);
    mesh.rotation.x = Math.PI * -.5;
    scene.add(mesh);
  }
  {
    const cubeSize = 4;
    const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
    const cubeMat = new THREE.MeshPhongMaterial({color: '#8AC'});
    const mesh = new THREE.Mesh(cubeGeo, cubeMat);
    mesh.position.set(cubeSize + 1, cubeSize / 2, 0);
    scene.add(mesh);
  }
  {
    const sphereRadius = 3;
    const sphereWidthDivisions = 32;
    const sphereHeightDivisions = 16;
    const sphereGeo = new THREE.SphereBufferGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
    const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});
    const mesh = new THREE.Mesh(sphereGeo, sphereMat);
    mesh.position.set(-sphereRadius - 1, sphereRadius + 2, 0);
    scene.add(mesh);
  }

  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(0, 10, 0);
    light.target.position.set(-5, 0, 0);
    scene.add(light);
    scene.add(light.target);
  }

  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() {

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();
</script>

注意:向上是在世界空间中,因此如果您向下看 Z 轴,(1,0,0) 只会将相机向右转动。要查看此问题,请在场景上拖动鼠标,直到相机向左或向右看 90 度,然后拖动 slider 。您会注意到发生的只是相机翻转。

如果您希望能够控制相机围绕其看起来的方式旋转,那么将相机作为其他对象的子对象可能会更容易。然后,您可以使用该其他对象调用 lookAt 并仅调整相机上的rotation.z。

  const cameraMount = new THREE.Object3D()
  scene.add(cameraMount);
  cameraMount.add(camera);
  cameraMount.position.set(0, 10, 20);

  // point camera backward because `lookAt` has inconstant
  // behavior. For Cameras it looks toward -Z. For non
  // cameras it looks toward +Z
  camera.rotation.y = Math.PI;  

  const controls = new OrbitControls(cameraMount, canvas);
  controls.target.set(0, 5, 0);
  controls.update();

现在我们让 OrbitControls 移动cameraMount,而不是相机。这让我们可以相对于安装座旋转相机。我们还可以调用cameraMount.lookAt来瞄准相机。

html, body {
  margin: 0;
  height: 100%;
}
#c {
  width: 100%;
  height: 100%;
  display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.module.js';
import {OrbitControls} from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/examples/jsm/controls/OrbitControls.js';
import {GUI} from 'https://threejsfundamentals.org/threejs/../3rdparty/dat.gui.module.js';

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});
  
  const fov = 45;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 100;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

  class DegRadHelper {
    constructor(obj, prop) {
      this.obj = obj;
      this.prop = prop;
    }
    get value() {
      return THREE.Math.radToDeg(this.obj[this.prop]);
    }
    set value(v) {
      this.obj[this.prop] = THREE.Math.degToRad(v);
    }
  }

  const gui = new GUI();
  gui.add(new DegRadHelper(camera.rotation, 'z'), 'value', -180, 180).name('cam.rot.z');

  const scene = new THREE.Scene();
  scene.background = new THREE.Color('black');

  const cameraMount = new THREE.Object3D()
  scene.add(cameraMount);
  cameraMount.add(camera);
  cameraMount.position.set(0, 10, 20);
  
  // point camera backward because `lookAt` has inconstant
  // behavior. For Cameras it looks toward -Z. For non
  // cameras it looks toward +Z
  camera.rotation.y = Math.PI;  

  const controls = new OrbitControls(cameraMount, canvas);
  controls.target.set(0, 5, 0);
  controls.update();


  {
    const planeSize = 40;

    const loader = new THREE.TextureLoader();
    const texture = loader.load('https://threejsfundamentals.org/threejs/resources/images/checker.png');
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.magFilter = THREE.NearestFilter;
    const repeats = planeSize / 2;
    texture.repeat.set(repeats, repeats);

    const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
    const planeMat = new THREE.MeshPhongMaterial({
      map: texture,
      side: THREE.DoubleSide,
    });
    const mesh = new THREE.Mesh(planeGeo, planeMat);
    mesh.rotation.x = Math.PI * -.5;
    scene.add(mesh);
  }
  {
    const cubeSize = 4;
    const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
    const cubeMat = new THREE.MeshPhongMaterial({color: '#8AC'});
    const mesh = new THREE.Mesh(cubeGeo, cubeMat);
    mesh.position.set(cubeSize + 1, cubeSize / 2, 0);
    scene.add(mesh);
  }
  {
    const sphereRadius = 3;
    const sphereWidthDivisions = 32;
    const sphereHeightDivisions = 16;
    const sphereGeo = new THREE.SphereBufferGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
    const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});
    const mesh = new THREE.Mesh(sphereGeo, sphereMat);
    mesh.position.set(-sphereRadius - 1, sphereRadius + 2, 0);
    scene.add(mesh);
  }

  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(0, 10, 0);
    light.target.position.set(-5, 0, 0);
    scene.add(light);
    scene.add(light.target);
  }

  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() {

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();
</script>

关于three.js - JS之三中camera LookAt和camera up的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58724726/

相关文章:

three.js - 使用节点的 WebGL 上下文

javascript - 带 for 循环的随机放置问题三.js

three.js - 如何获得与使用 PointsMaterial 相同的结果,但对点系统使用 ShaderMaterial

javascript - THREE.js 通过对象克隆实现多个 Canvas 的多个 View 。陷阱?

angular - 如何将 three.js OrbitControls 限制到特定的 Angular2 组件或 Div

javascript - Three.js 粒子系统每个粒子都有不同的纹理

javascript - 如何在 ar js/three.js 中禁用 fps 显示?

three.js - 如何在三个js中切换渲染目标的纹理?

javascript - WebGL 多 Canvas three.js 示例

javascript - 设置 THREE.PerspectiveCamera 的距离而不改变 View 方向