[Astro] #176 3D Gaussian Splatting Viewer — ブラウザだけで 3DGS を見て、切って、測って、VR でも見る
はじめに
前回は、不具合の修正でした。Draco の置き場所、VRM が壊れる原因、壊れても戻るアバターを、まとめて直しました。
[Astro] #175 Draco まわりの不具合をまとめて修正 — 置き場所、VRM が壊れる原因、壊れても戻るアバター
GLB Compressor の Draco が 404 で動かない件から始まって、Draco の WASM の置き場所の整理、VRM が壊れる原因、壊れても戻るアバターの復旧まで、1 日でまとめて直しました。
lain-lab.com今回は、新しいツールです。3D Gaussian Splatting(以下 3DGS)のビューアを作りました。ブラウザにファイルを落とすだけで、見られます。
3DGS のビューアは、すでにたくさんあります。そこで、ただ見られるだけではなく、見たあとに使える機能を、いくつか足しました。不要な部分を切る、距離を測る、書き出す、録画する、そして VR で見る、です。
画面
実写のサボテン(143 万点)
1,437,067 点、SH 3 次の PLY(323.4 MB)です。読み込みは 2.32 秒、FPS は 38 でした(DevTools を開いたままの、1 回の計測です)。
クロップと計測
クロップ箱で片側を削ると、残った側が見えます。これは、断面を計算しているわけではありません。手前側の粒が消えて、奥の粒が見えている状態です。
ヤスデ(11 万点)
116,374 点、3.6 MB の .splat です。読み込みは 0.08 秒でした。three.js のサンプル(webgpu_gaussian_splat)にあったデータを、使わせてもらいました。サボテンのデータは、NOTE の記事で配られていたものです。どちらも、配布元の条件に従ってください。
動画(PC)
動画(WebXR)
CREDIT
撮影したサンプルデータはsteam studio / 3D SCAN STUDIO iris 様からお借りしています。
【ダウンロード可】3Dガウシアン スプラッティング データ(PLY)|steam studio / 3D SCAN STUDIO iris
3Dガウシアン スプラッティング データである3DGS PLYデータをダウンロードできるようにまとめました。検証などにご自由にお使いください! ※CC0で公開いたします これはNikon Z7IIの8k jpegですpostshotで一番重い8k(ダウンスケールなし) jpeg427枚 25kStep スプラット数2百万の3DGSです 重いだけあって写真と遜色無いです。 3Dガウシアン スプラッティング データ(PLY)サンプルダウンロード Box app.box.com サンプルデータ内訳 比較用にメッシュもありますpostshotから書き出した
note.comThree.js公式サイト:
three.js examples
説明なし
threejs.org3DGS とは
3DGS は、点群に似ていますが、中身が違います。
- 点群は、位置と色だけを持つ点の集まりです。近づくと、隙間が見えます
- 3DGS は、1 つずつが、大きさ、向き、不透明度を持つ、半透明の楕円の粒です。重ねて描くので、隙間が埋まって、面のように見えます。さらに、見る角度で色が変わります(球面調和関数、SH)
描き方は、次のとおりです。
- 粒を、画面上の楕円に投影する
- 奥から手前の順に並べる
- 奥から順に、半透明で重ねる
作ったもの
読み込み
.splat(1 つが 32 バイト:位置、大きさ、色、回転)- 3DGS 標準の、バイナリ
.ply(SH は 0〜3 次を、自動で判定) - ドラッグ&ドロップ、ファイル選択、URL
読み込みは、Web Worker の中で、少しずつ読みます。ファイル全体を、一度にメモリへ載せません。
表示
- 投影は EWA(楕円の形を、視点に合わせて計算する方式)です。ぼかしの足し込み(+0.3)と、AA 補正(遠景のちらつきを抑える)を、選べます
- SH は 0〜3 次を、切り替えられます
- Splat、Solid(楕円を塗りつぶす)、Points(中心点だけ)の 3 モードです
- 描画解像度のスケール、動的解像度(操作中だけ下げる)、視錐台カリング、極小の粒の除外を、入れました
- 上方向の向きは、プリセットで切り替えられます。COLMAP と 3DGS の標準は、Y が下向きなので、それを既定にしました
編集と出力
- 不透明度とサイズのフィルタ
- クロップ箱(箱の内側を消す、反転も可)と、残った件数の表示
- 2 点間の距離の計測。実際の長さを入れて、スケールを補正できます(3DGS は、学習時のスケールが不定なので、そのままでは実寸になりません)
- フィルタ適用後の
.splatと.plyの書き出し - PNG の保存(1、2、4 倍、背景の透過も可)と、録画(WebM)
カメラ
左ドラッグで回転、右ドラッグか Shift+ドラッグで移動、ホイールでズームです。ダブルクリックで、その点へ注視点が移ります。WASD と Q/E で、飛び回ることもできます。自動回転もあります。
仕組み
内部のデータ
1 つの粒を、GPU のテクスチャに、次の形で詰めています。
- 位置、大きさ:float
- 色と不透明度:8 ビット × 4
- 回転:四元数を、最大の成分を捨てて、10 ビット × 3 に詰める(10-10-10-2)
- SH:8 ビットに圧縮(符号付きの平方根で、小さい値の精度を残す)
SH なしなら 1 つ 32 バイト、SH 3 次なら、それに 48 バイトが足されます。400 万点の SH 3 次では、GPU 側のデータは 320 MB でした。
並べ替え
並べ替えは、Worker で、非同期にやります。奥行きを float のビット列として見て、16 ビットずつ 2 回の基数ソートで、奥から手前の順にします。視点が動くたびに、新しく並べ直します。
描画
粒 1 つにつき、四角形 1 枚を、インスタンス描画します。頂点シェーダーで楕円を計算して、フラグメントシェーダーで、ガウス分布の濃さを塗ります。ブレンドは、事前乗算のアルファです。
作り方:正しさを、どう確かめたか
3DGS は、描画が少しずれても、それらしく見えてしまいます。そのため、見た目ではなく、数字で確かめました。
- Worker の単体テスト(23 件):読み込みが元データと厳密に一致するか、SH の各次数、異常なファイルの扱い、ソートの順序、書き出しと再読み込みの往復、ピック
- 描画の数値テスト(23 件):ブラウザで描いた画像を、独立に書いた CPU の参照実装と比べました。参照実装は、投影のヤコビアンを、解析式ではなく、数値微分で出しています。等方、異方、視点が軸から外れた場合で、平均の誤差は 0.3 段階未満でした
- 操作テスト(23 件):回転、ズーム、ピック、クロップ、計測、書き出し、PNG、録画
- VR のテスト(19 件):後述
最大の誤差が 5 段階になる箇所がありました。原因は、ガウスの打ち切りの境界で、1 画素ずれることでした。内側の領域では、最大 1.0 段階です。
テストが見つけた、本物の不具合もありました。頂点シェーダーとフラグメントシェーダーで、整数の精度の指定がずれていて、ページが起動しませんでした。見た目の確認だけでは、気づけなかったと思います。
VR(Meta Quest 3)
WebXR で、VR にも対応しました。VR に対応したブラウザでだけ、ENTER VR ボタンが出ます。
- 左スティック:前後左右の移動
- 右スティック:左右で回転、上下で上昇と下降
- 握る(左右):縮小と拡大
- 右の B ボタン:最初の配置に戻す
最初は、モデルが、正面 1.6 m、最大辺 1.2 m に、収まって表示されます。
左右の目ごとに、行列を作って描きます。1 枚のテクスチャを、左右で分け合うので、はみ出さないように、目ごとに描画範囲を切っています。並べ替えは、頭の位置を基準に、1 回だけです。
実機がなくても確かめられるように、偽の WebXR セッションを作って、左右の目の描画が、参照実装と合うことを確かめました。視野が非対称な場合も、含めています。そのうえで、Meta Quest 3 の実機でも、動きました。ただし、最初から動いたわけではありません。次の節のとおり、PCVR(PC につないで使う方式)では動いたのに、Quest 単体のブラウザでは真っ黒でした。
Quest 単体で真っ黒になった件
PCVR(Link)では、問題なく動きました。ところが、Quest 単体のブラウザでは、VR に入ると、真っ黒のままでした。2D の画面では、同じ Quest のブラウザで、ちゃんと表示されていました。
最初は、性能の問題かと思いました。でも、小さいファイル(11 万点)でも黒でした。性能が足りないだけなら、遅くなるはずで、黒にはなりにくいはずです。
原因を探るために、診断を入れました。VR を終了したあとに、フレーム数やエラーを表示する、というものです。デバッグ画面がない Quest 単体では、こうして、終了後にページに残すしかありませんでした。PCVR では、フレームも描画も、エラーなしでした。
それでも、単体では、診断を入れた版でも黒のままでした。そこで、背景を塗るだけの、最小のテストページを作って、レイヤーの設定を 1 つずつ変えて、比べました。
| 設定 | 結果 |
|---|---|
alpha: false(ビューアと同じ設定) | 真っ黒 |
alpha: false、foveation なし | 真っ黒 |
| 設定なし(既定値) | 表示された |
alpha: true(ほかはビューアと同じ) | 表示された |
違いは、XRWebGLLayer の alpha だけでした。alpha: true にすると、Quest 単体でも、表示されました。画面は、背景を α=1 で塗っているので、見た目は、不透明のままです。
「透明にしないのだから、alpha: false のほうが軽いだろう」と考えて入れた設定でしたが、実機で確かめていませんでした。PCVR では、この設定でも動いていたので、気づけませんでした。
教訓は、2 つです。PCVR で動いても、単体の Quest で動く保証には、ならないこと。そして、デバッグ画面がない環境では、最小のテストページで、設定を 1 つずつ変えて比べるのが、早いことです。
大きいデータの検証
400 万点、SH 3 次、約 1 GB の PLY を、合成して読ませました。
- 読み込みは、最後まで通りました(エラーなし)
- GPU 側のデータは、320 MB でした
- 並べ替えは、約 0.2 秒でした
読み込みの時間は、184〜260 秒かかりましたが、この環境が、ソフトウェア描画だったためで、実機の値ではありません。
SH の圧縮の処理が、無駄に重かったので、直しました。Worker のパースだけを、Node で測ると、400 万点の SH 3 次で、85 秒から 34 秒になりました(Node の値なので、実機の Worker とは、絶対値が違います)。
確認できたこと
- 実データ(サボテン 143 万点、ヤスデ 11 万点)で、表示、クロップ、計測、録画が動く(録画ファイルは WebM 形式です)
- Meta Quest 3 で、VR が動き、左スティックで移動、右スティックで回転と上下の移動ができる(PCVR と、Quest 単体のブラウザの、両方)
- 描画は、独立の参照実装と、数値で合う
- 400 万点、約 1 GB の PLY が、落ちずに読み込める(ソフトウェア描画で)
未確認のこと・未対応のこと
- 400 万点を超えるデータや、実機の GPU での速度は、測っていません。物によっては、重いと思います
- VR の、握りと B ボタンの動作は、確かめていません。VR での FPS は、測っていません。Quest 単体で、サボテン(143 万点)は、設定を軽くすればギリギリ動きますが、コマ落ちしました(体感で 10〜20 FPS 程度。測った値ではありません)。軽いデータなら、問題ありませんでした
- 他のビューアと比べて、速い、きれいといった比較は、していません
.spz、.ksplat、圧縮 PLY には、対応していません。.spzの新しい版は、データを ZSTD で圧縮していると、Niantic の説明にありました。ブラウザの標準では解凍できないので、対応するなら、デコーダを入れる必要があります- VR の中の編集機能(計測やクロップ)と、コントローラの表示は、ありません
おわりに
3DGS は、点群に似ていても、描き方は、まったく別でした。投影の式、並べ替え、SH を、自分で書いて、数字で確かめたことで、仕組みが、だいぶわかりました。
見た目が、それらしく出ても、正しいとは限りません。参照実装と比べる、という確かめ方は、3DGS に限らず、使えると思います。
今は、v1 です。重いデータに当たったり、気になるところが出てきたりしたら、そのときに、バージョンアップします。