[{"id":"L1","fact":"`Unitless` is a legacy scale of **16**, applied in `ComputeLightBrightness`","source":"`Runtime/Engine/Private/Components/RectLightComponent.cpp`, `ComputeLightBrightness` else-branch","engine":"5.7","ue":["5.7"],"bl":[],"section":"UE lighting — intensity units","value":"`LightBrightness *= 16`"},{"id":"L2","fact":"`Unitless` → candela conversion factor is **16/10000 = 0.0016**","source":"`Runtime/Engine/Private/Components/LocalLightComponent.cpp:149` `GetUnitsConversionFactor`","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE lighting — intensity units","value":"src Unitless ⇒ `CnvFactor = 16`; dst Candelas ⇒ `*= 1/100/100`"},{"id":"L3","fact":"`OuterConeAngle` is the **half angle**, so a spot's solid angle is `2π(1−cos θ)`","source":"same function — the Lumens branch is written in terms of `CosHalfConeAngle`","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE lighting — intensity units","value":"`Ω = 2π(1 − cos θ_outer)`"},{"id":"L4","fact":"Lumens → candela for a spot","source":"derived from L3; matches the engine's own Lumens branch `100*100/2/π/(1−CosHalfConeAngle)`","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE lighting — intensity units","value":"`cd = lm / (2π(1 − cos θ_outer))`"},{"id":"L5","fact":"RectLight unit multipliers in `ComputeLightBrightness`","source":"`RectLightComponent.cpp:183-220`","engine":"5.7","ue":["5.7"],"bl":[],"section":"UE lighting — intensity units","value":"Candelas `×100·100`; Nits `×(W·H)`; Lumens `×(100·100/π)`; else `×16`"},{"id":"L6","fact":"A RectLight's colour is **divided by half its source area** in the proxy","source":"`RectLightComponent.cpp`, `FRectLightSceneProxy::GetLightShaderParameters`","engine":"5.7","ue":["5.7"],"bl":[],"section":"UE lighting — intensity units","value":"`LightColor /= 0.5 · SourceWidth · SourceHeight`"},{"id":"L7","fact":"RectLight clamps its source dimensions to a **minimum of 1.0**","source":"`RectLightComponent.cpp:332-333`","engine":"5.7","ue":["5.7"],"bl":[],"section":"UE lighting — intensity units","value":"`SourceWidth = Max(1.0, SourceWidth)` (same for height)"},{"id":"L8","fact":"UE 5.7 RectLight has a **`Nits`** unit that scales by source area; 5.3.2 does not","source":"`RectLightComponent.cpp:191-193`","engine":"5.7","ue":["5.7"],"bl":[],"section":"UE lighting — intensity units","value":"`AreaInCm2 = SourceWidth * SourceHeight`"},{"id":"L9","fact":"The inverse-squared attenuation window is a **smooth fade to zero at the radius**, no hard edge","source":"`Engine/Shaders/Private/DeferredLightingCommon.ush:516`","engine":"5.7","ue":["5.7"],"bl":[],"section":"UE lighting — falloff and colour","value":"`LightRadiusMask = Square(saturate(1 − Square(DistanceSqr · Square(InvRadius))))`, i.e. `(1 − (d/R)⁴)²`"},{"id":"L10","fact":"Solving L9 for **≤5% at distance d** gives the radius to use **[Reviewed 2026-09-21 against the code - STALE in part: by L9's window R=1.2d leaves 26.8%; ~13% is R~1.12d. R=1.05d leaves 3.1% holds (DeferredLightingCommon.ush + arithmetic)]**","source":"arithmetic on L9","engine":"—","ue":[],"bl":[],"section":"UE lighting — falloff and colour","value":"`R ≈ 1.05 · d` (and `R ≈ 1.2 · d` leaves ~13%)","status":"superseded","replacedBy":"L9"},{"id":"L11","fact":"Colour temperature **multiplies** `LightColor`, it does not replace it — so enabling it requires setting the colour to white or the old tint stacks","source":"`LightComponent.cpp`, `ULightComponent::GetColoredLightBrightness`","engine":"5.7","ue":["5.7"],"bl":[],"section":"UE lighting — falloff and colour","value":"`Energy = FLinearColor(LightColor) * LightBrightness; if (bUseTemperature) Energy *= GetColorTemperature();`"},{"id":"L12","fact":"`GetColorTemperature()` is the working colour space's blackbody","source":"`LightComponent.cpp:1565-1567`","engine":"5.7","ue":["5.7"],"bl":[],"section":"UE lighting — falloff and colour","value":"`FColorSpace::GetWorking().MakeFromColorTemperature(Temperature)`"},{"id":"L13","fact":"`LightFalloffExponent` is **inert** while `bUseInverseSquaredFalloff` is true","source":"`DeferredLightingCommon.ush:516-521`","engine":"5.7","ue":["5.7"],"bl":[],"section":"UE lighting — falloff and colour","value":"the shader takes the `LightRadiusMask` branch, the exponent branch is the `else`"},{"id":"L14","fact":"`bUseInverseSquaredFalloff` **does not exist** on `RectLightComponent` — rect lights are always physical","source":"live editor `get_property` on a RectLight template, port 8823","engine":"5.7","ue":["5.7"],"bl":[],"section":"UE lighting — falloff and colour","value":"property read returns \"not found\""},{"id":"L15","fact":"The brightness a light's scene proxy is given is `LightColor x ComputeLightBrightness()` (x the temperature colour when enabled), so `ComputeLightBrightness()` is the one number to read whatever unit a light is authored in, IES scaling included","source":"5.8 `Runtime/Engine/Private/Components/LightComponent.cpp:1623-1633`, 5.8 `Runtime/Engine/Private/Components/LightComponent.cpp:1712`","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"`OutProxyDesc.Color = GetColoredLightBrightness()`; `Energy = LightColor * ComputeLightBrightness()`"},{"id":"L16","fact":"For a point or spot light with inverse-squared falloff, and for every rect light, `ComputeLightBrightness()` is **candela x 100 x 100 in every unit**, so candela = brightness / 10000; a point or spot light with the falloff off has no physical unit. `lighting_report` reports candela this way. Measured: Candelas as written, Unitless 5000 -> 8, a Lumens 1000 spot at a 45-degree cone -> 543.4, a Candelas 10 rect -> 10 (test_lighting_report L101, 5.8.2 and 5.3.2)","source":"5.3 `Runtime/Engine/Private/Components/PointLightComponent.cpp:196`, 5.3 `Runtime/Engine/Private/Components/RectLightComponent.cpp:108`, 5.8 `Runtime/Engine/Private/Components/PointLightComponent.cpp:127`, 5.8 `Runtime/Engine/Private/Components/RectLightComponent.cpp:181`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"`candela = ComputeLightBrightness() / 10000`"},{"id":"L17","fact":"A post-process volume's exposure MinBrightness/MaxBrightness are **EV100 only when `r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange` is 1** (the renderer tests `== 1`); at 0 each number IS the white-point luminance. The engine default is 0; the new-project wizard writes `True`, which the ini loader turns into 1. Measured: a volume at -2/-2 renders a white point of -2 with the option at 0, and 0.25 with it switched to 1 at runtime (test_lighting_report L104 and L111, test_set_exposure X110; 5.8.2 and 5.3.2)","source":"5.8 `Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp:339-344`, 5.8 `Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp:659-668`, 5.3 `Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp:177-182`, 5.8 `Runtime/Engine/Private/SceneView.cpp:200-207`, 5.8 `Editor/GameProjectGeneration/Private/GameProjectUtils.cpp:147-153`, 5.8 `Runtime/Core/Private/Misc/ConfigUtilities.cpp:129-147`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"on: `W = LuminanceMax x 2^v`; off: `W = v`"},{"id":"L18","fact":"With the luminance-range option on, the renderer's `LuminanceMax = 0.78 / max(q, 0.01)`, q = `r.EyeAdaptation.LensAttenuation` (default 0.78, so 1); with it off the maths is hardcoded to 1.0","source":"5.8 `Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp:376-389`, 5.8 `Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp:104-108`","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"`LuminanceMax = bExtended ? 0.78 / max(q, 0.01) : 1`"},{"id":"L20","fact":"`AEM_Manual` ignores MinBrightness and MaxBrightness entirely: the exposure is then the bias and, with `AutoExposureApplyPhysicalCameraExposure`, the camera. `set_exposure` refuses a write it would ignore (test_set_exposure X108)","source":"5.8 `Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp:652`, 5.3 `Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp:422`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"`MinWhitePointLuminance = MaxWhitePointLuminance = CalculateManualAutoExposure(View, false)`"},{"id":"L21","fact":"A view starts from `FPostProcessSettings` defaults (min/max -10/20 as EV100 with the option on, 0.03/8.0 as luminance with it off), then the project defaults (`r.DefaultFeature.AutoExposure` 0 locks the white point at 1; otherwise the method comes from `r.DefaultFeature.AutoExposure.Method`), then each volume in ascending priority: numbers **lerp** by blend weight (`LERP_PP`), the method and `AutoExposureApplyPhysicalCameraExposure` are **set** (`SET_PP`). Measured: four unbound volumes blended as this predicts (test_lighting_report L104, 5.8.2 and 5.3.2)","source":"5.8 `Runtime/Engine/Private/Scene.cpp:499-514`, 5.8 `Runtime/Engine/Private/SceneView.cpp:1622-1624`, 5.8 `Runtime/Engine/Private/SceneView.cpp:2048-2078`, 5.3 `Runtime/Engine/Private/SceneView.cpp:1440-1441`, 5.3 `Runtime/Engine/Private/SceneView.cpp:1823-1841`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"`Dest = Lerp(Dest, Src, Weight)` where overridden"},{"id":"L22","fact":"Two unbound post-process volumes at one priority are ordered by nothing an author sets: insertion (load) order on 5.3-5.6; from 5.7 the volume's bounding-box size, then an arbitrary per-volume GUID (an unbound volume counts as the largest). `lighting_report` reports a tie that sets the same exposure field (exposure_priority_tie)","source":"5.3 `Runtime/Engine/Private/World.cpp:9146-9167`, 5.7 `Runtime/Engine/Classes/Interfaces/Interface_PostProcessVolume.h:27-28`, 5.8 `Runtime/Engine/Classes/Interfaces/Interface_PostProcessVolume.h:23-28`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"-"},{"id":"L23","fact":"A property edit on a placed actor re-registers its components (`ReregisterComponentsWhenModified`), and a post-process volume that re-registers is removed from and re-added to the world's priority-sorted list - so a Priority change sent through the editor's own notification re-sorts it","source":"5.8 `Runtime/Engine/Private/ActorEditor.cpp:170-179`, 5.8 `Runtime/Engine/Private/ActorEditor.cpp:333-341`, 5.8 `Runtime/Engine/Private/PostProcessVolume.cpp:78-86`","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"-"},{"id":"L24","fact":"`SetLightBrightness` is the engine's inverse of `ComputeLightBrightness`, each light class undoing its own unit (log2 for EV, / 16 for Unitless, the cone for a Lumens spot) - EXCEPT with `bUseIESBrightness`, where it writes `IESTexture->Brightness`, a field of the shared IES profile ASSET that every light on the profile reads, not the light's `IESBrightnessScale`. Measured: `calibrate_lights` scales such a light through `IESBrightnessScale` and the stock `/Engine/EngineLightProfiles/180Degree_IES` Brightness stays unchanged (test_calibrate_lights C111, 5.8.2 and 5.3.2)","source":"5.3 `Runtime/Engine/Private/Components/LightComponent.cpp:508`, 5.8 `Runtime/Engine/Private/Components/LightComponent.cpp:592`, 5.8 `Runtime/Engine/Private/Components/SpotLightComponent.cpp:112`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"`IESTexture->Brightness = InBrightness / IESBrightnessScale / IESTexture->TextureMultiplier`"},{"id":"L25","fact":"Writing `IntensityUnits` through `set_property` does not convert `Intensity`: a point light at 8 Candelas written Unitless keeps 8 and reads 0.0128 cd in `lighting_report`. Write the units first, then the intensity","source":"live: `set_property` then `lighting_report` on the a scratch project probe, 2026-09-25","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"`0.0128 = 8 x 16 / 10000`"},{"id":"L26","fact":"A point light placed with `spawn_actor_in_level` arrives **Stationary** at 8 cd in the project's default units (`r.DefaultFeature.LightUnits`, Candelas); a PointLightComponent added to a Blueprint is **Movable** and keeps the component default, 5000 Unitless = 8 cd (test_lighting_report L101 and L103, 5.8.2 and 5.3.2)","source":"5.8 `Runtime/Engine/Private/Light.cpp:190`, 5.8 `Runtime/Engine/Private/Components/SceneComponent.cpp:124`","engine":"5.3, 5.8","ue":["5.3","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"light actor constructor sets Stationary; `USceneComponent` default Movable"},{"id":"L28","fact":"Engine defaults of the exposure and light-unit project settings: `r.DefaultFeature.AutoExposure` 1, `.Method` 0 (histogram), `.Bias` **1.0** (so an EV100 written without a bias renders one stop brighter than it says), `.ExtendDefaultLuminanceRange` 0, `r.DefaultFeature.LightUnits` 1 (Candelas)","source":"5.8 `Runtime/Engine/Private/SceneView.cpp:182-207`, 5.8 `Runtime/Engine/Private/SceneView.cpp:249-255`","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"-"},{"id":"L29","fact":"The only code in the engine that converts a new light to the project's light units (`r.DefaultFeature.LightUnits`, engine default 1 = Candelas) is the editor's actor factories: `UActorFactoryPointLight`, `UActorFactorySpotLight` and `UActorFactoryRectLight::PostSpawnActor` multiply `Intensity` by `GetUnitsConversionFactor(current units, default units)` and set `IntensityUnits`, for native components only (`CreationMethod == Native`). So a light placed through a factory goes from the component default 5000 Unitless to 8 cd (L16 and L26 measure the result), while a light built without a factory - a runtime `UWorld::SpawnActor`, a component added in a Blueprint or to a placed actor - keeps 5000 Unitless. The spot factory converts through its own cone (`GetCosHalfConeAngle()`), the rect factory passes 0.5. The details panel's reset-to-default of `IntensityUnits` also uses the cvar, for components whose archetype is not in a Blueprint. 5.3 and 5.4 leave `IntensityUnits` at its zero (Unitless) initial value, 5.5 and later set Unitless in the constructor.","source":"5.3 `Editor/UnrealEd/Private/Factories/ActorFactoryPointLight.cpp:12-28`, 5.3 `Editor/UnrealEd/Private/Factories/ActorFactorySpotLight.cpp:12-28`, 5.3 `Editor/UnrealEd/Private/Factories/ActorFactoryRectLight.cpp:11-28`, 5.3 `Runtime/Engine/Private/SceneView.cpp:223-225`, 5.3 `Runtime/Engine/Private/Components/LocalLightComponent.cpp:17`, 5.3 `Runtime/Engine/Private/Components/LocalLightComponent.cpp:149`, 5.3 `Runtime/Engine/Private/Components/LocalLightComponent.cpp:199`, 5.3 `Runtime/Engine/Private/Components/LocalLightComponent.cpp:204`, 5.4-5.6 factories `:12-28` (rect `:11-28`), 5.8 `Editor/UnrealEd/Private/Factories/ActorFactoryPointLight.cpp:14-30`, 5.8 `Editor/UnrealEd/Private/Factories/ActorFactorySpotLight.cpp:14-30`, 5.8 `Editor/UnrealEd/Private/Factories/ActorFactoryRectLight.cpp:13-30`, 5.8 `Runtime/Engine/Private/SceneView.cpp:249-255`, 5.8 `Runtime/Engine/Private/Components/LocalLightComponent.cpp:13-14`, 5.8 `Runtime/Engine/Private/Components/LocalLightComponent.cpp:146-226`, 5.8 `Editor/DetailCustomizations/Private/LightComponentDetails.cpp:337-371`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"Intensity x GetUnitsConversionFactor(Unitless, Candelas) = 5000 x 16 / 100 / 100 = 8 cd; no runtime path converts"},{"id":"L30","fact":"A point or spot light with `bUseInverseSquaredFalloff` off is forced to `IntensityUnits = Unitless` every time it loads, and again on every editor property change; `Intensity` is not converted, only relabeled, and the units field cannot be edited while the falloff is off. `USpotLightComponent` derives from `UPointLightComponent`, so the rule covers both.","source":"5.3 `Runtime/Engine/Private/Components/PointLightComponent.cpp:273`, 5.3 `Runtime/Engine/Private/Components/PointLightComponent.cpp:287-290`, 5.3 `Runtime/Engine/Private/Components/PointLightComponent.cpp:304`, 5.3 `Runtime/Engine/Private/Components/PointLightComponent.cpp:327-330`, 5.3 `Runtime/Engine/Classes/Components/SpotLightComponent.h:16`, 5.4 `Runtime/Engine/Private/Components/PointLightComponent.cpp:288-291`, 5.5 `Runtime/Engine/Private/Components/PointLightComponent.cpp:290-293`, 5.6 `Runtime/Engine/Private/Components/PointLightComponent.cpp:302-305`, 5.7 `Runtime/Engine/Private/Components/PointLightComponent.cpp:302-305`, 5.8 `Runtime/Engine/Private/Components/PointLightComponent.cpp:216`, 5.8 `Runtime/Engine/Private/Components/PointLightComponent.cpp:230-233`, 5.8 `Runtime/Engine/Private/Components/PointLightComponent.cpp:247-250`, 5.8 `Runtime/Engine/Private/Components/PointLightComponent.cpp:270-273`, 5.8 `Runtime/Engine/Classes/Components/SpotLightComponent.h:17`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"`if (!bUseInverseSquaredFalloff) IntensityUnits = Unitless` in Serialize (loading) and PostEditChangeProperty; Intensity untouched"},{"id":"L33","fact":"`USkyAtmosphereComponent`'s constructor builds `OtherAbsorption` as `OtherAbsorptionRaw * (1.0f / OtherAbsorptionRaw.G)` with G = 0.001881f, and the shipped editors hold **G = 0.99999994** (0x3F7FFFFF), not 1.0: the float32 product, not folded into a division. `FLinearColor`'s scalar `operator*` scales alpha too, and the raw color's alpha is the constructor's default 1.0, so the default's **A is 531.63208**, not 1. The Rayleigh default built the same way (B = 0.0331f) comes out B = 1.0 exactly, A = 30.211479. A text export rounds to six decimals and prints G=1.000000, so a differs-from-default test on these fields must compare floats with a tolerance, and an OtherAbsorption saved as (R, 1, B, 1) differs from the default in G and in A.","source":"5.3 `Runtime/Engine/Private/Components/SkyAtmosphereComponent.cpp:53-54`, 5.3 `Runtime/Engine/Private/Components/SkyAtmosphereComponent.cpp:66-68`, 5.3 `Runtime/Core/Public/Math/Color.h:73`, 5.3 `Runtime/Core/Public/Math/Color.h:193-201`, 5.8 `Runtime/Engine/Private/Components/SkyAtmosphereComponent.cpp:108-109`, 5.8 `Runtime/Engine/Private/Components/SkyAtmosphereComponent.cpp:121-123`, 5.8 `Runtime/Core/Public/Math/Color.h:130`, 5.8 `Runtime/Core/Public/Math/Color.h:261-269`; measured live 2026-09-25 with `get_property` (its typed value) on /Script/Engine.Default__SkyAtmosphereComponent, 5.8.2 (a scratch project, port 8871) and 5.3.2 (a scratch project, port 8872): the same four values on both","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it","value":"G = f32(0.001881 x f32(1 / 0.001881)) = 0.99999994; A = 1 x f32(1 / 0.001881) = 531.63208"},{"id":"L34","fact":"`FLinearColor::GetLuminance()` changed weights at 5.5. 5.3 and 5.4: `R x 0.3 + G x 0.59 + B x 0.11`, inline. 5.5 onward: the working color space's luminance factors (Rec. 709 by default: 0.2126, 0.7152, 0.0722), the old weights only with `r.LegacyLuminanceFactors` set. So one color's luminance differs by engine: the transmittance of L35 is the same RGB on both, and its luminance reads **0.80288 on 5.3.2 and 0.7993 on 5.8.2** - exactly the two weight sets. Anything scaled by a luminance moves with it: calibrate_lights' sun proposal by ~0.5% there, its sky proposal by ~2% (L36), and the response reports the weights it used.","source":"5.3 `Runtime/Core/Public/Math/Color.h:380-384`, 5.4 `Runtime/Core/Public/Math/Color.h:392-395`, 5.5 `Runtime/Core/Private/Math/Color.cpp:303`, 5.8 `Runtime/Core/Public/Math/Color.h:440`, 5.8 `Runtime/Core/Private/Math/Color.cpp:301-307`; measured by tools/test_calibrate_lights.py C117 and C123 on a scratch project (5.8.2, port 8871) and a scratch project (5.3.2, port 8872), 2026-09-27","engine":"5.3-5.8 (split at 5.5)","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it"},{"id":"L35","fact":"A directional light marked Atmosphere Sun Light - every one is by default - reaches the renderer as its Intensity times the atmosphere's transmittance toward it (per-pixel transmittance aside, which the shader applies instead), so its Intensity is the illuminance ABOVE the atmosphere. The engine's own ground-level value is `USkyAtmosphereComponent::GetAtmosphereTransmitanceOnGroundAtPlanetTop(light)` (ENGINE_API, BlueprintCallable, 5.3-5.8). For a default SkyAtmosphere and the sun 45 degrees up it is **(0.897, 0.78745, 0.62889)** on 5.3.2 and 5.8.2 alike, and lower at 10 degrees up (C118): a sun set to the lux a surface should receive lands at ~80% of it at 45 degrees, less the lower it stands. calibrate_lights divides by that luminance.","source":"5.3 `Runtime/Engine/Private/Components/DirectionalLightComponent.cpp:598-613`, `:1024`, 5.3 `Runtime/Engine/Classes/Components/SkyAtmosphereComponent.h:216`, 5.3 `Runtime/Engine/Private/Components/SkyAtmosphereComponent.cpp:390-399`, 5.8 `Runtime/Engine/Private/Components/DirectionalLightComponent.cpp:602-617`, `:1067`, 5.8 `Runtime/Engine/Classes/Components/SkyAtmosphereComponent.h:244`, 5.8 `Runtime/Engine/Private/Components/SkyAtmosphereComponent.cpp:511-520`; measured by tools/test_calibrate_lights.py C117 and C118 on a scratch project (5.8.2, port 8871) and a scratch project (5.3.2, port 8872), 2026-09-27","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it"},{"id":"L36","fact":"The stock `/Engine/MapTemplates/Sky/DaylightAmbientCubemap` as a specified-cubemap sky light at Intensity 1, `r.SkylightIntensityMultiplier` 1, gives an up-facing surface open to the sky an irradiance of **(0.45852, 0.68684, 0.86384)** - `Dot(the capture's SH, FSHVector3::CalcDiffuseTransfer(+Z))`, the Lambert convolution the renderer bakes into its sky constants - identical on 5.3.2 and 5.8.2; its luminance is 0.63782 on 5.3.2 and 0.65107 on 5.8.2 (L34). A matte surface of albedo a under it reads a x Intensity x that / pi.","source":"5.3 `Runtime/Core/Public/Math/SHMath.h:308`, 5.8 `Runtime/Core/Public/Math/SHMath.h:310`; measured by tools/test_calibrate_lights.py C123 on a scratch project (5.8.2, port 8871) and a scratch project (5.3.2, port 8872), 2026-09-27","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE lighting - exposure, and a light's brightness as the renderer takes it"},{"id":"M9a5c5c","fact":"USceneThumbnailInfo::OrbitZoom is a DISTANCE OFFSET in world units added to the computed camera distance, not a multiplier - measured on a StaticMesh, 0.25, 8.0 and -3.0 all produce a byte-identical baseline image while +/-1000 and +/-100000 each change it. The endpoint reports orbit:{zoom:2} faithfully because it really did write 2, and both halves being correct is what makes it read as a broken parameter.","source":"docs/02_GOTCHAS.md §13 § orbitZoom is a DISTANCE OFFSET (lines 1631-1647); tools/test_thumbnails.py T401 uses 1000 for this reason and says so","engine":"","ue":[],"bl":[],"section":"UE thumbnails","value":"`OutOrbitZoom = TargetDistance + OrbitZoom`"},{"id":"M277a5c","fact":"A RectLight's rectangle is spanned by its component's LOCAL Y and Z, with emission along local +X: `SourceWidth` lies along **local Y** and `SourceHeight` along **local Z**. This is what a Blender AREA lamp's `size` (local X) and `size_y` (local Y, emission along local -Z) have to be mapped onto, so the pair does NOT transfer straight across - the axes rotate as well as the units scaling by 100.","source":"Read in UE_5.3 at Engine/Shaders/Private/RectLight.ush:663-667, where Rect.Extent's x pairs with Axis[0] and y with Axis[1], together with Engine/Source/Runtime/Engine/Private/Components/RectLightComponent.cpp:238-244 which sets `Direction = -GetDirection()` (the component +X), `Tangent` = the third column of WorldToLight, `SourceRadius = SourceWidth*0.5` and `SourceLength = SourceHeight*0.5`. An earlier note filed the width axis as an INFERENCE by elimination; the shader's cross product settles it by reading. Re-runnable: both files ship with the engine.","engine":"5.3","ue":["5.3"],"bl":[],"section":"UE lighting / RectLight","value":"`Rect.Axis[1]=Tangent(=local Z); Rect.Axis[2]=Direction(=local X); Rect.Axis[0]=cross(Z,X)=Y; Rect.Extent=(SourceWidth/2, SourceHeight/2)`"},{"id":"M95bdc1","fact":"A local light's Unitless intensity converts to candela by x16/10000, so 150000 Unitless = 240 cd; the same 16 factor converted to Lumens is 16 * 2*PI*(1-cos(halfAngle))/10000, which for a 52-degree OUTER (half) cone gives ~580 lm at 150000.","source":"Engine/Source/Runtime/Engine/Private/Components/LocalLightComponent.cpp:149-220, ULocalLightComponent::GetUnitsConversionFactor (else-branch CnvFactor=16.f at :202; Candelas target *=1/100/100 at :108-of-block; Lumens target *= 2*PI*(1-CosHalfConeAngle)/100/100)","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE lighting","value":"`Unitless->Candela: CnvFactor = 16 * (1/100/100) = 0.0016 ; Unitless->Lumens: CnvFactor = 16 * 2*PI*(1-CosHalfConeAngle)/100/100`"},{"id":"Ma2d6de","fact":"A RectLight's Unitless intensity is scaled x16, Lumens x(100*100/PI = 3183), and the resulting colour is then DIVIDED by 0.5 * SourceWidth * SourceHeight - so a 120x9 cm batten at Unitless 10 emits an effective 0.296.","source":"Engine/Source/Runtime/Engine/Private/Components/RectLightComponent.cpp:108-130 (URectLightComponent::ComputeLightBrightness) and :231-232 (FRectLightSceneProxy::GetLightShaderParameters, `LightColor /= 0.5f * SourceWidth * SourceHeight;`)","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE lighting","value":"`ComputeLightBrightness: Unitless *=16, Lumens *=(100*100/UE_PI); then LightColor /= 0.5f * SourceWidth * SourceHeight (=540 for 120x9)`"},{"id":"M23f363","fact":"Colour temperature MULTIPLIES LightColor rather than replacing it, so bUseTemperature stacks on top of any existing tint - the fixture must be set to white in the same edit.","source":"Engine/Source/Runtime/Engine/Private/Components/LightComponent.cpp:1385-1396, ULightComponent::GetColoredLightBrightness","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE lighting","value":"`FLinearColor Energy = FLinearColor(LightColor) * LightBrightness; if (bUseTemperature) Energy *= GetColorTemperature();`"},{"id":"U80","fact":"**`import_asset` accepts no FBX build settings.** Its whole parameter surface is `file (filename, sourcePath), destination (destinationPath, path), name (destinationName), factory, replaceExisting (overwrite), replaceExistingSettings, save, importAnimations`. Generate lightmap UVs, minimum lightmap resolution, distance-field generation and Nanite are therefore all a POST-import `set_property` pass; there is no Nanite endpoint and `NaniteSettings` is reached the same way. **[Reviewed 2026-09-21 against the code - STALE in part: set_nanite_settings exists and maps field names per engine (MifBridgeCommon.cpp:182; MifBridgeImport.cpp:1242-1246); import_asset half holds; set_nanite_settings exists (MifBridgeImport.cpp:1241-1254; MifBridgeCollision.cpp:1224)]**","source":"read 2026-09-18 from `describe_endpoint {name:'import_asset'}` on 8860 (`acceptedSummary`), cross-checked against the MifBridge session's reading of the endpoint table and the note returned by `generate_lods` (\"nanite - already reachable, set_property on NaniteSettings\"). See U76 for the absence of a Nanite endpoint in the 458-endpoint registry.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE","status":"superseded"},{"id":"U90","fact":"**A capture of a dark level needs a PINNED MANUAL EXPOSURE, and it is reachable entirely through `set_property` on a spawned `PostProcessVolume`.** The working paths are `bUnbound` (bool), `Priority` (float), `Settings.bOverride_AutoExposureMethod`, `Settings.AutoExposureMethod` = `AEM_Manual`, `Settings.bOverride_AutoExposureBias` and `Settings.AutoExposureBias`. Without it, two captures of the same prop are not comparable: histogram auto-exposure adapts to whatever is in frame, so adding a light makes the picture brighter AND changes the adaptation, and the result is either black or pure white. There is no exposure endpoint - `self_audit` carries no `*exposure*` or `*postprocess*` name. SCOPE, added 2026-09-22: the exposure half of this row does NOT apply to `capture_camera` - its one-shot scene capture has no view state, so its exposure is fixed and an unbound manual volume at bias 0 and -2 gave the same pixels (VL22). It applies to a view that keeps eye adaptation, such as the editor viewport.","source":"measured 2026-09-18 on UE 5.8 against the live editor on 8860. Each path was read back with `get_property` on a freshly spawned volume BEFORE writing, which is how they were confirmed rather than guessed: `Settings.AutoExposureMethod` returned `AEM_Histogram`, `Settings.AutoExposureBias` returned 1, `Settings.AutoExposureMinBrightness` returned 0.0299999993, and all three `bOverride_*` returned False. After writing, `AutoExposureMethod` read back `AEM_Manual`. Re-runnable as `tools/inspect_prop.py`, which prints the read-back every run and warns if it did not take. NOTE `list_object_properties` with a `propertyPath` of `Settings` did NOT enumerate the struct's members - it returned nothing containing 'AutoExposure' - so the paths had to be confirmed by `get_property` one at a time.","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE materials, actor identity and PIE"},{"id":"U469","fact":"**A material's ambient occlusion reaches a movable sky light only when the project disallows static lighting: with r.AllowStaticLighting on, the base pass has no G-buffer room for AO and multiplies it into the BAKED indirect irradiance.** Measured through render_thumbnail {lighting:\"sky\"} on 5.8.2: AO 1 and AO 0.05 on a 0.5-grey sphere rendered identical pixels under the Asset Viewer HDRI sky light with static lighting allowed; with r.AllowStaticLighting=False the AO 0.05 sphere was 28% darker at the centre (5.8.2 and 5.3.2). The reply's sky.materialAOAffectsSky reads the cvar.","source":"5.3 `Shaders/Private/DeferredShadingCommon.ush:874` and 5.8 `Shaders/Private/DeferredShadingCommon.ush:1017` (the ALLOW_STATIC_LIGHTING branch: AO folded into IndirectIrradiance); tools/test_ue_spec_extensions_b.py SX702. Read and measured 2026-10-04","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE thumbnails, sky light and AO, scene capture exposure, property switches"},{"id":"U470","fact":"**The engine thumbnail scene lights with no sky: FPreviewScene's sky light is SLS_SpecifiedCubemap and no cubemap is set unless SetSkyCubemap is called, which FThumbnailPreviewScene does not do.** So a material thumbnail shows neither AO nor sky bounce (measured: AO 1 against 0.05 differs 0.3-6% at the centre under the engine thumbnail, 28% under a sky light, 5.3.2 and 5.8.2).","source":"5.3 `Runtime/Engine/Private/PreviewScene.cpp:84` and 5.8 `Runtime/Engine/Private/PreviewScene.cpp:92`; tools/test_ue_spec_extensions_b.py SX702. Read and measured 2026-10-04","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE thumbnails, sky light and AO, scene capture exposure, property switches"},{"id":"U471","fact":"**A USceneCaptureComponent2D without a view state renders at pre-exposure 1 whatever its post-process exposure says; bAlwaysPersistRenderingState gives it one, and then manual exposure compensation is 2^AutoExposureBias.** Measured on 5.8.2: bias 0 and bias 3 produced the same pixels without a view state; with one, bias 3 raised a sphere's centre luminance from 86 to 227.","source":"5.8 `Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp:1528` (PreExposure = 1, then the no-view-state branch) and 5.8 `Runtime/Renderer/Private/PostProcess/PostProcessEyeAdaptation.cpp:459` (2^AutoExposureBias). Read and measured 2026-10-04","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE thumbnails, sky light and AO, scene capture exposure, property switches"},{"id":"U472","fact":"**ULightComponent's Temperature has no EditCondition - it applies only while bUseTemperature is on and says so with DisplayAfter alone - while every FPostProcessSettings field carries `editcondition = \"bOverride_<Name>\"` (lower-case key; metadata keys are case-insensitive FNames).** So a property's switch is read from EditCondition first and, failing that, from a sibling bool named for it (add_sequence_track / set_sequence_keys / key_property_for_range propertySwitches).","source":"5.3 `Runtime/Engine/Classes/Components/LightComponent.h:49` and 5.8 `Runtime/Engine/Classes/Components/LightComponent.h:56`; 5.3 `Runtime/Engine/Classes/Engine/Scene.h:1431` and 5.8 `Runtime/Engine/Classes/Engine/Scene.h:1628`; tools/test_ue_spec_extensions_b.py SX1000-SX1002. Read and measured 2026-10-04","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE thumbnails, sky light and AO, scene capture exposure, property switches"},{"id":"U591","fact":"**`r.MotionBlurQuality=0` under [SystemSettings] in DefaultEngine.ini turns motion blur off even where a post-process volume or component overrides MotionBlurAmount (here an unbound weather component at 0.5); SystemSettings ini priority beats Scalability. Set live with the console command it reads back 0.**","source":"Live on UE 5.8.3, 2026-10-17: `execute_console_command(\"r.MotionBlurQuality 0\")` then `get_console_variable_int_value` = 0; the post-process component's override read in Python (override_motion_blur_amount True, 0.5). Priority order: `IConsoleManager` ECVF_SetBySystemSettingsIni above ECVF_SetByScalability (Engine/Source/Runtime/Core/Public/HAL/IConsoleManager.h lines 159-165: Scalability 0x02000000, SystemSettingsIni 0x05000000, higher wins).","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U656","fact":"**A `ULocalLightComponent` (point, spot, rect) created in C++ defaults to `IntensityUnits = ELightUnits::Unitless` and `Intensity = 5000`; the project's default light units apply only to lights placed in the editor, so C++ code must call `SetIntensityUnits` before `SetIntensity` when it means candelas.**","source":"5.8: Runtime/Engine/Private/Components/LocalLightComponent.cpp:13-14 (constructor)","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"}]