ボリューム・空気・透明

ボリュームレイマーチング

Volume Ray Marching

視線に沿って密度と光を積み重ね雲や霧を描く。

1 / 10

インタラクティブデモ

デモを準備しています…

ボリュームレイマーチング

ドラッグで視点を回転、Ctrl(⌘)+ スクロールでズーム。タッチ操作は 2 本指です。

ひとことで

雲、霧、煙、爆発の炎のように、表面を持たず空間を満たしている「もや」を描く方法です。画素ごとに視線を飛ばし、一定の歩幅で少しずつ進みながら、各地点の密度と、そこで散乱されて目に届く光を積み重ねていきます。進むほど後ろの景色は見えにくくなり(透過率が下がり)、途中の霧が光って見えます。

ゲームでの使いどころ

  • 空の雲:『Horizon Zero Dawn』『Microsoft Flight Simulator』『レッド・デッド・リデンプション 2』などの立体的な雲は、ボリュームレイマーチングで描かれています。
  • 霧・もや・砂嵐:地面を這う霧、洞窟の湿気、砂漠の砂嵐。
  • 煙・爆発:流体シミュレーションの結果(3D テクスチャの密度)を描く。
  • 魔法・エネルギー体:内部が渦巻く光の球、ポータル、オーラ。

しくみ

視線上の位置 tt での密度(消散係数)を σ(t)\sigma(t) とすると、そこまでに光が弱まる割合(透過率)はビール・ランバート則で表せます。

T(t)=exp⁡ ⁣(−∫0tσ(s) ds)T(t) = \exp\!\left(-\int_0^t \sigma(s)\,ds\right)

目に届く光は、途中の各地点で散乱された光を、そこまでの透過率で弱めて足し合わせたものです。

L=∫0DT(t) σs(t) L光(t) dt  +  T(D) L背景L = \int_0^{D} T(t)\,\sigma_s(t)\,L_{\text{光}}(t)\,dt \;+\; T(D)\,L_{\text{背景}}

これを一定の歩幅 Δt\Delta t の和で近似するのがレイマーチングです。各地点では、

  1. 密度 σ\sigma を調べる(ノイズの雲、3D テクスチャなど)
  2. その地点に届く光を求める:光源の方向へも短く進み、途中の密度から自己影を計算する
  3. 光の散乱方向の偏り(位相関数)を掛ける
  4. 「この区間で散乱されて届く光」を足し、透過率を e−σΔte^{-\sigma\Delta t} 倍に下げる

透過率が十分小さくなったら(奥がもう見えない)、そこで打ち切れます。

実装のポイント

  • 開始位置をずらす(ジッター):歩幅が粗いと、同じ深さで標本を取る画素が並んで縞模様(バンディング)になります。画素ごとに開始位置を乱数でずらすと、縞が細かいノイズに変わり、時間方向に平均(TAA)すると消えます。
  • 低解像度で描く:ボリュームは重いので、画面の 1/4〜1/16 の解像度で描いて拡大したり、毎フレーム一部の画素だけ更新したりします。
  • 空白を飛ばす:密度 0 の領域は、境界ボックスや距離場で大きく飛ばします。
  • 区間内の積分:散乱光を T⋅σ⋅ΔtT \cdot \sigma \cdot \Delta t で足すと、密度が高いときに明るくなりすぎます。区間内で積分した (1−e−σΔt)(1 - e^{-\sigma\Delta t}) を使うとエネルギーが保たれます(Frostbite の改良)。

つまずきやすい点

  • 負荷が大きい:画素数 × 歩数 × 影の歩数 だけ密度を評価します。デモの設定でも 1 画素あたり数百回です。
  • 不透明な物体との前後関係:ボリュームの中に物体がある場合は、深度バッファを読んで、物体の手前で打ち切る必要があります(ボリューメトリックフォグのデモはそうしています)。
  • 自己影の歩数:影の歩数を減らすと、雲の内部の陰影が単調になります。影には粗い密度(低い解像度のノイズ)を使う工夫があります。

デモで試すこと

  1. 「視線方向の歩数」を 6〜10 まで下げ、「開始位置を画素ごとにずらす」をオフにすると、雲に縞模様が出ます。オンに戻すと縞がざらつきに変わります。
  2. 「太陽方向の自己影」をオフにすると、雲の内部が一様に明るくなり、立体感が失われます。
  3. 水色の点は、雲を横切る 1 本の視線上の標本点です。歩数を変えると間隔が変わります。
  4. 「雲の濃さ」を上げると、奥の景色が見えなくなり、内部の陰影も濃くなります。

参考資料

Unreal Engine Sky Atmosphere ↗

分野全体の資料で、この項目を直接扱っているとは限りません。