Renderer · type

OutputTransform

What happens to a shaded colour on its way to the framebuffer.

Explained in Hello world.

type OutputTransform = 'none' | 'srgb' | 'aces' | 'shoulder' | 'filmic'
import type { OutputTransform } from '@driftengine/core';

In depth

none writes it unchanged, which is what this renderer has always done and what its worlds are authored against.

srgb applies the sRGB transfer function. This is not a stylistic choice: shading maths is linear and a display is not, so writing linear values into an 8-bit framebuffer crushes the midtones — a surface at 0.2 linear should read as 0.48 and instead reads as 0.2. It is why an untransformed render looks dark and muddy in exactly the places a lit one should look ordinary.

aces adds filmic tone mapping before that, rolling highlights off instead of clipping them flat. Together they are what three.js does by default, which is why a world ported from it looks wrong until this exists.

shoulder keeps sRGB's midtones exactly and eases only the highlights: past 0.8 the brightest channel rolls toward 1 and the other two are scaled with it, so an overbright red spark stays red where srgb clips each channel and turns its core pink and wide. It is the curve a ported game's own resolve applied — for a port that has to match one — and it has none of ACES's toe or desaturation. See HIGHLIGHT_SHOULDER_GLSL for the arithmetic.

filmic is a film curve shaped by five numbers — a slope, a toe, a shoulder and how far black and white clip — that holds a mid grey of 0.18 at 0.18 where aces gives about 0.11, so a look graded against such a curve is matched by its numbers (setFilmicCurve) rather than by a lookup table. Applied by the composite alone, so it needs screenEffects and hdrScene; without them every pass grades with aces and the renderer says so once. See filmicCurve.ts.