How to Create Split Screen Cameras in Three.js
Rendering multiple cameras simultaneously in Three.js is a powerful technique for creating multiplayer split-screen games, minimaps, or multi-angle CAD viewers. This article provides a straightforward, step-by-step guide on how to configure your renderer viewport and scissor test to display multiple camera views on a single canvas.
To display multiple cameras simultaneously, you do not need multiple
renderers. Instead, you use a single WebGLRenderer and
manipulate its active viewport and scissor dimensions during the render
loop. This tells WebGL exactly which section of the canvas to draw to
for each camera.
Step 1: Disable Auto-Clear
By default, the WebGL renderer clears the entire canvas before rendering a scene. To prevent the second camera’s render from erasing the first camera’s render, you must disable automatic clearing.
renderer.autoClear = false;Step 2: Define Your Cameras
Create the cameras you want to display. In a standard two-player split-screen setup, you will need two cameras. You must also adjust their aspect ratios to match their respective halves of the screen.
const width = window.innerWidth;
const height = window.innerHeight;
// Left Camera (half width)
const cameraLeft = new THREE.PerspectiveCamera(60, (width / 2) / height, 0.1, 1000);
cameraLeft.position.set(-5, 2, 10);
cameraLeft.lookAt(0, 0, 0);
// Right Camera (half width)
const cameraRight = new THREE.PerspectiveCamera(60, (width / 2) / height, 0.1, 1000);
cameraRight.position.set(5, 2, 10);
cameraRight.lookAt(0, 0, 0);Step 3: Implement the Render Loop
In your animation loop, you will manually clear the renderer, define the viewport and scissor area for each camera, and render them one by one.
The scissor test is essential here because it restricts rendering operations (like clearing background colors) to a specific pixel box, preventing overlapping cameras from bleeding into each other’s screen space.
function animate() {
requestAnimationFrame(animate);
const w = window.innerWidth;
const h = window.innerHeight;
const halfWidth = w / 2;
// 1. Clear the entire canvas manually
renderer.clear();
// Enable the scissor test
renderer.setScissorTest(true);
// 2. Render Left Viewport
// setViewport and setScissor parameters: (x, y, width, height)
renderer.setViewport(0, 0, halfWidth, h);
renderer.setScissor(0, 0, halfWidth, h);
renderer.render(scene, cameraLeft);
// 3. Render Right Viewport
renderer.setViewport(halfWidth, 0, halfWidth, h);
renderer.setScissor(halfWidth, 0, halfWidth, h);
renderer.render(scene, cameraRight);
// Disable scissor test back to default behavior if needed elsewhere
renderer.setScissorTest(false);
}Step 4: Handle Window Resizing
When the window resizes, update the renderer’s size and adjust the aspect ratios of both cameras to maintain correct proportions.
window.addEventListener('resize', () => {
const w = window.innerWidth;
const h = window.innerHeight;
renderer.setSize(w, h);
// Update left camera aspect ratio
cameraLeft.aspect = (w / 2) / h;
cameraLeft.updateProjectionMatrix();
// Update right camera aspect ratio
cameraRight.aspect = (w / 2) / h;
cameraRight.updateProjectionMatrix();
});