「作る」から「撮る」へ

第8章では、Three.jsやmodel-viewerを使ってglbをWebで表示し、圧縮して配信するところまでを扱いました。 ここまでのモデルは、人がBlenderなどで一から作ったものが前提でした。 この章では発想を逆にして、現実にある物や場所を写真から3Dにする方法を見ていきます。

現在よく使われる手法は大きく3つあります。写真から幾何学的に形を復元するフォトグラメトリ、 ニューラルネットワークにシーンを覚えさせるNeRF、そして数百万個のぼやけた粒でシーンを表す3D Gaussian Splatting(3DGS)です。 3つとも入力は「いろいろな角度から撮った写真」で同じですが、出力の形と得意なことがまったく違います。 フォトグラメトリはメッシュを出力するので、第3章以降で見てきたパイプラインにそのまま乗ります。 NeRFと3DGSはメッシュを作らず、「どの視点から見ても正しく見える画像」を作ることに特化しています。

Poisson Surface Reconstruction

Kazhdanらが向き付き点群から閉じた面を作る手法を発表。フォトグラメトリのメッシュ化工程で今も使われる

COLMAP(Structure-from-Motion Revisited)

Schönberger & FrahmがCVPR 2016で発表。インクリメンタルSfMとMVSを備えたOSSの定番

NeRF

Mildenhallら(ECCV 2020)。MLPでシーンを表し、ボリュームレンダリングで新しい視点の画像を作る

Instant-NGP

Müllerら。多重解像度ハッシュグリッドでNeRFの学習を秒〜分単位に縮めた

3D Gaussian Splatting

Kerblら(SIGGRAPH 2023)。1080pで30fps以上のリアルタイム描画を実現

GLOMAP

グローバルSfM。COLMAPと同等以上の精度で、桁違いに速いと報告

KHR_gaussian_splatting RC

KhronosがglTF拡張のリリース候補を発表

visionOS 27

RealityKitにGaussianSplatComponentが入り、標準でスプラットを描画

レジストリ上で批准済み表示

KhronosのGitHubレジストリでKHR_gaussian_splattingのStatusがRatifiedに更新

フォトグラメトリ — 写真から幾何を解く

フォトグラメトリは「同じ点が複数の写真に写っていれば、三角測量でその点の3D位置が分かる」という古典的な幾何学に基づく手法です。 処理は大きく、カメラの位置と疎な点群を求めるSfM(Structure-from-Motion)と、 画素単位で深度を求めて密な点群を作るMVS(Multi-View Stereo)の2段に分かれます。 定番のOSSであるCOLMAPの公式チュートリアルに沿って、流れを追ってみます。

graph TB
  A[写真群] --> B[特徴点抽出 SIFT]
  B --> C[マッチング]
  C --> D[幾何検証]
  D --> E[インクリメンタルSfM]
  E --> F[バンドル調整]
  F --> G[カメラ姿勢+疎点群]
  G --> H[MVSで深度推定]
  H --> I[融合して密点群]
  I --> J[Poissonでメッシュ化]
  J --> K[テクスチャ付け]

  style G fill:#8b5cf6,stroke:#6d28d9,color:#fff
  style K fill:#14b8a6,stroke:#0d9488,color:#fff
フォトグラメトリの処理の流れ。前半(〜疎点群)がSfM、後半がMVS。NeRFと3DGSも前半のSfMの結果を入力に使う

SfM: カメラの位置と疎な点群を求める

特徴点の抽出とマッチング

最初に、各画像から角や模様の目立つ場所を特徴点として検出し、その周辺の見た目を数値ベクトル(記述子)にします。 COLMAPのデフォルトはSIFTで、学習ベースのALIKEDなども選べます。 次に画像ペアの間で記述子が似ている点同士を対応付けます。 全ペアを総当たりするexhaustive、動画のように隣り合うフレームだけを見るsequential、大量の画像向けのvocabulary treeなど、いくつかのモードがあります。

幾何検証

記述子が似ているだけでは誤った対応がたくさん混ざります。 そこで「2枚の写真が同じ3D空間を別の位置から撮ったものなら、対応点はこういう幾何的な関係を満たすはず」という制約を使って、矛盾する対応を捨てます。 一般には、RANSACで2画像間の関係(基礎行列など)を推定し、それに合わないマッチを外れ値として除きます。 ここを通ったinlierのマッチだけが、以降の復元に使われます。

インクリメンタルSfMとバンドル調整

COLMAPのSfMはインクリメンタル方式です。 条件の良い初期ペアから始め、画像を1枚ずつ登録してカメラ姿勢を求め、新しく見えた点を三角測量で3D点として追加していきます。 つながらない画像群があると、モデルが複数に分かれます。

1枚追加するたびに誤差は少しずつ積み上がるので、途中で全体をまとめて直す工程が入ります。 これがバンドル調整です。 すべてのカメラのパラメータとすべての3D点を同時に動かし、「3D点をカメラに投影した位置」と「実際に写真で観測した位置」のずれ(再投影誤差)の二乗和を最小化する非線形最小二乗問題として解くのが一般的です。 Webエンジニアの感覚で言えば、ローカルで逐次コミットしたあとに、全体の整合性をまとめて取り直すリベースに近い作業です。

MVS: 密な点群からメッシュへ

SfMで得られる点群は、特徴点の場所にしか点がない疎なものです。 MVSでは、カメラ姿勢が分かった状態で画素ごとに深度を推定します。 COLMAPでは歪み補正のあと、PatchMatch Stereoで画像ごとの深度マップと法線マップを作り、 それらを融合して法線付きの密点群にします。

最後に点群を面に変えます。代表的な手法がPoisson Surface Reconstruction(2006年)です。 物体の内側で1、外側で0になる「指示関数」を考えると、その勾配は表面以外ではほぼ0で、表面上では法線の向きを持ちます。 つまり法線付きの点は「指示関数の勾配のサンプル」とみなせます。 そこから指示関数を逆算する問題がポアソン方程式に帰着し、その等値面を取り出すと表面になります。 全点を一度に扱う大域的な解き方なので、ノイズに強いのが特長です。 メッシュができたら、元の写真を投影してテクスチャを焼き込み、必要に応じてQEMで面数を減らせば完成です。

苦手な被写体と撮影のコツ

フォトグラメトリは「同じ点が別の写真にも同じ見た目で写る」ことを前提にしています。 この前提が成り立たない被写体は苦手です。

  • 無地(白い壁など): 特徴点がほとんど検出されず、マッチングもMVSの写真一致度も手がかりを失います。
  • 反射・鏡面: 映り込みの模様が視点によって動くので、表面に固定された点ではなくなり、視点間の対応が誤ったものになります。
  • 透明: 背後の物が透けて見えるため、表面の位置と画素の色が一致しません。

COLMAPの公式チュートリアルは、撮影のコツとして次の点を挙げています。 テクスチャのある被写体を選び、無地の面には必要ならポスターや物を置くこと。 照明を一定に保ち、逆光や光沢の強い面を避けること。 各部分が最低3枚に写るようにすること。 そして、その場で回転するだけでなく、移動しながら撮って視差を得ることです。 AppleのObject Captureも、連続する写真の重なりを70%以上(最低50%)にするよう推奨しています。

NeRF — シーンをニューラルネットに覚えさせる

2020年のNeRF(Neural Radiance Fields)は、フォトグラメトリとまったく違う考え方を持ち込みました。 形を明示的に復元するのではなく、「空間のある点を、ある方向から見たら何色で、どれくらい詰まっているか」を答える関数をニューラルネットワークで学習させます。

5D入力のMLP

NeRFの本体は全結合のMLPです。 入力は位置(x, y, z)と視線方向(θ, φ)の5次元、出力は体積密度σと、視線方向によって変わる色cです。 視線方向を入力に含めているので、見る角度で変わる光沢や反射も表現できます。 ネットワークの重みは約5MBで、シーン全体がこの重みの中に埋め込まれます。

ただし、座標をそのままMLPに入れると細かい模様を学習できず、ぼやけた結果になります。 そこでNeRFはpositional encodingを使い、座標をさまざまな周波数のsin/cosに展開してから入力します。 論文では位置に10段階(L = 10)、方向に4段階(L = 4)の周波数を使っています。 低次元の入力を高周波まで表現できるようにする工夫で、Transformerの位置エンコーディングと同じ発想です。

光線上のサンプリングと透過率による積分

画像を作るときは、カメラから各ピクセルへ光線を飛ばし、その光線上に点をサンプリングしてMLPに問い合わせます。 論文では、まず粗いネットワークで64点を調べ、密度の高そうな場所に細かいネットワーク用の128点を追加で配置する階層サンプリングを使います。

集めた点の色は、手前から順に「半透明のフィルムを重ねる」ように合成します。 i番目の区間の不透明度は αi = 1 − exp(−σiδi)(δiは隣の点までの距離)です。 そこまで光が遮られずに届く割合を透過率 Ti = Πj<i(1 − αj) とすると、ピクセルの色は次の式で求まります。

C = Σi Ti αi ci

手前に濃い物があればTiが小さくなり、奥の点はほとんど寄与しません。 この計算は微分可能なので、レンダリングした画像と実際の写真との差から、MLPの重みを勾配降下で更新できます。 必要な入力は「カメラ姿勢付きの画像群」だけで、姿勢は通常COLMAPのSfMで求めます。

遅さと、Instant-NGPによる高速化

弱点は速度です。原論文では、NVIDIA V100 1枚で10万〜30万反復、学習に約1〜2日かかります。 1画像を描くには1.5〜2億回のネットワーク問い合わせが必要で、1フレームあたり約30秒かかります。 リアルタイム表示にはほど遠い数字です。

2022年のInstant-NGPは、大きなMLPの代わりに、学習可能な特徴ベクトルを多重解像度のハッシュテーブル(hash grid)に置き、小さなMLPと組み合わせました。 座標をハッシュして特徴を引くので、ネットワークが小さくても品質を保てます。ハッシュの衝突は、複数の解像度を重ねることで区別します。 論文によれば、15秒ほどで元のNeRF並み、1〜5分でmip-NeRF並みの品質に達します。 学習が「日」から「分」に縮んだことで、NeRFは研究室の外でも試せる技術になりました。

3D Gaussian Splatting — 粒を投げて並べる

2023年の3D Gaussian Splattingは、NeRFのような暗黙的な関数をやめ、シーンを数百万個の異方性3Dガウシアン(楕円体状のぼやけた粒)の集合として明示的に持ちます。 これらを画面に投影して塗り重ねる(splat)ことで画像を作り、1080pで30fps以上のリアルタイム描画を実現しました。

1個のガウシアンが持つ59個の値

学習の出発点は、SfMの副産物として得られる疎な点群です。各点をガウシアンの初期位置にし、初期の大きさは最寄り3点までの平均距離を半径とする等方的な球にします。 オリジナル実装で、球面調和関数(SH)を次数3まで使う場合、1個のガウシアンは次の59個の値を持ちます。

パラメータ個数意味
位置 μ3ガウシアンの中心座標
スケール s33軸それぞれの広がり。exp活性で正の値に保つ
回転 q4楕円体の向きを表す単位クォータニオン
不透明度 α1重ねるときの濃さ。sigmoid活性で0〜1に収める
SH係数48次数3で1色あたり16係数 × RGB。見る方向で色が変わる表現を担う
合計593 + 3 + 4 + 1 + 48

形を決めるのは3×3の共分散行列Σです。Σを直接最適化すると、楕円体として成り立つ条件(正定値性)が崩れることがあります。 そこで3DGSは Σ = R S Sᵀ Rᵀ と分解し、スケールの対角行列Sと、クォータニオンから作る回転行列Rを別々に学習します。 スケールにはexp、不透明度にはsigmoidの活性化関数をかけ、値の範囲を守りつつ滑らかな勾配を得ます。 SHは最初に次数0(方向に依存しない基本色)だけを最適化し、1000反復ごとに1バンドずつ増やして、最終的に4バンド(次数3)まで使います。

Adaptive Density Control

SfMの点群は疎なので、そのままでは粒が足りない場所と、粗すぎる場所が出ます。 3DGSは学習中に粒の数を自動調整します。 形が描き切れていない場所(under-reconstruction)では、同じ大きさのガウシアンを複製(clone)して勾配の方向へずらします。 大きなガウシアンが細部を覆ってしまっている場所(over-reconstruction)では、小さなガウシアンに分割(split)します。 ほぼ透明になった粒は削除します。 これにより、細かい場所には粒が集まり、平坦な場所は少ない粒で済むようになります。

16×16タイルと基数ソートによる高速描画

描画では、まず各3Dガウシアンをビュー変換とヤコビアンで2D画面上の楕円(Σ' = J W Σ Wᵀ Jᵀ)に投影します。 そのうえで次の手順を踏みます。

  1. 画面を16×16ピクセルのタイルに分割し、視錐台の外にあるガウシアンを捨てる
  2. 各ガウシアンを、重なるタイルの数だけ複製し、「タイルID+深度」を組み合わせたキーを付ける
  3. GPUの基数ソート(Radix sort)を1回だけ実行し、フレーム全体の粒をタイルごと・深度順に並べる
  4. タイルごとに1つのスレッドブロックが、手前から順にα合成する。ピクセルのαが飽和したら打ち切る

ピクセルごとにソートせず、ガウシアン単位で1回だけソートすることが、速さの主な理由です。 厳密には重なり方によって順序が近似になりますが、学習が収束すると影響は無視できる程度だと論文は述べています。 第6章で見たラスタライズの「物体を画面へ投げる」構造に戻ったからこそ、GPUの得意な処理に乗せられたわけです。

NeRFと3DGSは同じ式を解いている

3DGSの論文は「点ベースのα合成とNeRF型のボリュームレンダリングは、本質的に同じ画像形成モデルを共有する」と明言しています。 3DGSでも、ピクセルの色は C = Σi ci αi Πj<i(1 − αj) で計算します。 NeRFの式と同じ形です。違うのはαiの出どころで、3DGSでは2Dに投影したガウシアンの値に学習した不透明度を掛けたものになります。

違いは「サンプル点をどう見つけるか」です。 NeRFは連続的な関数なので、どこに物があるか分からず、光線を歩きながら何度もMLPに問い合わせる必要があります。 3DGSは粒の位置を明示的に持っているので、粒を画面に投げてから並べ替えるだけでサンプルが揃います。 同じ「手前から順に半透明を重ねる」計算を、NeRFは光線を歩いて点を探しながら行い、3DGSは点を先に画面へ投げて並べてから行う、と覚えておくと整理しやすくなります。

graph LR
  subgraph N[NeRF]
    N1[ピクセルから光線] --> N2[光線上を<br/>サンプリング]
    N2 --> N3[点ごとに<br/>MLPを評価]
  end
  subgraph G[3DGS]
    G1[ガウシアンを投影] --> G2[タイル+深度で<br/>基数ソート]
  end
  N3 --> A[同じα合成の式]
  G2 --> A
  A --> P[ピクセル色]

  style A fill:#8b5cf6,stroke:#6d28d9,color:#fff
NeRFと3DGSは同じα合成の式で色を決める。違うのは合成する点の集め方

品質はほぼ同等、速度は桁違い

3DGS論文のTable 1から、Mip-NeRF360データセットでの結果を見てみます。 比較相手のMip-NeRF360は、当時のNeRF系で最高クラスの品質を持つ手法です。

手法PSNR↑SSIM↑LPIPS↓学習時間FPSメモリ
Plenoxels23.080.6260.46325分49秒6.792.1GB
INGP-Big25.590.6990.3317分30秒9.4348MB
Mip-NeRF36027.690.7920.23748時間0.068.6MB
3DGS-7K25.600.7700.2796分25秒160523MB
3DGS-30K27.210.8150.21441分33秒134734MB

Mip-NeRF360データセットでのPSNR(dB、高いほど良い)

同データセットでの描画速度(fps)。Mip-NeRF360の0.06fpsは棒が見えないほど小さい

読み方には注意が必要です。PSNRはMip-NeRF360(27.69)のほうが3DGS-30K(27.21)よりわずかに高く、SSIMとLPIPSでは3DGS-30Kが上回ります。 つまり3DGSは「すべての指標で勝った」わけではなく、品質はほぼ同等で、PSNRではわずかに劣るが、描画速度が桁違いに速い手法です。 134fps対0.06fpsは2000倍以上の差で、学習時間も48時間に対して約41分です。 なお、3DGSの数値はRTX A6000で著者が計測したもので、Mip-NeRF360の品質値は原論文からの転載です。

3手法とLiDARの使い分け

ここまでの3手法に、レーザーで距離を直接測るLiDARを加えて比較します。 LiDARはiPhone 12 Pro(2020年10月発表)で初めてiPhoneに搭載され、身近になりました(Apple製品としては同年3月のiPad Proが最初です)。

手法出力得意苦手・注意既存パイプラインとの互換
フォトグラメトリメッシュ+テクスチャ形状がはっきりした物体無地・反射・透明・細い構造高い(普通のメッシュ)
NeRFMLPの重み(暗黙的)視点で変わる見た目(反射)学習・描画が遅い。編集が難しい低い
3DGSガウシアンの集合(明示的)写実的な見た目をリアルタイムに表示メモリが大きい。メッシュ化や物理シミュレーションは不得意中(標準化が進行中)
LiDAR点群(多くは色付き)絶対スケール・幾何精度テクスチャには別途カメラが必要高い(測量・BIM向け)

選び方の目安としては、ゲームやECで使う単体の物体ならフォトグラメトリでメッシュを作るのが素直です。 建物の内外や景色の見た目をそのまま残したいなら3DGSが向いています。 測量や建築など寸法が重要な用途にはLiDARを使い、写真でテクスチャを補うのが基本です。 NeRFは研究の文脈では重要ですが、実際にキャプチャして使う場面では3DGSへの移行が進んでいます。

実際に試すためのツール

撮影と学習

  • Scaniverse(Niantic): スマートフォン上で処理が完結し、無料で3DGSを作れます。PLYやSPZで書き出せます。
  • Polycam: iOS・Android・Webに対応し、クラウドで処理します。LiDAR、フォトグラメトリに加え、2023年9月から3DGSにも対応しています。
  • Postshot(Jawset): Windows用のデスクトップアプリで、NVIDIA GPUが必要です。動画や画像から直接学習できます。
  • Brush: WebGPUを使うため、CUDAなしでAMDやIntelのGPUでも動きます。Apache 2.0。入力にはCOLMAPかnerfstudio形式のカメラ姿勢が必要です。
  • nerfstudio splatfacto: 研究寄りのフレームワークnerfstudioに含まれる3DGS実装で、描画部分にgsplatを使います。
# nerfstudioで動画から3DGSを学習する流れ(CUDA対応のPyTorchが前提)
pip install nerfstudio

# 動画からフレームを切り出し、COLMAPでカメラ姿勢を推定
ns-process-data video --data my.mp4 --output-dir data/my

# splatfactoで学習(メモリ目安は約6GB)
ns-train splatfacto --data data/my

# 学習結果を .ply として書き出す
ns-export gaussian-splat --load-config outputs/.../config.yml --output-dir exports/splat

公式実装のライセンスとgsplat

3DGSを製品に組み込むときは、ライセンスに注意が必要です。 論文の公式実装(graphdeco-inria/gaussian-splatting)は、InriaとMPIIをライセンサーとする研究・評価目的の非商用ライセンスで、商用利用には事前の明示的な許可が要ります。 公式実装から派生したラスタライザも同じ条件を引き継ぐので、知らずに依存すると下流のプロダクトまで制約が及ぶおそれがあります。

これに対し、nerfstudio-projectのgsplatはApache 2.0の独立実装です。 公式実装の結果を再現する学習スクリプトを同梱しつつ、Mip-NeRF360で学習メモリを最大4分の1に、学習時間を最大15%短くしたと公表しています。 論文は誰でも読めるがコードは非商用、という研究ライセンスの制約を、寛容なライセンスで再実装することで回避した形です。

Webで表示する

Webエンジニアにとって身近なのは表示側です。 Three.js向けには、World LabsのSparkが現在の推奨です。 以前よく使われたGaussianSplats3Dは開発を終了し、READMEでSparkを勧めています。 Spark 2.0(2026年4月)はLoDツリー、注視方向を優先するストリーミング、固定量のGPUメモリを使い回すページングを備え、1億スプラットを超えるシーンを対象にしています。

import * as THREE from 'three';
import { SplatMesh } from '@sparkjsdev/spark';

const scene = new THREE.Scene();
// 通常のMeshと同じようにシーンへ追加できる
const butterfly = new SplatMesh({ url: 'https://sparkjs.dev/assets/splats/butterfly.spz' });
butterfly.position.set(0, 0, -3);
scene.add(butterfly);

編集にはPlayCanvasのSuperSplatが便利です。 ブラウザだけで、学習時にできた浮遊ゴミ(floaters)を消したり、範囲を切り抜いたりできます。 PlayCanvasはオープンな圧縮形式SOGも公開しており、属性をWebP画像に量子化することで、400万ガウシアンで1GBのPLYを42MB(約95%減)にした例を示しています。 学習結果の無圧縮PLYは数百MBになりがちなので、配信前にSuperSplatで掃除し、SOGやSPZに変換するのが定石です。

標準化とプラットフォーム対応

KHR_gaussian_splatting

第7章で見たglTFにも、3DGSを格納する拡張が加わりました。KHR_gaussian_splattingは、ガウシアンを点プリミティブとして扱い、位置・回転・スケール・不透明度・SHを頂点属性として格納します。 描画アルゴリズムは規定せず、現時点で定義されているカーネルは楕円(ellipse)だけです。 非対応のビューアでも点群として表示できるようにフォールバックが考えられています。

経緯を整理すると、2025年8月に実験的な拡張として発表され、2026年2月3日にKhronosがリリース候補(RC)を公表しました。 このときは2026年第2四半期の批准を目標としていました。 その後、2026年9月のコミットでKhronosのGitHubレジストリ上のStatusが「Complete, Ratified by the Khronos Group」に更新され、GitHubのレジストリ上で批准済みと表示されています。 ただし、執筆時点で批准を告知するプレスリリースは見当たりません。 対応実装としては、CesiumJS 1.139やCesium for Unrealが2026年3月に対応し、Babylon.jsやPlayCanvasも続いています。

サイズの問題は別の拡張で扱う方針で、Niantic SpatialのSPZとQualcommのL-GSCが圧縮拡張として提案されています。 圧縮拡張の最終的な状態はまだ固まっていないので、採用する際は最新のレジストリを確認してください。

まとめ

  • フォトグラメトリはSfM(特徴点→マッチング→幾何検証→インクリメンタル復元→バンドル調整)とMVS(深度推定→密点群→Poisson→テクスチャ)でメッシュを作る。無地・反射・透明が苦手
  • NeRFは5D入力のMLPを光線上で積分する。学習1〜2日・約30秒/フレームと遅いが、Instant-NGPのhash gridで学習が分単位になった
  • 3DGSは1個59パラメータのガウシアンを、16×16タイルと1回の基数ソートでα合成する。品質はMip-NeRF360とほぼ同等で、速度は2000倍以上
  • NeRFと3DGSは同じα合成の式を使い、点の集め方が違う
  • 公式3DGS実装は非商用、gsplatはApache 2.0。KHR_gaussian_splattingはGitHubのレジストリ上で批准済みと表示され、visionOS 27も対応した

この章では、カメラで撮った現実を3Dにする方法を見てきました。 では、写真すら撮らず、テキストや1枚の画像から3Dモデルを作ることはできるでしょうか。 実は、この章で登場したNeRFや3D Gaussianは、3D生成AIの内部表現としても使われています。 第10章では、SDSからTRELLIS.2・Hunyuan3Dまで、3D生成AIの系譜をたどります。

主な参考資料

理解度チェック

問題 0 / 50%
Q1

フォトグラメトリの処理を、最初に行うものから順に並べてください。

矢印ボタンで正しい順序に並べ替えてください

1画像から特徴点を抽出する
2インクリメンタルSfMでカメラ姿勢と疎点群を求める
3Poisson再構成でメッシュ化する
4PatchMatch Stereoで深度マップを作る
5画像ペア間でマッチングし幾何検証する