プログラミング
Three.jsの新しいネイティブGaussian Splatsの使い方
How to Use Three.js's New Native Gaussian Splats (ben3d.ca)
要約
Three.js r186でネイティブな3D Gaussian Splattingサポートが追加され、スプラットがエンジンの第一級市民となりました。この記事では、Gaussian Splatsの用途、数行のコードでの読み込みとレンダリング方法、ファイル形式の選択、そして現実世界のキャプチャからThree.jsシーンへのインポートまでのパイプラインについて解説します。SPZ形式が最も効率的ですが、PLY、KSPLAT、SPLAT、glTF形式にも対応しています。
全文翻訳
新しくリリースされたThree.js r186では、ネイティブな3D Gaussian Splattingサポートが追加され、これは大きな進歩です。スプラットは以前からコミュニティのアドオンを通じてThree.jsで利用可能でしたが、今や組み込みのメッシュタイプと主要フォーマットのローダーを備えた、エンジンの第一級市民となりました。この投稿は、その下にある技術的な詳細を以前の記事「Adding Native Gaussian Splatting Support to Three.js」でカバーしたことに対する実践的な companion です。Gaussian Splatsが何に役立つのか、数行のコードでどのように読み込み、レンダリングするのか、どのファイル形式を選ぶべきか、そして現実世界のキャプチャからThree.jsシーンにドロップできるスプラットまでどのように移行するのかを説明します。
Gaussian Splatsとは
A Gaussian Splatは、各点がハードな頂点ではなく、ぼやけた、向き付けられた、色付きの3D楕円体(「スプラット」)である点群です。数千から数百万個のスプラットを、奥から手前にソートしてレンダリングすると、従来の表面再構築に必要なメッシュ化、UV展開、マテリアルベーキングなしで、フォトリアリスティックな画像にブレンドされます。これにより、スプラットは現実世界のオブジェクトやシーンを高い忠実度でキャプチャし、表示するのに適しています。特に、葉、毛皮、反射または半透明の表面、散らかった部屋、博物館の工芸品など、手作業でのモデリングが難しい被写体に最適です。スプラットは手作業で作成されたメッシュではなく写真から直接構築されるため、結果は従来の再構築作業のわずかな労力でソースマテリアルに似た外観になり、一度ロードされると、シーン内の他のオブジェクトと同じように扱われ、毎フレームごとにソートおよびシェーディングされます。ただし、知っておくべきスケーリングの制限があります。Gaussian Splatは、単一のキャプチャされたオブジェクトまたは部屋スケールのシーン向けに構築されており、都市ブロック全体向けではありません。レベルオブディテール(LOD)ストリーミングや空間セグメンテーション、カリングがないため、都市スケールのキャプチャやマルチギガバイトのスプラットクラウドは、スムーズに実行される前に手動でタイリングまたは削減する必要があります。大規模シーンのツールは、この基盤の上に後で構築でき、その基礎については実装に関する投稿で詳しく説明します。
SPZファイルの読み込み
以下は、最初から最後まで、パイプライン全体です。spzファイルをロードし、メッシュでラップし、レンダリングします。
```javascript
import * as THREE from 'three/webgpu';
import { SPZLoader } from 'three/addons/loaders/SPZLoader.js';
import { GaussianSplat } from 'three/addons/objects/GaussianSplat.js';
const renderer = new THREE.WebGPURenderer();
await renderer.init();
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.01, 100 );
camera.position.set( 0, 0.3, 2 );
// 1. スプラットデータをロード
const splatGeometry = await new SPZLoader().loadAsync( 'model.spz' );
// 2. メッシュでラップしてシーンに追加
const splats = new GaussianSplat( splatGeometry );
scene.add( splats );
// 3. 通常通りレンダリング。メッシュはデフォルトで毎フレーム自己ソートします。
renderer.setAnimationLoop( () => {
renderer.render( scene, camera );
} );
```
これで完了です。ローダーの呼び出しが1回、新しい GaussianSplat( geometry ) が1回、scene.add() が1回です。GaussianSplatはTHREE.Meshを拡張しているため、他のオブジェクトと同じようにシーングラフの他の部分と合成できます。そのため、トランスフォーム、visible、レイキャスティンググループなどは期待どおりに機能します。考慮すべき要件が1つあります。GaussianSplatはWebGPURendererを必要とします。レンダラーはTSLノードと深度ソート用のコンピュートシェーダーで構築されているため、import mapで three/webgpu と three/tsl の両方が解決されることを確認してください。
ファイル形式の選択
スプラットは、キャプチャまたはエクスポートされた場所に応じていくつかのファイル形式で提供され、Three.jsはそれらをカバーするために5つのローダーを提供しています。これら5つすべてが同じ内部BufferGeometry形状(position、covariance、color、およびオプションのpacked sphericalHarmonics1..3属性)を生成するため、どのローダーを使用しても、GaussianSplatは結果を同じように消費します。
| Loader | Extension | Notes |
|---|---|---|
| SPZLoader | .spz | 推奨。Nianticのコンパクトフォーマット。v4はzstd圧縮され、セクションごとにストリーミングされます。ファイルサイズが最小でロードも最速です。レガシーv1–v3(gzip)も読み取ります。 |
| GaussianSplatPLYLoader | .ply | 最も相互運用性が高い。元の3D Gaussian Splattingリサーチコードとほとんどのトレーニング/クリーアップツールのネイティブ出力であり、最も頻繁に受け取るフォーマットです。非圧縮で、頂点ごとにテキスト/バイナリであり、ディスク上では大きく、.spzと比較してロードは遅いです。 |
| KSPLATLoader | .ksplat | GaussianSplats3Dビューアで使用されるフォーマット。そのパイプラインからアセットを既に持っている場合に便利です。 |
| SPLATLoader | .splat | オリジナルの32バイト/スプラット固定フォーマット(antimatter15/splat)。非圧縮で、生成が容易ですが、ディスク上では大きいです。 |
| GLTFGaussianSplatLoaderExtension | .gltf / .glb | KHR_gaussian_splatting glTF拡張機能を実装しているため、スプラットはメッシュ、カメラ、アニメーションと一緒に通常のglTFアセット内に含めることができます。フォーマットを選択できる場合は、ビューアにはSPZバージョン4を使用してください。転送サイズが最小で、パースも最速になります。
PLYスプラットの読み込み
スプラットは、元の3D Gaussian Splattingリサーチコードと多くのトレーニングおよびクリーアップツールのネイティブ出力であるため、.plyファイルとして提供されることもよくあります。GaussianSplatPLYLoaderは、SPZLoaderおよびSPLATLoaderと同じパターンに従って、それらを処理します。
```javascript
import { GaussianSplatPLYLoader } from 'three/addons/loaders/GaussianSplatPLYLoader.js';
import { GaussianSplat } from 'three/addons/objects/GaussianSplat.js';
const splatGeometry = await new GaussianSplatPLYLoader().loadAsync( 'point_cloud.ply' );
scene.add( new GaussianSplat( splatGeometry ) );
```
スプラットが生の.plyエクスポートとしてのみ存在する場合には、このローダーを使用するのが適切です。
glTFスプラットの読み込み
スプラットがglTFファイルに埋め込まれている場合、追加のセットアップステップが1つあります。GaussianSplatはWebGPURendererを必要とするため、GLTFLoaderはglTFスプラットプラグインを自動的に登録しないため、自分で登録する必要があります。
```javascript
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { GLTFGaussianSplatLoaderExtension } from 'three/addons/loaders/GLTFGaussianSplatLoaderExtension.js';
const loader = new GLTFLoader();
loader.register( ( parser ) => new GLTFGaussianSplatLoaderExtension( parser ) );
const gltf = await loader.loadAsync( 'scene.gltf' );
scene.add( gltf.scene ); // splatプリミティブはGaussianSplatインスタンスとして到着します
```
登録すると、KHR_gaussian_splattingを使用するメッシュプリミティブはGaussianSplat(またはマルチプリミティブメッシュの場合はそのグループ)としてロードされ、ファイルにそれらが含まれている場合、通常のメッシュ、カメラ、アニメーションと混在して、返されるシーングラフに他のglTFノードと同様に着陸します。
SPLATおよびKSPLATファイルの読み込み(レガシーフォーマット)
SPLATLoaderおよびKSPLATLoaderは、主にレガシー互換性のために存在し、antimatter15/splatおよびGaussianSplats3Dビューアを中心に構築されたアセットおよびパイプラインをカバーします。APIはSPZLoaderとほぼ同じなので、それらの間で切り替えるのは、適切なローダークラスを選択し、対応する拡張機能を指定するだけです。
```javascript
import { SPLATLoader } from 'three/addons/loaders/SPLATLoader.js';
import { GaussianSplat } from 'three/addons/objects/GaussianSplat.js';
const splatGeometry = await new SPLATLoader().loadAsync( 'model.splat' );
scene.add( new GaussianSplat( splatGeometry ) );
```
```javascript
import { KSPLATLoader } from 'three/addons/loaders/KSPLATLoader.js';
import { GaussianSplat } from 'three/addons/objects/GaussianSplat.js';
const splatGeometry = await new KSPLATLoader().loadAsync( 'model.ksplat' );
scene.add( new GaussianSplat( splatGeometry ) );
```
どちらのローダーもSPZLoaderと同じBufferGeometry形状を生成するため、GaussianSplatおよびそれ以降のすべて(ソート、レンダリング、glTFエクスポート)は、どのローダーを使用してそこに到達したかに関係なく、同様に動作します。
キャプチャ、クリーアップ、変換、レンダリング
現実世界の被写体からThree.jsシーンのスプラットへの移行には、4つのステップが必要です。
1. キャプチャ
モバイルスキャンアプリで被写体の周りを歩くことから始めます。重複する写真またはビデオがアプリ(またはそのクラウドバックエンド)に送信され、そこからスプラットが再構築されます。いくつかのアプリがこの機能をうまくカバーしています。
Polycam: モバイルアプリでのGaussian Splatキャプチャ、クラウド処理。
Scaniverse: Nianticのモバイルスキャンアプリ、o