Skip to content

BabylonJS - Reflection Probes

ReflectionProbe is a powerful tool in Babylon.js used to capture the surroundings of a specific point in the scene and generate a cube map texture. This cube map can then be applied to materials (like StandardMaterial or PBRMaterial) as a reflection texture, creating realistic reflections on objects like mirrors, polished metals, or water surfaces.

Think of a reflection probe as a small camera at a specific point capturing a 360-degree view (up, down, left, right, front, back). This captured view is stored as a CubeTexture. When applied to a material, the object appears to reflect the environment captured by the probe.

You create a ReflectionProbe by providing a name, the size of the generated cube map faces (power of 2, e.g., 512), and the scene.

import * as BABYLON from '@babylonjs/core';
// Assuming 'scene' is defined
const probeResolution = 512; // Quality of the reflection texture (e.g., 256, 512, 1024)
const reflectionProbe = new BABYLON.ReflectionProbe("myProbe", probeResolution, scene);
// By default, the probe captures everything. You often want to specify WHICH meshes
// should appear in the reflection. Add meshes to the probe's render list:
// reflectionProbe.renderList.push(meshToIncludeInReflection1);
// reflectionProbe.renderList.push(meshToIncludeInReflection2);

The probe’s position determines the viewpoint for the reflection capture. By default, it’s at (0,0,0), but you can attach it to a mesh or set its position manually: reflectionProbe.position = new BABYLON.Vector3(x, y, z);.

To make an object reflective using the probe, assign the probe’s generated cube texture (probe.cubeTexture) to the reflectionTexture property of the object’s material.

// Assuming 'reflectiveMesh' and its 'material' (Standard or PBR) are defined
// Assign the probe's cube map to the material's reflection texture slot
material.reflectionTexture = reflectionProbe.cubeTexture;
// Optional: Control reflection intensity and appearance
// For StandardMaterial:
// material.reflectionFresnelParameters = new BABYLON.FresnelParameters();
// material.reflectionFresnelParameters.bias = 0.1; // Controls falloff
// For PBRMaterial (reflection is controlled by metallic/roughness/reflectivity):
// material.metallic = 1.0;
// material.roughness = 0.1;
// or
// material.reflectivityColor = new BABYLON.Color3(1, 1, 1); // Full reflection
// material.microSurface = 0.1; // Smooth surface

The probe.renderList is crucial. It’s an array containing the meshes that should be rendered into the reflection probe’s cube map. If the list is empty (or null, the default), the probe attempts to render all meshes in the scene, which can be inefficient and might include unwanted objects (like the reflective object itself, causing infinite reflections). It’s best practice to explicitly add only the necessary surrounding meshes to the renderList.

Babylon.js also has MirrorTexture which creates planar reflections (like a flat mirror). ReflectionProbe creates cube map reflections suitable for curved or complex surfaces reflecting the entire surrounding environment from a point.

