Screen Space Reflections

Real-time reflections using render targets to create realistic water surface reflections with interactive controls.

What this code does

- Render Target Reflections: uses WebGLRenderTarget to capture scene from mirrored camera position for realistic reflections.
- Reflection Camera: mathematically mirrors main camera across reflection plane for accurate perspective.
- Dynamic Water Surface: adjustable water level with reflective material properties and transparency controls.
- Clipping Planes: prevents objects below water surface from appearing in reflection for realistic depth.
- Interactive Controls: adjust reflection intensity, blur, water level, and material properties in real-time.
- Animated Scene: moving objects, rotating lights, and bobbing animations demonstrate reflection quality.

JavaScript (plain)

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
camera.position.set(0, 5, 8)
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)

renderer.shadowMap.enabled = true
document.querySelector('#app').appendChild(renderer.domElement)

// Create reflection render target
const reflectionRenderTarget = new THREE.WebGLRenderTarget(512, 512)
const reflectionCamera = camera.clone()

// Create reflective floor
const floorGeometry = new THREE.PlaneGeometry(20, 20)
const floorMaterial = new THREE.MeshStandardMaterial({
  color: 0x444444,
  roughness: 0.1,
  metalness: 0.8
})
const floor = new THREE.Mesh(floorGeometry, floorMaterial)
floor.rotation.x = -Math.PI / 2
floor.position.y = 0
scene.add(floor)

// Add reflectable objects
const boxGeometry = new THREE.BoxGeometry(1, 2, 1)
const materials = [
  new THREE.MeshStandardMaterial({ color: 0xff4444 }),
  new THREE.MeshStandardMaterial({ color: 0x44ff44 }),
  new THREE.MeshStandardMaterial({ color: 0x4444ff })
]

for (let i = 0; i < 3; i++) {
  const box = new THREE.Mesh(boxGeometry, materials[i])
  box.position.set((i - 1) * 3, 1, 0)
  box.castShadow = true
  scene.add(box)

}

// Lighting
const ambientLight = new THREE.AmbientLight(0x404040, 0.3)
scene.add(ambientLight)

const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
directionalLight.position.set(5, 10, 5)
directionalLight.castShadow = true
scene.add(directionalLight)

function animate() {
  requestAnimationFrame(animate)

  // Update reflection camera (mirror main camera across floor plane)
  reflectionCamera.position.copy(camera.position)
  reflectionCamera.position.y = -camera.position.y + 2 * 0 // water level
  reflectionCamera.lookAt(0, -camera.position.y, 0)

  // Render reflection
  floor.visible = false
  renderer.setRenderTarget(reflectionRenderTarget)
  renderer.render(scene, reflectionCamera)

  // Render main scene
  floor.visible = true
  floor.material.map = reflectionRenderTarget.texture
  renderer.setRenderTarget(null)
  renderer.render(scene, camera)
}
animate()