- 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>
Output