关于 advanced-rendering
advanced-rendering 是面向 Decentraland 开发的技能,介绍如何用 SDK 渲染组件配置 3D 视觉效果。内容涵盖文字、面向摄像机的实体、材质、模型节点覆盖、可见性和纹理动画,并说明如何在 main-entities.ts 中编写实体。
主要功能
- 01用 Billboard 创建面向摄像机的实体
- 02用 TextShape 渲染 3D 文字
- 03配置 PBR 材质和纹理
- 04切换实体可见性
- 05覆盖 GLTF 节点材质
- 06动画化材质纹理移动
适用场景
- 01为物体添加浮动标签
- 02制作发光或透明表面
- 03按条件隐藏实体
- 04滚动水面或传送带纹理
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dcl-regenesislabs/ opendcl --skill advanced-rendering安装后在 Claude Code 中输入/advanced-rendering确认已加载。
详细介绍
Decentraland 中的高级渲染
编写方式拆分
Billboard、TextShape、Material、MeshRenderer、GltfContainer、VisibilityComponent 和 GltfNodeModifiers 均支持在 main-entities.ts 中使用——在其中完整声明视觉实体(标牌、标签、公告板、发光道具、带有逐节点覆盖的模型)及其视觉组件。下面展示 engine.addEntity() 后接 Transform.create + MeshRenderer / Billboard / TextShape 的示例,采用的是 main-entities.ts 出现之前的写法——请将实体声明移至 main-entities.ts,并仅在 src/index.ts 中保留运行时修改(例如,VisibilityComponent.getMutable(entity).visible = false)。
// Example: floating label
// main-entities.ts
shop_label: {
components: {
Transform: { position: { x: 8, y: 3, z: 8 } },
TextShape: { text: 'OPEN', fontSize: 4, textColor: { r: 1, g: 1, b: 1, a: 1 } },
Billboard: { billboardMode: 7 } // BM_ALL
}
}
如何选择渲染功能
| 需求 | 组件 | 使用场景 |
|---|---|---|
| 实体面向摄像机 | Billboard | 名称标签、标牌、类似精灵的对象 |
| 3D 世界中的文本 | TextShape | 标签、标牌、实体上方的悬浮文本 |
| 自定义材质外观 | Material.setPbrMaterial | 金属、粗糙、透明、自发光表面 |
| 显示/隐藏但不移除 | VisibilityComponent | LOD 系统、切换对象、条件显示 |
| 修改 GLTF 模型节点 | GltfNodeModifiers | 覆盖特定网格节点的材质或阴影投射 |
选择流程:
- 需要在屏幕上显示文本?→ 改用 build-ui(React-ECS Label)
- 需要在 3D 空间中显示文本?→
TextShape(+Billboard以面向摄像机) - 需要发光/透明材质?→ 使用
Material.setPbrMaterial设置自发光/透明度 - 需要覆盖模型节点上的材质?→ 使用带有
modifiers数组的GltfNodeModifiers
Billboard(面向摄像机)
让实体始终旋转并面向玩家的摄像机:
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 模式
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适用于粒子或需要始终直接面向摄像机的特效。
TextShape(3D 文本)
直接在 3D 空间中渲染文本:
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
})
文本对齐选项
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)
组合使用 Billboard 和 TextShape,创建始终面向玩家的标签:
const floatingLabel = engine.addEntity()
Transform.create(floatingLabel, { position: Vector3.create(8, 4, 8) })
TextShape.create(floatingLabel, {
text: 'NPC Name',
fontSize: 16,
textColor: Color4.White(),
outlineColor: Color4.Black(),
outlineWidth: 0.08,
textAlign: TextAlignMode.TAM_BOTTOM_CENTER
})
Billboard.create(floatingLabel, {
billboardMode: BillboardMode.BM_Y
})
高级 PBR 材质
金属度和粗糙度
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
})
透明度
// 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/cutout.png' }),
transparencyMode: MaterialTransparencyMode.MTM_ALPHA_TEST,
alphaTest: 0.5
})
自发光(发光效果)
// 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/diffuse.png' }),
emissiveTexture: Material.Texture.Common({ src: 'assets/emissive.png' }),
emissiveIntensity: 1.0,
emissiveColor: Color3.White()
})
纹理贴图
Material.setPbrMaterial(entity, {
texture: Material.Texture.Common({ src: 'assets/diffuse.png' }),
bumpTexture: Material.Texture.Common({ src: 'assets/normal.png' }),
emissiveTexture: Material.Texture.Common({ src: 'assets/emissive.png' })
})
GltfContainer 可见性蒙版
使用碰撞蒙版控制 GLTF 模型中特定网格层的可见性和碰撞:
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
显示或隐藏实体,而不将其移除:
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
// Useful for LOD (Level of Detail)
function lodSystem() {
const playerPos = Transform.get(engine.PlayerEntity).position
for (const [entity, transform] of engine.getEntitiesWith(Transform, MeshRenderer)) {
const distance = Vector3.distance(playerPos, transform.position)
if (distance > 30) {
VisibilityComponent.createOrReplace(entity, { visible: false })
} else {
VisibilityComponent.createOrReplace(entity, { visible: true })
}
}
}
engine.addSystem(lodSystem)
逐节点修改器(GltfNodeModifiers)
覆盖 GLTF 模型中特定节点的材质或阴影投射。支持在 main-entities.ts 中使用:
// main-entities.ts
armored_knight: {
components: {
Transform: { position: { x: 8, y: 0, z: 8 } },
GltfContainer: { src: 'models/knight.glb' },
GltfNodeModifiers: {
modifiers: [
{
path: 'RootNode/Armor', // GLTF hierarchy path
castShadows: false // disable shadows for this node
}
]
}
}
}
覆盖整个模型:传入 path: ''(空字符串),将修改器应用于模型中的每个节点。适用于通过一次材质替换为整个模型重新贴皮:
red_team_unit: {
components: {
Transform: { position: { x: 4, y: 0, z: 8 } },
GltfContainer: { src: 'models/unit.glb' },
GltfNodeModifiers: {
modifiers: [
{
path: '',
material: {
material: {
$case: 'pbr',
pbr: { albedoColor: { r: 1, g: 0, b: 0, a: 1 } }
}
}
}
]
}
}
}
纹理补间(为材质纹理添加动画)
随时间改变材质的纹理偏移/平铺——适用于水、熔岩、传送带、滚动背景。Tween 组件支持在 main-entities.ts 中使用:
// main-entities.ts — continuous texture scroll
conveyor: {
components: {
Transform: { position: { x: 8, y: 1, z: 8 } },
MeshRenderer: { mesh: { $case: 'plane', plane: { uvs: [] } } },
Material: { material: { $case: 'pbr', pbr: { texture: { tex: { $case: 'texture', texture: { src: 'images/belt.png' } } } } } },
Tween: {
duration: 2000,
easingFunction: 0, // EF_LINEAR
mode: {
$case: 'textureMoveContinuous',
textureMoveContinuous: { direction: { x: 1, y: 0 }, speed: 0.5 }
// movementType defaults to 0 = TMT_OFFSET; use 1 = TMT_TILING to scale instead
}
}
}
}
要实现有限的纹理移动,可以使用 { $case: 'textureMove', textureMove: { start: { x: 0, y: 0 }, end: { x: 1, y: 0 }, movementType: 0 } },并设置 duration。
运行时辅助函数(位于 src/index.ts):Tween.setTextureMove(entity, from, to, durationMs) 和 Tween.setTextureMoveContinuous(entity, direction, speed)。
头像纹理
从玩家的头像生成纹理:
Material.setPbrMaterial(portraitFrame, {
texture: Material.Texture.Avatar({ userId: '0x...' })
})
纹理模式
控制纹理的过滤和环绕方式:
import { TextureFilterMode, TextureWrapMode } from '@dcl/sdk/ecs'
Material.setPbrMaterial(entity, {
texture: Material.Texture.Common({
src: '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(镜像平铺)。
最佳实践
- 使用
BillboardMode.BM_Y而不是BM_ALL,效果更自然,渲染速度也更快 - 保持
fontSize清晰易读(世界内文字使用 16-32) - 为 TextShape 添加
outlineColor和outlineWidth,确保文字在任何背景下都清晰可辨 - 将
emissiveColor与较暗的albedoColor搭配使用,以获得最明显的发光效果 MTM_ALPHA_TEST比MTM_ALPHA_BLEND更省资源;无需平滑透明效果时,请使用镂空模式- 组合使用 Billboard + TextShape,在 NPC 或物体上方显示浮动名称标签
- 使用 VisibilityComponent 实现 LOD 系统,而不是移除后再重新添加实体