28. ボリュームレンダリングと高度なNPR

此の章は2つの部分から出来ています。
前半はボリュームレンダリングです。
霧、煙、雲、空、医用スキャンの様に、空間其の物が光を吸収し散乱させる物を扱います。
後半は高度なノンフォトリアリスティックレンダリングです。
アニメ調のキャラクター、線画、ハッチング、水彩、画像フィルタの様に、絵がどう見えるべきかを敢えて自分で決める技術です。

どちらも第1部の約束を果たします。
第12章(12.6節)は関与媒質とランベルト・ベールの法則を紹介し、ボリュームレンダリングは後で扱うと書きました。
第16章は可視化(16.15節)を見せて放射・吸収モデルとGPUでのボリュームレンダリングを約束し、其のNPRの節(16.8〜16.12節)が後半の出発点です。
第9章(9.3節)ではボクセルとCTデータを学びました。何れも繰り返さずにリンクします。

此れは第2部の内容で、検定のシラバスを超えますが、シラバスの3つの項目を深めます。
写実的表現法(第12章の関与媒質)、そして第16章の可視化とノンフォトリアリスティックレンダリングです。
第23章のモンテカルロ法も使い、此の章のボリュームレンダリング積分は第29章のNeRFが使う物其の物です。

28.1 関与媒質の中の光

起こり得る4つの事

霧を横切る光線は、小さな粒子や分子に出会います。
其の度に、光線に沿って進む放射輝度Lを次の4つの何れかが変えます:

  • 吸収:光は熱に変わって無くなります。光線は暗くなります。
  • 外向き散乱:光は別の方向へ跳ね返ります。光線は暗くなりますが、光は失われず、別の所へ進みます。
  • 内向き散乱:他の方向へ進んでいた光が光線の中へ跳ね返って来ます。光線は明るくなります。霧の中で街灯の周りが光るのは此の為です。
  • 放射(発光):炎、爆発、熱いガスの様に、媒質自身が光を出します。光線は明るくなります。

4つの相互作用:吸収は光を熱に変え、外向き散乱は光を別の方向へ送り、内向き散乱は光源からの光を加え、放射は媒質が作る光を加える

係数

此れらの事象がどれだけ起こるかは、「毎メートル」の単位を持つ係数で表します:

  • 吸収係数σa
  • 散乱係数σs
  • 消散係数(減衰係数)σt=σa+σs。光線から光を取り除く物全てを数えます。

σtは単位長さ当たりの確率として読みます。
非常に短い距離dsで、光は確率σtdsで相互作用します。
2つの派生した数が、媒質を一目で表します。
散乱アルベドρ=σs/σtは、相互作用の内、吸収ではなく散乱である割合です。
雲や真っ白な霧では1に近く、煤では0に近くなります。
平均自由行程1/σtは、相互作用と相互作用の間の平均の距離です。

例。
σa=0.02、σs=0.08毎メートルの霧は、σt=0.1毎メートル、アルベド0.08/0.1=0.8、平均自由行程10 mです。

一般に係数は点ごとに変わり(第26章のシミュレーションの煙は非一様な媒質です)、波長によっても変わります。
レンダラーはRGBのチャンネルごとに1つの値を持つ事で波長を扱います。
一様な媒質では、係数は何処でも同じです。

28.2 透過率とランベルト・ベールの法則

導出

消散だけがある(光が加わらない)媒質の中で光線を追います。
短い距離dsで光の割合σtdsが取り除かれるので:

dLds=−σt(s)L

減る速さが残っている量に比例します。
指数関数的な減衰の式です。
0からdまでの経路での解は:

L(d)=L(0)T(d),T(d)=e−τ(d),τ(d)=∫0dσt(s)ds

Tは透過率、つまり生き残る光の割合で、τは光学的厚さ、つまり経路に沿って足し合わせた消散です。
一様な媒質ではτ=σtdで、12.6節のランベルト・ベールの法則T=e−σtdに戻ります。

計算例

上の霧(σt=0.1毎メートル)では、第12章と同じくT(10)=e−1=0.368、T(20)=e−2=0.135です。
次は2層の媒質です。
σt=0.5の濃い煙が2 m、其の後にσt=0.1の薄い霧が3 m:

τ=0.5⋅2+0.1⋅3=1.3,T=e−1.3=0.273

層ごとに計算しても同じ数になります:e−1⋅e−0.3=0.368⋅0.741=0.273。
光学的厚さは足され、従って透過率は掛け合わされます。
此の章の全てのレイマーチャーが此の性質を使います。
1つの累積の透過率を持ち、小さな歩みごとの透過率を其れに掛けて行くのです。

消散係数0.05、0.1、0.2毎メートルと、2層の媒質の、距離に対する透過率。2層の媒質の5 mでの値0.273に印

色の付いた吸収

チャンネルごとに係数を持つと、透過率は光に色を付けます。
純水は青よりも赤をずっと強く吸収します。
大まかな値は、赤で0.3毎メートル、緑で0.06、青で0.01です。
10 mの水の後の透過率はe−3=0.05、e−0.6=0.55、e−0.1=0.90です。
深い水は青緑に見え、深く潜る程赤が消えて行きます。
第26章で触れた深い水の色合いは丁度此れです。

28.3 ボリュームレンダリング積分

放射伝達方程式

次は4つの事象を全て起こします。
光線に沿った短い距離dsで、吸収と外向き散乱がσtLdsを取り除き、放射がσaLedsを、内向き散乱がσsLsdsを加えます:

dLds=−σtL+σaLe+σsLs,Ls(𝐱,ω)=∫S2p(ω′,ω)Li(𝐱,ω′)dω′

Leは放射される放射輝度、Liは方向ω′から其の点に届く放射輝度、pは位相関数(28.4節)で、ω′からの光の内どれだけがωへ散乱されるかを表します。
積分は方向の球全体S2に渡ります。
媒質の中の点は、面の様に半球からだけでなく、あらゆる方向から光を受けられるからです。
此れが放射伝達方程式(RTE)で、グラフィックスより遥か前から天体物理学で研究されて来ました(スブラマニアン・チャンドラセカールの著書『Radiative Transfer』、1950年)。

カメラのレイに沿って積分する

目から出て距離Dで面(又は背景)に当たるカメラのレイに沿って此の式を解くと、ボリュームレンダリング積分が得られます:

L=∫0DT(t)[σa(t)Le(t)+σs(t)Ls(t)]dt+T(D)Lsurf,T(t)=e−∫0tσt(s)ds

媒質の後ろの面は、経路全体の透過率で暗くなった放射輝度を寄与します。
其の間の各点は、自分が放射する光や此方へ散乱する光を、其の点と目の間の透過率で暗くして加えます。
第12章の「指数フォグ」は、内向き散乱の光Lsが一定の霧の色である一様な媒質という特別な場合です。

ジェームズ・カジヤとブライアン・フォン・ハーゼンが此れをグラフィックスに持ち込み(「Ray Tracing Volume Densities」、SIGGRAPH 1984)、単一散乱で雲をレンダリングしました。
ネルソン・マックスのサーベイ「Optical Models for Direct Volume Rendering」(IEEE Transactions on Visualization and Computer Graphics、1995年)は、簡略化を梯子の様に並べました。
吸収のみ、放射のみ、放射と吸収、影付きの単一散乱、多重散乱です。

放射・吸収モデル

医療や科学の直接ボリュームレンダリング(第16章)は、梯子の3段目、放射・吸収モデルを使います。
散乱を捨て、各点は色c(t)で光り、消散σ(t)で光を遮ります。
どちらも伝達関数が決めます:

L=∫0DT(t)σ(t)c(t)dt+T(D)Lbg

光源も影も無いので物理的ではありませんが、速く、データの中を見るのに分かり易い方法です。

積分から合成へ

レイを長さΔtの区間に分け、区間iではσiとciが一定だと仮定します。
Tiを目から区間の始まりまでの透過率とすると、区間の寄与は

∫0ΔtTie−σisσicids=Tici(1−e−σiΔt)=Tiαici,αi=1−e−σiΔt

で、其の後の透過率はTi+1=Ti(1−αi)です。
A=1−Tとすれば、此れは16.15節の前から後ろへの合成C←C+(1−A)αici、A←A+(1−A)αi其の物です。
つまりサンプルの不透明度αは自由なパラメータではなく、1つの区間の消散1−e−σΔtなのです。

計算例。
長さΔt=0.5の3つの区間が、σ=0.4,2.0,1.0、c=1.0,0.5,0.8を持ちます:

区間 αi=1−e−σiΔt Ti 重みTiαi 後のC
1 1−e−0.2=0.1813 1 0.1813 0.1813
2 1−e−1.0=0.6321 0.8187 0.5175 0.1813+0.5175⋅0.5=0.4400
3 1−e−0.5=0.3935 0.3012 0.1185 0.4400+0.1185⋅0.8=0.5348

最後の透過率は0.3012⋅(1−0.3935)=0.1827で、レイ全体のランベルト・ベールの法則と一致します:e−(0.4+2.0+1.0)⋅0.5=e−1.7=0.1827。

不透明度の補正

伝達関数は普通、1つの基準の歩幅Δref(多くは1ボクセル)に対する不透明度として設計されます。
レンダラーが別の歩幅Δを使うなら、1−α=e−σΔなので変換しなければなりません:

α=1−(1−αref)Δ/Δref

例: 1ボクセル当たりαref=0.5で、歩幅が半ボクセルならα=1−0.50.5=0.293です。
其の半歩を2回で1−(1−0.293)2=0.5となり、1歩と同じです。
補正しないと、α=0.5の半歩2回で1−0.52=0.75になり、細かく標本化しただけでボリュームが濃く見えてしまいます。

28.4 位相関数

位相関数が表す物

