advanced-rendering

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Advanced Rendering in Decentraland

Decentraland 中的高级渲染

When to Use Which Rendering Feature

各渲染功能的适用场景

NeedComponentWhen
Entity faces the camera
Billboard
Name tags, signs, sprite-like objects
Text in the 3D world
TextShape
Labels, signs, floating text above entities
Custom material appearance
Material.setPbrMaterial
Metallic, rough, transparent, emissive surfaces
Show/hide without removing
VisibilityComponent
LOD systems, toggling objects, conditional display
Modify GLTF model nodes
GltfNodeModifiers
Override materials or shadow casting on specific mesh nodes
Decision flow:
  1. Need text on screen? → Use build-ui (React-ECS Label) instead
  2. Need text in 3D space? →
    TextShape
    (+
    Billboard
    to face camera)
  3. Need glowing/transparent materials? →
    Material.setPbrMaterial
    with emissive/transparency
  4. Need to override material on a model node? →
    GltfNodeModifiers
    with
    modifiers
    array
需求组件使用场景
实体朝向摄像机
Billboard
名牌、标牌、精灵类对象
3D 世界中的文字
TextShape
标签、标牌、实体上方的浮动文字
自定义材质外观
Material.setPbrMaterial
金属、粗糙、透明、自发光表面
无需移除即可显示或隐藏
VisibilityComponent
LOD 系统、对象切换、条件显示
修改 GLTF 模型节点
GltfNodeModifiers
覆盖特定网格节点的材质或阴影投射
决策流程:
  1. 需要屏幕上的文字?→ 使用 build-ui(React-ECS Label),而不是 3D 文字
  2. 需要 3D 空间中的文字?→ 使用
    TextShape
    (配合
    Billboard
    朝向摄像机)
  3. 需要发光或透明材质?→ 使用带有自发光或透明设置的
    Material.setPbrMaterial
  4. 需要覆盖模型节点的材质?→ 使用带有
    modifiers
    数组的
    GltfNodeModifiers

Billboard (Face the Camera)

Billboard(朝向摄像机)

Make entities always rotate to face the player's camera:
typescript
import { engine, Transform, Billboard, BillboardMode, MeshRenderer } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

const sign = engine.addEntity()
Transform.create(sign, { position: Vector3.create(8, 2, 8) })
MeshRenderer.setPlane(sign)

// Rotate only on Y axis (most common — stays upright)
Billboard.create(sign, {
  billboardMode: BillboardMode.BM_Y
})
使实体始终旋转并朝向玩家的摄像机:
typescript
import { engine, Transform, Billboard, BillboardMode, MeshRenderer } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

const sign = engine.addEntity()
Transform.create(sign, { position: Vector3.create(8, 2, 8) })
MeshRenderer.setPlane(sign)

// Rotate only on Y axis (most common — stays upright)
Billboard.create(sign, {
  billboardMode: BillboardMode.BM_Y
})

Billboard Modes

Billboard 模式

typescript
BillboardMode.BM_Y      // Rotate on Y axis only (stays upright) — most common
BillboardMode.BM_ALL    // Rotate on all axes (fully faces camera)
BillboardMode.BM_X      // Rotate on X axis only
BillboardMode.BM_Z      // Rotate on Z axis only
BillboardMode.BM_NONE   // No billboard rotation
  • Prefer
    BM_Y
    over
    BM_ALL
    for most use cases — it looks more natural and is cheaper to render.
  • BM_ALL
    is useful for particles or effects that should always directly face the camera.
  • No
    oppositeDirection
    flag.
    The SDK7
    Billboard
    component exposes only
    billboardMode
    — there is no way to invert which model face points at the camera. If a model shows its back instead of its front, rotate the model 180° on Y (
    Quaternion.fromEulerDegrees(0, 180, 0)
    ). On a parent-Billboard + child-model setup, apply the rotation to the child — the Billboard owns the parent's rotation.
  • Porting note: SDK6 → SDK7 ports occasionally show a billboarded model facing away from the camera that was correct under SDK6. The two SDKs appear to disagree on which face the billboard points at the camera. Same fix — rotate the displayed model 180° on Y. See [[migrate-sdk6-to-sdk7]] (Common Pitfalls) for context.
