How to Use Three.js WebGLRenderTarget

In Three.js, the WebGLRenderTarget class allows you to render a 3D scene to an off-screen frame buffer instead of directly to the screen. This technique, commonly known as render-to-texture, is essential for creating advanced visual effects like dynamic mirrors, security camera feeds, portal effects, and custom post-processing passes. This article provides a straightforward, step-by-step guide on how to set up a WebGLRenderTarget, render a secondary scene to it, and apply the resulting texture to an object in your main scene.


Step 1: Create the WebGLRenderTarget

First, instantiate the WebGLRenderTarget. You need to define the width and height of the off-screen texture in pixels. Higher resolutions yield sharper textures but require more GPU memory and processing power.

const rtWidth = 512;
const rtHeight = 512;
const renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight);

Step 2: Set Up the Off-Screen Scene

To render something to your target, you need a separate scene and camera setup. This represents the “virtual camera” capturing the off-screen content.

// Create the off-screen scene
const offscreenScene = new THREE.Scene();
offscreenScene.background = new THREE.Color(0x111111);

// Create the off-screen camera
const offscreenCamera = new THREE.PerspectiveCamera(45, rtWidth / rtHeight, 0.1, 100);
offscreenCamera.position.z = 5;

// Add an object to the off-screen scene
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const offscreenCube = new THREE.Mesh(geometry, material);
offscreenScene.add(offscreenCube);

Step 3: Use the Render Target Texture in Your Main Scene

The WebGLRenderTarget object contains a .texture property. You can map this texture directly to a material used by a mesh in your main visible scene.

// Create your main scene and camera
const mainScene = new THREE.Scene();
const mainCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
mainCamera.position.z = 5;

// Create a plane mesh that will display the off-screen render texture
const displayGeometry = new THREE.PlaneGeometry(3, 3);
const displayMaterial = new THREE.MeshBasicMaterial({ map: renderTarget.texture });
const displayPlane = new THREE.Mesh(displayGeometry, displayMaterial);

mainScene.add(displayPlane);

Step 4: Update the Render Loop

In your animation loop, you must perform two render passes. First, point the renderer to your WebGLRenderTarget and render the off-screen scene. Then, reset the renderer target to null (which defaults back to the screen’s canvas) and render the main scene.

function animate() {
  requestAnimationFrame(animate);

  // 1. Rotate the off-screen object for visible animation
  offscreenCube.rotation.x += 0.01;
  offscreenCube.rotation.y += 0.01;

  // 2. Direct the renderer to draw to the off-screen render target
  renderer.setRenderTarget(renderTarget);
  renderer.render(offscreenScene, offscreenCamera);

  // 3. Reset the renderer target back to the canvas (screen)
  renderer.setRenderTarget(null);
  
  // 4. Render the main scene showing the updated texture
  renderer.render(mainScene, mainCamera);
}

animate();

Step 5: Clean Up Resources

Because render targets occupy GPU memory, you should properly dispose of them when they are no longer needed to prevent memory leaks.

renderTarget.dispose();