Captured images
Common mistakes with captured images, thumbnails, scene captures, lights and sky in Unreal through MifBridge. Read it when a capture is blank, shows the old view, ignores a lighting or exposure setting, or a light is far dimmer or brighter than its number says.
A capture call can return ok: true with every field correct and a useless picture. The causes sit in the engine's contracts, so anything else that reads pixels hits them too. If a picture matters, open the file and measure it.
Viewport captures
A capture can show the old camera
The viewport read returns the backbuffer, not a fresh render. A viewport that is not realtime, which is any level viewport once the editor loses focus, does not redraw on its own. Move the camera with set_viewport_camera, capture, and you can get a byte-identical image of the old view while the response reports the new camera position.
capture_viewport forces a redraw before reading and says so with forcedRedraw: true. If you write your own capture step, do the same: invalidate, draw, flush rendering commands, then read.
Realtime has two values
set_viewport_camera with realtime: true stores true, but an unfocused editor can still hold the viewport non-realtime. The engine adds a "Background Process" override on every tick the app lacks focus while "Use Less CPU when in Background" is on, which is the default on UE 5.3 to 5.8. In one measurement of an editor with only its own window minimized, 5.3 to 5.6 read false after every write and 5.7 and 5.8 stayed true. The difference was the per-user, per-engine-version preference, not the engine code.
The viewport endpoints report three fields: realtime, realtimeSetting and realtimeOverride. Judge the setting, and judge the effective value only where no override is named. When an engine split shows up in a sweep of several editors, read the preference for each engine before calling it engine behavior. The same cause stops placed Niagara effects from advancing, see Common mistakes with Niagara.
The alpha channel is not coverage
The backbuffer's alpha is leftover renderer state. In one measurement it was 0 on 99.97% of pixels, so the written PNG was fully transparent: correct RGB underneath, blank in any viewer that honors alpha. capture_viewport forces alpha to 255 before compressing. capture_camera and the thumbnail path were checked and are opaque already.
A blank-frame check that tests for black will not fire
The transparent capture composited to white over its viewer's background and passed an all-black guard, twice, and each time was read as "the scene must be empty". Measure uniformity instead: the fraction of the frame that is the single most common color. capture_viewport reports distinctColours, uniformity and dominantColour on every response, so a caller can apply its own threshold.
Scene captures
Unlit view mode does not turn lighting off
Setting a view mode sets show flags for it, but never switches Lighting, Atmosphere or Fog off. The editor viewport runs an override for that every frame, and a scene capture component never does. A capture asked for unlit was a lit frame with post-processing off, so in a level with no lights it was one flat black at any exposure. capture_camera now runs the override and restores r.Nanite, which the override switches off for Clay, Zebra, FrontBackFace and RandomColor.
A scene capture ignores exposure until it keeps a view state
A scene capture without a view state ignores the post process volume's exposure: bias 0 and bias 3 produced the same pixels. Giving the capture a view state (bAlwaysPersistRenderingState) fixed it, after which manual exposure is 2^bias. If a capture looks unaffected by an exposure setting, check that first.
A cancelled Render Movie capture leaves a fixed time step
The engine's Render Movie capture turns its fixed time step on when the play session starts and turns it off only when the capture finalizes after it began. A capture cancelled before then leaves the editor on a fixed step at the capture's rate. A frame-capture run then read 60 of 60 frames at exactly 33.333 ms, and an injected 150 ms stall as 33.333 ms. The code is the same on 5.3 to 5.8. render_movie_capture restores the previous state once the capture and its play session end. If frame times look suspiciously identical, restart the editor before trusting them.
Thumbnails and preview lighting
The default preview sky light lights nothing
The engine's plain preview scene builds a sky light that uses a specified cubemap and never sets one, so the sky brightness setting multiplies an empty capture. Passing a brightness of 0.8 and of 0.0 render identically. The engine's own thumbnail renderer therefore cannot show ambient occlusion: AO only scales sky and indirect light, and there is none.
render_thumbnail with lighting: "sky" renders a preview scene of its own instead. Editors that look well lit, such as the skeletal mesh and static mesh editors, use a separate advanced preview scene that loads an HDRI. A viewport built on the plain scene needs its own light rig.
The preview scene's one real light is fixed in world space
Its rotation defaults to pitch -40, yaw -67.5 and nothing re-aims it as the camera orbits. A viewport that frames its subject from a measured angle is lit from a different direction for every asset, and from behind for some. When aimed 35 to 40 degrees off the camera axis, the light lit almost nothing the camera saw (center luminance 67 against 86 along the axis, same sky). Keep the key light within about 15 degrees of the camera, and compose rotations in the camera's own frame. With advanced features disabled, direct lights are all the viewport has, and the unlit side of a one-light rig is black, not dim.
Lights and sky
Material AO reaches a movable sky light only when static lighting is off
With r.AllowStaticLighting on, which is the engine default, the base pass has no G-buffer room for AO and multiplies it into the baked indirect irradiance. On 5.8.2 with static lighting allowed, AO 1 and AO 0.05 rendered identical pixels under a sky light. With r.AllowStaticLighting=False, the same change made the center 28% darker.
An atmosphere sun's lux is above the atmosphere
A directional light marked as the Atmosphere Sun Light, which every directional light is by default, reaches the ground at its Intensity times the atmosphere's transmittance toward it. A sun set to the lux a surface should receive is dimmer than that, more so the lower it stands. calibrate_lights divides by the transmittance. With two atmosphere suns on one index the renderer dims only the brighter.
A sky light that captures the scene cannot be calibrated from its capture. Its only CPU-side irradiance is from its last capture, which is the level as it was then, and any property write recaptures it. A real-time capture never reaches the CPU. Only a specified cubemap's capture is independent of the level, so calibrate_lights proposes a sky light only for one.
Writing a light's intensity unit can turn its IES intensity off
set_property on IntensityUnits writes the unit's edit-condition flag by default (overrideFlag: "set"), and the unit's condition is !bUseIESBrightness. So writing the unit, even to the value it already has, switches Use IES Intensity off (measured on 5.8.2). Set IES settings after the unit, or pass overrideFlag: "ignore".
The Details panel's unit change on a light with Use IES Intensity on is worse: it rewrites the shared IES profile asset's Brightness, with no undo, and every light using that profile changes. convert_light_units skips those lights, and calibrate_lights scales their own IESBrightnessScale instead.
Stationary is Movable in most UE5 projects
The light's scene proxy drops static shadowing when static lighting is off (r.AllowStaticLighting 0) and, on 5.8, whenever virtual shadow maps are in use. 5.3 to 5.7 do it for non-Nanite virtual shadow maps. Where baking does happen, each shadow-casting Stationary light takes one of four shadow-map channels shared by overlapping lights. A fixture Blueprint therefore makes its light Movable with shadows off unless asked, and warns when a Stationary one is requested in a project with static lighting off.
An inverse-squared light reaches nothing at its attenuation radius
The deferred shader multiplies 1/(d^2+1) by (1 - (d/R)^4)^2. At half the radius a surface gets 0.879 of the light its candela promises, and at 0.8 R only 0.349. A target lux that ignores this window is too low near the edge of the radius.
Height fog: one draws, and a new one is black
The renderer draws only the first exponential height fog. With two fogs, which one is drawn depends on which registered first and which moved last, and atmosphere_pass refuses a level with two. A new height fog's inscattering color is black. Its visible color is the SkyAtmosphere's contribution, which is 0 when the scene has none, so a fog placed into a level with no SkyAtmosphere and no color only darkens.