LatLong Tone Map Preview
Actually see your HDR panorama in ComfyUI
- image
- IMAGE
You built an HDR panorama - or rotated one with LatLong Float Processor - and every preview node shows you a white rectangle with grey smears. That's not a bug. A normal preview widget clips at 1.0 and treats the numbers as display-referred; an HDRI is scene-linear with highlights several stops above 1, so a straight view is physically meaningless. You need a display transform, which is all this node is.
It's deliberately one slider: exposure in stops, then a tone curve, then an sRGB encode, out the other side as something a Preview Image can show you. That's the entire user interface, and the restraint is the feature - you're not grading here, you're checking that the geometry and the stops are intact.
How it works
The maths, in order:
- Values below zero are clamped away (
clamp(min=0)) - negatives are meaningless as light. - Multiply by
2 ** exposure_stops. Positive stops brighten, negative stops darken; the range is -20 to +20, default 0. - Reinhard:
x / (1 + x). This is a cheap, monotonic, nowhere-blowing-out tone curve. Every value maps into [0,1), and it compresses highlights hard rather than clipping them - which is exactly what you want for inspecting a sky you can't otherwise see. - Linear-to-sRGB transfer, with the proper piecewise curve (
12.92xbelow 0.0031308, the1.055x^(1/2.4)-0.055branch above it), so what you look at is actually the sRGB encoding of the tone-mapped linear value, not a gamma guess.
Because Reinhard asymptotes rather than clips, you'll never see a pure white sun disc here. You'll see how many stops of headroom you have, which is the point.
exposure_stops is the only input besides image, and the single output is a display-ready IMAGE.
How to wire it
The pattern the author describes is a side branch, and it's worth following: keep the original float signal on your main line, and tap it into Tone Map Preview purely for the benefit of a preview node.
Float Processor (rotate) ──┬──> Tone Map Preview ──> Preview Image
└──> float-capable saver / downstream HDR path
Two things this buys you. The preview never becomes your deliverable, so you can't accidentally save a tone-mapped file as the master. And the geometry branch keeps its float values, so whatever consumes the HDR downstream sees the real numbers.
If all you need is a quick look, exposure at 0 is usually close: Reinhard is gentle near 1.0, so a scene that's roughly correctly exposed reads normally, and you raise stops when the interesting part is the part you can't see yet. Around +3 to +6 is where interiors and dusk skies start showing up.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/cedarconnor/comfyui-LatLong
cd comfyui-LatLong
python -m pip install -r requirements.txt
Manager: search "ComfyUI LatLong". ComfyUI's Python (3.10+), the five usual dependencies, no models to download. Restart - the node is under LatLong/HDR, next to Float Processor.
Common issues
It assumes scene-linear input, and that's the trap. Feed it an ordinary sRGB PNG and it will treat that gamma-encoded data as linear light, tone map it, and then encode it again - you get a washed-out, flat image with lifted shadows and that's the reason. If your input is already display-referred, this is the wrong node; you want a gamma or exposure adjust pass, not a tone mapper (post-processing in ComfyUI has plenty of those - spacepxl's Exposure Adjust is the one most people settle on, and it does f-stop exposure with an optional tonemap). This node's job is the HDR side only.
One exposure value and one curve means no shadow control, no highlight roll-off, no local contrast. That's the intentional limit - it's a viewer, not a grade. Grading happens in Resolve, Nuke or Blender on the float file.
And it's per-frame like the rest of the pack, so a 60-frame HDR sweep is 60 tone-mapped images held in a batch on top of the source. Preview a single frame; run the batch through the geometry branch.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| exposure_stops | FLOAT | 0.00-20–20 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |