TECHNICAL NOTE · NEURAL RENDERING × CAD
CAD で使われる NURBS 曲面は形状を厳密な式で表すが、写実的な描画や学習には向かない。一方、NeRF(Neural Radiance Fields)は空間の各点の密度と色を返す連続関数をニューラルネットで表し、任意視点の画像を合成できる。本稿ではまず、点群と NeRF を結び付ける研究と、CAD・NURBS をニューラル表現と結び付ける研究を整理する。続いて、付属デモが採用した「NURBS → 有理ベジエ分解 → 面積一様な点群 → 多視点画像 → ハッシュ格子 NeRF」という手順を式とともに説明し、実測した精度を示す。同じマグカップ形状で比べると、学習に使っていない視点の PSNR は、学習画像 64 px のとき 26.9 dB、192 px のとき 32.2 dB だった(いずれも 3,000 ステップ)。
NURBS(Non-Uniform Rational B-Spline)は、制御点 Pij、重み wij、ノット列から曲面を定める表現で、円・球・トーラスなどの二次曲面も重みを使えば正確に表せる。曲面上の点は次の有理式で与えられる。
NeRF は、位置 x から密度 σ と色 c を返す関数 Fθ を学習し、画素ごとに光線に沿って積分して画像を作る。離散化した形は次のとおりで、δ は光線上の刻み幅、Ti はそこまでの透過率である。
NURBS は「正確だが離散的な操作が多い」表現、NeRF は「近似だが連続で微分可能」な表現である。NURBS から点群を経由して NeRF を作ると、CAD 形状を学習ベースの描画・生成・再構成の枠組みに載せられる。その代わり、元の式が持っていた厳密さは近似誤差に置き換わる。この交換がどの程度の誤差で成り立つかを見るのが、本稿の後半の主題である。
以降の節で使う用語を先に説明する。あわせて、NeRF の「学習」が何を入力にして、どれだけの量を使うのかを具体的に示す。
最も基本的な形のニューラルネットワークである。数値の組を入力し、「重みを掛けて足し合わせ、少し曲げる(非線形関数を通す)」処理を何段か繰り返して、数値の組を出力する。各段の計算ユニットをニューロンと呼び、入力と出力の間にある段を隠れ層と呼ぶ。
本デモの MLP は、空間の 1 点について「そこにどのくらいの濃さで、何色の物があるか」を答える関数である。重み(調整できる数値)を変えると関数の形が変わり、学習とはこの重みを目的に合うよう調整することを指す。
重みをどう調整するかを決める方法の名前である。霧の中で、足元の傾きだけを頼りに谷底を探す状況を想像するとよい。勾配は足元の傾きで、各重みをどちらへ動かせば誤差が減るかを表す。最も単純な方法(勾配降下法)は、傾きの向きに一定の歩幅で進む。Adam はこれに 2 つの工夫を加える。
g は勾配、θ は重み、η は学習率(歩幅の基準)で、m̂, v̂ は学習の初期に平均が小さく偏るのを補正した値である。細かな調整をしなくても安定して学習が進むため、深層学習で最も広く使われている。デモの「学習率」のスライダーは η にあたる。
2 枚の画像がどれだけ一致しているかを表す数値で、単位は dB(デシベル)である。画素値を 0〜1 として、全画素の誤差の二乗の平均(MSE)から次の式で求める。
値が大きいほど一致している。10 dB 上がるごとに MSE は 1/10 になる。
| PSNR | 平均的なずれ | 見た目の目安 |
|---|---|---|
| 20 dB | 約 10% | はっきり違う |
| 26.9 dB | 約 4.5% | 本デモの 64 px 学習。輪郭のぼけが分かる |
| 32.2 dB | 約 2.5% | 本デモの 192 px 学習。かなり近い |
| 40 dB | 約 1% | ほぼ見分けがつかない |
「平均的なずれ」は、画素値の二乗平均平方根誤差を最大値に対する割合で表したもの(10−PSNR/20)である。PSNR は画素ごとの差しか見ないため、人の目に目立つ輪郭のずれと、目立たない全体の色のずれを区別しない点に注意が要る。
Node.js は、JavaScript をブラウザの外(コマンドライン)で実行するためのソフトウェアである。デモを使うのに Node.js は必要なく、ブラウザだけで動く。開発中の検証にだけ使った。理由は次の 3 つである。
学習で入力するのは、点群を描いた画像と、その画像を撮ったカメラの位置・向きの組である。点群の座標そのものを NeRF のネットワークに入れることはしない。各画素には、色(RGB)、不透明度(物体か背景か)、深度(カメラからの距離)の 3 種類の値がある。
| 設定 | 点群 | 学習画像 | 画素の総数 | 1 ステップの光線 | 3,000 ステップで見る画素 |
|---|---|---|---|---|---|
| 標準 | 4 万点 | 24 枚 × 64×64 | 98,304 | 256 | 約 77 万(全画素の約 7.8 周分) |
| 高精細 | 25 万点 | 48 枚 × 192×192 | 1,769,472 | 1,024 | 約 307 万(約 1.7 周分) |
| 参考:元の NeRF 論文の合成データ | — | 100 枚 × 800×800 | 6,400 万 | 4,096 | — |
1 回の学習(1 ステップ)は次の順に進む。
これを数千回繰り返すと、どの方向から見ても学習画像と矛盾しない「空間の密度と色の分布」ができる。その結果、学習画像にない視点の画像も描けるようになる。点群の形状を直接使うのは、補助として「占有グリッド」(点がない空間を飛ばす)と「深度教師」(予測した距離が正しいかを確かめる)を ON にしたときだけである。
学習画像を増やすと描画精度は上がるが、それに応じて学習時間も延びる。ブラウザで動かせる規模に収めるため、本デモは元の論文より 1〜2 桁小さい画素数で学習している。
| 論文 | 点群の使い方 | 要点 |
|---|---|---|
| Point-NeRF Xu ほか, CVPR 2022 | 特徴ベクトル付きの点群そのものを放射輝度場の表現に使う | 光線上の標本点で近傍の点の特徴を集約して密度と色を求める。点の刈り込みと追加で外れ値や欠損を補い、NeRF と同等の画質を約 30 倍短い学習時間で得る。 |
| Points2NeRF Zimny ほか, Pattern Recognition Letters 185, 2024 | 色付き点群からハイパーネットワークで NeRF の重みを直接生成 | ShapeNet(車・椅子・飛行機)で、各物体 2,048 点の色付き点群と、200×200 の画像 50 枚を使う。生成した重みを初期値にすると、NeRF の学習が速く収束する。 |
| Depth-supervised NeRF(DS-NeRF) Deng ほか, CVPR 2022 | SfM で得た疎な点群の深度を追加の損失に使う | 少ない視点でも形状が崩れにくく、学習も速くなる。本デモの「深度教師」はこの考え方に対応する。 |
| Near-surface sampling Yoo ほか, Neural Processing Letters, 2024 | 深度から作った点群で物体表面の位置を推定し、その近くだけを標本化 | 光線あたりの標本数を約 1/4 に減らしても元の NeRF と同等の画質を得る。本デモの「占有グリッド」はこれに近い。 |
| 論文 | CAD・NURBS の使い方 | 要点 |
|---|---|---|
| CAD-NeRF Wen ほか, Frontiers of Computer Science | CAD モデルを検索し、NeRF の密度の初期化とカメラ姿勢の初期化に使う | 姿勢の分からない 3〜9 枚の画像から NeRF を再構成する。ShapeNet の 9 視点で PSNR 21.9 dB を報告。 |
| NeuroNURBS Fan ほか, arXiv 2411.10848, 2024 | NURBS の制御点・重み・ノットを直接符号化する表現学習 | UV 格子で曲面を近似する従来法と比べ、学習時の GPU メモリを 86.7%、立体の保存容量を 79.9% 削減。 |
本デモの基盤は、元の NeRF [1] と、多重解像度ハッシュ格子による高速化 Instant-NGP [2] である。点群の使い方は DS-NeRF [3] と Near-surface sampling [6] に近い。Point-NeRF [4] のように点群そのものを表現に組み込む方式は採っていない。
各ノット値の重複度が次数 p に達するまでノットを挿入すると、NURBS 曲面は多項式区間ごとの有理ベジエパッチに分かれる(The NURBS Book のノット挿入 [9])。挿入は同次座標 (wx, wy, wz, w) 上の線形補間で行うので、形は一切変わらない。パッチ上の点は、同次座標で de Casteljau 法を適用してから w で割って求める。この部分は添付のスライス断面ビューアのコードをそのまま使っている。
パラメータ空間で一様に点を取ると、球の極のように引き伸ばしの強い部分に点が集まる。そこで各パッチを 6×6(パッチが 60 枚を超える形状では 4×4)に分割して三角形の 3D 面積を求め、面積に比例して三角形を選ぶ。三角形の内部では、重心座標
で (u, v) を決め、その点を厳密な有理ベジエ式で評価する。三角形は選ぶ確率の計算にだけ使うので、点そのものは常に曲面上にある。法線はパラメータ方向の差分の外積で求め、極のように縮退する点ではパラメータを内側へずらして求め直す。点の色は「曲面ごとの色 × (0.3 + 0.7 |n·l|)」で、光源方向 l を固定したランバート陰影である。この色は視点によって変わらない。
点群を原点中心・半径 0.95 の球に収まるよう正規化し、半径 3.3 の球面上にフィボナッチ格子でカメラを並べる(画角 40°)。各点は、点の平均間隔 s = √(面積 / 点数) を半径とする円盤として Z バッファ描画し、面が隙間なく埋まるようにする。2×2 の超標本化で平均を取り、RGB・被覆率(不透明度)・深度の 3 種類の画像を作る。NeRF が見るのはこの画像だけで、点群の座標を直接は学習しない。
Instant-NGP [2] と同じ多重解像度ハッシュ格子で位置を符号化する。レベル l の格子解像度は Nl = ⌊16·bl⌋ で、b は最細レベルが指定値(256〜2048)になるよう決める。格子点の数が表サイズ T 以下のレベルでは格子点をそのまま並べ、それより細かいレベルでは次のハッシュで表を引く。
各レベルで周囲 8 格子点の 2 次元特徴を三線形補間し、全レベルを連結した 2L 次元のベクトルを MLP(隠れ層 1 層、幅 64、ReLU)に入れる。出力 4 値のうち、密度は σ = exp(o0)、色は c = sigmoid(o1..3) とする。点の色が視点によらないので、視線方向は入力しない。
1 ステップごとに、ランダムな視点・画素から光線を 256〜1,024 本選ぶ。背景色は光線ごとに乱数で決め、正解画像もその背景色と合成する。こうすると、背景色に頼って不透明度をごまかす解が選ばれなくなる。損失は色の平均二乗誤差に、不透明な画素だけで深度の二乗誤差(重み 0.05)を足したものである。勾配はすべて手で導出して実装した。密度に対する勾配は、後ろの標本点の寄与の累積和 Si を持ちながら光線を逆向きにたどって計算する。
最適化は Adam(β = 0.9, 0.99)で行う。学習率は 1×10−2 から 3 万ステップかけて 1/10 まで下げる。ハッシュ表は、そのステップで勾配が付いた要素だけを更新する。これで表を 218 まで大きくしても、1 ステップの費用は光線数に比例したままになる。
占有グリッド:点を含むセルと、その周囲(点の間隔の 1.5 倍以上)を「占有」とし、それ以外のセルでは標本点を取らない。グリッドの解像度は学習画像の解像度に合わせて 48³〜128³ に変える。光線あたりの実際の標本数は、刻み数 128〜384 に対して 3〜10 点程度まで減る。深度教師:学習画像の深度と、NeRF の期待深度 ΣTiαiti の差を損失に加える。どちらも OFF にすれば、画像だけから学習する通常の NeRF になる。
| 項目 | 方法 | 結果 |
|---|---|---|
| NURBS 評価(Python 版) | Rhino の openNURBS の PointAt / NormalAt と 500 点ずつ比較 | 誤差 ≤ 1.8×10⁻¹⁵ |
| 点の位置(JS 版) | 半径 1.5 の球・トーラス上の点と、式から求めた距離の差 | ≤ 1.3×10⁻⁷ |
| 法線(JS 版) | 球上の点で、法線と半径方向の内積 | 1.000000 |
| 一様性 | 球上 5 万点の z 座標を 10 区間に分けた度数(均一なら各 5,000) | 4,834〜5,130 |
| 面積の推定 | 点を選ぶ確率に使う面積(6×6 分割)の真値との差 | −1.0〜−1.4% |
| 逆伝播(色) | 全パラメータ種別で数値微分と比較(最大相対誤差) | 5.3×10⁻⁴ |
| 逆伝播(色+深度) | 同上 | 9.9×10⁻⁴ |
JS 版の点の誤差は、座標を 32 ビット浮動小数で保存したことによる丸め誤差の大きさである。面積の推定が 1% ほど小さいのは粗い三角形分割のためだが、これは選ぶ確率がわずかに偏るだけで、点の位置には影響しない。逆伝播の誤差も 32 ビット演算の精度の範囲に収まっている。
マグカップ形状(本体と取っ手の 2 曲面)で、学習画像 64 px の標準設定と 192 px の高精細設定を比べた。評価には、学習に使っていない視点(方位角 0.9 rad、仰角 0.45 rad)を 192×192 で描いた画像を使う。正解は点群を円盤で描いた画像で、どちらも白背景で合成して PSNR を求めた。
| 設定 | 点数 | 学習画像 | パラメータ | ms/ステップ | PSNR |
|---|---|---|---|---|---|
| 標準(64 px) | 40,000 | 24 × 64² | 788,292 | 22 | 26.9 dB |
| 高精細(192 px) | 250,000 | 48 × 192² | 8,390,980 | 約 130 | 32.2 dB |
ms/ステップは Node.js(1 スレッド)での平均で、評価の時間は除いている。ブラウザでは学習と描画が 2 つのスレッドで並行して動く。こちらの 2 コア環境では、標準設定で毎秒 28〜45 ステップ、高精細で毎秒約 5 ステップだった。
| 形状 | 占有セル率 | 標本点/光線 | PSNR(2,000 ステップ) |
|---|---|---|---|
| トーラス(有理) | 15.5% | 5.0 | 28.6 dB |
| マグカップ | 13.8% | 4.2 | 28.3 dB |
| 主翼と胴体 | 3.2% | 0.8 | 29.7 dB |
評価は、学習に使っていない視点を 48×48 で描いたもの。飛行機は画面に占める面積が小さく、背景の画素が多いため PSNR が高めに出る。PSNR 30 dB は、画素値の二乗平均平方根誤差が最大値の約 3.2% にあたる。
学習画像の解像度。物体中心の距離で画素 1 個が覆う幅は 2·3.3·tan 20° / R で、R = 64 なら 0.0375、192 なら 0.0125 である(物体の直径は 1.9)。NeRF はこれより細かい変化を学ぶ手掛かりを持たない。解像度を上げると画質は上がるが、細部を埋めるのに必要な点数・ハッシュ表の大きさ・光線上の刻み数も合わせて増やす必要がある。
点群の密度。正解画像は点を円盤で描いたものなので、点が疎だと円盤の縁の凹凸が「正解」に入り込む。ここで示した PSNR は点群の描画との一致度であり、元の NURBS 曲面との距離ではない。曲面に対する形状誤差を測るには、NeRF の密度場から面を取り出して、元の曲面との Chamfer 距離などを求める必要がある。
面が「薄い層」になる。NeRF は面を密度の高い薄い層として表すので、輪郭が半透明になり、深度にもぼけが出る。面の位置そのものを精度よく求めたい用途では、符号付き距離関数(SDF)を学ぶ NeuS 系の手法の方が向いている。
視点に依存しない色。点の色が固定のランバート陰影なので、視線方向の入力は省いている。鏡面反射のような、見る向きで変わる質感は扱っていない。
計算資源。すべて CPU 上の JavaScript で動くため、192 px を超える解像度や多数の視点では学習に時間がかかる。WebGPU の計算シェーダに移せば、同じ手順で 1〜2 桁の高速化が見込める。