ComfyUI Node

LatLong Tone Map Preview

Actually see your HDR panorama in ComfyUI

By cedarconnor·Created about a year ago·Updated about 15 hours ago· 2
LatLong Tone Map Preview
  • image
  • IMAGE
exposure_stops0.00

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:

  1. Values below zero are clamped away (clamp(min=0)) - negatives are meaningless as light.
  2. Multiply by 2 ** exposure_stops. Positive stops brighten, negative stops darken; the range is -20 to +20, default 0.
  3. 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.
  4. Linear-to-sRGB transfer, with the proper piecewise curve (12.92x below 0.0031308, the 1.055x^(1/2.4)-0.055 branch 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.

CategoryLatLong/HDR

Inputs (2)

NameTypeDefaultDescription
imageIMAGE
exposure_stopsFLOAT0.00-20–20

Outputs (1)

NameTypeDescription
IMAGEIMAGE