lighting-environment

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Lighting and Environment in Decentraland

Decentraland 中的光照与环境

Point Lights

Point Light

Emit light in all directions from a position:
typescript
import { engine, Transform, LightSource } from '@dcl/sdk/ecs'
import { Vector3, Color3 } from '@dcl/sdk/math'

const light = engine.addEntity()
Transform.create(light, { position: Vector3.create(8, 3, 8) })

LightSource.create(light, {
  type: LightSource.Type.Point({}),
  color: Color3.White(),
  intensity: 16000  // candela
})
从某个位置向所有方向发射光线:
typescript
import { engine, Transform, LightSource } from '@dcl/sdk/ecs'
import { Vector3, Color3 } from '@dcl/sdk/math'

const light = engine.addEntity()
Transform.create(light, { position: Vector3.create(8, 3, 8) })

LightSource.create(light, {
  type: LightSource.Type.Point({}),
  color: Color3.White(),
  intensity: 16000  // candela
})

Colored Point Light

彩色 Point Light

typescript
LightSource.create(light, {
  type: LightSource.Type.Point({}),
  color: Color3.create(1, 0.5, 0),  // Warm orange
  intensity: 16000,
  range: 15  // Maximum distance in meters
})
Defaults (from the protocol):
active
true,
color
white,
intensity
16000 candela,
range
-1 (auto),
shadow
false.
color
is
Color3
(RGB, each 0–1).
typescript
LightSource.create(light, {
  type: LightSource.Type.Point({}),
  color: Color3.create(1, 0.5, 0),  // Warm orange
  intensity: 16000,
  range: 15  // Maximum distance in meters
})
默认值(来自协议):
active
为
true
,
color
为白色,
intensity
为 16000 坎德拉,
range
为 -1(自动),
shadow
为
false
。
color
的类型为
Color3
(RGB,每个分量的范围为 0–1)。

Spot Lights

Spot Light

Emit a cone of light in a direction:
typescript
import { Quaternion } from '@dcl/sdk/math'

const spotlight = engine.addEntity()
Transform.create(spotlight, {
  position: Vector3.create(8, 4, 8),
  rotation: Quaternion.fromEulerDegrees(-90, 0, 0)  // Point downward
})

LightSource.create(spotlight, {
  type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
  color: Color3.White(),
  intensity: 16000
})
  • innerAngle
    — full-brightness cone angle (degrees). Default
    21.8
    . Min
    0
    , max
    179
    .
  • outerAngle
    — outer fade angle (degrees). Default
    30
    . Max
    179
    .
  • innerAngle
    cannot exceed
    outerAngle
    — if it does, the engine clamps them to the same value.
  • The light direction follows the entity's forward vector (set via Transform rotation).
  • type
    is a discriminated union. To read/mutate spot params at runtime, narrow first:
    if (comp.type?.$case === 'spot') { comp.type.spot.innerAngle = 30 }
沿指定方向发出锥形光:
typescript
import { Quaternion } from '@dcl/sdk/math'

const spotlight = engine.addEntity()
Transform.create(spotlight, {
  position: Vector3.create(8, 4, 8),
  rotation: Quaternion.fromEulerDegrees(-90, 0, 0)  // Point downward
})

LightSource.create(spotlight, {
  type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
  color: Color3.White(),
  intensity: 16000
})
  • innerAngle
    ——达到完整亮度的锥角(度)。默认值为
    21.8
    。最小值为
    0
    ,最大值为
    179
    。
  • outerAngle
    ——外侧渐隐角(度)。默认值为
    30
    。最大值为
    179
    。
  • innerAngle
    不能大于
    outerAngle
    ;如果大于,引擎会将二者限制为相同值。
  • 灯光方向遵循实体的前向向量(通过 Transform rotation 设置)。
  • type
    是判别联合类型。如需在运行时读取或修改 Spot Light 参数,请先收窄类型:
    if (comp.type?.$case === 'spot') { comp.type.spot.innerAngle = 30 }

