Dynamic CubeCamera Reflections in Three.js

Creating realistic, real-time reflections in Three.js can significantly enhance the visual fidelity of a 3D scene. This article explains how to implement a dynamic reflection probe using CubeCamera and WebGLCubeRenderTarget to capture the surrounding environment and update a mesh’s reflection map on every frame. You will learn how to set up the camera, apply the texture to a reflective material, and optimize the rendering loop to avoid self-reflection artifacts.

1. Create the Cube Render Target and CubeCamera

To capture a 360-degree view of the scene, you need a helper object that renders the scene from six different directions into a cube map. This is achieved using WebGLCubeRenderTarget and CubeCamera.

// Define the resolution of the cube map (must be a power of 2, e.g., 256, 512)
const cubeMapSize = 512;

// Create the cube render target
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(cubeMapSize, {
  generateMipmaps: true,
  minFilter: THREE.LinearMipmapLinearFilter
});

// Create the CubeCamera (near plane, far plane, render target)
const cubeCamera = new THREE.CubeCamera(0.1, 1000, cubeRenderTarget);
scene.add(cubeCamera);

2. Apply the Reflection Map to a Material

To make an object reflective, assign the texture from the cube render target to the envMap property of the object’s material. Use MeshStandardMaterial or MeshPhysicalMaterial with low roughness and high metalness for a mirror-like finish.

const mirrorMaterial = new THREE.MeshStandardMaterial({
  metalness: 1.0,
  roughness: 0.0,
  envMap: cubeRenderTarget.texture
});

const mirrorGeometry = new THREE.SphereGeometry(2, 32, 32);
const mirrorMesh = new THREE.Mesh(mirrorGeometry, mirrorMaterial);
scene.add(mirrorMesh);

// Position the CubeCamera at the center of the reflective object
cubeCamera.position.copy(mirrorMesh.position);

3. Update the Reflection Every Frame

To make the reflection dynamic, you must update the CubeCamera inside your animation loop. Because the camera is inside the reflective object, you must temporarily hide the reflective object before updating the CubeCamera to prevent the object from rendering its own interior.

function animate() {
  requestAnimationFrame(animate);

  // 1. Animate your scene objects here (e.g., rotating meshes)

  // 2. Hide the reflective object to avoid self-rendering artifacts
  mirrorMesh.visible = false;

  // 3. Keep the CubeCamera synced with the mesh position if it moves
  cubeCamera.position.copy(mirrorMesh.position);

  // 4. Update the CubeCamera
  cubeCamera.update(renderer, scene);

  // 5. Make the reflective object visible again
  mirrorMesh.visible = true;

  // 6. Render the main scene with the default perspective camera
  renderer.render(scene, camera);
}

animate();

Performance Considerations

Updating a CubeCamera every frame requires rendering the scene six additional times per frame (once for each face of the cube). To maintain high performance: * Reduce Resolution: Keep the WebGLCubeRenderTarget resolution as low as acceptable (e.g., 256 or 512). * Selective Layers: Use Three.js layers to exclude complex background geometry or particle systems from being rendered by the CubeCamera. * Throttle Updates: If real-time reflections are not critical, update the CubeCamera every few frames instead of every single frame.