typescript
BillboardMode.BM_Y      // Rotate on Y axis only (stays upright) — most common
BillboardMode.BM_ALL    // Rotate on all axes (fully faces camera)
BillboardMode.BM_X      // Rotate on X axis only
BillboardMode.BM_Z      // Rotate on Z axis only
BillboardMode.BM_NONE   // No billboard rotation
  • 大多数场景应优先使用
    BM_Y
    而不是
    BM_ALL
    ——它看起来更自然,渲染成本也更低。
  • BM_ALL
    适用于需要始终正对摄像机的粒子或特效。
  • 不存在
    oppositeDirection
    标志。
    SDK7 的
    Billboard
    组件仅公开
    billboardMode
    ——无法反转模型正面指向摄像机的方式。如果模型显示的是背面而不是正面,请在 Y 轴上将模型旋转 180°(
    Quaternion.fromEulerDegrees(0, 180, 0)
    )。在父级 Billboard 与子级模型的结构中,应将旋转应用于子级——父级的旋转由 Billboard 控制。
  • 移植说明:从 SDK6 移植到 SDK7 后,有时原本在 SDK6 中正确朝向摄像机的 billboard 模型会朝向相反方向。两个 SDK 对 billboard 指向摄像机时模型面的处理似乎存在差异。可使用同样的方法解决——在 Y 轴上将显示的模型旋转 180°。有关背景信息,请参阅 [[migrate-sdk6-to-sdk7]](常见陷阱)。

Face another entity —
targetEntity

面向其他实体——
targetEntity

Billboard
has an optional
targetEntity?: Entity
field. When set, the entity reorients to face that target entity instead of the camera.
typescript
// Face a specific entity instead of the camera
Billboard.create(card, { targetEntity: sphere })

// Yaw-only tracking of a target (BM_Y respected while targeting)
Billboard.create(card, { targetEntity: target, billboardMode: BillboardMode.BM_Y })

// Retarget at runtime
Billboard.getMutable(card).targetEntity = otherEntity
  • Unset (default) → faces the main camera, exactly as before.
    targetEntity
    is fully backwards-compatible.
  • Setting
    targetEntity
    to the camera reserved entity (
    engine.CameraEntity
    , id
    2
    ) is equivalent to leaving it unset.
  • billboardMode
    still applies:
    BM_Y
    with a
    targetEntity
    yaws to face the target on the Y axis only.
  • Gotcha: if the referenced target entity does not exist or is deleted, billboard reorientation is disabled (the entity freezes at its last orientation) until the target exists again.
  • Multiplayer: a camera-facing billboard is computed locally per player (each player sees it facing themselves, nothing is synced). A
    targetEntity
    billboard instead faces the same way for all players, because the target's position is scene state. Use
    targetEntity
    when every player must see the same orientation.
Billboard
包含一个可选字段
targetEntity?: Entity
。设置该字段后,实体会朝向目标实体,而不是摄像机。
typescript
// Face a specific entity instead of the camera
Billboard.create(card, { targetEntity: sphere })

// Yaw-only tracking of a target (BM_Y respected while targeting)
Billboard.create(card, { targetEntity: target, billboardMode: BillboardMode.BM_Y })

// Retarget at runtime
Billboard.getMutable(card).targetEntity = otherEntity
  • 未设置(默认)→ 朝向主摄像机,与之前的行为完全相同。
    targetEntity
    完全向后兼容。
  • 将
    targetEntity
    设置为摄像机保留实体(
    engine.CameraEntity
    ,ID 为
    2
    ),效果等同于不设置该字段。
  • billboardMode
    仍然生效:使用
    targetEntity
    时,
    BM_Y
    只会让实体在 Y 轴上转向目标。
  • 注意:如果引用的目标实体不存在或已被删除,billboard 重定向功能会被禁用,实体将保持最后一次的朝向,直到目标实体重新存在。
  • 多人游戏:朝向摄像机的 billboard 会在每名玩家的本地分别计算,因此每名玩家看到的实体都朝向自己,不进行任何同步。使用
    targetEntity
    的 billboard 则会因为目标位置属于场景状态而对所有玩家保持相同朝向。当所有玩家必须看到相同朝向时,应使用
    targetEntity
    。

TextShape (3D Text)

TextShape(3D 文字)

Render text directly in 3D space:
typescript
import { engine, Transform, TextShape, TextAlignMode } from '@dcl/sdk/ecs'
import { Vector3, Color4 } from '@dcl/sdk/math'

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