Shadows

阴影

Enable shadows on point or spot lights:
typescript
LightSource.create(spotlight, {
  type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
  shadow: true,
  intensity: 800
})
Note: shadows are only rendered for spot lights, not point lights.
shadow
is a top-level boolean on the component (not inside
Spot
/
Point
).
为 Point Light 或 Spot Light 启用阴影:
typescript
LightSource.create(spotlight, {
  type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
  shadow: true,
  intensity: 800
})
注意:只有 Spot Light 才会渲染阴影,Point Light 不会。
shadow
是组件顶层的布尔值(不在
Spot
/
Point
内部)。

Shadow Mask Textures (Gobos)

阴影遮罩纹理(Gobos)

Project a pattern through the light:
typescript
const maskedLight = LightSource.getMutable(spotlight)
maskedLight.shadowMaskTexture = Material.Texture.Common({
  src: 'assets/Images/lightmask1.png'
})
  • Set
    shadowMaskTexture = undefined
    to remove the mask again.
  • The mask projects light shape (e.g. a window pattern) — simulating caustics/soft shadows. Used on spot lights.
通过灯光投射图案:
typescript
const maskedLight = LightSource.getMutable(spotlight)
maskedLight.shadowMaskTexture = Material.Texture.Common({
  src: 'assets/Images/lightmask1.png'
})
  • 将
    shadowMaskTexture
    设置为
    undefined
    可再次移除遮罩。
  • 遮罩用于投射灯光形状(例如窗户图案),模拟焦散或柔和阴影。适用于 Spot Light。

Toggling Lights

切换灯光

typescript
// Toggle on/off
const lightData = LightSource.getMutable(light)
lightData.active = !lightData.active
typescript
// Toggle on/off
const lightData = LightSource.getMutable(light)
lightData.active = !lightData.active

Light Limits

灯光限制

  • A scene may create many lights (the engine test scene spawns ~9 across 2 parcels); the renderer decides how many render.
  • Depending on the player's quality settings, between ~4 and ~10 lights render at once. If the scene has more than that, only the closest lights to the player are rendered.
  • Up to ~3 shadow-casting lights render at once.
  • The renderer auto-culls lights based on quality settings and proximity.
  • Intensity is in candela (lumens/m² at 1m, i.e. lumens/4π). Default
    16000
    .
  • range
    default is
    -1
    → auto-computed as
    intensity^0.25
    (fourth root, in meters). Set an explicit
    range
    to override — this also limits a light's influence and saves performance.
  • Spread lights out so few are near the player at once (only the closest ones render).
  • 场景可以创建许多灯光(引擎测试场景在 2 个 parcel 中生成了约 9 个);具体渲染多少个由渲染器决定。
  • 根据玩家的质量设置,每次会同时渲染约 4~10 个灯光。如果场景中的灯光更多,则只渲染距离玩家最近的灯光。
  • 每次最多同时渲染约 3 个投射阴影的灯光。
  • 渲染器会根据质量设置和距离自动剔除灯光。
  • 强度以坎德拉为单位(1 米处的 lumens/m²,即 lumens/4π)。默认值为
    16000
    。
  • range
    的默认值为
    -1
    ,此时会根据
    intensity^0.25
    自动计算(四次方根,单位为米)。设置明确的
    range
    可覆盖自动计算结果,同时也能限制灯光的影响范围并节省性能。
  • 应将灯光分散布置,使玩家附近同时只有少量灯光(只有距离最近的灯光会被渲染)。

SkyboxTime (Day/Night Cycle)

SkyboxTime(昼夜循环)

Use SkyboxTime for atmosphere — nighttime scenes with point lights create dramatic environments.
使用 SkyboxTime 营造环境氛围——夜景场景结合 Point Light 可以营造富有戏剧性的效果。

Fixed Time in scene.json

在 scene.json 中固定时间

Set a permanent time of day without code. Two valid locations:
json
// Genesis City scene — top-level
{ "skyboxConfig": { "fixedTime": 43200 } }

