NVIDIA Omniverse(デジタルツインPF) と Cesium(3D地図PF) を連携させて出来ること (基本編)

## 概要

デジタルツイン分野において、リアルタイム3Dグラフィックス共同開発プラットフォームとして知名度を上げてきている **NVIDIA Omniverse** (以降、Omniverse と記載) というものが存在します。こちらについての詳細は [弊社コラム: デジタルツインの概要と現状の課題](https://www.intellilink.co.jp/column/dx/2024/021400.aspx) にて解説していますのでご覧ください。
Omniverse は、NVIDIA RTX GPU を使用したリアルタイムレンダリングによる高品質な3D表現や、物理シミュレーション、および Nucleus という中央サーバを介した複数アプリケーションとのリアルタイムコラボレーションに強みがあります。しかし、地図データを取得して表示する機能については現在発展途上であり、地図データ上に3Dオブジェクトを表示させるというユースケースにおいて、デフォルトでは(まともに)動作しません。

一方で、3D地理空間データを視覚化・分析・共有するためのオープンソースのプラットフォームとして **Cesium** というものが存在します。こちらについての詳細は <a href="https://qiita.com/haystacker/items/2b17287ebf60bf87e892" target="_blank" rel="noopener noreferrer">Qiita記事: Cesium とは</a> にて解説されていますのでご覧ください。

本記事では、上記の NVIDIA Omniverse(デジタルツインPF) と Cesium(3D地図PF) を連携させることで、具体的にどのようなことが出来るか試してみた結果をまとめます。

## Cesium for Omniverse プラグイン

Omniverse と Cesium を連携させるためのプラグインとして [Cesium for Omniverse](https://cesium.com/downloads/#cesium-for-omniverse) というものがリリースされています。これは以下の特徴を持ちます。

- 正確でフルスケールのWGS84バーチャル地球儀がベース
- Cesiumの3D地理空間プラットフォームと、Omniverseのリアルタイムレイトレーシングによるビジュアライゼーション、およびAIを活用したアナリティクスを組み合わせることで、3D地理空間ワークフローを実現
- 膨大な3D地理空間データのための効率的な3Dタイルストリーミングエンジンを搭載
- Cesium World Terrain、Microsoft Bing画像、Cesium OSM Buildings、高解像度写真測量などのグローバルコンテンツのストリーミング機能
- キュレーションされた3D地理空間データにアクセスするための Cesium ion とのインテグレーションと、実世界の3D地理空間データをストリーミング用に最適化するための3Dタイリングパイプライン
- Apache 2.0ライセンスのもとOSSのため無償で使用可能 (注: プラグインを無料で使えるという意味で、Cesium や Omniverse の使用においては用途に応じてライセンスが必要)

![スクリーンショット 2024-02-28 145204.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/1ba34480-e55f-b525-cb6f-2947a587482c.png)

(引用元：https://cesium.com/platform/cesium-for-omniverse/)

抽象的で分かりづらいと思いますので、以下で USD Composer 2023.2.2 において、Cesium for Omniverse プラグインを使用して Omniverse と Cesium を連携することで具体的に何が出来るかをご紹介します。

## 3D地図データの表示

Cesium上にAssetsとして保存されているデータをOmniverse上に表示できます。ここでは、Cesium World Terrain データと Bing Maps image を組み合わせたものを読み込んでいます。1クリックで簡単に結果を表示できます。

![スクリーンショット 2024-02-09 165427.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/c5263305-10db-25a5-a4f0-c36e49ba28de.png)

`CesiumGeoreference` の緯度・経度の座標を変更すれば、場所を移動することも簡単にできます。以下は街に移動し、Cesium OSM Buildings(建物データ)を重畳表示させてみたものです。

![スクリーンショット 2024-02-09 165624.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/2aa77a70-5a42-107f-a928-46324eb06f13.png)

このように地図データをOmniverse上で簡単に表示できます。ここまでの内容はSaveすることでUSDファイルとして保存できますが、地図データ自体はCesiumからストリーミングで取得しているものですので、USD内には含まれていないことに注意してください。

## 地図データ上にOmniverseで新たな3Dオブジェクトを配置＆座標変換

Cesiumからインポートした地図データ上に、Omniverseで生成したオブジェクトを配置することもできます。今回は18mの木を街中に配置してみました。もちろん移動・回転など自在に出来ます。

![スクリーンショット 2024-02-09 173525.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/08804b03-bcc6-8a83-23ce-ebf5af34e1c8.png)

注意したいのは、Cesium地図データの座標とOmniverse上のオブジェクト座標はデフォルトでは同期していないという点です。デフォルトでは、Cesiumの緯度・経度を変更することで表示場所を変えても、Omniverseで生成したオブジェクトは画面から動きません。この問題解決のためのソリューションも用意されています。`Cesium Globe Anchor` という設定項目を追加することで、Omniverse上で生成したオブジェクトに対して、Cesium地図データの座標に変換できます。これにより、地図を移動してもOmniverse上で生成したオブジェクトはあるべき地図上の座標に留まり続けます。

![スクリーンショット 2024-02-09 173544.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/e5ce859e-f90c-c50e-58e3-46f724880954.png)

この座標変換を行うPython APIも用意されているため、自動化が可能です。

```python
import omni.kit.commands
import omni.usd
from pxr import Sdf, UsdGeom, Usd
from cesium.usd.plugins.CesiumUsdSchemas import (
    GlobeAnchorAPI as CesiumGlobeAnchorAPI
)

trees = [
    {"name":"tree_01","latitude":37.79130, "longitude":-122.39032,"height":-28.88791},
    {"name":"tree_02","latitude":37.79114, "longitude":-122.39018,"height":-28.88791},
    {"name":"tree_03","latitude":37.79147, "longitude":-122.39042,"height":-28.88791},
    {"name":"tree_04","latitude":37.79167, "longitude":-122.39053,"height":-28.88791},
]

context = omni.usd.get_context()
stage = context.get_stage()

for t in trees:
    anchor_prim_path = "/World/"+t["name"]+"_anchor"
    payload_prim_path = anchor_prim_path+"/"+t["name"]

    anchor_prim: Usd.Prim = UsdGeom.Xform.Define(stage, Sdf.Path(anchor_prim_path)).GetPrim()
    payload_prim: Usd.Prim = UsdGeom.Xform.Define(stage, Sdf.Path(payload_prim_path)).GetPrim()
    omni.kit.commands.execute("AddPayload",
        stage=stage,
        prim_path = payload_prim_path,
        payload=Sdf.Payload(
            assetPath = "http://omniverse-content-production.s3-us-west-2.amazonaws.com/Assets/Vegetation/Plant_Tropical/Cuban_Royal_Palm.usd",
            primPath = Sdf.Path.emptyPath
        )
    )
    
    globe_anchor = CesiumGlobeAnchorAPI.Apply(anchor_prim)
    globe_anchor.GetAnchorLatitudeAttr().Set(t["latitude"])
    globe_anchor.GetAnchorLongitudeAttr().Set(t["longitude"])
    globe_anchor.GetAnchorHeightAttr().Set(t["height"])
```

## ライティングや天候・日照のシミュレーション

Cesiumからインポートした地図データおよびそれらにOmniverse上で追加したオブジェクトに対して、自在にライティングをしたり、天候・日照のシミュレーションができます。これらはOmniverseの機能(およびRTX GPU)によって実現しています。

Domelight を追加して座標を変更することでライティング方向を変更できます。

![スクリーンショット 2024-02-09 174904.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/349b2565-75fb-dca8-8a97-6841523163c7.png)

Render Setting で レイトレーシング機能を使って水面の反射具合を調整したり、関節拡散照明の設定などもできます。

![スクリーンショット 2024-02-09 175211.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/c04f1650-a6c4-ed2c-7f68-427e5342e7d8.png)

霧を細かく設定して表示させることもできます。

![スクリーンショット 2024-02-09 175421.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/e1a28fb3-2210-f4d8-facb-f1ff340dc238.png)

空や日照のシミュレーションもできます。空を設定後、緯度・経度および北の方向を設定することで、スライダを動かすことで日照シミュレーションが簡単に行えます(上から6:00, 12:00, 18:00それぞれの日照シミュレーション結果)

![スクリーンショット 2024-02-09 180611.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/c7f1a6ae-80bb-d09b-6fe9-c277a3e19f5a.png)
![スクリーンショット 2024-02-09 180624.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/386d6070-b3d8-cdd0-2e6e-7587c4d66627.png)
![スクリーンショット 2024-02-09 180634.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/d85236c6-7ae9-64b3-a6ad-a69dd5eebfd9.png)

## 画像や動画のキャプチャ

Omniverse上で画像・動画のキャプチャが可能です。特に先程の地図データ+日照シミュレーション結果を保存する際などに役立つでしょう。なお、ここでは詳細を割愛しますが、予めカメラのアニメーションを設定しておくことで、地球全体を映して始めて、サンフランシスコ上空にズームダウンするカメラショットをOmniverse上で作成することもできます。

![スクリーンショット 2024-02-09 181726.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/a219ecad-2331-b31a-01ea-f92c6a5a7807.png)

![スクリーンショット 2024-02-09 182326.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/917b2814-13e5-5f62-8af8-999aac356270.png)

## 地図上へのテレポート(1人称視点)

USD Composer 上で Teleport Extension を有効にすると、USD Presenter で標準利用できる「Teleport」機能が使用できます。これはCesiumからインポートした地図データにも有効です。テレポートしたい場所を選択すると、地図の中に入り込み1人称視点で見ることができます。

![スクリーンショット 2024-02-09 183011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/554c95ac-83ba-e0f9-ac78-1f47242fb398.png)

## Markup(コメント付加)機能

USD Composer 上で Markup Extension を有効にすると、USD Presenter で標準利用できる「Markup」(コメント付加)機能が使用できます。これはCesiumからインポートした地図データにも有効です。自由にコメントを書き込むことができます。レビュー用途で有用です。

![スクリーンショット 2024-02-09 183203.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/57413f10-f468-aa87-655e-911e5d51f13f.png)

## Measure(距離や角度の測定)機能

USD Composer 上で Measure Extension を有効にすると、距離や角度を測定できる「Measure」機能が使用できます。これはCesiumからインポートした地図データにも有効です。

![スクリーンショット 2024-02-09 183520.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/80526417-31cf-e12d-70c1-6161fc01524b.png)
![スクリーンショット 2024-02-09 183615.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692574/9a1f7c38-67f5-d644-9b02-993aa09947c1.png)

## まとめ

以上のように、OmniverseとCesiumを連携することで、Cesiumの地図データの強みと、Omniverseのシミュレーションの強みを両方活かせます。今回ご紹介したこれらの機能は、[Cesium for Omniverse Tutorial](https://cesium.com/learn/omniverse/) の左サイドバーに掲載された代表的な項目の内容になりますので、ご興味のある方はぜひ触ってみていただけば良いと思います。(CesiumとOmniverseは両方とも無償で使い始めることができます)

次回は、応用編ということで、Omniverseで直接取り込めないデータをCesium経由で取り込んでみたり、Cesiumで解析した内容の取り込み可否などを検証した結果について、ご紹介します。