godot-3d-lighting

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

3D Lighting

3D灯光

Expert guidance for realistic 3D lighting with shadows and global illumination.
关于带阴影和全局光照的写实3D灯光的专家指导。

NEVER Do

绝对不要做

  • NEVER use VoxelGI without setting a proper extents — Unbound VoxelGI tanks performance. Always set
    size
    to tightly fit your scene.
  • NEVER enable shadows on every light — Each shadow-casting light is expensive. Use shadows sparingly: 1-2 DirectionalLights, ~3-5 OmniLights max.
  • NEVER forget directional_shadow_mode — Default is ORTHOGONAL. For large outdoor scenes, use PARALLEL_4_SPLITS for better shadow quality at distance.
  • NEVER use LightmapGI for fully dynamic scenes — Lightmaps are baked. Moving geometry won't receive updated lighting. Use VoxelGI or SDFGI instead.
  • NEVER set omni_range too large — Light attenuation is quadratic. A range of 500 affects 785,000 sq units. Keep range as small as visually acceptable.

  • 绝对不要在未设置合适范围的情况下使用VoxelGI —— 无边界的VoxelGI会严重拖垮性能。务必将
    size
    设置为紧密贴合你的场景。
  • 绝对不要给每盏灯都启用阴影 —— 每盏投射阴影的灯光都会消耗大量性能。谨慎使用阴影:最多1-2盏DirectionalLight,3-5盏OmniLight。
  • 绝对不要忘记设置directional_shadow_mode —— 默认值是ORTHOGONAL。对于大型户外场景,使用PARALLEL_4_SPLITS可提升远距离阴影质量。
  • 绝对不要在全动态场景中使用LightmapGI —— 光照贴图是烘焙好的。移动的几何体无法获得更新后的光照。请改用VoxelGI或SDFGI。
  • 绝对不要将omni_range设置得过大 —— 灯光衰减是二次方的。范围设为500会影响785000平方单位。请将范围保持在视觉可接受的最小程度。

Available Scripts

可用脚本

MANDATORY: Read the appropriate script before implementing the corresponding pattern.
强制要求:在实现对应模式前,请阅读相应的脚本。

day_night_cycle.gd

day_night_cycle.gd

Dynamic sun position and color based on time-of-day. Handles DirectionalLight3D rotation, color temperature, and intensity curves. Use for outdoor day/night systems.
基于时间的动态太阳位置与颜色。处理DirectionalLight3D的旋转、色温以及强度曲线。适用于户外昼夜系统。

light_probe_manager.gd

light_probe_manager.gd

VoxelGI and SDFGI management for global illumination setup.
用于全局光照设置的VoxelGI与SDFGI管理工具。

lighting_manager.gd

lighting_manager.gd

Dynamic light pooling and LOD. Manages light culling and shadow toggling based on camera distance. Use for performance optimization with many lights.
动态灯光池与LOD(细节层次)。基于相机距离管理灯光剔除与阴影开关。适用于多灯光场景的性能优化。

volumetric_fx.gd

volumetric_fx.gd

Volumetric fog and god ray configuration. Runtime fog density/color adjustments and light shaft setup. Use for atmospheric effects.

体积雾与上帝光线配置。运行时雾密度/颜色调整以及光轴设置。适用于大气效果。

DirectionalLight3D (Sun/Moon)

DirectionalLight3D(太阳/月亮)

Shadow Cascades

阴影级联

gdscript
undefined
gdscript
undefined

For outdoor scenes with camera moving from near to far

适用于相机在近远区间移动的户外场景

extends DirectionalLight3D
func _ready() -> void: shadow_enabled = true directional_shadow_mode = SHADOW_PARALLEL_4_SPLITS
# Split distances (in meters from camera)
directional_shadow_split_1 = 10.0   # First cascade: 0-10m
directional_shadow_split_2 = 50.0   # Second: 10-50m
directional_shadow_split_3 = 200.0  # Third: 50-200m
# Fourth cascade: 200m - max shadow distance

directional_shadow_max_distance = 500.0

# Quality vs performance
directional_shadow_blend_splits = true  # Smooth transitions
undefined
extends DirectionalLight3D
func _ready() -> void: shadow_enabled = true directional_shadow_mode = SHADOW_PARALLEL_4_SPLITS
# 分割距离(以相机为基准的米数)
directional_shadow_split_1 = 10.0   # 第一级级联:0-10米
directional_shadow_split_2 = 50.0   # 第二级:10-50米
directional_shadow_split_3 = 200.0  # 第三级:50-200米
# 第四级级联:200米 - 最大阴影距离

directional_shadow_max_distance = 500.0

# 质量与性能平衡
directional_shadow_blend_splits = true  # 平滑过渡
undefined

Day/Night Cycle

昼夜循环

gdscript
undefined
gdscript
undefined