// World — inside worldConfiguration
{ "worldConfiguration": { "name": "my-name.dcl.eth", "skyboxConfig": { "fixedTime": 36000 } } }
Time values (seconds since midnight, full day = 86400): 0 = midnight, 21600 = 6 AM, 43200 = noon, 64800 = 6 PM (dusk), 86400 = full day.
Precedence (verified against the engine test scenes):
worldConfiguration.skyboxConfig.fixedTime
wins over top-level
skyboxConfig.fixedTime
; either JSON value is in turn overridden at runtime by a
SkyboxTime
component on
engine.RootEntity
.
无需编写代码即可设置固定的每日时刻。有两个有效位置:
json
// Genesis City scene — top-level
{ "skyboxConfig": { "fixedTime": 43200 } }

// World — inside worldConfiguration
{ "worldConfiguration": { "name": "my-name.dcl.eth", "skyboxConfig": { "fixedTime": 36000 } } }
时间值的单位为距午夜的秒数,一整天为 86400 秒:0 表示午夜,21600 表示上午 6 点,43200 表示正午,64800 表示下午 6 点(黄昏),86400 表示一整天。
优先级(已根据引擎测试场景验证):
worldConfiguration.skyboxConfig.fixedTime
的优先级高于顶层的
skyboxConfig.fixedTime
;运行时,实体
engine.RootEntity
上的
SkyboxTime
组件又会覆盖这两个 JSON 值。

Read Current World Time

读取当前世界时间

typescript
import { getWorldTime } from '~system/Runtime'

executeTask(async () => {
  const time = await getWorldTime({})
  console.log('Seconds since midnight:', time.seconds)
})
typescript
import { getWorldTime } from '~system/Runtime'

executeTask(async () => {
  const time = await getWorldTime({})
  console.log('Seconds since midnight:', time.seconds)
})

Change Time Dynamically

动态更改时间

typescript
import { engine, SkyboxTime, TransitionMode } from '@dcl/sdk/ecs'

// Set time of day (must target the root entity)
SkyboxTime.create(engine.RootEntity, { fixedTime: 43200 })  // Noon

// Change with transition direction
SkyboxTime.createOrReplace(engine.RootEntity, {
  fixedTime: 64800,  // Dusk (6 PM)
  transitionMode: TransitionMode.TM_BACKWARD  // TM_FORWARD (0, default) or TM_BACKWARD (1)
})

// Remove the component to hand control back to global/world time
SkyboxTime.deleteFrom(engine.RootEntity)
  • transitionMode
    (optional) sets the animation direction when the time changes. Default
    TM_FORWARD
    .
  • The component must live on
    engine.RootEntity
    . Deleting it reverts to the scene.json/world time (or the global day/night cycle).
typescript
import { engine, SkyboxTime, TransitionMode } from '@dcl/sdk/ecs'

// Set time of day (must target the root entity)
SkyboxTime.create(engine.RootEntity, { fixedTime: 43200 })  // Noon

// Change with transition direction
SkyboxTime.createOrReplace(engine.RootEntity, {
  fixedTime: 64800,  // Dusk (6 PM)
  transitionMode: TransitionMode.TM_BACKWARD  // TM_FORWARD (0, default) or TM_BACKWARD (1)
})

// Remove the component to hand control back to global/world time
SkyboxTime.deleteFrom(engine.RootEntity)
  • transitionMode
    (可选)用于设置时间变化时的动画方向。默认值为
    TM_FORWARD
    。
  • 该组件必须位于
    engine.RootEntity
    上。删除组件后,将恢复使用 scene.json/world 中的时间(或全局昼夜循环)。

Day/Night Cycle System

昼夜循环系统

typescript
let currentTime = 43200
const CYCLE_SPEED = 100  // Time units per second

function dayNightCycle(dt: number) {
  currentTime = (currentTime + CYCLE_SPEED * dt) % 86400
  SkyboxTime.createOrReplace(engine.RootEntity, {
    fixedTime: currentTime
  })
}