TextShape.create(label, {
  text: 'Hello World!',
  fontSize: 24,
  textColor: Color4.White(),
  outlineColor: Color4.Black(),
  outlineWidth: 0.1,
  textAlign: TextAlignMode.TAM_MIDDLE_CENTER
})
  • Keep
    fontSize
    readable — 16-32 for in-world text.
  • Always add
    outlineColor
    and
    outlineWidth
    for legibility against any background.
直接在 3D 空间中渲染文字:
typescript
import { engine, Transform, TextShape, TextAlignMode } from '@dcl/sdk/ecs'
import { Vector3, Color4 } from '@dcl/sdk/math'

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

TextShape.create(label, {
  text: 'Hello World!',
  fontSize: 24,
  textColor: Color4.White(),
  outlineColor: Color4.Black(),
  outlineWidth: 0.1,
  textAlign: TextAlignMode.TAM_MIDDLE_CENTER
})
  • 确保
    fontSize
    易于阅读——场景内文字建议使用 16-32。
  • 始终添加
    outlineColor
    和
    outlineWidth
    ,以确保文字在任何背景上都清晰可辨。

Text Alignment Options

文字对齐选项

typescript
TextAlignMode.TAM_TOP_LEFT
TextAlignMode.TAM_TOP_CENTER
TextAlignMode.TAM_TOP_RIGHT
TextAlignMode.TAM_MIDDLE_LEFT
TextAlignMode.TAM_MIDDLE_CENTER
TextAlignMode.TAM_MIDDLE_RIGHT
TextAlignMode.TAM_BOTTOM_LEFT
TextAlignMode.TAM_BOTTOM_CENTER
TextAlignMode.TAM_BOTTOM_RIGHT
For the floating-label pattern (Billboard + TextShape combined into a camera-facing label), see the Floating Label (Billboard + TextShape) section in
{baseDir}/references/rendering-patterns.md
.
typescript
TextAlignMode.TAM_TOP_LEFT
TextAlignMode.TAM_TOP_CENTER
TextAlignMode.TAM_TOP_RIGHT
TextAlignMode.TAM_MIDDLE_LEFT
TextAlignMode.TAM_MIDDLE_CENTER
TextAlignMode.TAM_MIDDLE_RIGHT
TextAlignMode.TAM_BOTTOM_LEFT
TextAlignMode.TAM_BOTTOM_CENTER
TextAlignMode.TAM_BOTTOM_RIGHT
有关浮动标签模式(将 Billboard 与 TextShape 组合成朝向摄像头的标签),请参阅
{baseDir}/references/rendering-patterns.md
中的**浮动标签(Billboard + TextShape)**部分。

Advanced PBR Materials

高级 PBR 材质

Metallic and Roughness

金属度与粗糙度

typescript
import { engine, Transform, MeshRenderer, Material, MaterialTransparencyMode } from '@dcl/sdk/ecs'
import { Color4, Color3 } from '@dcl/sdk/math'

// Shiny metal
Material.setPbrMaterial(entity, {
  albedoColor: Color4.create(0.8, 0.8, 0.9, 1),
  metallic: 1.0,
  roughness: 0.1
})

// Rough stone
Material.setPbrMaterial(entity, {
  albedoColor: Color4.create(0.5, 0.5, 0.5, 1),
  metallic: 0.0,
  roughness: 0.9
})
typescript
import { engine, Transform, MeshRenderer, Material, MaterialTransparencyMode } from '@dcl/sdk/ecs'
import { Color4, Color3 } from '@dcl/sdk/math'

// Shiny metal
Material.setPbrMaterial(entity, {
  albedoColor: Color4.create(0.8, 0.8, 0.9, 1),
  metallic: 1.0,
  roughness: 0.1
})

// Rough stone
Material.setPbrMaterial(entity, {
  albedoColor: Color4.create(0.5, 0.5, 0.5, 1),
  metallic: 0.0,
  roughness: 0.9
})

Transparency

透明效果

typescript
// Alpha blend — smooth transparency
Material.setPbrMaterial(entity, {
  albedoColor: Color4.create(1, 0, 0, 0.5), // 50% transparent red
  transparencyMode: MaterialTransparencyMode.MTM_ALPHA_BLEND
})

