Proximity Fading Effects in ThreeJS

Overview

Subtle visual cues can dramatically improve clarity in 3D interfaces. One elegant and efficient approach is to make objects fade in and out based on how close the player or camera is to them. The applyDistanceFade utility is a minimalist function that does exactly this — fading an object’s opacity based on proximity.

Let’s explore how this tool works, why it’s effective, and how to make the most of it.

What Is applyDistanceFade?

applyDistanceFade is a simple yet powerful utility function in Three.js that fades an object’s material opacity based on its distance to another object (typically the player or camera). It dynamically adjusts opacity each frame, making objects appear or disappear smoothly within a defined distance range.

import * as THREE from "three";

export function applyDistanceFade(object, player, minDistance, maxDistance) {
  let distance = player.position.distanceTo(object.position);
  let opacity = 1.0 - (distance - minDistance) / (maxDistance - minDistance);
  object.material.opacity = THREE.MathUtils.clamp(opacity, 0.0, 1.0);
}

How It Works

Let’s break down what happens here:

  • distanceTo: Calculates the Euclidean distance between the object and the player.
  • opacity = 1.0 – (…): Normalizes the distance between minDistance and maxDistance into a 0–1 range, then inverts it. That way:
  • When the object is very close (<= minDistance), opacity is 1 (fully visible).
  • When it is farther than maxDistance, opacity is 0 (fully transparent).
  • In between, it linearly fades.
  • THREE.MathUtils.clamp: Prevents the opacity from going outside the valid range (0.0 to 1.0).

Typical Use Case

This kind of fading is ideal for:

  • UI panels or 3D labels that should only appear when you’re near them.
  • Locks, gates, terminals, or panels in a VR/AR setting that should respond to player proximity.
  • Hints or tooltips that guide user interaction subtly.

Usage Example

In your render loop or animation frame, use the function like this:

import { applyDistanceFade } from './FadeUtil.js';

function animate() {
  requestAnimationFrame(animate);
  
  // Assume player and lockPlane are mesh objects
  applyDistanceFade(lockPlane, player, 1.2, 3.5);
  
  renderer.render(scene, camera);
}

Material Prerequisites

Make sure the object’s material supports transparency:

lockPlane.material.transparent = true;
lockPlane.material.depthWrite = false; // Optional, prevents z-buffer issues

If you’re using MeshStandardMaterial or MeshBasicMaterial, this will work without issues. For complex shaders or custom materials, you may need to implement similar fading logic in GLSL.

How to Extend It

1. Add Easing

Replace the linear fade with a smoother transition:

let t = THREE.MathUtils.clamp((distance - minDistance) / (maxDistance - minDistance), 0.0, 1.0);
let eased = t * t * (3 - 2 * t); // Smoothstep easing
object.material.opacity = 1.0 - eased;

2. Fade Entire Groups

If the object has children (like a panel with icons/text), you can extend the function to traverse the hierarchy:

object.traverse((child) => {
  if (child.material) {
    child.material.transparent = true;
    child.material.opacity = ...; // Use same fade logic
  }
});

3. Fade In Only or Fade Out Only

Allow configuration if you want to only fade out when far but not fade in when near, by clamping the direction:

if (distance > maxDistance) {
  object.material.opacity = 0.0;
} else if (distance < minDistance) {
  object.material.opacity = 1.0;
}

Performance Considerations

  • This function is lightweight and safe to call every frame.
  • Avoid using it on hundreds of objects unless batched or throttled.
  • For better performance, group objects and use one fade logic on shared materials.

Leave a comment

Your email address will not be published. Required fields are marked *