0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

deck.gl v9 (WebGPU) 対応: 3D Tiles 1.1 アップグレードと互換性問題

Posted at

🎯 目的

  • 既存の都市モデルデータ(3D Tiles 1.0 / B3DM形式)を最新の標準仕様である 3D Tiles 1.1 に移行する。
  • deck.gl v9 の WebGPU レンダラで発生する互換性エラーを解消し、表示を最適化する。

💥 遭遇した主な課題

1. Error: size: 1 によるレンダリング失敗

deck.gl v9 (luma.gl v9) は WebGPU をサポートするため、属性データのバリデーションが非常に厳格になりました。

  • 現象: タイルをロードしようとすると ScenegraphLayerError: size: 1 というエラーが発生し、描画が止まる。
  • 原因: 変換元の .b3dm に含まれていたレガシーな _BATCHID 属性(各頂点がどの建物に属するかを示すID)が、GLTFのアクセサとして残存していました。この属性はスカラ(1成分)ですが、WebGPU のパイプライン設定と不整合を起こし、無効な属性として拒否されました。
  • 複雑化要因: データが Draco圧縮 されていたため、単純に JSON の attributes からキーを削除しても、Draco 拡張定義 (KHR_draco_mesh_compression) 側に定義が残っており、ローダーが展開時に属性を復活させてしまっていました。

2. 破壊的なバイナリ操作によるファイル破損

.glb ファイルはバイナリデータ(BINチャンク)のアライメント(4バイト境界)に厳密です。手作業や単純なスクリプトで JSON 部分のみを編集してサイズが変わると、後続の BIN チャンクへのオフセットがずれ、パースエラー(No valid loader found)を引き起こしました。

3.「地球2つ分」の座標ズレ (Double Transform Problem)

WebGPU 対応のために拡張機能 CESIUM_RTC (Center Coordinate) を削除し、そのオフセット値を標準の tileset.jsontransform 行列に移動させました。

  • 現象: ズームインするとモデルが消える(あるいは遥か上空や地下に飛ぶ)。
  • 原因: 親タイルにも子タイルにも「絶対座標(ECEF: 地球中心固定座標)」の移動行列を設定してしまったため。3D Tiles のトランスフォームは階層的に乗算されるため、「親の絶対座標」×「子の絶対座標」となり、地球の直径の数倍の彼方にモデルが配置されていました。

🛠️ 解決策のアプローチ

アプローチ1: 完全な「バニラ化」 (Compression & Extension Removal)

互換性問題を根本から断つため、特定のローダーや拡張機能に依存しない、最も標準的な 「バニラな GLB (Uncompressed GLB)」 形式への変換を選択しました。

  1. Draco 圧縮の解除:
    gltf-transformgltf-pipeline を使用して、幾何データを一度完全に解凍しました。これにより、ブラックボックス化していた _BATCHID 属性をメモリ上の明確なアクセサとして展開し、安全に削除可能にしました。また、deck.gl v9 側のデコーダー依存も排除できました。

  2. 拡張機能の全廃:
    CESIUM_RTC などのベンダー拡張をファイルから削除し、純粋な POSITION (頂点) と NORMAL (法線) のみを持つシンプルな GLTF 構造にしました。

アプローチ2: 再帰的な相対座標計算

tileset.json 側で座標系を正しく管理するための修正を行いました。

  1. RTC Center の抽出: 各 GLB ファイルが持っていた CESIUM_RTC.center (絶対座標) を抽出。
  2. 相対トランスフォームの計算:
    • Rootタイル: 絶対座標をそのまま transform に適用(世界座標系への配置)。
    • Childタイル: 親タイルの絶対座標と、自分自身の本来の絶対座標(RTC)の差分(相対ベクトル) を計算し、それを transform に設定。
      Child.transform = Translation(Child.RTC - Parent.AbsolutePos)

これにより、階層が深くなっても、「親の位置 + 子の相対位置 = 正しい絶対位置」が成立するようになりました。

💻 実施した処理フロー (自動化スクリプト概略)

今回の解決のために作成したスクリプト(apply_fix_batch.js)のロジック概要です。

function processTileHierarchy(tile, parentAbsolutePosition) {
    let myAbsolutePosition = parentAbsolutePosition; // デフォルトは親と同じ

    if (tile.hasContent) {
        // 1. GLBを読み込み、CESIUM_RTC (絶対座標) を抽出
        const rtcCenter = extractRTC(tile.uri);
        
        // 2. GLBから不要な属性(_BATCHID)と拡張(Draco, RTC)を削除・解凍
        //    (gltf-transform copy --no-compress 等を使用)
        cleanGLB(tile.uri);

        // 3. このタイルの「あるべき絶対位置」をRTC中心とする
        myAbsolutePosition = rtcCenter;

        // 4. 親からの相対位置を計算して transform に設定
        //    Relative = Target - Parent
        const relativeOffset = subtract(myAbsolutePosition, parentAbsolutePosition);
        tile.transform = matrixFromTranslation(relativeOffset);
    }

    // 子タイルへ再帰。このタイルの絶対位置を「親の位置」として渡す
    tile.children.forEach(child => 
        processTileHierarchy(child, myAbsolutePosition)
    );
}

📝 まとめと推奨事項

WebGPU 時代の 3D Tiles / deck.gl 利用において、以下の点が重要になります。

  1. レガシー属性のクリーンアップ: 古いコンバータで生成されたデータには、現在のグラフィックスパイプラインで予期せぬエラーを引き起こす属性が含まれていることがあります。移行時には gltf-transform 等で属性を検査・掃除することを推奨します。
  2. 座標系の理解: tileset.jsontransform は階層的に作用します。RTC (Model-Space) から Tileset (World-Space) への移行を行う際は、「絶対座標」の連鎖になっていないか、数学的な整合性を常に確認する必要があります。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?