sun_controller.gd

sun_controller.gd

extends DirectionalLight3D
@export var time_of_day := 12.0 # 0-24 hours @export var rotation_speed := 0.1 # Hours per second
func _process(delta: float) -> void: time_of_day += rotation_speed * delta if time_of_day >= 24.0: time_of_day -= 24.0
# Rotate sun (0° = noon, 180° = midnight)
var angle := (time_of_day - 12.0) * 15.0  # 15° per hour
rotation_degrees.x = -angle

# Adjust intensity
if time_of_day < 6.0 or time_of_day > 18.0:
    light_energy = 0.0  # Night
elif time_of_day < 7.0:
    light_energy = remap(time_of_day, 6.0, 7.0, 0.0, 1.0)  # Sunrise
elif time_of_day > 17.0:
    light_energy = remap(time_of_day, 17.0, 18.0, 1.0, 0.0)  # Sunset
else:
    light_energy = 1.0  # Day

# Color shift
if time_of_day < 8.0 or time_of_day > 16.0:
    light_color = Color(1.0, 0.7, 0.4)  # Orange (dawn/dusk)
else:
    light_color = Color(1.0, 1.0, 0.9)  # Neutral white

---
extends DirectionalLight3D
@export var time_of_day := 12.0 # 0-24小时 @export var rotation_speed := 0.1 # 每秒小时数
func _process(delta: float) -> void: time_of_day += rotation_speed * delta if time_of_day >= 24.0: time_of_day -= 24.0
# 旋转太阳(0°=正午,180°=午夜)
var angle := (time_of_day - 12.0) * 15.0  # 每小时15°
rotation_degrees.x = -angle

# 调整强度
if time_of_day < 6.0 or time_of_day > 18.0:
    light_energy = 0.0  # 夜晚
elif time_of_day < 7.0:
    light_energy = remap(time_of_day, 6.0, 7.0, 0.0, 1.0)  # 日出
elif time_of_day > 17.0:
    light_energy = remap(time_of_day, 17.0, 18.0, 1.0, 0.0)  # 日落
else:
    light_energy = 1.0  # 白天

# 颜色偏移
if time_of_day < 8.0 or time_of_day > 16.0:
    light_color = Color(1.0, 0.7, 0.4)  # 橙色(黎明/黄昏)
else:
    light_color = Color(1.0, 1.0, 0.9)  # 中性白色

---

OmniLight3D (Point Light)

OmniLight3D(点光源)

Attenuation Tuning

衰减调优

gdscript
undefined
gdscript
undefined

torch.gd

torch.gd

extends OmniLight3D
func _ready() -> void: omni_range = 10.0 # Maximum reach omni_attenuation = 2.0 # Falloff curve (1.0 = linear, 2.0 = quadratic/realistic)
# For "magical" lights, reduce attenuation
omni_attenuation = 0.5  # Flatter falloff, reaches farther
undefined
extends OmniLight3D
func _ready() -> void: omni_range = 10.0 # 最大照射范围 omni_attenuation = 2.0 # 衰减曲线(1.0=线性,2.0=二次方/写实)
# 对于“魔法”灯光,降低衰减值
omni_attenuation = 0.5  # 更平缓的衰减,照射更远
undefined

Flickering Effect

闪烁效果

gdscript
undefined
gdscript
undefined

campfire.gd

campfire.gd

extends OmniLight3D
@export var base_energy := 1.0 @export var flicker_strength := 0.3 @export var flicker_speed := 5.0
func _process(delta: float) -> void: var flicker := sin(Time.get_ticks_msec() * 0.001 * flicker_speed) * flicker_strength light_energy = base_energy + flicker

---
extends OmniLight3D
@export var base_energy := 1.0 @export var flicker_strength := 0.3 @export var flicker_speed := 5.0
func _process(delta: float) -> void: var flicker := sin(Time.get_ticks_msec() * 0.001 * flicker_speed) * flicker_strength light_energy = base_energy + flicker

---

SpotLight3D (Flashlight/Headlights)

SpotLight3D(手电筒/车头灯)

Setup

设置

gdscript
undefined
gdscript
undefined

flashlight.gd

flashlight.gd

extends SpotLight3D
func _ready() -> void: spot_range = 20.0 spot_angle = 45.0 # Cone angle (degrees) spot_angle_attenuation = 2.0 # Edge softness
shadow_enabled = true

# Projector texture (optional - cookie/gobo)
light_projector = load("res://textures/flashlight_mask.png")
undefined
extends SpotLight3D
func _ready() -> void: spot_range = 20.0 spot_angle = 45.0 # 圆锥角度(度) spot_angle_attenuation = 2.0 # 边缘柔化
shadow_enabled = true

# 投影纹理(可选 - 遮罩图案)
light_projector = load("res://textures/flashlight_mask.png")
undefined

Follow Camera

跟随相机

gdscript
undefined
gdscript
undefined

player_flashlight.gd

player_flashlight.gd

extends SpotLight3D
@onready var camera: Camera3D = get_viewport().get_camera_3d()
func _process(delta: float) -> void: if camera: global_transform = camera.global_transform

---
extends SpotLight3D
@onready var camera: Camera3D = get_viewport().get_camera_3d()
func _process(delta: float) -> void: if camera: global_transform = camera.global_transform

---

Global Illumination: VoxelGI vs SDFGI

全局光照:VoxelGI与SDFGI对比

Decision Matrix

决策矩阵

FeatureVoxelGISDFGI
SetupManual bounds per roomAutomatic, scene-wide
Dynamic objectsFully supportedPartially supported
PerformanceModerateHigher cost
Use caseIndoor, small-medium scenesLarge outdoor scenes
Godot version4.0+4.0+
特性VoxelGISDFGI
设置每个房间手动设置边界自动,全场景
动态物体完全支持部分支持
性能中等较高消耗
使用场景室内、中小型场景大型户外场景
Godot版本4.0+4.0+

VoxelGI Setup

VoxelGI设置

gdscript
undefined
gdscript
undefined

room_gi.gd - Place one VoxelGI per room/area

room_gi.gd - 每个房间/区域放置一个VoxelGI节点

extends VoxelGI
func _ready() -> void: # Tightly fit the room size = Vector3(20, 10, 20)
# Quality settings
subdiv = VoxelGI.SUBDIV_128  # Higher = better quality, slower

# Bake GI data
bake()
undefined
extends VoxelGI
func _ready() -> void: # 紧密贴合房间 size = Vector3(20, 10, 20)
# 质量设置
subdiv = VoxelGI.SUBDIV_128  # 数值越高质量越好,速度越慢

# 烘焙GI数据
bake()
undefined

SDFGI Setup

SDFGI设置

gdscript
undefined
gdscript
undefined

world_environment.gd

world_environment.gd

extends WorldEnvironment
func _ready() -> void: var env := environment
# Enable SDFGI
env.sdfgi_enabled = true
env.sdfgi_use_occlusion = true
env.sdfgi_read_sky_light = true

# Cascades (auto-scale based on camera)
env.sdfgi_min_cell_size = 0.2  # Detail level
env.sdfgi_max_distance = 200.0

---
extends WorldEnvironment
func _ready() -> void: var env := environment
# 启用SDFGI
env.sdfgi_enabled = true
env.sdfgi_use_occlusion = true
env.sdfgi_read_sky_light = true

# 级联(基于相机自动缩放)
env.sdfgi_min_cell_size = 0.2  # 细节级别
env.sdfgi_max_distance = 200.0

---

LightmapGI (Baked Static Lighting)

LightmapGI(烘焙静态光照)

When to Use

适用场景

  • Static architecture (buildings, dungeons)
  • Mobile/low-end targets
  • No dynamic geometry
  • 静态建筑(房屋、地牢)
  • 移动端/低端设备目标平台
  • 无动态几何体

Setup

设置

gdscript
undefined
gdscript
undefined

Scene structure:

场景结构:

- LightmapGI node

- LightmapGI节点

- StaticBody3D meshes with GeometryInstance3D.gi_mode = STATIC

- StaticBody3D网格,且GeometryInstance3D.gi_mode = STATIC

lightmap_baker.gd

lightmap_baker.gd

extends LightmapGI
func _ready() -> void: # Quality settings quality = LightmapGI.BAKE_QUALITY_HIGH bounces = 3 # Indirect light bounces
# Bake (editor only, not runtime)
# Click "Bake Lightmaps" button in editor

---
extends LightmapGI
func _ready() -> void: # 质量设置 quality = LightmapGI.BAKE_QUALITY_HIGH bounces = 3 # 间接光反弹次数
# 烘焙(仅编辑器中可用,运行时不可用)
# 点击编辑器中的“烘焙光照贴图”按钮

---

Environment & Sky

环境与天空

HDR Skybox

HDR天空盒

gdscript
undefined
gdscript
undefined

world_env.gd

world_env.gd

extends WorldEnvironment
func _ready() -> void: var env := environment
env.background_mode = Environment.BG_SKY
var sky := Sky.new()
var sky_material := PanoramaSkyMaterial.new()
sky_material.panorama = load("res://hdri/sky.hdr")
sky.sky_material = sky_material
env.sky = sky

# Sky contribution to GI
env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
env.ambient_light_sky_contribution = 1.0
undefined
extends WorldEnvironment
func _ready() -> void: var env := environment
env.background_mode = Environment.BG_SKY
var sky := Sky.new()
var sky_material := PanoramaSkyMaterial.new()
sky_material.panorama = load("res://hdri/sky.hdr")
sky.sky_material = sky_material
env.sky = sky

