Nodes/radiance/LensDistortion
ComfyUI Node

LensDistortion

Barrel, pincushion, and an ST map you can hand to Nuke

By FXTD-Studios·Created 8 months ago·Updated about 18 hours ago· 246
LensDistortion
  • image
  • image
  • st_map
◄k10.000►
◄k20.000►
◄scale1.00►
◄center_x0.50►
◄center_y0.50►
◄padding_modezeros►
◄invertfalse►

Two jobs, one node. First: you generated a clean render and want it to look like it came off a wide lens rather than out of a renderer. Second: you have a plate that's been through a real lens and you need the distortion out before tracking, matchmoving or compositing.

Radiance's LensDistortion does both directions with proper Brown-Conrady radial distortion, and it hands you an ST map so the same warp can be applied in Nuke or Fusion without re-deriving the maths.

How it works

Brown-Conrady models a lens as a radial function of distance from the optical centre: r² = (x - cx)² + (y - cy)², and the sample position is scaled by 1 + k1·r² + k2·r⁴. Two coefficients:

  • k1 - the primary radial term, and 95% of the effect. Negative is barrel (bulging outward, what wide lenses do). Positive is pincushion (pinching inward, what long lenses do).
  • k2 - the secondary term, affecting the extreme corners. The tooltip says "use sparingly" and it means it; large k2 produces folding and crazy corner behaviour fast.

scale is a uniform zoom applied after the warp, and its main practical use is cropping out the black borders that distortion pulls in. center_x / center_y move the optical centre from the default 0.5/0.5 - worth using when the real lens wasn't centred on the sensor, which is more often than you'd think.

padding_mode decides what fills the edges: zeros gives black, reflection mirrors, border clamps the edge pixels outward.

The invert toggle is the part I'd actually point people at. It's not a negative-k1 hack - it's an exact closed-form inverse, which means you can undistort a plate and then re-apply the identical distortion after compositing, and the warp round-trips. The one degenerate case is a singularity at 1 + k1·r² + k2·r⁴ = 0, and the node clamps that automatically rather than producing NaN pixels. That's the correct engineering choice and worth noting because it's exactly where naive implementations fall over.

Inputs and outputs

Seven controls plus the image itself: image, k1, k2, scale, center_x, center_y, padding_mode, invert. No colour management happens - pixels are only resampled - so any encoding works and you can put this anywhere in the chain without worrying about whether it's linear or display.

Two outputs. image is the warped result. st_map is the useful one for pipeline work: R holds U and G holds V as normalised [0, 1] sample coordinates, which is exactly the convention Nuke's STMap node and Fusion's DisplaceImage expect. So you can do the artistic version in ComfyUI and hand production a map that reproduces it exactly downstream without anyone re-solving coefficients.

For grain or defocus that should follow the distortion, put those after the undistort and before the re-distort - you want the artifacts living in the warped space.

Installing Radiance

Manager → search Radiance → install → restart → refresh. Or:

cd ComfyUI/custom_nodes
git clone https://github.com/fxtd-studios/radiance.git
cd radiance
python -m pip install -r requirements.txt

Windows portable users: use python_embeded\python.exe for that pip line. No model downloads. The pack itself - GPL-3.0, ~147 visible nodes from FXTD Studios - installs OpenEXR, OpenImageIO, OpenColorIO, diffusers and accelerate into ComfyUI's Python environment, which is a genuinely large footprint if you keep a lean install.

Troubleshooting

Black borders everywhere. Normal at large |k1| with scale = 1. Raise scale to about 1.05–1.15 to crop in, or switch padding_mode to reflection if you're going to crop later anyway.

The image went to mush in the corners. k2 is too big, or k1 has pushed r past the point where the radial function reverses. Keep k2 inside ±0.1 for realistic lens simulation and remember that the coefficients in your lens metadata are calibrated in normalised units, not pixels.

Distortion appears off-centre. Your lens wasn't mounted perfectly on axis. Adjust center_x/center_y a hundredth at a time.

Round trip doesn't line up. Check you're using the same k1/k2/centre on both passes and that nothing else resampled the image in between. Any resize, crop or rotate between the two breaks the exactness, since the model is defined relative to the optical centre.

Motion blur or grain looks wrong after distorting. You put them on the wrong side. Distortion is a lens-stage operation; artifacts the sensor sees should be applied inside the warp, not outside it.

Careful with the whole class of "AI lens effects". Distortion, aberration and grain are the cheapest way to make a generated frame read as photographed, and they're also easy to overdo. A k1 of −0.5 looks like a fisheye, not like a 35mm lens. Start at a third of what looks right on the first pass and step back.

CategoryFXTD STUDIOS/Radiance/VFX

Inputs (8)

NameTypeDefaultDescription
imageIMAGEImage or sequence to distort or undistort. Pixels are only resampled (no colour change), so any encoding works.
k1FLOAT0.000-1–1Primary radial distortion. k1<0 = barrel, k1>0 = pincushion.
k2FLOAT0.000-1–1Secondary radial distortion. Affects extreme corners. Use sparingly.
scaleFLOAT1.000.1–2Uniform scale applied after distortion. Use to crop black borders.
center_xFLOAT0.500–1Optical center X (0.5 = image center).
center_yFLOAT0.500–1Optical center Y (0.5 = image center).
padding_modeCOMBOzerosEdge fill: zeros=black, reflection=mirrored, border=edge-clamped.
invertBOOLEANfalseInvert warp for plate undistortion. Uses exact closed-form inverse — valid for all k1/k2 combinations except the degenerate singularity at 1+k1·r²+k2·r⁴=0 (clamped automatically).

Outputs (2)

NameTypeDescription
imageIMAGEDistorted image.
st_mapIMAGEST-Map: R=U, G=V normalized [0,1] sample coordinates. Wire to Nuke STMap / Fusion DisplaceImage for downstream use.