Three.js Material Transparency and Opacity Guide

Achieving realistic transparency in Three.js requires more than just adjusting a single opacity value. This article provides a straightforward guide on how to correctly configure transparency and opacity on Three.js materials, covering the essential properties, handling depth sorting issues, and implementing best practices for flawless 3D rendering.

The Core Properties: Transparent and Opacity

To enable transparency on any Three.js material (such as MeshBasicMaterial, MeshStandardMaterial, or MeshPhongMaterial), you must configure two key properties: transparent and opacity.

By default, materials are fully opaque to optimize rendering performance. To make an object transparent, you must explicitly set transparent to true. Once enabled, you can control the level of transparency using the opacity property, which accepts a float value between 0.0 (fully invisible) and 1.0 (fully opaque).

Here is the standard implementation:

import * as THREE from 'three';

// Create a transparent material
const material = new THREE.MeshStandardMaterial({
    color: 0x00ff00,
    transparent: true,  // Must be set to true
    opacity: 0.5        // Value between 0.0 and 1.0
});

// Apply to a mesh
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);

Resolving Depth and Sorting Issues

A common issue when working with transparent materials in WebGL is “depth fighting” or sorting artifacts, where objects behind a transparent surface are incorrectly hidden or clipped. Three.js renders transparent objects from back to front, but complex scenes can confuse the depth buffer.

To fix these visual artifacts, you can adjust how the material interacts with the depth buffer using the following properties:

1. Depth Write (depthWrite)

Setting depthWrite to false prevents the transparent material from writing to the depth buffer. This ensures that objects rendered behind the transparent material are still drawn correctly.

const glassMaterial = new THREE.MeshPhysicalMaterial({
    color: 0xffffff,
    transparent: true,
    opacity: 0.3,
    depthWrite: false // Prevents clipping of objects behind this material
});

2. Depth Test (depthTest)

If you want an object to be visible regardless of what is in front of it (for example, a UI overlay or helper gizmo), you can disable depthTest.

material.depthTest = false;

3. Render Order (renderOrder)

For scenes with multiple overlapping transparent meshes, you can manually define the rendering sequence using the renderOrder property on the Mesh objects. Lower numbers render first, and higher numbers render on top.

backgroundMesh.renderOrder = 0;
foregroundMesh.renderOrder = 1;

Textured Transparency with Alpha Maps

If you want to apply transparency based on an image texture (like a leaf or a chain-link fence), use an alphaMap combined with alphaTest.

const textureLoader = new THREE.TextureLoader();
const alphaTexture = textureLoader.load('path/to/alpha-map.png');

const cutoutMaterial = new THREE.MeshStandardMaterial({
    color: 0xffffff,
    alphaMap: alphaTexture,
    transparent: true,
    alphaTest: 0.5 // Discards pixels with opacity below 0.5
});

By correctly combining transparent, opacity, and depth buffer configurations, you can achieve clean, artifact-free transparency in your Three.js projects.