The encode_video.sh bundled with every image / EXR frame sequence.
Two very different jobs, depending on the frames in the ZIP:
LDR sequences (PNG / WebP / JPEG) are already exactly what the
viewer showed — renderToImageData reads the framebuffer after the
mega-shader has applied exposure, offset, gamma, tone mapping and the
sRGB encode. The script must therefore apply no colour maths at all;
it just muxes frames into a container.
EXR sequences are scene-linear and pre-grade. The EXR capture mode
(hdr-effects-pre-tone) deliberately bypasses the whole display
transform so the archive keeps unclipped HDR. Encoding those floats
without reversing that decision produces a dark, colour-shifted video:
measured on a real capture, mean RGB (26, 47, 39) against the viewer's
own (67, 67, 71). So for EXR the script reproduces the shader chain —
exposure → offset → gamma → tone map → sRGB — and reproduces it
exactly, as a geq expression carrying the same constants as
three's tone-mapping functions, because ffmpeg's built-in tonemap
curves are not the same functions. Measured against the viewer's own
PNG of the same frame (PSNR, higher is closer):
viewer ACES exact geq 38.2 dB · tonemap=hable 16.0 · nothing 23.8
(the same maths with PNG out, i.e. no codec, tops
out at 40.3 dB — that is the ceiling, not this chain)
viewer Reinhard tonemap=reinhard 34.8 dB, and the exact expression
measured closer still
hable, the usual stand-in for ACES, is worse than no tone mapping
at all, which is why this module does not offer the approximations.
AgX is the one mode with no practical closed form here (four matrix
stages around a log-space polynomial would expand to hundreds of terms
per channel), so its script says so and points at the LDR-sequence
route for a pixel-exact match.
The
encode_video.shbundled with every image / EXR frame sequence.Two very different jobs, depending on the frames in the ZIP:
LDR sequences (PNG / WebP / JPEG) are already exactly what the viewer showed —
renderToImageDatareads the framebuffer after the mega-shader has applied exposure, offset, gamma, tone mapping and the sRGB encode. The script must therefore apply no colour maths at all; it just muxes frames into a container.EXR sequences are scene-linear and pre-grade. The EXR capture mode (
hdr-effects-pre-tone) deliberately bypasses the whole display transform so the archive keeps unclipped HDR. Encoding those floats without reversing that decision produces a dark, colour-shifted video: measured on a real capture, mean RGB (26, 47, 39) against the viewer's own (67, 67, 71). So for EXR the script reproduces the shader chain — exposure → offset → gamma → tone map → sRGB — and reproduces it exactly, as ageqexpression carrying the same constants asthree's tone-mapping functions, because ffmpeg's built-intonemapcurves are not the same functions. Measured against the viewer's own PNG of the same frame (PSNR, higher is closer):viewer ACES exact geq 38.2 dB · tonemap=hable 16.0 · nothing 23.8 (the same maths with PNG out, i.e. no codec, tops out at 40.3 dB — that is the ceiling, not this chain) viewer Reinhard tonemap=reinhard 34.8 dB, and the exact expression measured closer still
hable, the usual stand-in for ACES, is worse than no tone mapping at all, which is why this module does not offer the approximations.AgX is the one mode with no practical closed form here (four matrix stages around a log-space polynomial would expand to hundreds of terms per channel), so its script says so and points at the LDR-sequence route for a pixel-exact match.