ボリュームレンダリング
煙・雲・フォグや光の筋(ゴッドレイ)がなぜ描けるのかを、参加媒質と放射伝達方程式から一気に理解できます。レイマーチングによる吸収・散乱の数値積分と、ボリュメトリックライティングの実装勘所まで押さえます。
- 1.煙や雲は面ではなく空間に分布する媒質(参加媒質)で、光は媒質を通る間に吸収・外散乱で減衰し、内散乱で新たな光を受け取る。これを表すのが放射伝達方程式。
- 2.解析解が無いため、視線に沿ってレイを一定間隔で刻み(レイマーチング)、各点で吸収・内散乱を足し込み、手前へ向かって放射輝度を数値積分する。
- 3.各サンプル点から光源へ影のレイ(シャドウレイ)を刻んで透過率を測ることで、雲の内部の陰影や光の筋(ゴッドレイ/ボリュメトリックライティング)が物理的に得られる。
面ではなく「空間そのもの」を描く
これまでのレンダリングは物体の 表面 に光が当たる前提でした。ポリゴンの面で光が反射・屈折し、面の間の空間は真空とみなします。しかし煙・雲・霧・炎・濁った水・大気そのものは、空間の各点に微粒子が分布し、光がそこを 通り抜ける間じゅう 相互作用します。こうした媒質を 参加媒質(participating media) と呼び、それを描く技法が ボリュームレンダリング です。
面の描画が「どこで光が跳ね返るか」を解くのに対し、ボリュームレンダリングは「視線が媒質を通過する経路上で、光がどう減り・どこから足されるか」を積分して解きます。だからこそ、雲の奥行き感、フォグの距離減衰、光源から差し込む光の筋(ゴッドレイ)が、アドホックな貼り込みではなく物理法則から自然に出てきます。
参加媒質と4つの相互作用
媒質内を進む光(放射輝度 L)は、微小距離 ds を進むごとに4種類のイベントで増減します。単位長さあたりの起こりやすさを表す係数(光学係数)で特徴づけます。
| 相互作用 | 係数 | L への効果 | 物理的意味 |
|---|---|---|---|
| 吸収 (absorption) | σa | 減る | 光が熱などに変わり消える(黒い煤煙) |
| 外散乱 (out-scattering) | σs | 減る | 光が別方向へ弾かれ視線から抜ける |
| 内散乱 (in-scattering) | σs | 増える | 他方向の光が視線方向へ弾かれ加わる |
| 自己発光 (emission) | σa·Le | 増える | 媒質自身が光る(炎・プラズマ) |
吸収と外散乱はどちらも視線上の光を弱めます。両者を合わせた σt = σa + σs を 消散係数(extinction coefficient) と呼び、これが「どれだけ速く光が減衰するか」を決めます。散乱のうち視線方向へ戻ってくる寄与が内散乱で、雲が白く明るく見えるのはこの内散乱が支配的だからです。逆に煤(すす)の多い黒煙は吸収が勝ちます。散乱の起こりやすさの割合 σs / σt を アルベド(single-scattering albedo) と呼び、1に近いほど白く、0に近いほど黒くなります。
放射伝達方程式と透過率
これらをまとめたのが 放射伝達方程式(radiative transfer equation, RTE) です。視線に沿う位置 s での放射輝度の変化率を、減る項と増える項で表します。地の文では擬似的に書き下します。
dL(s)/ds = − σt(s)·L(s) ← 減衰(吸収+外散乱)
+ σs(s)·∫[Ω] p(ω,ω') L(s,ω') dω' ← 内散乱
+ σa(s)·Le(s) ← 自己発光
σt : 消散係数(σa + σs)
σs : 散乱係数
p : 位相関数(散乱方向の分布。BRDF の体積版)
∫[Ω]: 全立体角にわたる他方向からの入射光の積分
減衰項だけを積分すると、区間 [a, b] を光が通り抜けたあとに残る割合= 透過率(transmittance) が得られます。これは指数関数(ベール–ランバートの法則)になります。
T(a, b) = exp( − ∫[a..b] σt(s) ds )
∫ σt ds を「光学的厚み(optical depth)τ」と呼ぶ
T = exp(−τ)
・τ = 0 → T = 1 (素通り、減衰なし)
・τ が大 → T ≈ 0 (厚い雲。奥が見えない)
一様な媒質なら τ = σt · 距離 で、距離に対し光は 指数的に 減ります。フォグが遠くほど濃く見え、近距離では薄いのはこの指数則の直接の帰結です。ゲームの距離フォグ(exponential fog)は、この透過率式を最も単純化したものに他なりません。
面の反射を表す BRDF(/graphics/shading-models-phong-pbr/ 参照)に対応するのが、体積内の散乱方向分布を表す 位相関数 p です。実用上は Henyey–Greenstein 関数が広く使われ、パラメータ g(−1 から 1)で異方性を調整します。g が正なら前方散乱(光が進行方向へ抜けやすい。雲・霧に特徴的で、太陽を背にした雲縁が明るく光るのはこのため)、負なら後方散乱、g が 0 なら等方散乱になります。位相関数は全立体角で積分すると 1 になるよう正規化され、エネルギーを増やしも減らしもしません。
レイマーチングによる数値積分
RTE には一般に解析解が無いため、視線レイを一定間隔 Δs で刻み、各サンプル点で寄与を足し込む レイマーチング(ray marching) で数値積分します。レイトレーシング(/graphics/ray-tracing-principles/ 参照)が「面との交点1つ」を解くのに対し、レイマーチングは「区間を多数の点でサンプルする」点が本質的な違いです。
視線の手前から奥へ進みながら、各点で「自己発光+内散乱」を、そこまでの透過率で重み付けして積算します。
レイマーチング(フロントトゥバック合成):
L = 0 # 積算した放射輝度
T = 1 # 手前からの透過率(1 = 素通り)
for s in ステップ列(カメラ→奥へ Δs 刻み):
σt = 消散係数(s)
# このステップでの透過率(ベール則の局所版)
Tstep = exp(−σt · Δs)
# このサンプルが視線方向へ足す光(内散乱+発光)
Lsample = 内散乱(s) + 自己発光(s)
# 手前の透過率で減衰させて積算
L += T · (1 − Tstep) · Lsample
T *= Tstep
if T < 0.01: break # ほぼ不透明。以降は寄与ゼロ、早期終了
return L, (1 − T) # 色と、その画素の不透明度
ミソは フロントトゥバック(front-to-back)合成 です。手前から進めると、それまでに蓄えた透過率 T が「奥の光がどれだけ減衰して届くか」を表すので、T がほぼ 0 になった時点で以降のサンプルは寄与しません。これを 早期レイ終了(early ray termination) と呼び、厚い媒質で大幅に高速化できます。ステップ幅 Δs を細かくするほど積分は正確になりますが、コストは反比例で増えます。均一な刻みは筋状のアーティファクト(バンディング)を生むため、各レイの開始位置を画素ごとに乱数でずらす ジッタリング で、バンディングを見た目のノイズへ分散させるのが定石です。
上の L += T·(1−Tstep)·Lsample; T *= Tstep は、半透明レイヤを重ねるアルファブレンディング(over 演算)を手前から奥へ書き下したものと数学的に同じ構造です。実際 α = 1 − exp(−σt·Δs) と置けば、参加媒質の積分は「無数の半透明レイヤを over で重ねる」操作に帰着します(固定機能パイプラインの over は逆に奥から手前へ処理しますが、蓄積透過率 T を持つこのフロントトゥバック形と結果は一致します)。医療用ボリューム(CT/MRI)の可視化では、密度からこの σt(と色)を対応づける関数を 転送関数(transfer function) と呼び、これを調整して臓器や骨を描き分けます。合成順序を取り違えると透過率の重みが狂い、正しく合成できない点に注意が必要です。
内散乱とボリュメトリックライティング
雲の内部の陰影や、光源から差し込む光の筋は、内散乱項をきちんと評価することで得られます。各サンプル点で「その点に光源からどれだけの光が届いているか」を知る必要があり、それには光源までの透過率が要ります。
内散乱(s) の評価(単一散乱):
Lscatter = 0
各光源について:
L_light = 光源の放射輝度
# 点 s から光源へ向かう影のレイを刻み、光学的厚みを測る
τ_light = Σ σt(x)·Δs (x は s→光源 の各サンプル)
T_light = exp(−τ_light) # 光源からの透過率
cosθ = 視線方向と光源方向のなす角の余弦(両方向ベクトルの内積)
Lscatter += σs(s) · p(cosθ) · T_light · L_light
return Lscatter
T_light が「その点が雲の奥にあって光源から遮られているか」を表すため、雲の陰影(セルフシャドウ)が自然に出ます。そして視線方向と光源方向のなす角に応じた位相関数 p(cosθ) が、太陽の周りのハロや、雲縁が明るく縁取られる効果を生みます。
この仕組みをシーン全体の空気に適用したものが ボリュメトリックライティング(volumetric lighting) です。空間に薄い媒質(塵やもや)が満ちていると仮定し、視線上の各点で「そこに光源の光が届くか(=影の中か)」を判定して内散乱を積算します。光源の光が遮蔽物の隙間を抜けて筋状に見える現象が ゴッドレイ(god rays/crepuscular rays、薄明光線) で、木漏れ日や窓から差す光の筋がこれにあたります。原理的には「シャドウマップで光の届く点だけ内散乱を足す」ことで得られ、リアルタイムでは視線方向のレイマーチングとシャドウマップ参照を組み合わせて実装します。
上の計算は光が媒質内で 1回だけ 散乱して視線へ戻る 単一散乱(single scattering) の近似です。実際の厚い雲では光は内部で何度も散乱し(多重散乱)、これが雲全体のふんわりした明るさと柔らかさを生みます。多重散乱を厳密に解くには体積内でパストレーシング(/graphics/path-tracing-global-illumination/ 参照)を行い、散乱イベントごとに次の方向を位相関数からサンプルします。リアルタイムでは重すぎるため、多重散乱を経験的な近似(散乱の減衰を弱めるフェイク項や、事前計算した大気テーブル)で代替するのが一般的です。単一散乱だけだと厚い雲が暗くなりすぎる、と覚えておくと破綻の原因を切り分けられます。
リアルタイム実装とパイプライン上の位置づけ
リアルタイム(ゲーム)では、画面解像度でレイマーチングすると重いため、視錐台を格子状のボクセル(フロクセル, froxel = frustum voxel)に区切り、各ボクセルへ消散係数と内散乱をあらかじめ書き込む ボリュメトリックフォグ が主流です。低解像度の3Dテクスチャに散乱結果を貯め、視線方向に一度だけ積分してから、通常の描画結果(/graphics/forward-deferred-rendering/ 参照)へ合成します。低解像度でも媒質は滑らかなので破綻しにくく、コストを大きく下げられます。
「フォグや雲がなぜ距離で指数的に濃くなるのか」を問われたら、透過率 T = exp(−∫σt ds) というベール–ランバートの法則が本質だと答えます。「レイマーチングでなぜ手前から合成するのか」には、フロントトゥバックだと蓄積透過率で早期レイ終了ができ、アルファ合成と同型になるから、と説明できると強いです。ゴッドレイの原理は「視線上の各点で光源への透過率(=影判定)を測り内散乱を積算する」単一散乱で、多重散乱まで厳密に解くなら体積パストレーシングが要る、と整理できるのが上級の理解です。
まとめ
- 参加媒質 は空間に分布する微粒子で、光は通過中に 吸収・外散乱 で減り、内散乱・発光 で増える。合わせた
σt = σa + σsが減衰速度を決める。 - 放射伝達方程式(RTE) がこれを支配し、減衰項の積分から 透過率
T = exp(−τ)(ベール–ランバートの法則) が導かれる。フォグの距離指数減衰はこの帰結。 - 解析解が無いため レイマーチング で視線を刻み、フロントトゥバック合成 で吸収・内散乱を積算する。蓄積透過率による 早期レイ終了 とジッタリングが実装の要。
- 位相関数 は BRDF の体積版で、前方散乱(Henyey–Greenstein の g)が雲縁の明るさを生む。各点から光源への 透過率(影のレイ) を測ることで雲の陰影とゴッドレイが得られる。
- 厳密な雲の明るさには 多重散乱 が要り、体積パストレーシングで解く。リアルタイムでは フロクセルベースのボリュメトリックフォグ で近似し、通常描画へ合成する。
グラフィックス Article
ボリュームレンダリングを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
レンダリング
比較で見る軸
難易度: advanced / カテゴリ: グラフィックス / タグ数: 5
導入後に効く点
解析解が無いため、視線に沿ってレイを一定間隔で刻み(レイマーチング)、各点で吸収・内散乱を足し込み、手前へ向かって放射輝度を数値積分する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- グラフィックス
- タグ数
- 5
判断チェックリスト
- 自社の用途が「レンダリング / ボリュームレンダリング」に近いか確認する。
- 強みである「煙や雲は面ではなく空間に分布する媒質(参加媒質)で、光は媒質を通る間に吸収・外散乱で減衰し、内散乱で新たな光を受け取る。これを表すのが放射伝達方程式。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。