光が散乱する時、何処へ行くのでしょうか?
位相関数p(ω′,ω)は、方向ω′で届いた光が方向ωへ出て行く、立体角当たりの確率密度です。
媒質にとってのBRDFの役割を果たしますが、違いが2つあります。
球全体を覆う事と、吸収は既にσaが扱っているので正規化されている事、つまり∫S2pdω=1です。
霧、煙、空気では粒子の向きがランダムなので、pは入って来る進行方向と出て行く方向の間の散乱角θだけに依存します。
θ=0は真っ直ぐ前、θ=180°は真後ろです。

  • 等方的:全ての方向でp=14π=0.0796。
  • レイリー(分子、12.6節):p(θ)=316π(1+cos2θ)。前方と後方は等しく(0.1194)、横は其の半分(0.0597)です。
  • 水滴や塵によるミー散乱は前方に強く尖ります。正確なミーの関数はギザギザで計算が重いので、レンダラーは滑らかなモデルを使います。

ヘニエイ・グリーンスタイン位相関数

標準的な滑らかなモデルは天文学から来ました。
ルイス・ヘニエイとジェシー・グリーンスタインが、星間塵による光の散乱の為に提案した物です(「Diffuse Radiation in the Galaxy」、Astrophysical Journal、1941年):

pHG(θ)=14π1−g2(1+g2−2gcosθ)3/2

唯一のパラメータg∈(−1,1)は非対称パラメータで、散乱した光についてのcosθの平均です。
g=0は等方、g>0は前方へ、g<0は後方へ散乱します。
雲の水滴はgが0.85程度、大気の靄はg=0.8で表す事が良くあります。

計算例:g=0.8。
θ=0では分母が(1−g)3なので、p=14π1+g(1−g)2=14π1.80.04=454π=3.581です。
θ=90°ではp=14π0.361.641.5=14π0.362.100=0.01364です。
θ=180°ではp=14π1−g(1+g)2=14π0.23.24=0.00491です。
前方は後方の(1+g1−g)3=93=729倍です。
幾つかのgについて数値積分してみると、合計は毎回1、平均の余弦はgに等しく、g=0.8では散乱した光の94.9%が前方の半球へ行きます。

左は、g = −0.3、0、0.3、0.5のHG位相関数の4π倍の極座標プロット。右は、g = 0、0.3、0.6、0.8の同じ関数を散乱角に対して対数目盛で描いた物と、破線のレイリー

図は4πpを描いているので、等方散乱が値1になります。
太陽が後ろにある時に雲の縁が光る(「シルバーライニング」)のは、前方散乱の為です。

HGの標本化

パストレーサーは、此の密度で散乱方向を引く必要があります。
第23章の逆関数法で閉じた式が得られます。
一様乱数uに対して:

cosθ=12g(1+g2−(1−g21−g+2gu)2)

方位角は入って来る方向の周りに一様に引きます。
例: g=0.8、u=0.5なら0.360.2+0.8=0.36なので、cosθ=1.64−0.12961.6=0.944、θ=19.3°です。
u=0.5は中央値なので、散乱した光の半分は真っ直ぐ前から19.3°以内に留まります。

実際の媒質は、2つのHGの山の混合で当てはめる事が良くあります。
例えば0.7pHG(0.6)+0.3pHG(−0.2)は、シルバーライニングの為の前方の山に、日の当たる側が平らに見えない様に少しの後方散乱を加えた物です。
重みの和が1の、正規化された関数の重み付き混合は、やはり正規化されています。

28.5 単一散乱のレイマーチング

単一散乱

内向き散乱の項は、全ての点に全ての方向から届く光Liを必要とし、其れ自身が媒質全体に依存します。
完全な問題は大域照明と同じ位難しいのです。
最初の大きな簡略化が単一散乱です。
光源から真っ直ぐ来て、カメラの方へ1回だけ散乱する光だけを考えます。
方向ωsunに沿って強さEで届く太陽の様な平行光源なら、内向き散乱の項は

Ls(t)=p(θ)ETlight(t)

になります。
Tlight(t)は其の点から光源の方への透過率で、媒質の自己遮蔽です。
光源の方への2つ目の短いマーチ(「シャドウレイ」)で求めるか、シャドウマップや事前計算した影のボリュームから読みます。

アルゴリズム

レイマーチングは、レイに沿って歩を進めてボリュームレンダリング積分を評価します(GLSLシリーズ第14章の距離場のレイマーチングは、代わりに面の方へ進む親戚です):

  1. レイと媒質のバウンディングボックスの交差を求め、[t0,t1]を得ます。
  2. 歩幅Δtを選び、t0に1歩のランダムな割合を足した所から始めます(ジッター)。
  3. 各歩でσtとσsを引き、Tlightと源S=σsp(θ)ETlight+σaLeを計算します。
  4. 其の歩の寄与をLに加え、累積の透過率Tにe−σtΔtを掛けます。
  5. Tが非常に小さくなったら(例えば0.01未満、16.15節の早期レイ終了)、又はレイが箱を出たら止め、TLsurfを加えます。

歩の寄与を正しく

歩iの素直な寄与はTiSiΔt(リーマン和)です。
しかし歩の中でも透過率は下がり続けるので、歩幅が大きいか媒質が濃いと、リーマン和は大き過ぎる値になります。
セバスチャン・イレール(「Physically Based and Unified Volumetric Rendering in Frostbite」、SIGGRAPH 2015 Advances in Real-Time Renderingコース)が広めたエネルギー保存版は、28.3節と同じ様に歩の中の減衰を正確に積分します:

ΔLi=TiSi1−e−σt,iΔtσt,i

計算例。
28.1節の霧(σs=0.08、σa=0.02、σt=0.1毎メートル)の中の、長さ20 mの光の筋です。
光は減衰せずにE=10で全ての点に届き、位相関数はg=0.5のHG、散乱角は30°です:

p=14π0.75(1.25−0.866)1.5=14π0.750.2380=0.2508,S=0.08⋅0.2508⋅10=0.2007

Sが一定なので、積分は閉じた形になります:

L=S1−e−σtDσt=0.2007⋅1−e−20.1=0.2007⋅8.647=1.735

5 mの4歩では、リーマン和は各歩の始まりの透過率1、0.6065、0.3679、0.2231を使い、0.2007⋅5⋅2.1975=2.205となって27%明る過ぎます。
エネルギー保存の歩は、歩数に関係無く丁度1.735を与えます。
一様な媒質では、其の仮定(歩の中でSとσtが一定)が正しいからです。
非一様な媒質ではもう正確ではありませんが、ずっと速く収束し、其の歩が散乱出来る以上の光を決して加えません。

コードで

下のC++プログラムが2つを比べます(線型代数シリーズのlinalg.hhのVec3、dot、normalizeを使います):

#include <cmath>
#include <cstdio>
#include <initializer_list>
#include "linalg.hh"

constexpr float kPi = 3.14159265f;

// Henyey-Greenstein: cosTheta = cosine of the angle between the light's direction of travel
// and the scattered direction (1 = straight on, -1 = straight back)
float phaseHG(float cosTheta, float g) {
  float d = 1.0f + g * g - 2.0f * g * cosTheta;
  return (1.0f - g * g) / (4.0f * kPi * d * std::sqrt(d));
}

// Single scattering along one camera ray through a homogeneous medium of length dist.
// The light reaches every point unattenuated (a "light shaft"), so the exact answer is known.
struct Medium { float sigmaS, sigmaA; };

float marchNaive(Medium m, float source, float dist, int steps) {
  float sigmaT = m.sigmaS + m.sigmaA, dt = dist / steps, T = 1.0f, L = 0.0f;
  for (int i = 0; i < steps; ++i) {
    L += T * source * dt;          // Riemann sum: S times the transmittance at the step's start
    T *= std::exp(-sigmaT * dt);
  }
  return L;
}

float marchEnergyConserving(Medium m, float source, float dist, int steps) {
  float sigmaT = m.sigmaS + m.sigmaA, dt = dist / steps, T = 1.0f, L = 0.0f;
  for (int i = 0; i < steps; ++i) {
    float stepT = std::exp(-sigmaT * dt);
    L += T * source * (1.0f - stepT) / sigmaT;   // integral of S e^(-sigmaT s) over the step
    T *= stepT;
  }
  return L;
}

int main() {
  Medium fog{0.08f, 0.02f};                        // per meter
  Vec3 lightTravel = normalize(Vec3{0.0f, -0.5f, -std::sqrt(3.0f) / 2.0f});
  Vec3 toCamera = {0.0f, 0.0f, -1.0f};             // scattered toward the camera
  float cosTheta = dot(lightTravel, toCamera);     // cos 30 degrees
  float E = 10.0f;                                  // light arriving at each point
  float source = fog.sigmaS * phaseHG(cosTheta, 0.5f) * E;
  float sigmaT = fog.sigmaS + fog.sigmaA, dist = 20.0f;
  float exact = source * (1.0f - std::exp(-sigmaT * dist)) / sigmaT;
  std::printf("cos theta = %.4f, p = %.4f, S = %.4f, exact L = %.4f\n",
              cosTheta, phaseHG(cosTheta, 0.5f), source, exact);
  for (int steps : {1, 4, 16, 64})
    std::printf("%3d steps: naive %.4f   energy-conserving %.4f\n", steps,
                marchNaive(fog, source, dist, steps),
                marchEnergyConserving(fog, source, dist, steps));
}

cos theta = 0.8660, p = 0.2508, S = 0.2007, exact L = 1.7351
  1 steps: naive 4.0134   energy-conserving 1.7351
  4 steps: naive 2.2049   energy-conserving 1.7351
 16 steps: naive 1.8458   energy-conserving 1.7351
 64 steps: naive 1.7624   energy-conserving 1.7351

素直な和は、2%以内に入るのに64歩必要です。

歩幅、ジッター、コスト

