Transparency in Shadows

Why Three.js shadows ignore opacity, and how alphaTest fixes it

Shadows are colorless and opaque by default

If you've worked through the previous article on shadow types, you know that Three.js shadows are generated from depth maps. The shadow camera renders the scene from the light's perspective and records how far away each surface is. That's it. No color, no opacity, no material properties. Just depth.

This means that by default, a red translucent sphere casts the exact same shadow as a white opaque sphere. The shadow system doesn't know or care that your object is transparent. It sees geometry, records depth, and produces a binary result: shadowed or not shadowed.

This is the single biggest "gotcha" people hit when working with shadows in Three.js. You set opacity: 0.3 on a material, expect a faint shadow, and get a pitch-black silhouette instead. Let's see it in action.

The demo above shows two spheres. The left sphere is fully opaque. The right sphere has opacity: 0.3 and transparent: true. Notice that both cast identical, fully opaque shadows. The shadow system completely ignores the material's transparency settings.

typescript
// This is the gotcha: opacity does NOT affect shadows
const transparentMat = new THREE.MeshStandardMaterial({
  color: 0xff4444,
  transparent: true,
  opacity: 0.3
});

const sphere = new THREE.Mesh(sphereGeom, transparentMat);
sphere.castShadow = true;

// The shadow will be fully opaque, as if opacity were 1.0.
// The shadow map only records depth. It has no concept
// of material transparency.

Why this happens: the depth-only pipeline

When Three.js renders the shadow map, it doesn't use your material's fragment shader. Instead, it uses a special depth material that writes only the distance from the light to each fragment. The standard depth material has no knowledge of your material's opacity, alphaMap, or color.

This is a deliberate design choice. Shadow map rendering needs to be fast because it happens once per light per frame (six times for point lights). Running your full PBR material shader during the shadow pass would be expensive and, for most opaque objects, pointless.

The consequence is that you need to explicitly tell Three.js how to handle transparency during the shadow pass. The simplest way to do this is with alphaTest.

alphaTest for hard cutouts

When you set an alphaTest threshold on your material, Three.js will discard fragments below that threshold during both the main render and the shadow pass. This works because the built-in depth material does respect alphaTest and alphaMap.

The limitation is that alphaTest is binary. A fragment is either fully visible or fully discarded. You don't get soft, semi-transparent shadows. But for things like leaves, fences, decals, and masked shapes, it works well.

The demo above shows two planes with the same checkerboard alpha pattern. The left plane has no alphaTest, so it casts a solid rectangular shadow. The right plane has alphaTest: 0.5, and its shadow correctly shows the cutout pattern. The depth material picks up the alpha test and discards the transparent fragments during the shadow pass.

typescript
// Create an alpha map (e.g., a checkerboard pattern)
const alphaTexture = createCheckerboardTexture();

// WITHOUT alphaTest. Casts solid rectangular shadow
const solidShadowMat = new THREE.MeshStandardMaterial({
  color: 0x44aa88,
  alphaMap: alphaTexture,
  transparent: true,
  side: THREE.DoubleSide
});

// WITH alphaTest. Shadow respects the alpha cutout
const cutoutShadowMat = new THREE.MeshStandardMaterial({
  color: 0x44aa88,
  alphaMap: alphaTexture,
  transparent: true,
  alphaTest: 0.5,
  side: THREE.DoubleSide
});

// alphaTest: 0.5 means any fragment with alpha < 0.5
// is discarded entirely. This applies to both the
// visual render AND the shadow map render.

alphaTest is the right tool when you have a texture with clear opaque and transparent regions. For more nuanced control over the shadow pass, or when alphaTest alone isn't enough, you'll need custom depth materials, which we cover in the next article.

←   Types of ShadowsCustom Shadow Materials   →