Three.js - PerspectiveCamera



There are different cameras in Three.js. The most common camera and the one we've been using is the PerspectiveCamera.

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

The first attribute is the Field of View (FOV). FOV is the part of the scene that is visible on display at any given moment. The value is in degrees. Humans have an almost 180-degree FOV. But since a regular computer screen doesn’t fill our vision,a smaller value is often chosen.Generally, for games, a FOV between 60 and 90 degrees is preferred.

Good default: 50

The second one is the Aspect ratio—the ratio between the horizontal and vertical sizes of the area where we're rendering the output.

Good default: window.innerWidth / window.innerHeight

The following two attributes are the near and far clipping plane. The camera renders the area between the near plane and the far plane on the screen.

The near property defines by how close to the camera Three.js should render the scene. Usually, we set this to a minimal value to directly render everything from the camera’s position.

Good default: 0.1

The far property defines how far the camera can see from the position of the camera. If we set this too low, a part of our scene might not be rendered, and if we set it too high, in some cases, it might affect the rendering performance.

Good default: 1000
Far Plane

Example

Check out the following example and play around with variables.

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>

Output

Orthographic Camera
threejs_cameras.htm
Advertisements