mcpField
A
advanced-rendering

advanced-rendering

github.com/decentraland開発4280 回インストール今日更新
リポジトリ

advanced-rendering について

advanced-rendering は、Decentraland SDK7 のシーン開発者向けレンダリングガイドです。エンティティの向き、3D テキスト、マテリアル、可視性、GLTF ノードの制御を、使用例や実装上の注意とともに説明します。

主な機能

  1. 01Billboard によるエンティティの向き制御
  2. 023D 空間へのテキスト描画
  3. 03PBR・透明・発光マテリアルの設定
  4. 04エンティティの表示切り替え
  5. 05GLTF ノードのマテリアルと影の変更

活用シーン

  1. 01カメラ向きの看板の作成
  2. 02シーンオブジェクトへの 3D ラベル追加
  3. 03透明または発光する表面の作成
  4. 04条件に応じたシーンエンティティの非表示

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/decentraland/sdk-skills --skill advanced-rendering

インストール後、Claude Code で/advanced-renderingを入力して読み込まれたか確認します。

同梱ファイルreferences/rendering-patterns.md

詳細

Decentraland の高度なレンダリング

各レンダリング機能の使い分け

必要な機能コンポーネント用途
エンティティをカメラに向けるBillboard名前タグ、看板、スプライトのようなオブジェクト
3D ワールド内のテキストTextShapeラベル、看板、エンティティの上に浮かぶテキスト
マテリアルの見た目をカスタマイズするMaterial.setPbrMaterial金属、粗い表面、透明な表面、発光する表面
削除せずに表示/非表示を切り替えるVisibilityComponentLOD システム、オブジェクトの切り替え、条件付き表示
GLTF モデルのノードを変更するGltfNodeModifiers特定のメッシュノードのマテリアルやシャドウの描画を上書きする

選択の流れ:

  1. 画面上にテキストが必要ですか? → 代わりに build-ui(React-ECS Label)を使用します
  2. 3D 空間にテキストが必要ですか? → TextShape(カメラに向けるには Billboard を併用)
  3. 発光/透明なマテリアルが必要ですか? → 発光/透明度を設定した Material.setPbrMaterial
  4. モデルノードのマテリアルを上書きする必要がありますか? → modifiers 配列を指定した GltfNodeModifiers

SDK7 では、反射、鏡、テクスチャへのレンダリングは利用できません。 リフレクションプローブ、鏡、カメラ映像をテクスチャにするコンポーネントはありません(@dcl/ecs のコンポーネント一覧、7.30。Material.reflectivityColor は鏡面反射の色合いであり、反射ではありません)。鏡は、反射させたジオメトリを複製し、色を付けた少し透明なガラス板の背後に配置することで再現します。また、動くものはすべて複製も動かす必要があります(プレイヤーの複製については npcs →「プレイヤーの複製」)。必要な空間を確保してください。反射には、ガラスの手前にある部屋と同じだけの奥行きが必要です。また、パーセル外のコンテンツはクリップされるため(add-3d-models)、パーセルの端に鏡を置く場合はパーセルを追加する必要があります。反射側の壁、床、天井が実物と接する部分は切り詰めてください。複製が重なった部分では継ぎ目に Z ファイティングが発生します(2026-10-12 に確認)。

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_ALL より BM_Y を優先してください。より自然な見た目になり、描画コストも抑えられます。
  • BM_ALL は、常にカメラの方を直接向かせたいパーティクルやエフェクトに適しています。
  • エンティティのローカル −Z がカメラを向きます。 Explorer はローカル +Z を視点から遠ざかる方向に向けます — Quaternion.LookRotation(billboardPos − cameraPosition) — そのため、Billboard を設定した親のローカル +Z にある子は背後に、ローカル −Z にある子は、どの視点からでも前面に描画されます。unity-explorer の Explorer/Assets/DCL/SDKComponents/Billboard/System/BillboardSystem.cs を確認済みです。コメントで +Z がカメラを向くとしている参照シーンは、Explorer v0.186.0-alpha では誤りです。
  • デフォルトでは、Billboard はアバターではなくカメラを向きます — 同じファイルにある、アクティブな仮想カメラのワールド座標です。三人称視点ではカメラとプレイヤーの位置が異なり、見える向きも異なります。そのため、隠れてから覗き込む仕掛けを確認するときは、フリーカメラで見回すのではなく、必ずプレイヤー自身のカメラを使ってください。別のもの — プレイヤーのアバター(targetEntity: engine.PlayerEntity)や任意のエンティティ — を向かせるには、targetEntity を設定します(以下の 別のエンティティに向ける — targetEntity を参照)。
  • oppositeDirection フラグはありません。 SDK7 の Billboard コンポーネントが公開するのは billboardMode と targetEntity のみで、カメラに向けるモデル面を反転する方法はありません。モデルの裏面が表示される場合は、モデルを Y 軸で 180° 回転させます(Quaternion.fromEulerDegrees(0, 180, 0))。Billboard を設定した親と子モデルを組み合わせる構成では、Billboard が親の回転を制御するため、回転は子に適用してください。
  • 移行に関する注意: SDK6 から SDK7 への移行では、SDK6 では正しく動作していたモデルが、Billboard の設定後にカメラとは反対の方向を向くことがあります。2 つの SDK では、Billboard がどちらの面をカメラに向けるかについて、動作が異なるようです。同じ対処法を使い、表示するモデルを Y 軸で 180° 回転させてください。詳細は [[migrate-sdk6-to-sdk7]] の「よくある落とし穴」を参照してください。
