Camera
Cameras define the viewpoint and projection for rendering 3D scenes.
Camera Types
PerspectiveCamera
Most common camera type with realistic perspective projection.
const camera = new THREE.PerspectiveCamera(
75, // Field of view (degrees)
window.innerWidth / window.innerHeight, // Aspect ratio
0.1, // Near clipping plane
1000 // Far clipping plane
);
// Position and orientation
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
// Update aspect ratio
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
OrthographicCamera
Parallel projection without perspective distortion.
const camera = new THREE.OrthographicCamera(
-10,
10, // Left, right
10,
-10, // Top, bottom
0.1,
100 // Near, far
);
// For responsive orthographic camera
const aspect = window.innerWidth / window.innerHeight;
const frustumSize = 10;
const camera = new THREE.OrthographicCamera(
(-frustumSize * aspect) / 2, // Left
(frustumSize * aspect) / 2, // Right
frustumSize / 2, // Top
-frustumSize / 2, // Bottom
0.1,
100 // Near, far
);
Camera Controls
OrbitControls
Allows orbiting around a target point.
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
// Configuration
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.enableZoom = true;
controls.enablePan = true;
controls.enableRotate = true;
controls.autoRotate = false;
controls.autoRotateSpeed = 2.0;
// Limits
controls.minDistance = 1;
controls.maxDistance = 100;
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI;
controls.minAzimuthAngle = -Infinity;
controls.maxAzimuthAngle = Infinity;
// Target
controls.target.set(0, 0, 0);
// Update in animation loop
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
TrackballControls
Free rotation without up vector constraints.
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls.js';
const controls = new TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
FlyControls
Flight simulator style controls.
import { FlyControls } from 'three/examples/jsm/controls/FlyControls.js';
const controls = new FlyControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.rollSpeed = 0.005;
controls.autoForward = false;
controls.dragToLook = true;
// Update with delta time
function animate() {
const delta = clock.getDelta();
controls.update(delta);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
FirstPersonControls
First-person shooter style controls.
import { FirstPersonControls } from 'three/examples/jsm/controls/FirstPersonControls.js';
const controls = new FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 10;
controls.lookSpeed = 0.1;
controls.lookVertical = true;
controls.constrainVertical = true;
controls.verticalMin = 1.0;
controls.verticalMax = 2.0;
PointerLockControls
Mouse pointer lock for immersive experiences.
import { PointerLockControls } from 'three/examples/jsm/controls/PointerLockControls.js';
const controls = new PointerLockControls(camera, renderer.domElement);
// Lock pointer on click
renderer.domElement.addEventListener('click', () => {
controls.lock();
});
// Listen for lock/unlock events
controls.addEventListener('lock', () => {
console.log('Pointer locked');
});
controls.addEventListener('unlock', () => {
console.log('Pointer unlocked');
});
// Movement
const velocity = new THREE.Vector3();
const direction = new THREE.Vector3();
function animate() {
if (controls.isLocked) {
// Handle movement input
direction.z = Number(moveForward) - Number(moveBackward);
direction.x = Number(moveRight) - Number(moveLeft);
direction.normalize();
if (moveForward || moveBackward) velocity.z -= direction.z * 400.0 * delta;
if (moveLeft || moveRight) velocity.x -= direction.x * 400.0 * delta;
controls.moveRight(-velocity.x * delta);
controls.moveForward(-velocity.z * delta);
velocity.x *= 0.9;
velocity.z *= 0.9;
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
Camera Animation
Manual Animation
// Animate camera position
function animate() {
const time = Date.now() * 0.001;
// Orbit around target
camera.position.x = Math.cos(time) * 10;
camera.position.z = Math.sin(time) * 10;
camera.lookAt(0, 0, 0);
// Smooth movement
camera.position.lerp(targetPosition, 0.1);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
Tween Animation
import { Tween, Easing } from '@tweenjs/tween.js';
// Animate to new position
const currentPosition = camera.position.clone();
const targetPosition = new THREE.Vector3(10, 5, 10);
new Tween(currentPosition)
.to(targetPosition, 2000)
.easing(Easing.Quadratic.Out)
.onUpdate(() => {
camera.position.copy(currentPosition);
camera.lookAt(0, 0, 0);
})
.start();
Camera Helpers
// Camera helper (visualizes camera frustum)
const cameraHelper = new THREE.CameraHelper(camera);
scene.add(cameraHelper);
// Update helper when camera changes
camera.updateProjectionMatrix();
cameraHelper.update();
Multiple Cameras
// Multiple cameras
const camera1 = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);
const camera2 = new THREE.OrthographicCamera(-10, 10, 10, -10, 0.1, 100);
let currentCamera = camera1;
// Switch cameras
function switchCamera() {
currentCamera = currentCamera === camera1 ? camera2 : camera1;
controls.object = currentCamera;
}
// Render with current camera
renderer.render(scene, currentCamera);
Viewport and Scissor
// Split screen with multiple cameras
const size = renderer.getSize(new THREE.Vector2());
// Left viewport
renderer.setViewport(0, 0, size.width / 2, size.height);
renderer.setScissor(0, 0, size.width / 2, size.height);
renderer.setScissorTest(true);
renderer.render(scene, camera1);
// Right viewport
renderer.setViewport(size.width / 2, 0, size.width / 2, size.height);
renderer.setScissor(size.width / 2, 0, size.width / 2, size.height);
renderer.render(scene, camera2);
// Reset
renderer.setScissorTest(false);
renderer.setViewport(0, 0, size.width, size.height);
Camera Utilities
World to Screen Coordinates
function worldToScreen(worldPosition, camera, renderer) {
const vector = worldPosition.clone();
vector.project(camera);
const size = renderer.getSize(new THREE.Vector2());
const x = ((vector.x + 1) * size.width) / 2;
const y = ((-vector.y + 1) * size.height) / 2;
return new THREE.Vector2(x, y);
}
Screen to World Coordinates
function screenToWorld(screenPosition, camera, renderer) {
const size = renderer.getSize(new THREE.Vector2());
const mouse = new THREE.Vector2(
(screenPosition.x / size.width) * 2 - 1,
-(screenPosition.y / size.height) * 2 + 1
);
const vector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
vector.unproject(camera);
const direction = vector.sub(camera.position).normalize();
return direction;
}
Raycasting from Camera
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
console.log('Clicked object:', intersects[0].object);
}
}
window.addEventListener('click', onMouseClick);
Responsive Camera
function onWindowResize() {
const aspect = window.innerWidth / window.innerHeight;
if (camera instanceof THREE.PerspectiveCamera) {
camera.aspect = aspect;
} else if (camera instanceof THREE.OrthographicCamera) {
const frustumSize = 10;
camera.left = (-frustumSize * aspect) / 2;
camera.right = (frustumSize * aspect) / 2;
camera.top = frustumSize / 2;
camera.bottom = -frustumSize / 2;
}
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onWindowResize);
Advanced Camera Techniques
Dolly Zoom Effect
function dollyZoom(camera, target, distance, fov) {
const currentDistance = camera.position.distanceTo(target);
const newDistance = distance;
// Calculate new FOV to maintain apparent size
const newFov =
(2 *
Math.atan(
Math.tan((fov * Math.PI) / 360) * (currentDistance / newDistance)
) *
180) /
Math.PI;
camera.fov = newFov;
camera.position.normalize().multiplyScalar(newDistance).add(target);
camera.updateProjectionMatrix();
}
Smooth Camera Follow
class CameraFollow {
constructor(camera, target) {
this.camera = camera;
this.target = target;
this.offset = new THREE.Vector3(0, 5, 10);
this.smoothing = 0.1;
}
update() {
const desiredPosition = this.target.position.clone().add(this.offset);
this.camera.position.lerp(desiredPosition, this.smoothing);
this.camera.lookAt(this.target.position);
}
}
const cameraFollow = new CameraFollow(camera, player);
function animate() {
cameraFollow.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}