# 天空对GI的贡献
env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
env.ambient_light_sky_contribution = 1.0
undefined

Volumetric Fog

体积雾

gdscript
extends WorldEnvironment

func _ready() -> void:
    var env := environment
    
    env.volumetric_fog_enabled = true
    env.volumetric_fog_density = 0.01
    env.volumetric_fog_albedo = Color(0.9, 0.9, 1.0)  # Blueish
    env.volumetric_fog_emission = Color.BLACK

gdscript
extends WorldEnvironment

func _ready() -> void:
    var env := environment
    
    env.volumetric_fog_enabled = true
    env.volumetric_fog_density = 0.01
    env.volumetric_fog_albedo = Color(0.9, 0.9, 1.0)  # 偏蓝
    env.volumetric_fog_emission = Color.BLACK

ReflectionProbe

ReflectionProbe(反射探针)

For localized reflections (mirrors, shiny floors):
gdscript
undefined
用于局部反射(镜子、光滑地面):
gdscript
undefined

reflection_probe.gd

reflection_probe.gd

extends ReflectionProbe
func _ready() -> void: # Capture area size = Vector3(10, 5, 10)
# Quality
resolution = ReflectionProbe.RESOLUTION_512

# Update mode
update_mode = ReflectionProbe.UPDATE_ONCE  # Bake once
# or UPDATE_ALWAYS for dynamic reflections (expensive)

---
extends ReflectionProbe
func _ready() -> void: # 捕获区域 size = Vector3(10, 5, 10)
# 质量
resolution = ReflectionProbe.RESOLUTION_512

# 更新模式
update_mode = ReflectionProbe.UPDATE_ONCE  # 烘焙一次
# 或使用UPDATE_ALWAYS实现动态反射(性能消耗大)

---

Performance Optimization

性能优化

Light Budgets

灯光预算

gdscript
undefined
gdscript
undefined

Recommended limits:

推荐限制:

- DirectionalLight3D with shadows: 1-2

- 带阴影的DirectionalLight3D:1-2盏

- OmniLight3D with shadows: 3-5

- 带阴影的OmniLight3D:3-5盏

- SpotLight3D with shadows: 2-4

- 带阴影的SpotLight3D:2-4盏

- OmniLight3D without shadows: 20-30

- 无阴影的OmniLight3D:20-30盏

- SpotLight3D without shadows: 15-20

- 无阴影的SpotLight3D:15-20盏

Disable shadows on minor lights

禁用次要灯光的阴影

@onready var candle_lights: Array = [$Candle1, $Candle2, $Candle3]
func _ready() -> void: for light in candle_lights: light.shadow_enabled = false # Save performance
undefined
@onready var candle_lights: Array = [$Candle1, $Candle2, $Candle3]
func _ready() -> void: for light in candle_lights: light.shadow_enabled = false # 节省性能
undefined

Per-Light Shadow Distance

单灯光阴影距离

gdscript
undefined
gdscript
undefined

Disable shadows for distant lights

禁用远距离灯光的阴影

extends OmniLight3D
@export var shadow_max_distance := 50.0
func _process(delta: float) -> void: var camera := get_viewport().get_camera_3d() if camera: var dist := global_position.distance_to(camera.global_position) shadow_enabled = (dist < shadow_max_distance)

---
extends OmniLight3D
@export var shadow_max_distance := 50.0
func _process(delta: float) -> void: var camera := get_viewport().get_camera_3d() if camera: var dist := global_position.distance_to(camera.global_position) shadow_enabled = (dist < shadow_max_distance)

---

Edge Cases

边缘情况

Shadows Through Floors

阴影穿透地板

gdscript
undefined
gdscript
undefined

Problem: Thin floors let shadows through

问题:薄地板导致阴影穿透

Solution: Increase shadow bias

解决方案:增加阴影偏移

extends DirectionalLight3D
func _ready() -> void: shadow_enabled = true shadow_bias = 0.1 # Increase if shadows bleed through shadow_normal_bias = 2.0
undefined
extends DirectionalLight3D
func _ready() -> void: shadow_enabled = true shadow_bias = 0.1 # 若阴影穿透则增大该值 shadow_normal_bias = 2.0
undefined

Light Leaking in Indoor Scenes

室内场景灯光泄漏

gdscript
undefined
gdscript
undefined

Problem: VoxelGI light bleeds through walls

问题:VoxelGI灯光穿墙泄漏

Solution: Place VoxelGI nodes per-room, don't overlap

解决方案:每个房间放置独立的VoxelGI节点,避免重叠

Also: Ensure walls have proper thickness (not paper-thin)

另外:确保墙壁有足够厚度(不要像纸一样薄)

undefined
undefined

Reference

参考资料

  • Master Skill: godot-master
  • 核心技能:godot-master