Full-screen post-processing pass (Uber-Shader).
Reads the HDR render texture and writes tone-mapped, gamma-corrected output
directly to the swap-chain (canvas). No intermediate ping-pong copies.
── Forward-Pass / Post-Pass tonemapping contract ────────────────────────────────────────────
The WebGPU forward pass deliberately sets global.gamma = 1.0 and global.exposure = 1.0 when
PostProcessing is enabled. This disables the Reinhard guard inside lighting_pbr.wgsl so that
the forward pass writes raw linear-HDR values into the render target — tonemapping and exposure
are owned exclusively by this PostProcess pass.
Consequence: if no ToneMappingElement is configured, this pass MUST still apply a tonemapper.
Without one the output is raw HDR × exposure = no tone curve = visibly dark / flat scene.
The u_toneMappingMode injection in _buildPipeline() defaults to Reinhard (1) to ensure this.
⚠️ Never add Reinhard or ACES to a WebGPU forward-pass shader without checking whether
PostProcessing is active. If it is, the forward pass must NOT tonemap (double-tonemapping
produces a systematically darker scene). See WebGPURenderer.ts for where gamma/exposure
are set to 1.0 and the full explanation of the two-pass pipeline.
Full-screen post-processing pass (Uber-Shader). Reads the HDR render texture and writes tone-mapped, gamma-corrected output directly to the swap-chain (canvas). No intermediate ping-pong copies.
── Forward-Pass / Post-Pass tonemapping contract ──────────────────────────────────────────── The WebGPU forward pass deliberately sets global.gamma = 1.0 and global.exposure = 1.0 when PostProcessing is enabled. This disables the Reinhard guard inside lighting_pbr.wgsl so that the forward pass writes raw linear-HDR values into the render target — tonemapping and exposure are owned exclusively by this PostProcess pass.
Consequence: if no ToneMappingElement is configured, this pass MUST still apply a tonemapper. Without one the output is raw HDR × exposure = no tone curve = visibly dark / flat scene. The u_toneMappingMode injection in _buildPipeline() defaults to Reinhard (1) to ensure this.
⚠️ Never add Reinhard or ACES to a WebGPU forward-pass shader without checking whether PostProcessing is active. If it is, the forward pass must NOT tonemap (double-tonemapping produces a systematically darker scene). See WebGPURenderer.ts for where gamma/exposure are set to 1.0 and the full explanation of the two-pass pipeline.