Reflector (mirror) floor

리플렉터(거울) 바닥

Turning a flat plane into a live mirror that reflects whatever sits above it in real time.

Also known as: THREE.ReflectorPlanar reflectionMirror surface
···
js
import * as THREE from 'three';
import { Reflector } from 'three/addons/objects/Reflector.js';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x05060c);
const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 100);
camera.position.set(0, 1.6, 5.2);
camera.lookAt(0, 0.4, 0);

const groundGeo = new THREE.CircleGeometry(3.4, 64);
const reflector = new Reflector(groundGeo, { textureWidth: 512, textureHeight: 512, color: 0x334455 });
reflector.rotation.x = -Math.PI / 2;
scene.add(reflector);

const group = new THREE.Group();
const colors = [0xff5c8a, 0x5cc9ff, 0xffd15c];
for (let i = 0; i < 3; i++) {
  const m = new THREE.Mesh(new THREE.IcosahedronGeometry(0.55, 0), new THREE.MeshStandardMaterial({ color: colors[i], roughness: 0.35, metalness: 0.2 }));
  const a = (i / 3) * Math.PI * 2;
  m.position.set(Math.cos(a) * 1.6, 0.9, Math.sin(a) * 1.6);
  group.add(m);
}
scene.add(group);
scene.add(new THREE.AmbientLight(0x445577, 1.2));
const dl = new THREE.DirectionalLight(0xffffff, 1.4);
dl.position.set(3, 5, 2);
scene.add(dl);

function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();
renderer.setAnimationLoop((t) => {
  const time = t * 0.001;
  group.rotation.y = time * 0.4;
  group.children.forEach((m, i) => { m.position.y = 0.9 + Math.sin(time * 1.5 + i) * 0.25; });
  camera.position.x = Math.sin(time * 0.2) * 1.2;
  camera.lookAt(0, 0.4, 0);
  renderer.render(scene, camera);
});

three/addons/objects/Reflector.js takes a plane geometry and renders the scene a second time from a camera mirrored across that plane, then maps the result onto its own surface as a render-target texture. It’s the same render-to-texture idea as portal, but Reflector handles the mirror-camera math and clip-plane setup internally — it’s all wired into an onBeforeRender hook, so scene.add(reflector) is enough; no manual render call needed.

In new Reflector(geometry, { textureWidth, textureHeight, color, clipBias }), textureWidth/Height set the reflection texture’s resolution (lower = blurrier but faster), color tints the reflection with the surface’s own color, and clipBias corrects for objects just below the mirror plane bleeding through as ghost reflections.

The demo makes a circular floor a Reflector and floats three differently colored polyhedra above it so you can watch the reflection ripple. Since it’s a genuine physical reflection, tilting the camera changes the reflection by exactly the same amount — but it only works on flat planes (no curved surfaces), and redrawing the scene once more each frame roughly doubles cost, the same tradeoff water-shader’s reflections face.

When to use

Marble floors, showcase pedestals, or any flat surface that needs a convincing real-time reflection.