kubakub brightness compensation
Fix the projector's hotspot before it hits the wall
- images
- scene
- regions
- images
- gain_map
- report
- gain
What it's for
A projector doesn't light a building evenly. The corner nearest the lens gets more light than the far corner, and a surface turned away from the projector (the side of a moulding, the reveal of a window) catches it at a grazing angle and goes dim. Nobody notices with a photo. With your content on the building, the top-left of a gradient is visibly brighter than the bottom-right and the whole show looks like it's lit by a torch.
kubakub brightness compensation pre-darkens the parts of your content that will land where the projector is already bright, so the result on the building reads evenly. Put it last, after everything generative, immediately before the export.
It's not a creative grade and it isn't guessing. It works from the geometry: the same scene the scene render node built, with the building's surfaces, their normals and the projector's viewpoint. That's why it needs a 3D scene, and why the answer is in metres and degrees rather than vibes.
How it works
It measures two physics facts over the projector's view: inverse-square falloff (far is dimmer) and incidence angle (turned away is dimmer). That gives a brightness map. Then it computes a gain per pixel - how much to scale your content so, multiplied by the lighting, the wall comes out even. It works in linear light, which is the only way this is honest: gains are multiplicative, and gamma-encoded arithmetic would get the falloff wrong.
Three decisions shape the result:
match-dim areasmakes everything as even as the dimmest parts. Most even, and darkest.averageonly brings the brightest parts down toward the average, so the building keeps more of its natural light.grazing_deg(60, advanced) - surfaces hit steeper than this are ignored when setting the level. This is the detail that saves the feature: side faces of mouldings are dim by nature, and if they set the target, the node would darken the whole building to match a shadowed reveal.smooth_px(8) - softens the gain map so you don't get visible steps where geometry changes.
The 0.3.0 changelog added the gain output (raw data instead of only a coloured picture), which is the difference between "I hope that's right" and "the report says the range was 0.42 to 1.00".
Inputs and outputs
images- your finished frames at matrix size, one still or a video batch.scene- from kubakub scene render, i.e. the projector's view of the building. There's no substitute for this input.match,strength(0 = unchanged, 1 = fully even; 0.5-0.7 keeps a little natural falloff, which usually looks better than fully flat),lift_dim_up_to,smooth_px,grazing_deg.regions(optional) - one gain per region instead of per pixel, so a window never has a gradient running through it. This is on the pack's facade partition, and it's the difference between "technically even" and "looks right", because a projector hotspot crossing a window frame is more visible than one crossing a wall.
lift_dim_up_to is 1 by default and means "never brighter than your content" - the safe setting. Above 1 the dim parts get lifted as well, and the report tells you how much clips. If you're painting with light, you generally don't want to brighten: you have a fixed lumen budget and lifting highlights just eats it.
Outputs: images (compensated), gain_map (a colourised view of what changed - dark means darkened), report (the target level, how much light is kept overall, how much clips), and gain (the real gain as a mask at your frames' size, linear: white = 1.0 = unchanged, darker = darkened more; with lift_dim_up_to above 1 white becomes the highest gain). In the docs' example, 3200×2160 across 25 frames runs in about 4 seconds.
Install
Manager → kubakub nodes → install → restart. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/kubakubkub/ComfyUI_KubaNodes
python -m pip install -r ComfyUI_KubaNodes/requirements.txt
ComfyUI 0.37.0+, opencv-python-headless as the only added package (keep your own OpenCV build). Because the scene comes from scene render, you need Blender 4.x for the upstream half - found in Program Files or on PATH, or set blender = ... in kubakub.ini. Scene results are cached on disk in kubakub_cache (cap cache_gb = 20), and Blender keeps a worker alive between jobs (blender_idle = 600) - that idle worker is why the second run feels much faster.
Common issues
- It needs the scene, not an image. If you don't have a 3D model of the building, you can't use this node honestly. There's no "estimate the hotspot from a photo" mode.
- The whole building went dark.
grazing_degis too high, so a genuinely dim surface is setting the target; ormatchisdim areaswithstrengthat 1. Tryaverageand 0.6. - Gradients across window frames. Wire
regions. One gain per region instead of per pixel flattens it exactly where you want it flat. - Highlights clipping. That's
lift_dim_up_toabove 1. The report counts the clipped pixels - read it, then decide whether the lift was worth it.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | Your finished frames at matrix size (one still or a video batch). | |
| scene | KUBA_SCENE | From kubakub scene render (the projector's view of the building). | |
| match | COMBO | dim areas | dim areas = everything as even as the dimmest parts (most even, darker); average = only the brightest parts come down. |
| strength | FLOAT | 1.000–1 | 0 = unchanged, 1 = fully even. 0.5-0.7 keeps a little of the natural falloff. |
| lift_dim_up_to | FLOAT | 1.001–4 | 1 = never brighter than your content (safe). Above 1 dim parts get lifted too; their highlights may clip. |
| smooth_px | FLOAT | 80–256 | Softens the gain map (pixels of the projection view), hides small steps. |
| grazing_deg | FLOAT | 600–90 | Surfaces hit steeper than this (side faces of mouldings) don't set the level: they are dim by nature and would darken everything else. |
| regionsopt | KUBA_REGIONS | One gain per region instead of per pixel, so no gradient inside a window. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | The compensated frames. |
| gain_map | IMAGE | How much each part is changed (dark = darkened, colours as in scene measure). |
| report | STRING | Target, how much light is kept overall and how much clips. |
| gain | MASK | The real gain as a mask at the size of your frames (in linear light): white (1) = a gain of 1.0 (unchanged), or the highest gain when lift_dim_up_to is above 1; darker = darkened more. The report says the range. |