別のエンティティを向く — targetEntity

Billboard には、任意の targetEntity?: Entity フィールドがあります。設定すると、エンティティはカメラではなく、指定したターゲットエンティティの方を向きます。

// 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 軸上でのみターゲットの方を向きます。
  • 注意点: 参照先のターゲットエンティティが存在しないか、削除された場合、ビルボードの向きの変更は無効になります(エンティティは最後の向きで固定されます)。ターゲットが再び存在するまで、この状態が続きます。
  • マルチプレイヤー: カメラを向くビルボードはプレイヤーごとにローカルで計算されるため(各プレイヤーには自分の方を向いて見え、同期はされません)、一方、targetEntity を指定したビルボードは、ターゲットの位置がシーンの状態であるため、すべてのプレイヤーに対して同じ向きになります。すべてのプレイヤーに同じ向きで見せる必要がある場合は、targetEntity を使用してください。

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: 5,  // world units: 10 ≈ 1 m tall, so this is about 0.5 m
  textColor: Color4.White(),
  outlineColor: Color4.Black(),
  outlineWidth: 0.1,
  textAlign: TextAlignMode.TAM_MIDDLE_CENTER
})
  • fontSize はピクセルではなくワールド単位です。10 を指定すると、テキストの高さは約 1 m になります(SDK ドキュメントによると、Explorer はその値をワールド空間の TextMeshPro にそのまま渡します(TMPProSdkExtensions.cs)。また、親の Transform.scale によって倍率がかかります)。区画の向こうから読める大きな看板には 16–32、モニター、銘板、ラベルには約 1–4、またはスケールを設定した親の下で小さいサイズを使います。モニターモデルからテキストがはみ出した事例(2026-10-08)は、レイアウトの不具合ではなく、この単位の不一致が原因でした。
  • TextShape(またはテクスチャ付き平面)をルールブックとして使わないでください。ワールド内の長い段落は小さすぎて読みづらく、読むのにも時間がかかるため、プレイヤーは立ち止まって目を凝らしません。看板ひとつにつきアイデアはひとつ、単語数は 10 語以下、大きな文字を使い、残りはビジュアルとゲームプレイで表現してください(game-design →「ルール:説明するのではなく、見せる」を参照)。
  • どのような背景でも読みやすくなるよう、必ず outlineColor と outlineWidth を設定してください。
  • カスタム書体を使うには、シーン内の .ttf を fontSrc に設定し、font をフォールバックとして指定します。SDK バージョン、Explorer とモバイルのサポート状況、プレビュー方法については fonts を参照してください。
  • TextShape はローカルの -Z 方向を向きます。 エンティティの -Z 側に立つ人には正しく読めるため、モデルの +Z 面にラベルを付けるには、ラベルをモデルの子にし、わずかに +Z 方向へずらしてから、Y 軸を中心に 180° 回転させます。この動作はワールド内で 2 回確認されています(SDK 7.30.1、2026-10-12)。向きを間違えると、プレイヤーが近づく側から見たとき、看板が反転しているか、見えなくなります。
  • スペースでは列を揃えられません。Font.F_MONOSPACE を使っても同じです。 表を作るために行をスペースで埋めても、描画時に間隔が詰まってしまいます(SDK 7.30.1、2026-10-12 に 3 列のボードでワールド内確認済み)。列ごとに TextShape を 1 つ使い、それぞれを独立した x 位置に配置して TAM_MIDDLE_LEFT/TAM_MIDDLE_RIGHT で揃え、各列の中では行を \n 区切りにしてください。