// Alpha test — cutout (binary visible/invisible based on threshold)
Material.setPbrMaterial(entity, {
  texture: Material.Texture.Common({ src: 'assets/Images/cutout.png' }),
  transparencyMode: MaterialTransparencyMode.MTM_ALPHA_TEST,
  alphaTest: 0.5
})
  • MTM_ALPHA_TEST
    is cheaper than
    MTM_ALPHA_BLEND
    — use cutout when smooth transparency isn't needed.
typescript
// Alpha blend — smooth transparency
Material.setPbrMaterial(entity, {
  albedoColor: Color4.create(1, 0, 0, 0.5), // 50% transparent red
  transparencyMode: MaterialTransparencyMode.MTM_ALPHA_BLEND
})

// Alpha test — cutout (binary visible/invisible based on threshold)
Material.setPbrMaterial(entity, {
  texture: Material.Texture.Common({ src: 'assets/Images/cutout.png' }),
  transparencyMode: MaterialTransparencyMode.MTM_ALPHA_TEST,
  alphaTest: 0.5
})
  • MTM_ALPHA_TEST
    比
    MTM_ALPHA_BLEND
    成本更低——不需要平滑透明效果时应使用镂空方式。

Emissive (Glow Effects)

自发光(辉光效果)

typescript
// Glowing 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
})

// Emissive with texture
Material.setPbrMaterial(entity, {
  texture: Material.Texture.Common({ src: 'assets/Images/diffuse.png' }),
  emissiveTexture: Material.Texture.Common({ src: 'assets/Images/emissive.png' }),
  emissiveIntensity: 1.0,
  emissiveColor: Color3.White()
})
  • Use
    emissiveColor
    with a dark
    albedoColor
    for maximum glow visibility.
typescript
// Glowing 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
})

// Emissive with texture
Material.setPbrMaterial(entity, {
  texture: Material.Texture.Common({ src: 'assets/Images/diffuse.png' }),
  emissiveTexture: Material.Texture.Common({ src: 'assets/Images/emissive.png' }),
  emissiveIntensity: 1.0,
  emissiveColor: Color3.White()
})
  • 使用
    emissiveColor
    时搭配较暗的
    albedoColor
    ,可以让辉光效果更加明显。

Texture Maps

纹理贴图

typescript
Material.setPbrMaterial(entity, {
  texture: Material.Texture.Common({ src: 'assets/Images/diffuse.png' }),
  bumpTexture: Material.Texture.Common({ src: 'assets/Images/normal.png' }),
  emissiveTexture: Material.Texture.Common({ src: 'assets/Images/emissive.png' })
})
typescript
Material.setPbrMaterial(entity, {
  texture: Material.Texture.Common({ src: 'assets/Images/diffuse.png' }),
  bumpTexture: Material.Texture.Common({ src: 'assets/Images/normal.png' }),
  emissiveTexture: Material.Texture.Common({ src: 'assets/Images/emissive.png' })
})

castShadows

castShadows

Both
setPbrMaterial
and
setBasicMaterial
accept
castShadows: boolean
(default
true
). Set
false
to stop a surface from casting shadows without changing its appearance:
typescript
Material.setPbrMaterial(entity, { albedoColor: Color4.Green(), castShadows: false })
For disabling shadows on a specific node inside a GLTF model, use
GltfNodeModifiers
with
castShadows: false
instead (see below).
setPbrMaterial
和
setBasicMaterial
都接受
castShadows: boolean
参数(默认值为
true
)。将其设置为
false
,可以在不改变外观的情况下阻止表面投射阴影:
typescript
Material.setPbrMaterial(entity, { albedoColor: Color4.Green(), castShadows: false })
如需禁用 GLTF 模型内特定节点的阴影,请改用带有
castShadows: false
的
GltfNodeModifiers
,详见下文。

GltfContainer Collision Masks

GltfContainer 碰撞遮罩

Use collision masks to control which collision layers respond to the different mesh layers in a GLTF model. GLTF models have two mesh layers: visible meshes (what players see rendered), and invisible layers (collider meshes, named internally with _collider):
typescript
import { engine, Transform, GltfContainer, ColliderLayer } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

const model = engine.addEntity()
Transform.create(model, { position: Vector3.create(4, 0, 4) })

GltfContainer.create(model, {
  src: 'models/myModel.glb',
  visibleMeshesCollisionMask: ColliderLayer.CL_PHYSICS | ColliderLayer.CL_POINTER,
  invisibleMeshesCollisionMask: ColliderLayer.CL_PHYSICS
})
使用碰撞遮罩控制 GLTF 模型中不同网格层对应的碰撞层。GLTF 模型包含两个网格层:可见网格(玩家看到的渲染内容)和不可见层(碰撞体网格,内部名称带有
_collider
):
typescript
import { engine, Transform, GltfContainer, ColliderLayer } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

