Three.js premultipliedAlpha and Transparent PNGs

This article explains how the material.premultipliedAlpha property affects the rendering of transparent PNG textures in Three.js. You will learn how WebGL handles color blending under the hood, why transparent PNGs sometimes exhibit dark halos, and how to configure your materials and textures to ensure clean, artifact-free transparency.

Understanding Premultiplied Alpha in WebGL

In computer graphics, transparency is represented by an alpha channel. There are two primary ways to store color and alpha data: * Straight Alpha (Unpremultiplied): Color channels (RGB) and the transparency channel (A) are kept entirely independent. * Premultiplied Alpha: The RGB values are multiplied by the alpha value beforehand (RGB × A). For example, a 50% transparent pure red pixel is stored as (0.5, 0.0, 0.0, 0.5) instead of (1.0, 0.0, 0.0, 0.5).

By default, WebGL and Three.js use premultiplied alpha for rendering because it produces mathematically correct color blending—especially when filtering textures, generating mipmaps, or blending overlapping transparent layers.

The Default Behavior of material.premultipliedAlpha

In Three.js, the material.premultipliedAlpha property defaults to true.

When this property is enabled, Three.js configures the WebGL blending equations to expect colors that have already been multiplied by their alpha values. The blending equation used on the GPU is:

\[\text{Output Color} = \text{Source Color} + \text{Destination Color} \times (1 - \text{Source Alpha})\]

For this to render correctly, the incoming texture must also be premultiplied. When you load a transparent PNG using TextureLoader, Three.js automatically sets texture.premultipliedAlpha = true and instructs the browser’s unpack process to premultiply the image’s color channels by its alpha channel during the upload to the GPU.

What Happens When Disabled (material.premultipliedAlpha = false)

If you set material.premultipliedAlpha = false, Three.js changes the WebGL blending equation to accommodate straight (unpremultiplied) colors:

\[\text{Output Color} = \text{Source Color} \times \text{Source Alpha} + \text{Destination Color} \times (1 - \text{Source Alpha})\]

If you disable this property on the material, you must also set texture.premultipliedAlpha = false on the associated PNG texture. If you do not align these two properties, you will encounter rendering artifacts.

Common Rendering Artifacts: The Dark Halo Effect

The most common issue when rendering transparent PNGs is a dark, pixelated border (or “halo”) around the edges of transparent shapes. This occurs due to a mismatch between the texture data and the material’s rendering state:

  1. Mismatch Scenario: If texture.premultipliedAlpha is true (the color is scaled down) but material.premultipliedAlpha is set to false, the GPU multiplies the already-reduced color by the alpha value a second time during blending. This causes the semi-transparent edge pixels to become excessively dark.
  2. Texture Filtering Issues: When WebGL scales a straight-alpha texture, it interpolates between transparent pixels (often stored as black with \(0\) alpha, i.e., 0, 0, 0, 0) and neighboring colored pixels. This interpolation introduces black into the RGB channels before the alpha is applied, resulting in dark fringes. Using premultiplied alpha prevents this interpolation error.

Summary of Best Practices

To ensure transparent PNGs render correctly in Three.js, use the following guidelines: