- Three.js Tutorial
- Three.js - Home
- Three.js - Introduction
- Three.js - Installation
- Three.js - Hello Cube App
- Three.js - Renderer and Responsiveness
- Three.js - Responsive Design
- Three.js - Debug and Stats
- Three.js - Cameras
- Three.js - Controls
- Three.js - Lights & Shadows
- Three.js - Geometries
- Three.js - Materials
- Three.js - Textures
- Three.js - Drawing Lines
- Three.js - Animations
- Three.js - Creating Text
- Three.js - Loading 3D Models
- Three.js - Libraries and Plugins
- Three.js Useful Resources
- Three.js - Quick Guide
- Three.js - Useful Resources
- Three.js - Discussion
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
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>