Configure Three.js ArrayCamera for Multiple Views
This article explains how to configure and use the
THREE.ArrayCamera in Three.js to render multiple camera
angles onto a single canvas. You will learn how to define sub-cameras,
assign custom viewports for each, and render a multi-view scene (such as
a split-screen multiplayer game or a grid of security monitors) in a
single render call.
Understanding the ArrayCamera
An ArrayCamera is a container camera that holds an array
of sub-cameras (usually PerspectiveCamera instances). Each
sub-camera has its own viewport property, defined as a
THREE.Vector4 representing
(x, y, width, height) in pixel coordinates. When the
renderer draws the scene using the ArrayCamera, it
automatically iterates through each sub-camera and renders its view onto
its designated area of the canvas.
Step-by-Step Configuration
To configure an ArrayCamera, you must set up your
renderer, define the viewport dimensions for each sub-camera, and
instantiate the parent array camera.
1. Initialize the Renderer and Scene
First, create your standard Three.js scene and a
WebGLRenderer. Ensure the renderer is configured to handle
the device pixel ratio for sharp rendering on high-resolution
screens.
import * as THREE from 'three';
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);2. Define Sub-Cameras and Viewports
To display a 2x2 grid of four distinct views on a single canvas,
divide the total canvas width and height by two. You then configure a
Vector4 viewport for each sub-camera.
const AMOUNT_X = 2;
const AMOUNT_Y = 2;
// Calculate the width and height of each sub-viewport in pixels
const subWidth = (window.innerWidth / AMOUNT_X) * window.devicePixelRatio;
const subHeight = (window.innerHeight / AMOUNT_Y) * window.devicePixelRatio;
const aspect = subWidth / subHeight;
const cameras = [];
for (let y = 0; y < AMOUNT_Y; y++) {
for (let x = 0; x < AMOUNT_X; x++) {
// Create a standard perspective camera for each view
const subCamera = new THREE.PerspectiveCamera(40, aspect, 0.1, 10);
// Define the viewport area (x, y, width, height) in pixels
subCamera.viewport = new THREE.Vector4(
Math.floor(x * subWidth),
Math.floor(y * subHeight),
Math.ceil(subWidth),
Math.ceil(subHeight)
);
// Position each sub-camera to look at the center from different angles
subCamera.position.set((x - 0.5) * 3, (y - 0.5) * 3, 3);
subCamera.lookAt(0, 0, 0);
// Mark the camera's matrix as updated
subCamera.updateMatrixWorld();
cameras.push(subCamera);
}
}3. Create the ArrayCamera and Render
Instantiate the ArrayCamera by passing the array of
configured sub-cameras into its constructor. You then pass this
ArrayCamera to your renderer’s render loop.
// Instantiate the parent ArrayCamera
const arrayCamera = new THREE.ArrayCamera(cameras);
// Render loop
function animate() {
requestAnimationFrame(animate);
// Render the scene using the ArrayCamera
renderer.render(scene, arrayCamera);
}
animate();Handling Window Resizing
Because sub-camera viewports are defined using pixel values, you must recalculate the viewport coordinates whenever the browser window is resized.
```window.addEventListener(‘resize’, onWindowResize);
function onWindowResize() { const width = window.innerWidth; const height = window.innerHeight;
renderer.setSize(width, height);
const subWidth = (width / AMOUNT_X) * window.devicePixelRatio;
const subHeight = (height / AMOUNT_Y) * window.devicePixelRatio;
const aspect = subWidth / subHeight;
let index = 0;
for (let y = 0; y < AMOUNT_Y; y++) {
for (let x = 0; x < AMOUNT_X; x++) {
const subCamera = arrayCamera.cameras[index];
subCamera.aspect = aspect;
subCamera.updateProjectionMatrix();
subCamera.viewport.set(
Math.floor(x * subWidth),
Math.floor(y * subHeight),
Math.ceil(subWidth),
Math.ceil(subHeight)
);
index++;
}
}
} ```