This demo shows a central reflective knot reflecting several surrounding spheres and a textured ground plane.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Babylon.js - Reflection Probe Demo</title>
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
<style>
html, body { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; }
#renderCanvas { width: 100%; height: 100%; touch-action: none; }
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>
<script type="text/javascript">
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const createScene = function () {
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(0.1, 0.1, 0.1, 1.0);
const camera = new BABYLON.ArcRotateCamera("camera1", -Math.PI / 2, Math.PI / 2.5, 15, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
camera.wheelPrecision = 50;
camera.minZ = 0.1;
const light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0.5, 1, 0.2), scene);
light.intensity = 0.9;
// Reflective Object
const knot = BABYLON.MeshBuilder.CreateTorusKnot("knot", { radius: 1, tube: 0.4, radialSegments: 128, tubularSegments: 64, p: 2, q: 3 }, scene);
const knotMaterial = new BABYLON.PBRMaterial("knotMat", scene); // Using PBR for better reflections
knotMaterial.metallic = 1.0; // Make it fully metallic
knotMaterial.roughness = 0.1; // Make it quite smooth/shiny
knot.material = knotMaterial;
// Objects to be reflected
const yellowSphere = BABYLON.MeshBuilder.CreateSphere("yellowSphere", { diameter: 1.5 }, scene);
yellowSphere.position = new BABYLON.Vector3(3, 0, 0);
const yellowMat = new BABYLON.StandardMaterial("yellowMat", scene);
yellowMat.diffuseColor = BABYLON.Color3.Yellow();
yellowSphere.material = yellowMat;
const blueSphere = BABYLON.MeshBuilder.CreateSphere("blueSphere", { diameter: 1.5 }, scene);
blueSphere.position = new BABYLON.Vector3(-3, 0, 0);
const blueMat = new BABYLON.StandardMaterial("blueMat", scene);
blueMat.diffuseColor = BABYLON.Color3.Blue();
blueSphere.material = blueMat;
const greenSphere = BABYLON.MeshBuilder.CreateSphere("greenSphere", { diameter: 1.5 }, scene);
greenSphere.position = new BABYLON.Vector3(0, 0, 4);
const greenMat = new BABYLON.StandardMaterial("greenMat", scene);
greenMat.diffuseColor = BABYLON.Color3.Green();
greenSphere.material = greenMat;
// Ground
const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 20, height: 20 }, scene);
const groundMaterial = new BABYLON.StandardMaterial("groundMat", scene);
try {
// Use a texture for the ground (ensure 'textures/ground.jpg' exists)
const groundTexture = new BABYLON.Texture("textures/ground.jpg", scene);
groundTexture.uScale = 5;
groundTexture.vScale = 5;
groundMaterial.diffuseTexture = groundTexture;
} catch (err) {
console.warn("Ground texture not found. Using color.");
groundMaterial.diffuseColor = new BABYLON.Color3(0.5, 0.4, 0.3);
}
groundMaterial.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1); // Dull specular
ground.material = groundMaterial;
ground.position.y = -2;
// --- Reflection Probe Setup ---
const reflectionProbe = new BABYLON.ReflectionProbe("mainProbe", 512, scene);
reflectionProbe.position = knot.position; // Center the probe on the knot
// Add ONLY the surrounding objects to the probe's render list
reflectionProbe.renderList.push(yellowSphere);
reflectionProbe.renderList.push(blueSphere);
reflectionProbe.renderList.push(greenSphere);
reflectionProbe.renderList.push(ground);
// DO NOT add the 'knot' itself to avoid self-reflection issues
// Apply the probe to the knot's material
knotMaterial.reflectionTexture = reflectionProbe.cubeTexture;
// For PBR, setting metallic/roughness is usually enough.
// For StandardMaterial, you'd add Fresnel parameters here.
// ------------------------------
// Animate spheres for visual interest
let angle = 0;
scene.onBeforeRenderObservable.add(() => {
angle += 0.01;
yellowSphere.position.y = Math.sin(angle * 2) * 0.5;
blueSphere.position.y = Math.cos(angle * 3) * 0.5;
greenSphere.position.y = Math.sin(angle * 1.5 + Math.PI / 4) * 0.5;
});
return scene;
};
const scene = createScene();
engine.runRenderLoop(function () {
scene.render();
});
window.addEventListener("resize", function () {
engine.resize();
});
</script>
</body>
</html>

The demo shows a shiny, metallic torus knot in the center. Surrounding it are yellow, blue, and green spheres bobbing up and down slightly, and a textured ground plane below. The surface of the central knot realistically reflects these surrounding spheres and the ground, thanks to the reflection probe centered on the knot. As you rotate the camera, the reflections change dynamically based on the viewing angle. Note that the knot itself does not appear in its own reflection because it was excluded from the probe’s renderList.

For the ground texture to appear, you need an image file named ground.jpg located in a textures folder relative to your HTML file.

Reflection probes add rendering overhead because they need to render the scene (or a part of it specified in renderList) six times (once for each face of the cube map). Use them strategically on key reflective objects rather than excessively. Lowering the probe’s resolution (probeResolution) can improve performance at the cost of reflection sharpness. For static scenes, reflections might only need to be rendered once. For dynamic scenes where objects or the reflective surface move, the probe might need to be updated periodically.