Manual fill lighting without GI
Create colored fill, limit its influence and verify shadows across an opaque divider.
When it fits
Manual fill can improve dark-side readability or add an artistic tint. The sample blue OmniLight3D approximates fill lighting. The developer chooses its color; it is not automatically derived from a neighboring wall.
It remains an ordinary direct light with range, layers, attenuation and shadows. Moving walls or editing materials does not automatically make its settings physically correct.
Run the complete example
Complete C# setup. Use SDK 1.0.0, net10.0 and an Entity3D-rooted .rscene level. Save both files below, run dotnet build and attach FillLightLab3D.cs to the root. Code creates the camera, cube, floor, divider and lights.
FillMaterials.cs
using Reckfell;
public static class FillMaterials
{
public static StandardMaterial3D Surface(Color color) => new() { AlbedoColor = color, Roughness = 0.7f };
public static Reckfell.Environment Dark() => new()
{
BackgroundMode = Reckfell.Environment.BGMode.Color,
BackgroundColor = Colors.Black,
AmbientLightSource = Reckfell.Environment.AmbientSource.Color,
AmbientLightColor = Colors.Black, AmbientLightEnergy = 1, AmbientLightSkyContribution = 0,
ReflectedLightSource = Reckfell.Environment.ReflectionSource.Bg
};
}FillLightLab3D.cs
using Reckfell;
public partial class FillLightLab3D : Entity3D
{
public OmniLight3D Fill { get; private set; } = null!;
public override void _Ready()
{
GetViewport().PositionalShadowAtlasSize = 1024;
AddChild(new WorldEnvironment { Environment = FillMaterials.Dark() });
AddChild(new MeshInstance3D
{
Mesh = new PlaneMesh { Size = new Vector2(8, 8) },
MaterialOverride = FillMaterials.Surface(Colors.White)
});
AddChild(new MeshInstance3D
{
Mesh = new BoxMesh { Size = new Vector3(1.4f, 2, 1.4f) }, Position = new Vector3(0, 1, 0),
MaterialOverride = FillMaterials.Surface(Colors.White)
});
AddChild(new MeshInstance3D
{
Mesh = new BoxMesh { Size = new Vector3(0.15f, 3, 4) }, Position = new Vector3(1.2f, 1.5f, 0),
MaterialOverride = FillMaterials.Surface(Colors.White)
});
AddChild(new DirectionalLight3D { RotationDegrees = new Vector3(-45, -20, 0), LightEnergy = 0.5f, ShadowEnabled = true });
Fill = new OmniLight3D
{
Position = new Vector3(2, 1.5f, 0), LightColor = new Color(0.08f, 0.25f, 1),
LightEnergy = 1, OmniRange = 6, ShadowEnabled = false
};
AddChild(Fill);
var camera = new Camera3D { Current = true, Position = new Vector3(-4, 3, 6), Fov = 55 };
AddChild(camera);camera.LookAt(new Vector3(0, 0.8f, 0));
}
}Fill is available after _Ready. First compare Fill.Visible=false and true with unchanged camera/exposure. Change it after the source is created, not before _Ready.
Actual 512×384 frames from the current local 1.0.0 Compatibility/OpenGL runtime. Colored-pixel changes are separately verified in Forward+/Vulkan. This demonstrates direct fill, not GI calculation or performance.
Limit influence
- Position the source on the intended fill side; more energy does not repair incorrect placement.
- Limit OmniRange to the intended area. Range 0.1 removes the tested distant color contribution.
- Choose LightEnergy/LightColor after base illumination. Strong blue makes this comparison clear.
- LightCullMask matches visual Layers; physics CollisionLayer/CollisionMask do not restrict light.
Coordinated visual layers can target particular gameplay objects. Masks are manual light influence controls, not reconstruction of physical bounce paths.
Check wall leakage
Fill sits to the right of the opaque divider. With ShadowEnabled=false, it also lights some surfaces on the left: a visible wall does not automatically block this light.
Enable Fill.ShadowEnabled=true. The sample configures its viewport PositionalShadowAtlasSize=1024 first. This is a shared positional-shadow atlas, not a substitute for the light’s shadow flag. Geometry also needs suitable CastShadow.
Both checked renderers reduce blue contribution behind opaque geometry when shadows are enabled. This does not establish leak-free behavior everywhere: inspect thin surfaces, bias, scale and coverage. Dynamic shadows may cost more than a soft artistic fill needs; decide from output and project profiling.
Diagnosis
| Problem | Check |
|---|---|
| No color contribution | Fill.Visible, distance/range, LightCullMask and material |
| Fill passes through a wall | ShadowEnabled, viewport atlas, CastShadow and geometry |
| Lighting stops fitting after level edits | Revisit manual source placement and coverage |
| Flat or overexposed image | Lower energy and distinguish fill from the key light |
| Automatic colored bounce is expected | A selected GI method and data are needed, not a manual Omni |