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:
- Mismatch Scenario: If
texture.premultipliedAlphaistrue(the color is scaled down) butmaterial.premultipliedAlphais set tofalse, 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. - 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:
- Stick to the Defaults (Recommended): Keep
material.premultipliedAlpha = trueand allow Three.js to handle texture loading automatically. This yields the most accurate blending and avoids dark outlines. - Keep Settings Synchronized: If you must disable
premultiplication for specific custom shaders or blending requirements,
ensure that both
material.premultipliedAlphaandtexture.premultipliedAlphaare set tofalse.