決まった歩のパターンは、全ての画素で同じ奥行きで媒質を標本化するので、バンディングとして現れます。
滑らかなグラデーションの中の等高線の様な縞です。
各画素の始点を1歩のランダムな割合だけ(毎フレーム違う値で)ずらすと、バンディングが細かいノイズに変わり、其れを時間方向アンチエイリアシング(20.6節)が平均して消します。

コストはすぐに膨らみます(28.9節で雲について数えますが、1フレームに数億回の密度の参照です)。
其の為リアルタイムのレンダラーは、低い解像度でマーチし、前のフレームを再利用し、出来る所では光の透過率を3次元テクスチャに事前計算します。

1つの雲、4通り

手続き的な雲(ノイズで削った塊)を、太陽を右上の後ろに置いて、上の方法でレイマーチングした物です:

手続き的な雲を、吸収のみ、等方の単一散乱、2つの山のHGの単一散乱、多重散乱のオクターブ近似を加えた物でレンダリングした物

吸収だけでは暗い煙の染みになります。
等方の単一散乱は雲を照らしますが、一様でぼんやりしています。
2つの山のHGの混合は、太陽に向いた薄い縁を光らせます。
最後の画像は、次の節の多重散乱の近似を加えた物です。
光の透過率は、格子の全ての点から太陽の方へマーチして、影のボリュームに事前計算しました。

28.6 多重散乱と不偏なトラッキング

雲に単一散乱では足りない理由

雲ではアルベドが殆ど1で光学的厚さが大きいので、光は出て行く迄に何度も散乱します。
ρ=0.99なら、100回散乱した後に残っているエネルギーの割合は0.99100=0.366です。
大部分がまだ其処にあるのです。
単一散乱は其れを全て捨てるので、本物の雲が真っ白な所で、其の雲の内部は暗い灰色になります。

アーティスト向けの近似

マグナス・レニンゲ、クリストファー・クーラ、ヴィクトル・ルンドクヴィスト(「Oz: The Great and Volumetric」、SIGGRAPH 2013のトーク)は、単一散乱のオクターブを足して多重散乱を近似します。
各オクターブは弱くなりますが、遮蔽が少なく、位相関数が丸くなります:

Lms=∑i=0N−1bi[single scattering with σt→aiσt toward the light and g→cig]

(括弧の中は「光源の方へのσtをaiσtに、gをcigにした単一散乱」です。)
既定値はN=8、a=b=c=12です。
考え方は、何度も散乱した光は方向を「忘れて」おり(丸い位相関数)、多くの経路で深い点に届くので、薄い遮蔽の媒質の様に働く、という物です。

例。
雲の深い所の点が、太陽の方へ光学的厚さτ=4を持ちます。
オクターブ0は透過率e−4=0.0183を得ます。
オクターブ1は重み0.5とe−2=0.135で0.0677、オクターブ2は0.25⋅e−1=0.0920です。
位相関数を無視すると、3つのオクターブで0.0183+0.0677+0.0920=0.178となり、単一散乱の値の約10倍です。
4枚目の雲の画像の様に、内部が明るくなります。

不偏な方法:自由行程の標本化とデルタトラッキング

映画のレンダラーは正しい答えが欲しいので、ボリュームをパストレースします(第23章)。
経路は媒質の中を飛び、ランダムに衝突し、位相関数から引いた方向へ散乱し、と続きます。
一様な媒質では、次の衝突までの距離は密度σte−σttを持ち、第23章の通り逆関数法でt=−ln(1−u)/σtと引けます。

非一様な媒質では、光学的厚さを積分しなければならないので、閉じた式が有りません。
デルタトラッキング(ウッドコック・トラッキングとも言います)は、原子核物理の技で此れを解きます(E・R・ウッドコック等、1965年、原子炉の中性子輸送の計算の為)。
媒質を何もしない架空の粒子で満たし、全体の密度が何処でも一定の優関数(メジャラント)σ¯≥σt(𝐱)になる様にします。
架空の粒子との衝突はヌル衝突で、経路は其のまま進みます。

  1. 仮の距離Δ=−ln(1−u)/σ¯を引き、其の分だけ進みます。
  2. 経路が媒質を出たら止めます。
  3. そうでなければ、確率σt(𝐱)/σ¯で実衝突を受け入れます。ヌル衝突なら手順1へ戻ります。

此れは不偏です。
実衝突は本当の非一様な媒質が置く場所に丁度落ち、緩い優関数の代償はヌル衝突が増える事だけです。

計算例。
長さ2のレイが、[0,1)でσt=0.5、[1,2)でσt=1.5の媒質を横切り、優関数はσ¯=2です。
乱数u=0.3,0.55,0.8,0.4,0.5,0.2は歩−ln(1−u)/2=0.178,0.399,0.805,0.255,0.347,0.112を与えます。
従って仮の衝突はt=0.178,0.578,1.382,1.638,1.984で、次の2.096は外です。
t=0.178での受け入れ確率は0.5/2=0.25で、2つ目の乱数0.6によりヌル衝突になります。
t=0.578では、2つ目の乱数0.1が0.25未満なので実衝突です。
其処で経路は散乱するか吸収されます。

計算例:優関数2の下の階段状の消散、比率トラッキングの係数0.75と0.25を持つ仮の衝突、0.578の実衝突、2.096での脱出

シャドウレイでは、衝突点ではなく透過率だけが必要です。
比率トラッキング(ヤン・ノヴァーク、アンドレア・セレ、ヴォイチェフ・ヤロシュ、「Residual Ratio Tracking for Estimating Attenuation in Participating Media」、SIGGRAPH Asia 2014)は、同じ仮の衝突をレイの終わり迄辿り、各点で1−σt/σ¯を掛けます。
上の歩みでは0.75⋅0.75⋅0.25⋅0.25⋅0.25=0.00879です。
1回の歩みはノイズの多い推定値ですが、平均は正しくなります。
200,000回の歩みを試すと、比率トラッキングで0.1346、デルタトラッキングで脱出した経路の割合として0.1351が得られ、正確な値T=e−(0.5+1.5)=0.1353と一致しました。
仮の衝突の回数の期待値はσ¯⋅D=4なので、実用のレンダラーは粗い格子の領域ごとに優関数を持ち、σ¯を小さく保ちます。

28.7 空と大気

モデル

12.6節は、レイリーの1/λ4で青空を説明しました。
レンダリング出来る大気には数値が要ります。
セバスチャン・イレール(「A Scalable and Production Ready Sky and Atmosphere Rendering Technique」、EGSR 2020)は、地球に次の値を使います:

成分 散乱(毎m) 吸収(毎m) 高度hに対する密度 位相関数
レイリー(分子) (5.802,13.558,33.1)×10−6 0 約e−h/8km レイリー
ミー(エアロゾル) 3.996×10−6 4.40×10−6 約e−h/1.2km HG、g=0.8
オゾン 0 (0.650,1.881,0.085)×10−6 高度25 km付近の層 無し

(分子の8 km、エアロゾルの1.2 kmというスケールハイトは、エリック・ブルネトンとファブリス・ネイレの「Precomputed Atmospheric Scattering」(2008年)等にある古典的な値です。)
3つのレイリーの値は、波長680、550、440 nmに対して丁度1/λ4に従います:33.1/5.802=5.705=(680/440)4。

計算例:沈む太陽が赤い理由

指数関数的な密度では、真上への光学的厚さは∫0∞βe−h/Hdh=βHです。
大気全体が、厚さ8 kmの海面の空気の層の様に働きます。
レイリー散乱だけについて:

チャンネル 真上のτ 真上のT 地平線のτ 地平線のT
赤 0.0464 0.955 1.64 0.194
緑 0.108 0.897 3.84 0.0216
青 0.265 0.767 9.37 0.0000856

地平線の列には経路の長さが要ります。
半径Rの惑星では、水平なレイはh≈s2/(2R)で昇るので、∫0∞e−s2/(2RH)ds=πRH/2=π⋅6371km⋅8km/2=283kmです。
真上の35.4倍です(本物の球で数値積分しても同じになります)。
正午の太陽は青の約4分の1を失うだけですが、地平線では青が1万分の1未満に削られ、赤は19%残ります。
大気の外の太陽光と比べると、赤は青の2,262倍強くなるのです。

空のレンダリング

西田友是等は、単一散乱で地球の大気をレンダリングしました(「Display of the Earth Taking into Account Atmospheric Scattering」、SIGGRAPH 1993)。
ブルネトンとネイレは多重散乱を4次元の表に事前計算し、物理ベースの空をリアルタイムで使える様にしました。
イレールの2020年の方法は小さな表を毎フレーム計算し直し、モバイルでも動きます。
256 × 64の透過率の表、32 × 32の多重散乱の表、200 × 100のスカイビューの表、そして空気遠近法の為の32 × 32 × 32のフロクセルのボリューム(28.8節)です。
空気遠近法とは、遠くの山を青っぽく、コントラストを低く見せる霞の事です。

表のレイリー・ミー・オゾンの値による単一散乱の空。太陽高度25°と2°

表の値を使い、全ての視線のレイを40歩でレイマーチングしました(各歩で12歩の光のマーチ)。
太陽が高いと空は青く、太陽の周りにミー散乱の白い輝きがあります。
太陽が低いと、地平線近くの空気に届く光は青を失い、橙色になります。
オゾンは薄明に効きます。
主に緑と赤を吸収するので(係数を見て下さい)、頭上の空が灰色になるのを防ぎます。

28.8 ゲームのボリューメトリックフォグ:フロクセル

問題

ゲームが欲しいのは、何十もの光源で照らされ、影があり、窓から光の筋が差し込み、局所的な霧のボリュームがあり、透明な物体にも正しく掛かる霧です。
全ての画素で全ての光源についてマーチするのは、余りにも高価です。

視錐台に沿ったボクセル