engine.addSystem(dayNightCycle)
typescript
let currentTime = 43200
const CYCLE_SPEED = 100  // Time units per second

function dayNightCycle(dt: number) {
  currentTime = (currentTime + CYCLE_SPEED * dt) % 86400
  SkyboxTime.createOrReplace(engine.RootEntity, {
    fixedTime: currentTime
  })
}

engine.addSystem(dayNightCycle)

Realm Info

Realm 信息

Detect which realm (server) the player is connected to:
typescript
import { getRealm } from '~system/Runtime'

executeTask(async () => {
  const realm = await getRealm({})
  console.log('Realm:', realm.realmInfo?.realmName)
  console.log('Network:', realm.realmInfo?.networkId)
  console.log('Base URL:', realm.realmInfo?.baseUrl)
})
检测玩家当前连接到的 realm(服务器):
typescript
import { getRealm } from '~system/Runtime'

executeTask(async () => {
  const realm = await getRealm({})
  console.log('Realm:', realm.realmInfo?.realmName)
  console.log('Network:', realm.realmInfo?.networkId)
  console.log('Base URL:', realm.realmInfo?.baseUrl)
})

Emissive Materials (Glow Effects)

自发光材质(辉光效果)

For a visual glow without casting light on surroundings:
typescript
import { engine, Material } from '@dcl/sdk/ecs'
import { Color4, Color3 } from '@dcl/sdk/math'

// Self-illuminated material (emissiveColor uses Color3, not Color4)
Material.setPbrMaterial(entity, {
  albedoColor: Color4.create(0, 0, 0, 1),
  emissiveColor: Color3.create(0, 1, 0),  // Green glow
  emissiveIntensity: 2.0
})
Note: emissive materials don't illuminate other surrounding entities, they just have a glow effect on them.
如果只需产生视觉辉光效果,而不会向周围投射光线,可以使用以下方式:
typescript
import { engine, Material } from '@dcl/sdk/ecs'
import { Color4, Color3 } from '@dcl/sdk/math'

// Self-illuminated material (emissiveColor uses Color3, not Color4)
Material.setPbrMaterial(entity, {
  albedoColor: Color4.create(0, 0, 0, 1),
  emissiveColor: Color3.create(0, 1, 0),  // Green glow
  emissiveIntensity: 2.0
})
注意:自发光材质不会照亮周围的其他实体,只会在自身产生辉光效果。

Combining Emissive + LightSource

结合自发光材质与 LightSource

For an object that both glows visually and casts light:
typescript
// Visual glow on the mesh
Material.setPbrMaterial(bulb, {
  emissiveColor: Color3.create(1, 0.9, 0.7),
  emissiveIntensity: 1.5
})

// Actual light emission
LightSource.create(bulb, {
  type: LightSource.Type.Point({}),
  color: Color3.create(1, 0.9, 0.7),
  intensity: 200,
  range: 10
})
如果对象既需要视觉辉光,又需要实际投射光线:
typescript
// Visual glow on the mesh
Material.setPbrMaterial(bulb, {
  emissiveColor: Color3.create(1, 0.9, 0.7),
  emissiveIntensity: 1.5
})

// Actual light emission
LightSource.create(bulb, {
  type: LightSource.Type.Point({}),
  color: Color3.create(1, 0.9, 0.7),
  intensity: 200,
  range: 10
})

Shadow Quality

阴影质量

shadow
is a top-level optional boolean on the LightSource component (default
false
). There is no shadow-type enum — quality is automatic and distance-based.
Spot({...})
accepts only
innerAngle?
and
outerAngle?
.
typescript
import { LightSource } from '@dcl/sdk/ecs'