const model = engine.addEntity()
Transform.create(model, { position: Vector3.create(4, 0, 4) })

GltfContainer.create(model, {
  src: 'models/myModel.glb',
  visibleMeshesCollisionMask: ColliderLayer.CL_PHYSICS | ColliderLayer.CL_POINTER,
  invisibleMeshesCollisionMask: ColliderLayer.CL_PHYSICS
})

VisibilityComponent

VisibilityComponent

Show or hide entities without removing them:
typescript
import { engine, VisibilityComponent } from '@dcl/sdk/ecs'

// Hide an entity
VisibilityComponent.create(entity, { visible: false })

// Toggle visibility
const visibility = VisibilityComponent.getMutable(entity)
visibility.visible = !visibility.visible
For a distance-based LOD (Level of Detail) system that toggles
VisibilityComponent
per frame, see the LOD via VisibilityComponent section in
{baseDir}/references/rendering-patterns.md
.
无需移除实体即可显示或隐藏实体:
typescript
import { engine, VisibilityComponent } from '@dcl/sdk/ecs'

// Hide an entity
VisibilityComponent.create(entity, { visible: false })

// Toggle visibility
const visibility = VisibilityComponent.getMutable(entity)
visibility.visible = !visibility.visible
有关每帧切换
VisibilityComponent
的基于距离的 LOD(Level of Detail)系统,请参阅
{baseDir}/references/rendering-patterns.md
中的通过 VisibilityComponent 实现 LOD部分。

propagateToChildren

propagateToChildren

Set
propagateToChildren: true
on a
VisibilityComponent
to apply visibility to all children in the hierarchy at once. This avoids having to mark every child entity individually:
typescript
VisibilityComponent.create(parentEntity, { visible: false, propagateToChildren: true })
Rules (verified against the
1,0-visibility-comp-propagation
test scene):
  • If a child has its own
    VisibilityComponent
    , that value wins regardless of what an ancestor propagates — even if the child's own
    propagateToChildren
    is
    false
    , the child stays at its own
    visible
    value and does not re-inherit the parent's.
  • If a child has no
    VisibilityComponent
    , it inherits from the nearest ancestor with
    propagateToChildren: true
    .
  • A child that overrides an invisible parent to
    visible: true
    can itself set
    propagateToChildren: true
    to force its own subtree visible again — propagation re-evaluates at each node that carries a
    VisibilityComponent
    .
  • Propagation follows the live
    Transform.parent
    hierarchy: re-parenting an entity at runtime changes which ancestor's propagated visibility applies to it.
在
VisibilityComponent
上设置
propagateToChildren: true
,即可一次性将可见性应用于层级结构中的所有子实体,从而无需逐个标记每个子实体:
typescript
VisibilityComponent.create(parentEntity, { visible: false, propagateToChildren: true })
规则(已通过
1,0-visibility-comp-propagation
测试场景验证):
  • 如果子实体拥有自己的
    VisibilityComponent
    ,无论祖先节点传播什么值,子实体自身的值都会优先——即使子实体自己的
    propagateToChildren
    为
    false
    ,它仍会保持自身的
    visible
    值,不会重新继承父级的值。
  • 如果子实体没有
    VisibilityComponent
    ,它会从最近一个设置了
    propagateToChildren: true
    的祖先节点继承可见性。
  • 子实体可以将不可见的父级覆盖为
    visible: true
    ,并自行设置
    propagateToChildren: true
    ,从而再次强制其整个子树可见——传播机制会在每个包含
    VisibilityComponent
    的节点重新计算。
  • 传播遵循实时的
    Transform.parent
    层级结构:在运行时重新设置父实体,会改变应用可见性传播的祖先节点。

Per-Node Modifiers (GltfNodeModifiers)

逐节点修改器(GltfNodeModifiers)

Override material or shadow casting on specific nodes within a GLTF model:
typescript
import { GltfNodeModifiers } from '@dcl/sdk/ecs'

