Three.js - PerspectiveCamera

Three.js 中有不同的相机。最常见的相机和我们一直在使用的相机是 PerspectiveCamera。

const camera = new THREE.PerspectiveCamera(fov, aspects, near, far)

第一个属性是视场 (FOV)。FOV 是场景中在任何给定时刻在显示屏上可见的部分。该值以度为单位。人类的视场几乎为 180 度。但由于普通电脑屏幕无法填满我们的视野,因此通常会选择较小的值。一般来说,对于游戏来说,FOV 最好在 60 到 90 度之间。

良好的默认值:50

第二个是纵横比 - 我们正在渲染输出的区域的水平和垂直大小之间的比率。

良好的默认值:window.innerWidth / window.innerHeight

以下两个属性是近裁剪平面和远裁剪平面。相机在屏幕上渲染近平面和远平面之间的区域。

near 属性定义 Three.js 应将场景渲染到离相机多近的位置。通常,我们将其设置为最小值,以便直接从相机的位置渲染所有内容。

良好的默认值:0.1

far 属性定义相机从相机位置可以看到多远。如果我们将其设置得太低,场景的一部分可能无法渲染,如果我们将其设置得太高,在某些情况下,它可能会影响渲染性能。

良好的默认值:1000
Far Plane

示例

查看以下示例并尝试使用变量。

prespective-cam.html

<!DOCTYPE html>
<html lang="en">
   <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="ie=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Three.js - Prespective camera</title>
      <style>
         html,
         body {
            margin: 0;
            height: 100%;
         }
         #threejs-container {
            width: 100%;
            height: 100%;
            display: block;
         }
         .split {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            display: flex;
         }
         .split > div {
            width: 100%;
            height: 100%;
         }
      </style>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.js"></script>
   </head>
   <body>
      <canvas id="threejs-container"></canvas>
      <div class="split">
         <div id="view1" tabindex="1"></div>
         <div id="view2" tabindex="2"></div>
      </div>
      <script type="module">
         // Three.js - Cameras - Prespective 2 views
         // from https://threejsfundamentals.org/threejs/threejs-camerasprespective-2-scenes.html
         import { OrbitControls } from "https://threejs.org/examples/jsm/contro
         ls/OrbitControls.js"
         function main() {
            const canvas = document.querySelector('#threejs-container')
            const view1Elem = document.querySelector('#view1')
            const view2Elem = document.querySelector('#view2')
            const renderer = new THREE.WebGLRenderer({ canvas, antialias: true })
            const fov = 45
            const aspect = 2 // the canvas default
            const near = 5
            const far = 100
            const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
            camera.position.set(0, 10, 20)
            const cameraHelper = new THREE.CameraHelper(camera)
            class MinMaxGUIHelper {
               constructor(obj, minProp, maxProp, minDif) {
                  this.obj = obj
                  this.minProp = minProp
                  this.maxProp = maxProp
                  this.minDif = minDif
               }
               get min() {
                  return this.obj[this.minProp]
               }
               set min(v) {
                  this.obj[this.minProp] = v
                  this.obj[this.maxProp] = Math.max(this.obj[this.maxProp], v + th
               is.minDif)
               }
               get max() {
                  return this.obj[this.maxProp]
               }
               set max(v) {
                  this.obj[this.maxProp] = v
                  this.min = this.min // this will call the min setter
               }
            }
            const gui = new dat.GUI()
            gui.add(camera, 'fov', 1, 180)
            const minMaxGUIHelper = new MinMaxGUIHelper(camera, 'near', 'far', 0.1)
            gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near')
            gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far')
            const controls = new OrbitControls(camera, view1Elem)
            controls.target.set(0, 5, 0)
            controls.update()
            const camera2 = new THREE.PerspectiveCamera(
               60, // fov
               2, // aspect
               0.1, // near
               500 // far
            )
            camera2.position.set(40, 10, 30)
            camera2.lookAt(0, 5, 0)
            const controls2 = new OrbitControls(camera2, view2Elem)
            controls2.target.set(0, 5, 0)
            controls2.update()
            const scene = new THREE.Scene()
            scene.background = new THREE.Color(0x262626)
            scene.add(cameraHelper) {
               const planeSize = 40
               const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize)
               const planeMat = new THREE.MeshLambertMaterial({
                  color: 0xffffff,
                  side: THREE.DoubleSide
               })
               const mesh = new THREE.Mesh(planeGeo, planeMat)
               mesh.rotation.x = Math.PI * -0.5
               scene.add(mesh)
            }
            {
               const cubeSize = 4
               const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize)
               const cubeMat = new THREE.MeshLambertMaterial({ color: 0x87ceeb })
               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.SphereGeometry(
                  sphereRadius,
                  sphereWidthDivisions,
                  sphereHeightDivisions
               )
               const sphereMat = new THREE.MeshLambertMaterial({ color: 0x71ba80 })
               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, 5)
               light.target.position.set(-5, 0, 0)
               scene.add(light)
               scene.add(light.target)
               const light2 = new THREE.DirectionalLight(color, intensity)
               light2.position.set(0, 10, -5)
               light2.target.position.set(-5, 0, 0)
               scene.add(light2)
               scene.add(light2.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 setScissorForElement(elem) {
               const canvasRect = canvas.getBoundingClientRect()
               const elemRect = elem.getBoundingClientRect()
               // compute a canvas relative rectangle
               const right = Math.min(elemRect.right, canvasRect.right) - canvasR
               ect.left
               const left = Math.max(0, elemRect.left - canvasRect.left)
               const bottom = Math.min(elemRect.bottom, canvasRect.bottom) - canv
               asRect.top
               const top = Math.max(0, elemRect.top - canvasRect.top)
               const width = Math.min(canvasRect.width, right - left)
               const height = Math.min(canvasRect.height, bottom - top)
               // setup the scissor to only render to that part of the canvas
               const positiveYUpBottom = canvasRect.height - bottom
               renderer.setScissor(left, positiveYUpBottom, width, height)
               renderer.setViewport(left, positiveYUpBottom, width, height)
               // return the aspect
               return width / height
            }
            function render() {
               resizeRendererToDisplaySize(renderer)
               // turn on the scissor
               renderer.setScissorTest(true)
               // render the original view 
               {
                  const aspect = setScissorForElement(view1Elem)
                  // adjust the camera for this aspect
                  camera.aspect = aspect
                  camera.updateProjectionMatrix()
                  cameraHelper.update()
                  // don't draw the camera helper in the original view
                  cameraHelper.visible = false
                  scene.background.set(0x262626)
                  // render
                  renderer.render(scene, camera)
               }
               // render from the 2nd camera 
               {
                  const aspect = setScissorForElement(view2Elem)
                  // adjust the camera for this aspect
                  camera2.aspect = aspect
                  camera2.updateProjectionMatrix()
                  // draw the camera helper in the 2nd view
                  cameraHelper.visible = true
                  scene.background.set(0x262626)
                  renderer.render(scene, camera2)
               }
               requestAnimationFrame(render)
            }
            requestAnimationFrame(render)
         }
         main()
      </script>
   </body>
</html>

输出

Orthographic Camera

threejs_cameras.html