// Spot light with shadows enabled
LightSource.create(spotEntity, {
  type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
  shadow: true,  // top-level boolean
  intensity: 800
})
Constraints:
  • Shadows are only supported for spot lights; point lights do not cast shadows.
  • Max 3 shadow-casting lights rendered at a time — disable
    shadow
    on lights that don't need it. Spot lights with shadows suit dramatic effects such as flashlights.
  • Shadow quality/culling is automatic, based on the light's distance from the player. Exact distances vary by light type and the player's quality settings; general rule:
Distance from playerResult
< 10 mSoft shadows (high quality)
10–20 mHard shadows (low quality)
> 20 mNo shadows rendered
  • The light itself keeps illuminating at much larger distances — it is only disabled when the player is more than 160 m away (10 parcels). This makes LightSource suitable for large-scale setups like stage lighting at live events, where most of the audience is far from the fixtures.
  • Lights only render while the player is standing inside the scene; outside, they are not rendered.
Need advanced material effects? See the advanced-rendering skill for metallic, roughness, transparency, texture maps, texture tweens, and texture modes.
shadow
是 LightSource 组件顶层的可选布尔值,默认值为
false
。不存在 shadow 类型枚举——质量会根据距离自动确定。
Spot({...})
仅接受
innerAngle?
和
outerAngle?
。
typescript
import { LightSource } from '@dcl/sdk/ecs'

// Spot light with shadows enabled
LightSource.create(spotEntity, {
  type: LightSource.Type.Spot({ innerAngle: 25, outerAngle: 45 }),
  shadow: true,  // top-level boolean
  intensity: 800
})
限制:
  • 只有 Spot Light 支持阴影;Point Light 不会投射阴影。
  • 每次最多渲染 3 个投射阴影的灯光——请关闭不需要阴影的灯光上的
    shadow
    。带阴影的 Spot Light 适合手电筒等富有戏剧性的效果。
  • 阴影质量和剔除是自动完成的,取决于灯光与玩家之间的距离。具体距离因灯光类型和玩家质量设置而异;一般规则如下:
与玩家的距离结果
< 10 m柔和阴影(高质量)
10–20 m硬阴影(低质量)
> 20 m不渲染阴影
  • 灯光本身在更远的距离上仍会继续照亮物体——只有当玩家距离超过 **160 m(10 个 parcel)**时才会禁用。这使 LightSource 适合现场活动舞台灯光等大规模场景,因为大多数观众都离灯具很远。
  • 只有当玩家位于场景内部时才会渲染灯光;在场景外部时不会渲染。
需要高级材质效果? 有关 metallic、roughness、透明度、纹理贴图、纹理渐变和纹理模式,请参阅 advanced-rendering skill。

Gotchas

常见问题

  • range
    left unset (
    -1
    ) is auto-derived from intensity as
    intensity^0.25
    — small intensities give surprisingly short range. Set
    range
    explicitly for predictable falloff.
  • shadow
    only affects spot lights; setting it on a point light has no effect.
  • Animating a light's direction: put a
    Tween
    /
    TweenSequence
    (Rotate mode) on the light entity — the beam follows the entity's forward vector.
  • SkyboxTime
    on
    RootEntity
    overrides any scene.json
    fixedTime
    ;
    deleteFrom
    reverts to it.
  • 如果不设置
    range
    (保持为
    -1
    ),系统会根据强度自动推导出
    intensity^0.25
    ——较小的强度可能会产生短得出乎意料的作用范围。请明确设置
    range
    ,以获得可预测的衰减效果。
  • shadow
    只对 Spot Light 生效;在 Point Light 上设置它不会产生任何效果。
  • 为灯光制作方向动画:在灯光实体上添加
    Tween
    /
    TweenSequence
    (Rotate 模式)——光束会跟随实体的前向向量。
  • RootEntity
    上的
    SkyboxTime
    会覆盖 scene.json 中的任何
    fixedTime
    ;调用
    deleteFrom
    后则会恢复使用该配置。

Example scenes

示例场景

Engine-team test scenes (real API, exercised against the engine):
引擎团队测试场景(使用真实 API,并已针对引擎进行验证):