GltfNodeModifiers.create(entity, {
  modifiers: [
    {
      path: 'RootNode/Armor',     // GLTF hierarchy path
      castShadows: false           // Disable shadow casting for this node
    }
  ]
})
To override the material or shadow casting of the entire model (
path: ''
) — including the nested
material: { material: { $case: 'pbr' | 'unlit', ... } }
shape — see the GltfNodeModifiers — Whole-Model Material Override section in
{baseDir}/references/rendering-patterns.md
.
Modifier details (from the
74,-8-gltfnodemodifier
test scene):
  • path
    is the GLTF node hierarchy path,
    /
    -separated (e.g.
    Scene_root/shark_skeleton/Sphere/Sphere.001
    ).
    path: ''
    targets the whole model; a nested path targets one node and its descendants.
  • material
    accepts either
    $case: 'pbr'
    (full PBR:
    albedoColor
    ,
    emissiveColor
    ,
    emissiveIntensity
    , textures, …) or
    $case: 'unlit'
    (
    diffuseColor
    , …). Different nodes in the same
    modifiers
    array can use different cases.
  • Textures work here too, including video:
    pbr: { texture: Material.Texture.Video({ videoPlayerEntity: someEntityWithVideoPlayer }) }
    .
  • castShadows: false
    per node (no
    material
    needed) disables shadow casting for that node only.
  • One
    modifiers
    array can contain many entries, each targeting a different
    path
    in a single call.
  • Debug trick: passing a
    path
    that does not exist logs the model's full GLTF node hierarchy to the scene console — use a deliberately wrong path to discover the correct node names.
  • Update with
    GltfNodeModifiers.createOrReplace(entity, { modifiers: [...] })
    ; remove all overrides with
    GltfNodeModifiers.deleteFrom(entity)
    .
Node paths are engine-visible names baked into the GLB, not arbitrary — if a target node has no material of the requested kind, the override may be ignored.
覆盖 GLTF 模型内特定节点的材质或阴影投射:
typescript
import { GltfNodeModifiers } from '@dcl/sdk/ecs'

GltfNodeModifiers.create(entity, {
  modifiers: [
    {
      path: 'RootNode/Armor',     // GLTF hierarchy path
      castShadows: false           // Disable shadow casting for this node
    }
  ]
})
如需覆盖整个模型的材质或阴影投射(
path: ''
)——包括嵌套的
material: { material: { $case: 'pbr' | 'unlit', ... } }
结构——请参阅
{baseDir}/references/rendering-patterns.md
中的GltfNodeModifiers——全模型材质覆盖部分。
修改器详情(来自
74,-8-gltfnodemodifier
测试场景):
  • path
    是以
    /
    分隔的 GLTF 节点层级路径,例如
    Scene_root/shark_skeleton/Sphere/Sphere.001
    。
    path: ''
    指向整个模型;嵌套路径则指向一个节点及其后代节点。
  • material
    可以使用
    $case: 'pbr'
    (完整的 PBR 配置,包括
    albedoColor
    、
    emissiveColor
    、
    emissiveIntensity
    、纹理等),也可以使用
    $case: 'unlit'
    (包括
    diffuseColor
    等)。同一个
    modifiers
    数组中的不同节点可以使用不同的 case。
  • 此处同样支持纹理,包括视频纹理:
    pbr: { texture: Material.Texture.Video({ videoPlayerEntity: someEntityWithVideoPlayer }) }
    。
  • 逐节点的
    castShadows: false
    无需设置
    material
    ,即可仅禁用该节点的阴影投射。
  • 一个
    modifiers
    数组可以在一次调用中包含多个条目,每个条目指向不同的
    path
    。
  • 调试技巧:传入不存在的
    path
    会将模型的完整 GLTF 节点层级输出到场景控制台——可以故意使用错误路径来查找正确的节点名称。
  • 使用
    GltfNodeModifiers.createOrReplace(entity, { modifiers: [...] })
    更新修改器;使用
    GltfNodeModifiers.deleteFrom(entity)
    移除所有覆盖项。
节点路径是烘焙在 GLB 中的、引擎可见的名称,不能随意指定——如果目标节点不存在所请求类型的材质,覆盖可能不会生效。

Avatar Texture

Avatar 纹理

Generate a texture from a player's avatar portrait:
typescript
Material.setPbrMaterial(portraitFrame, {
  texture: Material.Texture.Avatar({ userId: '0x...' })
})
This will fetch a thumbnail image with a closeup of the player's face, wearing the wearables that this player currently has on.
根据玩家的 Avatar 头像生成纹理:
typescript
Material.setPbrMaterial(portraitFrame, {
  texture: Material.Texture.Avatar({ userId: '0x...' })
})
这会获取一张缩略图,其中包含玩家面部的特写,并显示该玩家当前佩戴的可穿戴设备。