バート・ウロンスキ(「Volumetric Fog: Unified, Compute Shader Based Solution to Atmospheric Scattering」、SIGGRAPH 2014 Advances in Real-Time Renderingコース、『アサシン クリード4 ブラック フラッグ』から)は、此の仕事を視錐台に沿った3次元テクスチャに移しました。
xとyは粗いタイルで画面に沿い、zはスライスで奥行きに沿います。
其のセルはフロクセル(frustum voxel、視錐台のボクセル)と呼ばれます。
ゲームは160 × 90 × 64(プラットフォームによっては128スライス)を使いました。
720pの画像の画素数と同じ位のセル数です。
アルゴリズムはコンピュートシェーダー(GLSLシリーズ第15章)で動きます:

  1. 媒質を注入します。各フロクセルで、全体の霧、高さの霧、動くノイズ、そして(後のエンジンでは)局所的な霧のボリュームからσsとσtを計算します。
  2. 各フロクセルを照らします。光源ごとのσsp(θ)EVを足し、可視性Vはシャドウマップから得ます。
  3. フロクセルの各列に沿って前から後ろへ積分し、各フロクセルに、カメラから其処までに溜まった内向き散乱の光と、其処までの透過率を保存します。
  4. 適用します。全ての画素(不透明でも透明でも)が自分の奥行きでボリュームを引き、L=LsurfaceT+Linを計算します。3次元テクスチャの1回の読み込みと、1回の積和です。

イレールの2015年のFrostbiteのトーク(28.5節)は此れを物理ベースで統一された物にしました。
全ての霧、ボリューム、パーティクルが散乱・吸収・位相・放射の同じパラメータを共有し、積分はエネルギーを保存し、フロクセルはジッターした位置で標本化されてTAAの様にフレームを跨いでブレンドされます。
執筆時点では、フロクセルによるボリューメトリックフォグは大きなエンジンの標準です。

指数的なスライス

カメラの近くでは霧が細かくなければなりませんが、遠くでは1スライス数メートルで十分です。
其の為スライスは、ニアの距離nとファーの距離fの間に指数的に配置します:

zk=n(fn)k/N,k=0,…,N

例: n=1 m、f=128 m、N=64スライス。
各スライスは前のスライスの1281/64=1.0788倍の奥行きです。
最初のスライスは1.079 mで終わり(厚さ7.9 cm)、16番目、32番目、48番目のスライスは3.36 m、11.31 m、38.05 mで終わり、最後のスライスは118.65 mから128 mまで(厚さ9.35 m)です。
メモリは160⋅90⋅64=921600フロクセルで、各8バイト(16ビット浮動小数点数4つ)なら1つのボリュームで7.4 MBです。
レンダラーは幾つか持ちます(媒質の性質、照らした物、積分した物、前のフレーム)。

上から見た視錐台を、等間隔の奥行きスライスと、1から16の間の指数的な奥行きスライスでフロクセルに分けた物

図は近くのスライスが見える様にn=1、f=16、8スライスを使っています。
指数的なスライスは毎回1.414倍深くなり(1、1.41、2、2.83、4、…、16)、フロクセルは近くから遠くまで似た形を保ちます。
低い解像度の代償は、ぼけと、薄い壁を通る光の漏れです。

28.9 雲

雲はフロクセルには大き過ぎ、粗い格子には細か過ぎるので、ゲームは直接レイマーチングします。
アンドリュー・シュナイダーとネイサン・ヴォス(「The Real-time Volumetric Cloudscapes of Horizon Zero Dawn」、SIGGRAPH 2015 Advances in Real-Time Renderingコース)は、多くのゲームが倣った設計を説明しました:

  • 形:2次元のウェザーマップ(被覆率、降水、雲の種類)と雲の種類ごとの高さの勾配を、3次元ノイズで削ります。1283の「パーリン・ウォーリー」テクスチャ(反転したウォーリーノイズで膨らませたパーリンノイズ)と、細部の為の323のウォーリーテクスチャです(ノイズはGLSLシリーズ第8章)。
  • 照明:ベールの法則、シルバーライニングの為のヘニエイ・グリーンスタイン、光に向いた縁を暗くする「パウダー」の項。光は太陽の方への円錐の中で6回標本化します。
  • コスト:視線のサンプルは64、地平線の方では128まで増やします。4 × 4のブロックごとに1画素だけを毎フレーム更新し、残りは再投影します。約2 msで、約20 msから下がりました。

例。
視線のサンプル64と、其々に光のサンプル6なら、1920 × 1080のフレームで1920⋅1080⋅(64+64⋅6)=9億2900万回の密度のサンプルが要ります。
16画素に1画素だけ更新すれば5800万回になります。

映画は逆の道を行きます。
雲や煙は疎なボクセル格子(OpenVDB、ケン・ムセス、「VDB: High-Resolution Sparse Volumes with Dynamic Topology」、ACM Transactions on Graphics、2013年)として保存し、デルタトラッキングと多くのバウンスでパストレースします。

28.10 可視化の為のGPUボリュームレンダリング

GPUの上のデータ

可視化(第16章)では、媒質は測定又はシミュレーションしたスカラー場で、伝達関数付きの放射・吸収モデルが普通の選択です。
GPUは此れを速くします:

  • ボリュームは3次元テクスチャに置き、16.15節の3線型補間はハードウェアが無料で行います。
  • 伝達関数は小さな1次元テクスチャで、サンプルの値がテクスチャ座標です。
  • 各画素のレイはバウンディングボックスから作ります。シェーダーでレイと箱の交差を求めるか(下の「スラブ」テスト)、箱の表面と裏面をラスタライズして入る点と出る点を得ます。イェンス・クリューガーとリュディガー・ヴェスターマン(「Acceleration Techniques for GPU-based Volume Rendering」、IEEE Visualization 2003)は、此の方法でGPUのレイキャスティングを作り、早期レイ終了と空領域スキップを組み込みました。

// Emission-absorption ray casting through a volume in the unit cube [0,1]^3, front to back.
float4 marchVolume(Texture3D<float> volume, Texture1D<float4> transfer, SamplerState linearSampler,
                   float3 origin, float3 dir, float stepSize, float refStep) {
  float3 invDir = 1.0 / dir;                              // slab test against the unit cube
  float3 t0 = (float3(0.0, 0.0, 0.0) - origin) * invDir;
  float3 t1 = (float3(1.0, 1.0, 1.0) - origin) * invDir;
  float3 tMin = min(t0, t1);
  float3 tMax = max(t0, t1);
  float tNear = max(max(max(tMin.x, tMin.y), tMin.z), 0.0);
  float tFar = min(min(tMax.x, tMax.y), tMax.z);
  float3 color = float3(0.0, 0.0, 0.0);
  float alpha = 0.0;
  for (float t = tNear; t < tFar && alpha < 0.99; t += stepSize) {   // early ray termination
    float value = volume.SampleLevel(linearSampler, origin + t * dir, 0);
    float4 c = transfer.SampleLevel(linearSampler, value, 0);
    float a = 1.0 - pow(1.0 - c.a, stepSize / refStep);              // opacity correction
    color += (1.0 - alpha) * a * c.rgb;
    alpha += (1.0 - alpha) * a;
  }
  return float4(color, alpha);
}
// Emission-absorption ray casting through a volume in the unit cube [0,1]^3, front to back.
vec4 marchVolume(sampler3D volume, sampler1D transfer,
                 vec3 origin, vec3 dir, float stepSize, float refStep) {
  vec3 invDir = 1.0 / dir;                                // slab test against the unit cube
  vec3 t0 = (vec3(0.0) - origin) * invDir;
  vec3 t1 = (vec3(1.0) - origin) * invDir;
  vec3 tMin = min(t0, t1);
  vec3 tMax = max(t0, t1);
  float tNear = max(max(max(tMin.x, tMin.y), tMin.z), 0.0);
  float tFar = min(min(tMax.x, tMax.y), tMax.z);
  vec3 color = vec3(0.0);
  float alpha = 0.0;
  for (float t = tNear; t < tFar && alpha < 0.99; t += stepSize) {   // early ray termination
    float value = textureLod(volume, origin + t * dir, 0.0).r;
    vec4 c = textureLod(transfer, value, 0.0);
    float a = 1.0 - pow(1.0 - c.a, stepSize / refStep);              // opacity correction
    color += (1.0 - alpha) * a * c.rgb;
    alpha += (1.0 - alpha) * a;
  }
  return vec4(color, alpha);
}
// Emission-absorption ray casting through a volume in the unit cube [0,1]^3, front to back.
float4 marchVolume(Texture3D<float> volume, Texture1D<float4> transfer, SamplerState linearSampler,
                   float3 origin, float3 dir, float stepSize, float refStep) {
  float3 invDir = 1.0 / dir;                              // slab test against the unit cube
  float3 t0 = (float3(0.0, 0.0, 0.0) - origin) * invDir;
  float3 t1 = (float3(1.0, 1.0, 1.0) - origin) * invDir;
  float3 tMin = min(t0, t1);
  float3 tMax = max(t0, t1);
  float tNear = max(max(max(tMin.x, tMin.y), tMin.z), 0.0);
  float tFar = min(min(tMax.x, tMax.y), tMax.z);
  float3 color = float3(0.0, 0.0, 0.0);
  float alpha = 0.0;
  for (float t = tNear; t < tFar && alpha < 0.99; t += stepSize) {   // early ray termination
    float value = volume.SampleLevel(linearSampler, origin + t * dir, 0);
    float4 c = transfer.SampleLevel(linearSampler, value, 0);
    float a = 1.0 - pow(1.0 - c.a, stepSize / refStep);              // opacity correction
    color += (1.0 - alpha) * a * c.rgb;
    alpha += (1.0 - alpha) * a;
  }
  return float4(color, alpha);
}

歩幅と空領域スキップ

2.6節の標本化定理からは、1ボクセルに少なくとも2つのサンプル、つまり半ボクセルの歩幅が良いと分かります。
例: 5123のボリュームの対角線は3⋅512=887ボクセルなので、対角線のレイは最大1,774サンプルを取ります。

典型的なボリュームの多くは、空気や、伝達関数が完全に透明にする他の値です。
空領域スキップは、ボリュームを例えば83ボクセルのマクロセルに分け、各セルの最小値と最大値を保存します。
643=262144セルで、16ビットの値なら各4バイト、全部で1 MiBです。
値の範囲[min,max]が不透明度0にしか写らないセルは、1歩で跳び越えます。
レイの長さの60%が空のセルにあるなら、1,774サンプルは約710に減り、レイが不透明な物に当たれば早期レイ終了が残りを削ります。

事前積分とシェーディング

歩幅が粗いと、伝達関数の細い山(或る値の薄い「皮膚」)が2つのサンプルの間に落ちて消える事があります。
事前積分伝達関数(クラウス・エンゲル、マルティン・クラウス、トーマス・エルトル、Graphics Hardware 2001)は、区間の始まりと終わりの値で引く2次元の表を使います。
表には其の2つの値の間での伝達関数の積分が入っているので、区間が通る全ての値が数えられます。
シェーディングでは、中心差分(9.3節)による勾配をブリン・フォンの法線として使います。
普通は勾配の長さで重み付けし、境界はシェーディングし、一様な領域はしない様にします。

医療用のワークステーションは、DVRを断面、MIP、等値面(16.15節)と組み合わせます。
執筆時点では、CTデータを物理ベースのボリュームパストレーシングで描く物もあり(「シネマティックレンダリング」等の名前で売られています)、柔らかい光と影が付きます。

検定のポイント

此処は関与媒質(第12章)と可視化(第16章)に繋がります:

  • 関与媒質:吸収、外向き散乱、内向き散乱、放射。σt=σa+σs。アルベドσs/σt。
  • ランベルト・ベール:T=e−∫σtds。光学的厚さは足され、透過率は掛け合わされる。
  • DVRは放射・吸収モデルを使う。区間の不透明度はα=1−e−σΔtで、前から後ろへの合成は積分から導かれる。歩幅を変えたら不透明度の補正が必要。
  • 前から後ろへなら早期レイ終了が出来る。空領域スキップは透明な領域を跳び越える。
  • レイリー散乱(分子、1/λ4、対称な位相関数)とミー散乱(水滴や塵、前方に尖る、殆ど色が付かない)。

28.11 アニメ調キャラクターの様式化した照明

素朴なトゥーンシェーディングでは足りない理由

16.9節は𝐧⋅𝐥を平らな段階に量子化しました。
本物のキャラクターモデルでは其れでは足りません。
顔には鼻、眼窩、唇、頬骨があり、其の法線はアニメーターなら決して描かない場所に影の境目を置きます。
染み、細い切れ端、そして頭が回ると這い回るギザギザの縁です。
其の為アニメ調のレンダラーは、トゥーンの段差其の物と同じ位丁寧に、其の入力を制御します。

凸凹のある球を、メッシュの法線による2トーン、転写した法線、柔らかいランプと影色としきい値マップ、リムライトとハイライトと輪郭線で描いた物

カスタム法線

1つ目の道具は法線編集です。
頂点法線を形状から計算せず、手で設定するか、単純な代理形状(頭には球や楕円体、手足には円柱)から転写します。
ブレンドの重みwで法線転写すると:

𝐧=normalize((1−w)𝐧mesh+w𝐧proxy)

計算例。
光は𝐥=(0.8,0,0.6)で、鼻の影の側の頂点の法線が𝐧mesh=(−0.6,0,0.8)とします。
𝐧⋅𝐥=−0.48+0.48=0なので、しきい値0.1では影になり、明るい頬の真ん中の小さな暗い島になります。
其処での代理の球の法線は(0.28,0,0.96)で、𝐧⋅𝐥=0.224+0.576=0.8です。
w=0.75なら0.25(−0.6,0,0.8)+0.75(0.28,0,0.96)=(0.06,0,0.92)、長さ0.922、正規化して(0.065,0,0.998)、𝐧⋅𝐥=0.651となり、周りの頬と同じく光が当たります。
図の最初の2枚は、小さな凸凹で覆われた球全体で同じ効果を見せています。
メッシュの法線では影の境目が砕け、85%転写した法線では1つの綺麗な、絵に描ける形になります。

『GUILTY GEAR Xrd』は此の様に作られました(本村・C・純也、「GuiltyGearXrd's Art Style: The X Factor Between 2D and 3D」、GDC 2015)。
主要な部分全てで法線を編集し、特に顔に手を掛けたそうです。

しきい値、ランプ、影色

2つ目の道具は、境目が何処に来るかの制御です。
同じ講演では、頂点カラーのチャンネルにしきい値のオフセットを入れていました。
其れを𝐧⋅𝐥に足すと、其の部分が影になり易く、又はなり難くなり、値0なら常に影のままでした。
テクスチャでも画素ごとに同じ事が出来ます。
しきい値マップです。
図の3枚目では、其処に描いた波打つ帯が、形状が落とさない前髪の影を作っています。
執筆時点では、更に顔について、各テクセルが影になる光の角度をテクスチャに焼き込み、光が回っても鼻や頬の影が手描きの形に従う様にするゲームもあります。

段差其の物は普通、smoothstep(xを[0,1]にクランプした3x2−2x3という、0から1への滑らかな坂)で少し柔らかくします。
影の色は暗くした複製ではなく、基本色に影色(多くは赤や紫寄り)を掛けた物です:

s=smoothstep(θ0−w,θ0+w,𝐧⋅𝐥+o),𝐜=lerp(𝐜base𝐭shadow,𝐜base,s)

計算例。
しきい値θ0=0.1、半幅w=0.05、基本色(1.0,0.85,0.75)(肌)、影色(0.8,0.62,0.68)とすると、影の色は(0.8,0.527,0.51)で、暗く赤くなります。
オフセット無しで𝐧⋅𝐥=0.12なら、x=0.12−0.050.1=0.7、s=3⋅0.49−2⋅0.343=0.784で、色は(0.8,0.527,0.51)+0.784(0.2,0.323,0.24)=(0.957,0.780,0.698)、殆ど光が当たった色です。
しきい値マップのオフセットo=−0.2があれば、同じ点は0.12−0.2=−0.08<0.05なのでs=0、影です。
ランプテクスチャ(16.9節)なら、境目の所に細く鮮やかな帯を加えられます。
画家が肌の光から影へ変わる所に置く暖かい縁を真似た物で、図の3枚目の赤っぽい線が其れです。

リムライトとハイライト

リムライトは、シルエットに沿った明るい縁で、キャラクターを暗い背景から切り離します。
安価な版は、シルエットで0に落ちる向きの比率𝐧⋅𝐯を使います:

r=smoothstep(0.6,0.7,1−𝐧⋅𝐯)⋅(1−s)

係数1−sで、一番効く影の側だけに置きます。
例: 𝐧⋅𝐯=0.25なら1−0.25=0.75≥0.7で完全なリム、𝐧⋅𝐯=0.35なら0.65で半分のr=0.5、𝐧⋅𝐯=0.5なら0.5でリム無しです。
ハイライトも硬い縁の形にします。
フォンの山の代わりに𝐧⋅𝐡の段差を使うか、髪には描いたハイライトのテクスチャを使います。

// Anime-style shading: soft two-tone step with a threshold offset, a shadow tint and a rim light.
float3 animeShade(float3 n, float3 l, float3 v, float3 baseColor, float3 shadowTint,
                  float thresholdOffset, float3 rimColor) {
  n = normalize(n);
  float nl = dot(n, normalize(l)) + thresholdOffset;  // offset from a vertex color or threshold map
  float lit = smoothstep(0.05, 0.15, nl);             // threshold 0.1, soft half width 0.05
  float3 color = lerp(baseColor * shadowTint, baseColor, lit);
  float rim = smoothstep(0.6, 0.7, 1.0 - saturate(dot(n, normalize(v)))) * (1.0 - lit);
  return color + rim * rimColor;                      // rim light on the shadow side only
}
// Anime-style shading: soft two-tone step with a threshold offset, a shadow tint and a rim light.
vec3 animeShade(vec3 n, vec3 l, vec3 v, vec3 baseColor, vec3 shadowTint,
                float thresholdOffset, vec3 rimColor) {
  n = normalize(n);
  float nl = dot(n, normalize(l)) + thresholdOffset;  // offset from a vertex color or threshold map
  float lit = smoothstep(0.05, 0.15, nl);             // threshold 0.1, soft half width 0.05
  vec3 color = mix(baseColor * shadowTint, baseColor, lit);
  float rim = smoothstep(0.6, 0.7, 1.0 - clamp(dot(n, normalize(v)), 0.0, 1.0)) * (1.0 - lit);
  return color + rim * rimColor;                      // rim light on the shadow side only
}
// Anime-style shading: soft two-tone step with a threshold offset, a shadow tint and a rim light.
float3 animeShade(float3 n, float3 l, float3 v, float3 baseColor, float3 shadowTint,
                  float thresholdOffset, float3 rimColor) {
  n = normalize(n);
  float nl = dot(n, normalize(l)) + thresholdOffset;  // offset from a vertex color or threshold map
  float lit = smoothstep(0.05, 0.15, nl);             // threshold 0.1, soft half width 0.05
  float3 color = lerp(baseColor * shadowTint, baseColor, lit);
  float rim = smoothstep(0.6, 0.7, 1.0 - saturate(dot(n, normalize(v)))) * (1.0 - lit);
  return color + rim * rimColor;                      // rim light on the shadow side only
}

シェーディング以外でも、此の様な作品は頂点ごとの幅を持つ16.10節の背面法を使い、内側の線をテクスチャに描き、多くはキャラクターごとに専用の光を持たせ、ポーズを保つアニメーションをします(『GUILTY GEAR Xrd』は全てのフレームをキーフレームとして扱いました)。

28.12 線のレンダリングの詳細

メッシュ上の滑らかなシルエット

16.10節のメッシュのシルエットの判定は面の法線を使うので、細かく分割した曲面ではメッシュの辺のジグザグになります。
アーロン・ハーツマンとデニス・ゾリン(「Illustrating Smooth Surfaces」、SIGGRAPH 2000)は、代わりにメッシュを滑らかな曲面の近似として扱いました。
各頂点で、補間した頂点法線𝐧、頂点の位置𝐩、カメラの位置𝐜からg=𝐧⋅(𝐜−𝐩)を計算します。
gを各三角形の上で線型補間すると、シルエットは其の零点の集合で、gの符号が変わる三角形を通る真っ直ぐな線分になります。

計算例。
三角形の頂点が(其の平面内で)𝐯0=(0,0)、𝐯1=(4,0)、𝐯2=(0,3)で、g=0.3、−0.1、0.2とします。
辺𝐯0𝐯1では、符号はt=0.30.3+0.1=0.75、点(3,0)で変わります。
辺𝐯1𝐯2では、𝐯1からt=0.10.1+0.2=13の所、(4,0)+13(−4,3)=(2.67,1)で変わります。
辺𝐯0𝐯2では符号は変わりません。
シルエットは此の三角形を(3,0)から(2.67,1)への線分として横切ります。
隣の三角形は共有する辺の上の交点を共有するので、線を其のまま続けます。

計算例の三角形:表向きと裏向きの部分と、3, 0から2.67, 1へのシルエットの線分

示唆的輪郭

輪郭だけでは疎に見え過ぎる事が良くあります。
画家は鼻の横や目の下に線を足します。
ダグ・デカルロ、アダム・フィンケルスタイン、シモン・ルシンキエヴィッチ、アンソニー・サンテラ(「Suggestive Contours for Conveying Shape」、SIGGRAPH 2003)は、其の様な線の多くが近くの視点からの輪郭である事に気付きました。
𝐰を、視線ベクトルを接平面に射影した物とします。
示唆的輪郭(サジェスティブコンター)は、𝐧⋅𝐯が0に届かずに𝐰の方向で極小になる点です。
同じ事ですが、半径方向の曲率κr(𝐰の方向の法曲率)が0で、𝐰の方へ増えて行く点です。
面がもう少し回れば、其の極小が0に落ちて本物の輪郭になります。

例。
浅い窪みを横切る𝐰に沿って、𝐧⋅𝐯が0.62、0.48、0.41、0.45、0.58という値を取ります。
極小の0.41は正なので輪郭ではありませんが、示唆的輪郭の点です。
論文は弱い物をしきい値で取り除きます。
𝐧と𝐯の角度はθcを超えなければならず(論文では20°〜30°、此処ではarccos0.41=65.8°なので通ります)、κrの微分も小さな値を超えなければなりません。
後者は殆ど平らな領域のノイズを除きます。

可視性、連結、様式化

メッシュから取り出した線(シルエット、16.10節のクリースエッジ、示唆的輪郭)は3次元の曲線です。
レンダラーはまだ、どの部分が見えるかを決めなければなりません。
普通は線に沿った点を深度バッファ(線が自分の面に隠れない様に小さなオフセットを付けて)又はIDバッファと比べます。
其の後線のスタイル化が、見える線分を長いストロークに繋ぎ、各ストロークを弧長でパラメータ化し、ブラシのテクスチャ、先細りの端、深度や曲率や照明で変わる幅を持った画面空間の三角形の帯としてレンダリングします(BlenderのFreestyleは此の様なエンジンの例です)。

時間的一貫性

静止画は簡単ですが、アニメーションは違います。
シルエットの曲線はフレームごとに伸び、縮み、分かれ、繋がるので、毎フレーム新しくパラメータ化したブラシのテクスチャは線に沿って滑ったり跳んだりします。
ロバート・カルニンス等(「Coherent Stylized Silhouettes」、SIGGRAPH 2003)はパラメータ化を引き継ぎます。
前のフレームのストロークをID画像にレンダリングし、新しい各サンプルがどの古いストロークの何処に落ちるかを引き、古い物に近いままの新しいパラメータ化を当てはめるのです。
画像空間の線(16.10節)は逆に、細く遠い部分でちらつきます。
線幅を少なくとも1画素にする事と、時間方向アンチエイリアシングで減らせます。
時間的一貫性こそが、静止したイラストの為のNPR技術と、ゲームや映画の為のNPR技術を分ける物です。

28.13 トーナルアートマップによるリアルタイムのハッチング

第16章は画面空間でハッチングを描いたので、ストロークは画面に貼り付き、物体は其の後ろを滑りました(シャワードア効果)。
テクスチャでストロークを面に貼り付ければ其れは解決しますが、濃淡は連続的に変わるのにテクスチャのストロークの密度は決まっており、遠くで縮小されたテクスチャはペンで描くより小さく密なストロークを見せてしまいます。

エミール・プラウン、ユーグ・ホップ、マシュー・ウェッブ、アダム・フィンケルスタイン(「Real-Time Hatching」、SIGGRAPH 2001)は、トーナルアートマップ(TAM)で両方を解決しました。
ハッチングの画像の格子で、一方向に明るい濃淡から暗い濃淡へ、もう一方向にミップマップのレベルが並びます。
鍵は両方向の入れ子です:

  • 明るい濃淡の全てのストロークは、全ての暗い濃淡に含まれる。
  • 粗いミップレベルの全てのストロークは、全ての細かいレベルに含まれる。

粗いレベルは同じ画素幅で描いたより少ないストロークを持つので、画面上のストロークの密度はどの距離でも大体一定に保たれます。
入れ子の2枚の画像をブレンドしても、ストロークが急に現れたり消えたりする事は無く、暗い方の余分なストロークが徐々に現れるだけです。
論文は6つの濃淡を2枚のテクスチャの赤・緑・青チャンネルに詰め、ブレンドの重みを頂点ごとに計算しました。
今は重みを画素ごとに計算します。

計算例。
6つの濃淡のインクの被覆率を15%、30%、45%、60%、75%、90%とします。
或る画素が暗さ0.52を必要とし、此れは濃淡3(0.45)と濃淡4(0.60)の間です。
重みはw4=0.52−0.450.60−0.45=0.467とw3=0.533で、平均の被覆率は0.533⋅0.45+0.467⋅0.60=0.52です。
入れ子なので、結果は濃淡3の全てのストロークを完全な強さで見せ、濃淡4の余分なストロークを46.7%で加えた物になります。

インク15%から90%の入れ子になった6つの濃淡のトーナルアートマップ、ランバートでシェーディングした球、一番近い2つの濃淡をブレンドしてハッチングした同じ球

此のTAMは、繰り返せるテクスチャに、各濃淡が其の被覆率に届く迄ランダムなストロークを足して作りました。
球は経度と緯度をテクスチャ座標に使っているので、ストロークは面に沿います。

28.14 絵画調と水彩のレンダリング

16.12節では、ストロークベースの絵画調レンダリングと、カーティス等の水彩のシミュレーション(SIGGRAPH 1997)を紹介しました。
カーティスの方法は水と顔料を層に分けて物理的にシミュレーションし、重ね塗りをクベルカ・ムンクのモデルで合成します。
暗い縁、バックラン、粒状化を再現しますが、遅いのです。

リアルタイムの方法は、画像空間で見た目を真似ます。
アドリアン・ブソー、マット・カプラン、ジョエル・トロ、フランソワ・シリオン(「Interactive Watercolor Rendering with Temporal Coherence and Abstraction」、NPAR 2006)は、まず画像を単純にし(画家の様に小さな細部を除き)、次にテクスチャから読んだ顔料密度dで全ての色を変調します。
紙の肌理、むらのある塗りの為の低周波の「乱流」、顔料の分散の為の細かいノイズです。
色の変更は

C′=C−(C−C2)(d−1)=C(1−(1−C)(d−1)),d=1+β(T−0.5)

で、T∈[0,1]はテクスチャの値、βは全体の強さです。
d>1は暗くし(顔料が多い)、d<1は明るくします。
例: C=0.6、d=1.3ならC′=0.6−(0.6−0.36)⋅0.3=0.528、d=0.8ならC′=0.648です。
白(C=1)は項C−C2が0なので決して変わりません。
塗っていない紙は紙のままです。
絶対的な効果は、C−C2が0.25で最大になる中間調で一番大きくなります。
エッジの暗化、つまり塗りの縁に顔料が溜まって出来る暗い縁は画像の勾配から加え、ウォブリングは紙のテクスチャの勾配に沿って画像を少し歪め、縁を手で塗った様に見せます。

アニメーションでは、テクスチャは画面ではなくシーンと一緒に動かなければなりません(またシャワードアの問題です)。
バーバラ・マイヤー(「Painterly Rendering for Animation」、SIGGRAPH 1996)は3次元の面の上のパーティクルにブラシストロークを付け、此れがストロークベースの様式の標準的な答えになりました。

28.15 画像フィルタによる様式化

桑原フィルタの計算例

第16章で桑原フィルタを説明しました。
画素の周りの4つの重なり合う正方形の領域を見て、分散が一番小さい物の平均を出力します。
縦のエッジ(左が暗く右が明るい)を跨ぐ5 × 5の近傍で、中心の画素はノイズで30になっています:

列0 列1 列2 列3 列4
行0 10 12 11 80 82
行1 11 10 13 81 79
行2 12 11 30 78 80
行3 10 13 11 82 81
行4 14 12 10 79 80

4つの3 × 3の領域は中心の画素を共有します:

領域 行、列 平均 分散
左上 0〜2、0〜2 13.33 35.56
右上 0〜2、2〜4 59.33 879.56
左下 2〜4、0〜2 13.67 34.89
右下 2〜4、2〜4 59.00 911.33

左下の領域が一番ばらつきが小さいので、出力は13.67です。
ノイズは消え、画素はエッジの暗い側に留まります。
3 × 3の平均値フィルタなら36.6となり、エッジを跨いでぼけます。

変種

古典的なフィルタには欠点があります。
正方形の領域がブロック状のアーティファクトを作り、2つの領域の分散が殆ど同じ時(上の35.56と34.89の様に)、どちらが勝つかをノイズが決めるので、出力がちらつきます。

  • 一般化桑原フィルタ(ジュゼッペ・パパリ、ニコライ・ペトコフ、パトリツィオ・カンピージ、IEEE Transactions on Image Processing、2007年)は、円形の窓をN個(典型的には8個)の扇形に分け、滑らかなガウスの重みを付けます。そして扇形の平均を、標準偏差と共に急に下がる重み、例えばwi∝σi−qでブレンドします。
  • 異方性桑原フィルタ(ヤン・エリック・キプリアニディス、ヘンリー・カン、ユルゲン・デルナー、Pacific Graphics 2009)は、円を局所的なエッジの方向(構造テンソル、つまり画像の微分の積を平滑化した行列から求めます)に沿った楕円に引き伸ばします。結果は形に沿ったブラシストロークの様に見えます。

例: 上の4つの領域にq=8でブレンドの重みを当てはめます。
標準偏差は5.963、29.66、5.907、30.19で、wi∝σi−8の正規化した重みは0.481、0.0000013、0.519、0.0000011です。
出力は0.481⋅13.33+0.519⋅13.67=13.51です。
2つの暗い領域は、片方が勝つのではなく滑らかに平均され、明るい領域は殆ど数えられません。
q=2なら明るい領域にも其々約2%が残り、結果を15.2まで引き上げます。
qが大きい程エッジが鋭くなります。

DoGとXDoG

ガウス差分(DoG)は、画像の2つのぼかしの差Gσ−Gkσです。
15.5節のLoGを近似し(Gσ−Gkσ≈−(k−1)σ2∇2G)、マーとヒルドレス以来k=1.6が普通の選択です。
ホルガー・ヴィンネメラー等は、しきい値処理したDoGの線にバイラテラルフィルタと色の量子化を組み合わせ、動画をリアルタイムでアニメ調にしました(「Real-Time Video Abstraction」、SIGGRAPH 2006)。
16.12節で触れたアニメ調の抽象化です。

XDoG(ホルガー・ヴィンネメラー、ヤン・エリック・キプリアニディス、スヴェン・オルセン、「XDoG: An eXtended Difference-of-Gaussians Compendium Including Advanced Image Stylization」、Computers & Graphics、2012年)は、2つの段階で此れを様式化の道具にします。
まず、鮮鋭化した画像です:

S=(1+p)Gσ−pGkσ=Gσ+p(Gσ−Gkσ)

此れは強いエッジの強調p(20前後)を持つアンシャープマスキング(第15章)です。
次に、柔らかいしきい値です:

T(S)={1S≥ε1+tanh(φ(S−ε))S<ε

εはどの濃淡が暗くなるかを、φは移り変わりの鋭さを決めます。
大きなφは白黒のインク、小さなφは柔らかい鉛筆や木炭の陰影になります。

計算例。
x=0で0.2から0.8に上がる1次元の段差で、σ=1、k=1.6、p=20、ε=0.25、φ=4とします。
ぼかした段差はGσ(x)=0.2+0.6Φ(x/σ)で、Φは正規分布の累積分布関数です:

x Gσ G1.6σ S T(S)
−6 0.2000 0.2001 0.199 0.80
−4 0.2000 0.2037 0.126 0.54
−2 0.2137 0.2634 −0.781 0.00
−1 0.2952 0.3596 −0.993 0.00
0 0.5 0.5 0.5 1
1 0.7048 0.6404 1.993 1
2 0.7863 0.7366 1.781 1

エッジから遠い所ではSは元の値に等しく、暗く平らな領域(0.2)は0.80の明るい灰色の濃淡に、明るい領域は白になります。
エッジのすぐ隣の暗い側では、Sが0より遥か下まで行き過ぎ、しきい値が数画素幅の黒い線を作ります。
つまりXDoGは、1つの式から線と濃淡を一緒に与えるのです。

ノイズのある手続き的なテスト画像と、其れに桑原フィルタ、8扇形の桑原フィルタ、しきい値処理したDoGの線、XDoGを掛けた物、扇形の桑原フィルタの画像にXDoGの線を掛け合わせた物

古典的な桑原フィルタの結果は草の所でブロック状になり、8扇形の版はより丸くなります。
DoGとXDoGは、軽くぼかした(σ = 1)輝度に掛けました。
DoGが輪郭だけを描く暗い窓、扉、木の葉の部分をXDoGは塗り潰し、最後の画像は桑原フィルタの色にXDoGの線を掛けた、安価なアニメ調フィルタです。
ヘンリー・カン、イ・スンヨン、チャールズ・チュイ(「Coherent Line Drawing」、NPAR 2007)は、DoGを局所的なエッジの方向に沿って平滑化し(フローベースのDoG)、途切れた破線を長く一貫した線にします。

28.16 スタイル変換と学習による様式化

16.12節では、ニューラルスタイル変換(ゲイティス、エッカー、ベートゲ、2015年)を紹介しました。
深い特徴がコンテンツ画像に合い、特徴の統計がスタイル画像に合う画像を探す方法です。
統計はグラム行列です。
ネットワークの層がM個の位置にC個のチャンネルを持ち、C×Mの行列𝐅として保存されているなら

𝐆=𝐅𝐅⊤,Gij=∑kFikFjk

は、何処でかに関係無く、チャンネルiとjがどれだけ一緒に反応するかを測ります。
例: 3つの位置の2つのチャンネル𝐅=[102011]は、𝐆=[5222]を与えます(1+0+4=5、0+0+2=2、0+1+1=2)。
3つの位置を入れ替えても𝐆は変わりません。
テクスチャや筆致は残し、配置は忘れるのです。
此処での「スタイル」の意味に丁度合っています。

ゲイティスの方法は画像ごとに一から最適化するので、数分掛かります。
ジャスティン・ジョンソン、アレクサンドル・アラヒ、リー・フェイフェイ(ECCV 2016)は、同じ損失でスタイルごとにフィードフォワードのネットワークを学習し、リアルタイムに十分な速さにしました。
シュン・ホアンとセルジュ・ベロンジー(ICCV 2017)は、適応的インスタンス正規化(AdaIN)で任意のスタイルを1回で出来る様にしました。
コンテンツの各特徴チャンネルに、スタイルのチャンネルの平均と標準偏差を与える物です:AdaIN(x,y)=σ(y)x−μ(x)σ(x)+μ(y)。

動画では、様式化は時間的に安定でもなければなりません。
各フレームを、オプティカルフロー(第27章)で変形した前のフレームと比べる損失を加える方法があります。
執筆時点では、拡散モデルによる画像生成(第29章)もテキストのプロンプトから画像や動画の様式を変えられ、存命の作家の画風を真似る事は18.7節の問題を提起します。
ゲームは今も主に、28.11〜28.15節の様な手で設計したシェーダーで見た目を作っています。
制御、安定性、コストの為です。

検定のポイント

此処はノンフォトリアリスティックレンダリング(第16章)に繋がります:

  • アニメ調のシェーディングはトゥーンの段差の入力を制御する:編集又は転写した法線、しきい値のオフセット(頂点カラー、しきい値マップ)、ランプと影色。リムライトは1−𝐧⋅𝐯から。
  • 滑らかな曲面の輪郭:𝐧⋅𝐯の零点の集合(三角形の中で補間)。示唆的輪郭:射影した視線方向での𝐧⋅𝐯の正の極小(半径方向の曲率が0)、つまり近くの視点からの輪郭。
  • アニメーションには時間的一貫性が欠かせない:面に付けたストローク(シャワードア無し)、フレームを跨いで引き継ぐパラメータ化。
  • トーナルアートマップ:幾つかの濃淡とミップレベルのハッチングのテクスチャで、入れ子なのでブレンドが滑らかで、画面上の密度が一定に保たれる。
  • 桑原フィルタ:一番ばらつきの小さい部分領域の平均でエッジを保つ。DoG ≈ LoG。XDoG=鮮鋭化した画像+柔らかいしきい値で、線と濃淡を与える。
  • スタイル変換は深い特徴のグラム行列の統計を合わせる。

28.17 まとめ

  • 関与媒質は光を吸収し、散乱し、放射する(σa、σs、σt、アルベド、位相関数)。透過率はe−τで、光学的厚さは足される。
  • ボリュームレンダリング積分は、レイに沿った放射と内向き散乱を、目までの透過率で暗くして足す。DVRの放射・吸収モデルはα=1−e−σΔtの前から後ろへの合成になり、歩幅を変えたら不透明度の補正をする。
  • ヘニエイ・グリーンスタイン位相関数のパラメータは平均の余弦gの1つだけ。単一散乱のレイマーチングは各歩を解析的に積分し、光源の方へシャドウマーチをする。多重散乱はオクターブで近似するか、デルタトラッキングと比率トラッキングでパストレースする。
  • 空はレイリー・ミー・オゾンの層と小さな表で描く。ゲームは霧をフロクセルで照らし、雲を時間的な再利用付きでレイマーチングする。可視化は3次元テクスチャ、空領域スキップ、早期レイ終了、事前積分を使う。
  • アニメ調のシェーディングは法線、しきい値、ランプ、影色、リムを制御する。線は滑らかなシルエット、示唆的輪郭、様式化したストローク、時間的一貫性を加える。トーナルアートマップは一貫したハッチングを与え、水彩は顔料密度を変調し、桑原フィルタの変種とXDoGが画像を様式化し、スタイル変換はグラム行列を合わせる。

28.18 用語

English 日本語
participating medium 関与媒質
absorption, out-scattering, in-scattering, emission 吸収、外向き散乱、内向き散乱、放射(発光)
absorption coefficient 吸収係数
scattering coefficient 散乱係数
extinction coefficient 消散係数(減衰係数)
single-scattering albedo 散乱アルベド
mean free path 平均自由行程
homogeneous / heterogeneous medium 一様な媒質/非一様な媒質
transmittance 透過率
optical depth 光学的厚さ
Beer–Lambert law ランベルト・ベールの法則
radiative transfer equation (RTE) 放射伝達方程式
volume rendering integral ボリュームレンダリング積分
emission–absorption model 放射・吸収モデル
opacity correction 不透明度の補正
phase function 位相関数
scattering angle 散乱角
isotropic scattering 等方散乱
Henyey–Greenstein phase function ヘニエイ・グリーンスタイン位相関数
asymmetry parameter 非対称パラメータ
ray marching レイマーチング
single scattering, multiple scattering 単一散乱、多重散乱
delta tracking (Woodcock tracking) デルタトラッキング(ウッドコック・トラッキング)
majorant 優関数(メジャラント)
null collision ヌル衝突
ratio tracking 比率トラッキング
aerial perspective 空気遠近法
froxel フロクセル
3D texture 3次元テクスチャ
empty-space skipping 空領域スキップ
pre-integrated transfer function 事前積分伝達関数
edited normals, normal transfer 法線編集、法線転写
threshold map しきい値マップ
rim light リムライト
suggestive contour 示唆的輪郭(サジェスティブコンター)
line stylization 線のスタイル化
temporal coherence 時間的一貫性
tonal art map (TAM) トーナルアートマップ(TAM)
pigment density 顔料密度
edge darkening エッジの暗化
generalized Kuwahara filter 一般化桑原フィルタ
anisotropic Kuwahara filter 異方性桑原フィルタ
difference of Gaussians (DoG) ガウス差分(DoG)
XDoG XDoG
Gram matrix グラム行列
adaptive instance normalization (AdaIN) 適応的インスタンス正規化(AdaIN)

28.19 課題

  1. ★ 霧がσa=0.05、σs=0.15毎メートルを持ちます。σt、アルベド、平均自由行程、5 m・10 m・20 mでの透過率を計算して下さい。次に光が此の霧を4 m、σt=0.01毎メートルの空気を6 m通ります。全体の透過率は幾つですか?
  2. ★ g=0.6のHG位相関数を0°、90°、180°で計算し、前方と後方の比が(1+g1−g)3である事を確かめて下さい。比が27になるgは幾つですか?
  3. ★★ 放射・吸収モデルで、長さΔt=0.25、σ=0,2,4,1、c=0,1,0.5,0.2の4つの区間を、値1の背景の前で合成して下さい。次に、Δref=0.25用に設計した伝達関数がαref=0.6を与えます。歩幅0.1と0.5では、レンダラーはどの不透明度を使わなければなりませんか?
  4. ★★ 手でデルタトラッキング:σ¯=1、t<2でσt=0.2、2≤t<4で0.8、レイの長さ4です。u=0.4,0.7,0.2,0.9で仮の衝突を列挙し、比率トラッキングによる透過率の推定値を求めて下さい。正確な値と比べ、違いを説明して下さい。
  5. ★★ フロクセルの格子がn=0.5 m、f=200 m、N=128の指数的なスライスを持ちます。スライス間の奥行きの比、最初と最後のスライスの厚さ、奥行き10 mを含むスライスを計算して下さい。16ビット浮動小数点数4つの240 × 135 × 128のボリュームには、どれだけのメモリが要りますか?
  6. ★★ 行が(50, 52, 49, 51, 50)、(48, 51, 50, 120, 122)、(50, 49, 90, 121, 119)、(52, 50, 118, 120, 121)、(49, 51, 119, 122, 120)の5 × 5のパッチの中心に、古典的な桑原フィルタ(3 × 3の領域4つ)を掛けて下さい。次に4つの領域の平均をwi∝σi−2でブレンドし、比べて下さい。
  7. ★★★ 図の単一散乱の雲の内部は何故暗い灰色なのでしょうか? 28.6節のオクターブ近似は何を誤魔化しているのでしょうか? ノイズがあっても、レンダラーがレイマーチングよりデルタトラッキングを選ぶのはどんな時でしょうか?

選択問題

問1 ★ 次のうち、関与媒質について正しい物はどれか。

あ 消散係数は、吸収係数から散乱係数を引いた物である。
い 続いた2つの層を通る透過率は、光学的厚さが足されるので、其々の透過率の積である。
う 外向き散乱は他の方向からの光を光線に加え、霧の中で街灯の周りを光らせる。
え 散乱アルベドが0の媒質は、光を散乱させるが決して吸収しない。
お 一様な媒質の中を通る距離を2倍にすると、透過率は半分になる。

問2 ★★ 次のうち、g=0.5のヘニエイ・グリーンスタイン位相関数について正しい物はどれか。

あ 関数は正規化されているので、0°と180°での値は等しい。
い パラメータg=0.5は、散乱した光の丁度半分が前方の半球へ行く事を意味する。
う 0°での値は0.54πである。
え 負のgにすると、g=0.5よりも強く前方へ散乱する。
お 0°での値は180°での値の27倍である。

問3 ★ 次のうち、NPRの手法について正しい物はどれか。

あ トーナルアートマップでは、ハッチングが繰り返しに見えない様に、暗い濃淡ごとに関係の無い新しいストロークの集合を使う。
い 示唆的輪郭は、面上の𝐧⋅𝐯=0となる点である。
う XDoGの柔らかいしきい値は、ε未満の全ての値を丁度0に写す。
え 桑原フィルタは分散が一番小さい部分領域の平均を出力するので、平らな領域を滑らかにしつつエッジを鋭く保つ。
お 代理の球から顔へ法線を転写すると、トゥーンの影の境目が小さな形状の細部により細かく従う様になる。

28.20 実践課題

雲のレイ、位相関数、アニメ調の球を手で。
方眼紙、定規、分度器、色鉛筆、電卓を用意して下さい。

パートA:雲を通る1本のレイ。
カメラのレイが、σt=0,0.4,1.2,0.8,0.2毎メートル、アルベド0.9(つまりσs=0.9σt)の5つの1 mのセルを横切ります。
太陽から各セルへの透過率Tlightは1、0.8、0.5、0.3、0.6、太陽の強さはE=10、位相関数は散乱角60°でg=0.5のHG、雲の後ろはLbg=0.5の空です。

  1. 位相関数。 g=0.5のpHGを60°で計算して下さい。
  2. 表。 各セルについて、始まりの透過率Ti、源Si=σs,ipETlight,i、係数1−e−σt,iσt,i(空のセルでは1)、寄与TiSi×其の係数を書いて下さい。Lと背景の項を足します。
  3. 比べる。 素直な寄与TiSi⋅1 mで繰り返して下さい。違いの大部分はどのセルから来ますか? 何故ですか?

パートB:紙の上のHG位相関数。
g=0.5で、θ=0°,30°,60°,…,180°の4πp(θ)を計算して下さい。

  1. 極座標プロット。 入って来る光を横軸に沿わせ、各値を其の方向への中心からの距離として印を付け(1単位2 cm)、軸の下に点を鏡映し、閉じた曲線で結んで下さい。比較の為に単位円(等方散乱)も描きます。
  2. 正規化。 同じ7つの角度で、シンプソンの公式(歩幅h=π/6、重みは1, 4, 2, 4, 2, 4, 1にh/3を掛けた物)により∫0π2πp(θ)sinθdθを見積もって下さい。

パートC:アニメ調の球。
半径R=5 cmの円を描きます。
正面から見た球(𝐯=(0,0,1))で、光は𝐥=(0.6,0,0.8)から来ます。
横の直径の上で、xでの法線は(xR,0,1−x2R2)です。

  1. 値。 x/R=−1,−0.95,−0.9,−0.8,−0.75,−0.7,−0.6,−0.3,0,0.3,0.6,0.9,1について、𝐧⋅𝐥、光の係数s=smoothstep(0.05,0.15,𝐧⋅𝐥)、リムr=smoothstep(0.6,0.7,1−𝐧⋅𝐯)⋅(1−s)を計算して下さい。
  2. 色。 28.11節の肌の色(基本色(1.0,0.85,0.75)、影(0.8,0.527,0.51))を使い、円板を塗って下さい。光の当たる側、細く柔らかい境目、影、そしてr>0の所に白いリムです。
  3. しきい値マップ。 次に帯y>0.6R(髪の影)にオフセットo=−0.5を加え、其の帯を描き直して下さい。境目は何処へ動きますか?

自分で確認する方法:

  • パートA:p=0.0919です。雲の後の透過率はe−2.6=0.0743なので、背景は0.0371を加えます。エネルギー保存の合計は約0.448(背景込みで0.485)、素直な合計は約0.646(0.683)です。素直な和が一番行き過ぎるのは、濃い3番目のセルです。
  • パートB:値は6、3.152、1.155、0.537、0.324、0.244、0.222です(最初と最後の比は27)。シンプソンの公式で約1.004となり、1に近くなります。
  • パートC:𝐧⋅𝐥はx=0.6Rで最大(1)です。境目(𝐧⋅𝐥=0.1)は直径をx≈−0.736Rで横切り、柔らかい帯は約−0.769Rから−0.701Rまでです。リムは影の側のx≈−0.917R(𝐧⋅𝐯=0.4)より外にだけ現れ、−0.954Rより外で最大になります。オフセットを加えると、其処では𝐧⋅𝐥が約0.6を超えないと光が当たらないので、影の帯が光の側へ大きく入り込みます。


コメント(0)

まだコメントはありません。