テキストの配置オプション
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) セクションを参照してください。

モデルのディスプレイに画面やテキストを合わせる

モニターモデルの原点に平面や TextShape を配置すると、ベゼルの手前や奥、またはベゼルをまたぐ位置に置かれます(2026-10:画面平面がベゼルから 6 cm 前に飛び出し、その後、テキストがディスプレイ幅の 2 倍になった事例)。まずジオメトリを確認します。add-3d-models のバウンディングボックススクリプトで GLB の頂点を出力し(法線が見る人の方を向いている面に絞り込みます)、くぼんだディスプレイの長方形と奥行きを測ります。そのセッションのモニターでは、ディスプレイのサイズは 0.654 × 0.497 m で、ベゼル面から 1.6 cm 奥にありました。平面をその長方形のサイズにし、ディスプレイ面の 2~5 mm 前(ベゼル面よりは奥)に配置します。テキストでは、推測したりオーナーに確認を頼んだりするのではなく、fontSize、width/height、textWrapping を同じ長方形に合わせて設定します。

高度な 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/Images/cutout.png' }),
  transparencyMode: MaterialTransparencyMode.MTM_ALPHA_TEST,
  alphaTest: 0.5
})
  • MTM_ALPHA_TEST は MTM_ALPHA_BLEND より低コストです。滑らかな透明度が不要な場合は、カットアウトを使用してください。
発光(グロー効果)
// 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()
})
  • グローを最大限に目立たせるには、暗い albedoColor とともに emissiveColor を使用してください。
テクスチャマップ
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

setPbrMaterial と setBasicMaterial は、どちらも castShadows: boolean(デフォルトは true)を受け付けます。見た目を変えずにサーフェスが影を落とさないようにするには、false を設定します。

Material.setPbrMaterial(entity, { albedoColor: Color4.Green(), castShadows: false })

GLTF モデル内の特定のノードの影を無効にするには、代わりに GltfNodeModifiers で castShadows: false を使用してください(以下を参照)。

GltfContainer の衝突マスク

衝突マスクを使用して、GLTF モデル内の異なるメッシュレイヤーに対して、どの衝突レイヤーが反応するかを制御します。GLTF モデルには 2 つのメッシュレイヤーがあります。可視メッシュ(プレイヤーにレンダリングされて見えるもの)と、不可視レイヤー(コライダーメッシュ。内部では _collider という名前が付けられています)です。

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

VisibilityComponent をフレームごとに切り替える、距離ベースの LOD(詳細度)システムについては、{baseDir}/references/rendering-patterns.md の LOD via VisibilityComponent セクションを参照してください。

propagateToChildren

VisibilityComponent に propagateToChildren: true を設定すると、階層内のすべての子に表示状態を一度に適用できます。これにより、すべての子エンティティに個別に設定する必要がなくなります。

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 階層に従います。実行時にエンティティの親を変更すると、どの祖先から伝播された表示状態が適用されるかが変わります。
ノードごとの修飾子(GltfNodeModifiers)

GLTF モデル内の特定のノードについて、マテリアルまたは影のキャストを上書きします。

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 — Whole-Model Material Override セクションを参照してください。

