Different color output when rendering to WebGLRenderTarget

Ideally, an HDR pipeline for the portal effect would look something like this:

Each render target is float16 or float32, in Linear-sRGB space. Most HDR effects, like bloom, would typically go after the main render pass (to the third render target). OutputPass (or postprocessing equivalent) would come after that, applying tone mapping and the sRGB OETF (“linear to sRGB”). You shouldn’t need to put tone mapping or the sRGB OETF into custom shaders unless they draw to the canvas, and shouldn’t need to manually set toneMapping={false} on any materials.

When entering the portal, we’re changing what’s written to the third render target, but not changing the color space of that target, or the effects and output transform coming after it.


That’s the ideal, and gives you the most flexibility about using post-processing throughout the pipeline. But if your performance budget doesn’t allow this, it’s also reasonable to cut some corners. That third render target could be direct output to the display instead, and that would be fine, in which case you have something more like this:

Here, again, no materials require toneMapped={false}. Tone mapping and the sRGB OETF are applied in materials drawing to the canvas, and not when drawing to the two Linear-sRGB render targets. I used the terms “Display” and “Canvas” inconsistently in these illustrations, but both refer to the same thing, rendering into the HTMLCanvasElement’s drawing buffer.

tl;dr – We’re trying to preserve linear [0, ∞] color data as long as possible, tone mapping and encoding to sRGB only when drawing to the canvas. That rule can be broken, with care, but will make things more complex.

9 Likes