3d-lighting · git:20261010.4ade947 · 2026-10-10 · sha256 01daffc68ff0234f
3d-lighting git:20261010.4ade947A
Immutable. This exact content never changes and is served at /api/v1/blob/01daffc68ff0234f.
---
name: 3d-lighting
description: "Set up 3D scene lighting — DirectionalLight3D vs Omni vs Spot, WorldEnvironment, sky, shadow tuning, ambient — per current Summer conventions."
license: MIT
compatibility: [Cursor, Claude Code, Windsurf, Codex]
category: rendering-and-lighting
user-invocable: false
allowed-tools: Read Grep summer_add_node summer_set_prop summer_set_resource_property summer_save_scene summer_run_script summer_screenshot
paths: ["**/*.tscn", "**/*.tres"]
---
# 3D Lighting for Summer Engine
Outdoor and indoor lighting setups for 3D scenes.
## Light Types
| Type | Use case |
|------|----------|
| DirectionalLight3D | Sun, moon, outdoor primary light |
| OmniLight3D | Point lights (lamps, torches) |
| SpotLight3D | Flashlights, stage lights |
## Basic Outdoor Setup
### 1. Directional Light (Sun)
Every tool below also takes a **required** `scenePath` (the exact `res://` scene
to mutate — get it from `summer_get_project_context`). It is elided in these
snippets for readability; a real call without it is rejected by the tool schema.
```
summer_add_node(scenePath="res://main.tscn", parent="./World", type="DirectionalLight3D", name="Sun")
summer_set_prop(scenePath="res://main.tscn", path="./World/Sun", key="rotation_degrees", value="Vector3(-45, 30, 0)")
summer_set_prop(path="./World/Sun", key="light_energy", value="1.0")
summer_set_prop(path="./World/Sun", key="shadow_enabled", value="true")
summer_set_prop(path="./World/Sun", key="light_color", value="Color(1, 0.95, 0.9, 1)")
```
Warm sunlight: `Color(1, 0.95, 0.9, 1)`. Cool moonlight: `Color(0.8, 0.85, 1, 1)`.
### 2. WorldEnvironment (Sky + Ambient)
```
summer_add_node(parent="./World", type="WorldEnvironment", name="WorldEnvironment")
summer_set_prop(path="./World/WorldEnvironment", key="environment", value="Environment")
```
Then configure the Environment's sub-properties. The Environment resource has:
- `background_mode` — 2 for sky, 1 for color, 3 for canvas (`Environment.BG_SKY` = 2, `BG_COLOR` = 1, `BG_CANVAS` = 3; default is 0, `BG_CLEAR_COLOR`)
- `sky` — Sky resource (ProceduralSkyMaterial or PhysicalSkyMaterial)
- `ambient_light_source` — AMBIENT_SOURCE_SKY or AMBIENT_SOURCE_COLOR
- `ambient_light_color`
- `tonemap_mode`
For a simple procedural sky:
```
summer_set_resource_property(nodePath="./World/WorldEnvironment", resourceProperty="environment", subProperty="background_mode", value="2")
```
A sky is a `Sky` resource in `environment.sky`, with a `ProceduralSkyMaterial` (or `PhysicalSkyMaterial`) in its `sky_material`. `summer_set_resource_property` only sets properties on a resource that already exists, so create the `Sky` and its material in one `summer_run_script` call.
**Simpler path:** start from the `3d-basic` template, which already has a sky, and copy its structure.
### 3. Fill Light (Optional)
A second, weaker directional or omni for shadow fill:
```
summer_add_node(parent="./World", type="DirectionalLight3D", name="FillLight")
summer_set_prop(path="./World/FillLight", key="rotation_degrees", value="Vector3(-30, -120, 0)")
summer_set_prop(path="./World/FillLight", key="light_energy", value="0.3")
summer_set_prop(path="./World/FillLight", key="shadow_enabled", value="false")
summer_set_prop(path="./World/FillLight", key="light_color", value="Color(0.7, 0.8, 1, 1)")
```
## Indoor / Point Lights
```
summer_add_node(parent="./World", type="OmniLight3D", name="Lamp")
summer_set_prop(path="./World/Lamp", key="position", value="Vector3(2, 3, 2)")
summer_set_prop(path="./World/Lamp", key="light_energy", value="0.8")
summer_set_prop(path="./World/Lamp", key="light_color", value="Color(1, 0.9, 0.7, 1)")
summer_set_prop(path="./World/Lamp", key="omni_range", value="8")
```
## Property Reference
| Property | Type | Example |
|----------|------|---------|
| light_energy | float | `1.0`, `1.5` |
| light_color | Color | `"Color(1, 0.95, 0.9, 1)"` |
| shadow_enabled | bool | `true` |
| rotation_degrees | Vector3 | `"Vector3(-45, 30, 0)"` |
| position | Vector3 | `"Vector3(0, 10, 0)"` |
| omni_range | float | `10` (OmniLight3D) |
| spot_range | float | `10` (SpotLight3D) |
| spot_angle | float | `45` (degrees, SpotLight3D) |
## Color Format
Always use `Color(r, g, b, a)` with values 0.0–1.0:
- Warm white: `Color(1, 0.95, 0.9, 1)`
- Cool white: `Color(0.9, 0.95, 1, 1)`
- Orange (torch): `Color(1, 0.6, 0.2, 1)`
- Green (alien): `Color(0.5, 1, 0.5, 1)`
## Baked lighting — what you can and cannot do from here
Real-time lighting (everything above) is fully scriptable. Baking mostly is too on
this build — the split that matters is *which process* runs the bake, not whether a
script can trigger it.
| Approach | Callable from MCP / a script? |
|---|---|
| DirectionalLight3D / Omni / Spot with `shadow_enabled` | Yes |
| `WorldEnvironment` + `Environment` (sky, ambient, tonemap, glow, SSAO, SSR) | Yes |
| `SDFGI` real-time GI | Yes — `Environment.sdfgi_enabled = true`, no bake step at all |
| `VoxelGI` | Yes — `VoxelGI.bake()` **is** bound to script (`ClassDB.class_has_method("VoxelGI", "bake")` is true) |
| **`LightmapGI` bake** | **Yes, from the live editor.** Summer binds `LightmapGI.bake(from_node, image_data_path)` to script (upstream leaves it button-only). Call it from `summer_run_script`. The GPU lightmapper needs a real renderer (a RenderingDevice): a normal desktop editor has one; a cloud container needs xvfb + GL (`running-in-the-cloud`); a **pure-headless** process has none and the call returns `BAKE_ERROR_NO_LIGHTMAPPER`. |
| **`OccluderInstance3D` occlusion bake** | **No.** `bake_single_node` is not script-callable. Editor-button only. |
So when the user asks for baked lighting: add and configure the `LightmapGI` node,
set the meshes' `gi_mode`, then run the bake yourself from `summer_run_script` —
`lgi.bake(lgi, "res://lightmaps/scene_lm.exr")` — and check the returned bake-error
enum (0 is OK; `BAKE_ERROR_NO_LIGHTMAPPER` means no renderer in this process, not a
missing binding). Verify with a `framing:"camera"` screenshot, which renders the
real environment. Only fall back to "press Bake Lightmaps in the editor" when
no renderer is available. If they want GI with zero bake step,
steer to `SDFGI` (`Environment.sdfgi_enabled`) or `VoxelGI`.
Note the related trap: `Engine.has_singleton("EditorInterface")` returns **true**
even in a headless non-editor process, so it is not a valid capability probe.
`ClassDB.can_instantiate("EditorScript")` returns false outside the editor and is
the reliable check.
## Fallback (no MCP — edit `.tscn` directly)
If Summer MCP isn't connected, append the lighting block to your scene file:
```
[node name="Sun" type="DirectionalLight3D" parent="World"]
transform = Transform3D(0.866, 0, -0.5, 0.354, 0.707, 0.612, 0.354, -0.707, 0.612, 0, 0, 0)
light_color = Color(1, 0.95, 0.9, 1)
light_energy = 1.0
shadow_enabled = true
[node name="WorldEnvironment" type="WorldEnvironment" parent="World"]
```
Then create `Environment` and `Sky` resources via the inspector or load from `.tres`.
## Collaborative protocol
This skill mutates the scene (adds lights + WorldEnvironment + tunes Environment sub-properties). Always ask before applying: "May I add a DirectionalLight3D Sun with shadows + a WorldEnvironment with a procedural sky?".