修飾子の詳細(74,-8-gltfnodemodifier テストシーンより):

  • path は GLTF ノード階層のパスで、区切り文字には / を使用します(例:Scene_root/shark_skeleton/Sphere/Sphere.001)。path: '' はモデル全体を対象とし、入れ子のパスは 1 つのノードとその子孫を対象とします。
  • material は $case: 'pbr'(フル PBR:albedoColor、emissiveColor、emissiveIntensity、テクスチャなど)または $case: 'unlit'(diffuseColor など)のいずれかを受け付けます。同じ modifiers 配列内で、ノードごとに異なるケースを使用できます。
  • 動画を含むテクスチャも使用できます:pbr: { texture: Material.Texture.Video({ videoPlayerEntity: someEntityWithVideoPlayer }) }。
  • ノードごとに castShadows: false を指定すると(material は不要)、そのノードだけが影を落とさなくなります。
  • 1 つの modifiers 配列に複数のエントリを含め、1 回の呼び出しでそれぞれ異なる path を対象にできます。
  • デバッグのヒント:存在しない path を渡すと、シーンのコンソールにモデルの GLTF ノード階層全体が記録されます。正しいノード名を見つけるには、意図的に間違ったパスを指定してください。
  • GltfNodeModifiers.createOrReplace(entity, { modifiers: [...] }) で更新し、すべての上書きを削除するには GltfNodeModifiers.deleteFrom(entity) を使用します。

ノードパスは GLB に組み込まれた、エンジンから認識可能な名前であり、任意に指定できるものではありません。対象ノードに要求された種類のマテリアルがない場合、上書きが無視されることがあります。

アバターテクスチャ

プレイヤーのアバターポートレートからテクスチャを生成します:

Material.setPbrMaterial(portraitFrame, {
  texture: Material.Texture.Avatar({ userId: '0x...' })
})

プレイヤーが現在着用しているウェアラブルを身に着けた、プレイヤーの顔のクローズアップ画像をサムネイルとして取得します。

テクスチャモード

テクスチャのフィルタリングとラップ方法を制御します:

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(タイル状に反転)。

box プリミティブと plane プリミティブのデフォルト UV

MeshRenderer.setBox / setPlane を uvs なしで使うと、デスクトップ Explorer に組み込まれたレイアウト(unity-explorer Utility/Primitives/BoxFactory.cs および PlaneFactory.cs、2026年10月に確認)が使用されます。box では面ごとに異なります。box を回転させていない場合、「正立」とは、その面を見る人から見てテクスチャの最上段が上にある状態を指します。

面テクスチャの見え方
−z正立
+z180°回転
−x と +x90°回転(テクスチャの u 軸が垂直方向に走る)
上面(+y)と下面(−y)90°回転(+z 方向を向く人から見て、u は z に沿い、v は x に沿う)

そのため、向きのあるテクスチャ(文字、レンガの積み段、木目、床の模様)は、4つの壁のうち2面と床・天井で回転して表示されます(2026年10月にワールド内で確認:「壁と天井が回転して表示された」)。box を90°ヨー回転させても、問題がもう一方の2面に移るだけです。推奨順の対処法:明示的な uvs を渡します(48個の浮動小数点値、6面 × 4頂点 ×(u, v)。ヘルパーについてはドキュメントの Materials ページにある Set UVs を参照してください)。または、面ごとに1つずつ setPlane エンティティで面を構成します。または、GLB を用意します。

plane は両面です。u はローカル +x に沿い、v はローカル +y に沿うため、−z 側から見るとテクスチャは正立で左右反転せず、+z 側から見ると左右反転します。[導出(ワールド内では未確認):Quaternion.fromEulerDegrees(90, 0, 0) で平らに配置すると、反転していない側が上を向き、テクスチャの上端はワールド +z 方向を向きます。]

テクスチャトゥイーン

アニメーション付きのテクスチャパターン(スクロールする Tween.setTextureMoveContinuous、一度だけスライドする Tween.setTextureMove、TMT_OFFSET と TMT_TILING の移動タイプ、および TweenSequence によるループ処理)については、すべて wrapMode: TWM_REPEAT のテクスチャが必要です。詳しくは {baseDir}/references/rendering-patterns.md の Texture Tweens セクションを参照してください。

FlatMaterial アクセサー

Material コンポーネントには、ネストされたユニオン構造を介さずにマテリアルへアクセスできるショートカットメソッドが用意されており、マテリアルに簡単にアクセスできます:

メソッド戻り値マテリアルがない場合に例外をスローするか
Material.getFlat(entity)読み取り専用の FlatMaterialはい
Material.getFlatOrNull(entity)読み取り専用の FlatMaterial | nullいいえ
Material.getFlatMutable(entity)読み取り/書き込み可能な FlatMaterialはい
Material.getFlatMutableOrNull(entity)読み取り/書き込み可能な FlatMaterial | nullいいえ
// 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' })

シーンの例

これらの API を実際のランタイムでテストする、エンジンチームのテストシーンです。

コメント