
2026/05/28 21:15
Tsplat –ターミナルでガウススプラットを実行
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
tsplat は、Rust で実装された高機能かつ CPU 専用のターミナル描画ツールであり、SSH セッション内かローカルマシン上で直接 3D ガウシアンスプラッティングシーンを表示することを可能にし、高価な GPU やディスプレイサーバーを必要とせず設計されています。本ソフトウェアは、位置、スケール(対数空間)、回転(単位四元数)、色(球面調和関数)、不透明度で定義される 3D データを最適化されたアルゴリズムによって 2D スクリーンにプロジェクションし、深度順の並び付けには 2 パスラジックスオーティング、アルファ合成にはペインターズのアルゴリズムに基づく手法を採用しています。tsplat は
.ply ファイル経由で INRIA ガーデンシーンのような事前学習済みデータセットをネイティブに描画でき、対応する最新のユニコードグラフィックプロトコルをサポートし、非対応の場合にはハーフブロック(マインクラフト風)レンダリングへのフォールバックも可能です。パフォーマンスは非常に細かくチューニング可能で、ユーザーは描画制限(デフォルト 200k スプラット)、Rayon を用いた並列処理向けのスレッド数を調整するか、フェードアウトした視覚を修正するための生不透明度モードを有効化できます。本ツールはキーボード入力を介したインタラクティブなナビゲーションをサポートし(例:WASD でパニング、矢印キーで回転)、異なるデータセットサイズにおけるパフォーマンスベンチマークを実行するための専用バイナリ実行可能ファイルも提供します。動的な不透明度に基づくバウンディングボックスのカットオフ、キャッシュフレンドリーなタイルビンニング、ヘープ配当回避のためにフレーム単位でのスカラーバッファ再利用といった最適化を採用することで、tsplat はリソース制限のあるハードウェアにおいても複雑な 3D モデルの効率的な可視化を実現します。本文
tsplat: Rust を使ったターミナル 3D ガウス Splatting
tsplat は、Rust で書かれたツールであり、ターミナル内で 3D ガウス splatting シーンを直接レンダリングします。
- プロトコル: Unicode ハーフブロックまたは対応するグラフィックプロトコルを使用。
- アーキテクチャ: CPU 専用(GPU や表示サーバー不要)。SSH 接続でも動作します。
インストール
Rust と
cargo がインストールされている必要があります。
リポジトリからの直接インストール
cargo install --git https://github.com/darshanmakwana412/tsplat
ローカルでの構築(クローン)
git clone https://github.com/darshanmakwana412/tsplat cd tsplat cargo build --release
クイックスタート
INRIA 3DGS 形式の
.ply シーンファイルが必要です。まずは提供されている事前学習済みの庭のシーンで動作を確認してください。
ベースの実行例
tsplat path/to/scene.ply
オプションコマンド
-
スプラット数の上限変更 (デフォルト 20 万)
tsplat path/to/scene.ply --max-splats 500000 # または無制限 tsplat path/to/scene.ply --no-cap -
暗すぎるシーンへの対応 (Whitening)
tsplat path/to/scene.ply --raw-opacity
注意: ターミナルがグラフィックプロトコルに対応していない場合は、半角ブロックレンダリングにフォールバックします(マインクラフトのような見た目になります)。
操作キー
| キー | 動作 |
|---|---|
| カメラを移動(パン) |
または 左右の矢印 | イエロー軸:回転(横方向) |
または 上下の矢印 | ピッチ:傾斜(縦方向) |
または マウススクロール | ズームイン/アウト |
| オート・オービット切り替え(滑らかなループ動作、録画推奨) |
| HUD の切り替えと値調整 |
, , | 終了 |
ベンチマークテスト
前方パスの処理速度を測定します。スレッド数を指定できます。
cargo run --release --bin bench_forward \ -- --threads 4,8 \ --splats 200000 \ --ply path/to/scene.ply
ガウス Splatting リンケージチュートリアル
ガウス splat の構造と、Rust を用いた前方パスレンダリングの実装について解説します。
ガウス Splat とは何か?
3D ガウス splat は、方向を持つ楕円体であり、色と不透明度(opacity)を持ちます。 シーン内の数百万のこれらの塊が、視点に重なり合って画像を形成します。
各スプラットは以下の構造で表現されます:
pub struct Splat { pub pos: Vec3, // ワールド空間における中心位置 pub scale: Vec3, // ローカル軸に沿うサイズ(対数空間保存) pub rot: Quat, // 方向(単位四元数) pub color: Vec3, // RGB 色(球調和関数からデコード済み) pub opacity: f32, // 不透明度 [0, 1](ロジット保存) }
球調和関数(SH)の係数
SH 係数は、観測方向に依存する色の周波数領域表現です。INRIA 3DGS フォーマットでは最大バンド 3 をサポートしています(RGB あたり 48 係数)。
バンド 0 デコード例: 基底関数 $Y_0^0 \approx 0.282$ を用いて、ファイルの DC 係数から RGB に変換します。
pub const SH_C0: f32 = 0.28209479177387814; pub fn sh_band0_to_rgb(f_dc: Vec3) -> Vec3 { (Vec3::splat(0.5) + SH_C0 * f_dc).clamp(Vec3::ZERO, Vec3::ONE) }
前方パスパイプライン
ステップ 1: Splats の射影
1.1. 3D コバリアンス行列の構築 各スプラットに対して、3×3 のコバリアンス行列 $\Sigma$ を計算します。再パラメータ化により、常に正半定な行列が保証されます($M \cdot M^T$)。
let r_mat = Mat3::from_quat(s.rot); let s_mat = Mat3::from_diagonal(s.scale); let m = r_mat * s_mat; let cov3d = m * m.transpose();
1.2. ビュー空間への変換 カメラ中心に移動し、ビュー行列で変換します。
let w_mat = Mat3::from_mat4(view); let w_mat_t = w_mat.transpose(); let cov3d_view = w_mat * cov3d * w_mat_t; // ポジションも同様に变换 let p_view = Vec3::new(p_view4.x, p_view4.y, p_view4.z);
1.3. 2D への射影とヤコビアン 視点投影は非線形ですが、局所的には線型化されます。ヤコビアン $J$ を用いて 2D コバリアンスを計算します。数値安定性を高めるために対角成分に
eps2d を加えます。
let c = &cov3d_view; let inv_zc = 1.0 / zc; let inv_zc2 = inv_zc * inv_zc; let j00 = fx * inv_zc; let j02 = fx * xv * inv_zc2; let j11 = fy * inv_zc; let j12 = fy * yv * inv_zc2; // ... 行列計算と逆行列の生成 (det <= 0 チェックあり) ...
ステップ 2: バウンディングボックスの計算
スプラットのサポート無限大のため、効率的に処理するために領域を絞り込みます。
- 3$\sigma$則: ガウスの 99.7% をカバーする半径 $r = 3\sqrt{\max(\lambda_1, \lambda_2)}$。
- 透明さに基づく最適化: 不透明度が低いスプラットは、可視閾値 $\tau$ の下で急速に減少するため、領域を狭めます(カットオフ距離 $k$ を調整)。
if s.opacity <= alpha_threshold { return None; } let k2 = (2.0 * (s.opacity / alpha_threshold).ln()).min(max_k2); // ... 範囲計算と画面座標へのクリップ ...
ステップ 3: 深さソート
画家のアルゴリズム(逆順合成)のため、スプラットを深度でソートします。浮動小数点数のビットパターン順序を利用し、16 ビット×2 パスのヒストグラムソートを使用することで高速化を図ります。
pub fn sort_by_depth(projected: &mut [Projected], scratch: &mut ScratchBuffers) { // 2パスラジックスート(低位・高位ビット) // ヒストグラムカウントとオフセット計算 } // パラレル版も実装あり(50,000 エレメント以上推奨)
ステップ 4: タイルビンニング
画像を 16×16 ピクセルのタイルに分割し、各スプラットがどのタイルと重複するかを管理します。
- パス 1: カウント(各タイルへの接触数)。
- パス 2: 分散(カウント累積和を用いてインデックス配置)。
これにより、ソート済みの順序で自動的に処理でき、キャッシュヒット率が向上します。
pub fn bin_splats(projected: &[Projected], width, height, bins) { // カウント段階 -> 前積和 -> インデックス分散 }
ステップ 5: アルファ合成
深さソートされた順にスプラットを合成します。
- 透過率 T:
を用いて、透過光と現行のスプラットの色を掛け算し累積します。1 - alpha_accum - 飽和判定: 不透明度が 0.999 に達したらそのピクセル以降はスキップ。
性能最適化:
- 行定数の引き上げ:
ループの外に係数を計算し、内部ループを高速な一次/二次式計算にする。py - 早期終了: 行の最大値(ピーク)が閾値より低ければその行全体をスキップ。
fn composite_splat(...) { // ... 係数事前計算 ... for py in y0..=y1 { // row_peak チェックで早期終了判断 let dx = px - sx; // ... ガウス関数評価と累積合成 ... } }
タイル並列合成
各タイルの領域が独立しているため、ロックなしで並列処理が可能です。Rust の Borrow Checker を回避するために
unsafe ブロック内で生ポインタ(FbPtr)を使用します。
pub fn composite_tiled(...) { // タイルごとに並列処理 (rayon) // 各タイルは独自のピクセル矩形を書き込み、競合なし }
おまとめの最適化テクニック
高速近似 Exp
標準の
exp() は重いですが、64 ビット精度が必要ない場合は以下の近似を使います(Schraudolph のトリック)。
fn fast_exp(x: f32) -> f32 { let x = x.max(-87.0); // 範囲制限 let v = (12102203.0 * x + 1065353216.0) as i32; f32::from_bits(v as u32) }
スcratch バッファのリユース
ソートなどの作業用バッファを毎フレーム再割り当てるのを避け、構造体に保持してリユースします。
pub struct ScratchBuffers { pub sort_aux: Vec<Projected>, // 最初のみ拡張し、以後縮小しない }
リンケージとライセンス
- 論文: Kerbl et al. "3D Gaussian Splatting for Real-Time Radiance Field Rendering"
- 参照実装: LichtFeld-Studio (C++ Reference)
本プロジェクトは MIT ライセンス で公開されています。