Texture Modes

纹理模式

Control how textures are filtered and wrapped:
typescript
import { TextureFilterMode, TextureWrapMode } from '@dcl/sdk/ecs'

Material.setPbrMaterial(entity, {
  texture: Material.Texture.Common({
    src: 'assets/Images/pixel-art.png',
    filterMode: TextureFilterMode.TFM_POINT,    // crisp pixels (no smoothing)
    wrapMode: TextureWrapMode.TWM_REPEAT        // tile the texture
  })
})
Filter modes:
TFM_POINT
(pixelated),
TFM_BILINEAR
(smooth),
TFM_TRILINEAR
(smoothest). Wrap modes:
TWM_REPEAT
(tile),
TWM_CLAMP
(stretch edges),
TWM_MIRROR
(mirror tile).
控制纹理的过滤和包装方式:
typescript
import { TextureFilterMode, TextureWrapMode } from '@dcl/sdk/ecs'

Material.setPbrMaterial(entity, {
  texture: Material.Texture.Common({
    src: 'assets/Images/pixel-art.png',
    filterMode: TextureFilterMode.TFM_POINT,    // crisp pixels (no smoothing)
    wrapMode: TextureWrapMode.TWM_REPEAT        // tile the texture
  })
})
过滤模式:
TFM_POINT
(像素化)、
TFM_BILINEAR
(平滑)、
TFM_TRILINEAR
(最平滑)。 包装模式:
TWM_REPEAT
(平铺)、
TWM_CLAMP
(拉伸边缘)、
TWM_MIRROR
(镜像平铺)。

Texture Tweens

纹理 Tween

For animated texture patterns (
Tween.setTextureMoveContinuous
scrolling,
Tween.setTextureMove
slide-once,
TMT_OFFSET
vs
TMT_TILING
movement types, and looping via
TweenSequence
) — all requiring a texture with
wrapMode: TWM_REPEAT
— see the Texture Tweens section in
{baseDir}/references/rendering-patterns.md
.
有关动画纹理图案——包括
Tween.setTextureMoveContinuous
滚动、
Tween.setTextureMove
单次滑动、
TMT_OFFSET
与
TMT_TILING
移动类型,以及通过
TweenSequence
实现循环——这些功能都要求纹理使用
wrapMode: TWM_REPEAT
,请参阅
{baseDir}/references/rendering-patterns.md
中的纹理 Tween部分。

FlatMaterial Accessors

FlatMaterial 访问器

The
Material
component provides shortcut methods that skip the nested union structure, making material access more ergonomic:
MethodReturnsThrows if no material?
Material.getFlat(entity)
Read-only
FlatMaterial
Yes
Material.getFlatOrNull(entity)
Read-only
FlatMaterial | null
No
Material.getFlatMutable(entity)
Read/write
FlatMaterial
Yes
Material.getFlatMutableOrNull(entity)
Read/write
FlatMaterial | null
No
typescript
// Read a property safely
const src = Material.getFlatOrNull(entity)?.texture?.src

// Mutate a texture in-place without knowing PBR vs Basic
Material.getFlatMutableOrNull(entity)!.texture = Material.Texture.Common({ src: 'assets/Images/new.png' })
Material
组件提供了一组快捷方法,可以跳过嵌套的联合类型结构,让材质访问更加便捷:
方法返回值没有材质时是否抛出异常?
Material.getFlat(entity)
只读
FlatMaterial
是
Material.getFlatOrNull(entity)
只读
FlatMaterial | null
否
Material.getFlatMutable(entity)
可读写
FlatMaterial
是
Material.getFlatMutableOrNull(entity)
可读写
FlatMaterial | null
否
typescript
// Read a property safely
const src = Material.getFlatOrNull(entity)?.texture?.src

// Mutate a texture in-place without knowing PBR vs Basic
Material.getFlatMutableOrNull(entity)!.texture = Material.Texture.Common({ src: 'assets/Images/new.png' })

Example scenes

示例场景

Engine-team test scenes exercising these APIs against the real runtime:
引擎团队提供的测试场景,用于在真实运行时中验证以下 API: