シャドウアクネ
Shadow Acne
概要
シャドウマップで影を付けるとき、光が当たっているはずの面に、縞や斑点の模様の影が出てしまう現象。面が自分自身の影を誤って受けることで起きる。深度を比べるときに少しずらす(バイアスをかける)ことで抑える。
デモ
使われる場面
リアルタイム 3D の影全般
光源から見た深度を画像に記録するシャドウマップは、ゲームで広く使われている影の手法で、ほぼ必ずバイアスの調整が必要になる。
朝や夕方の低い太陽
光が地面をかすめるように当たると、面の傾きが深度の差として大きく現れ、縞が出やすい。
広い屋外の場面
一枚のシャドウマップで広い範囲を覆うと、1 画素が受け持つ範囲が広がり、縞が粗くなる。距離ごとに範囲を分けて複数のシャドウマップを使う手法(カスケードシャドウ)がある。
キャラクターの顔や服
自分自身に影を落とす入り組んだ形では、細かな縞や、首元の影の浮きが目立ちやすい。
判断基準
| こういうとき | こうする |
|---|---|
| 光が当たっている面に、縞や斑点の模様が出る | シャドウアクネである。深度のバイアスを少しずつ上げ、模様が消える最小の値にする。 |
| 光源の角度によって縞が出たり消えたりする | 面の傾きに応じてずらす量を変える(傾きに応じたバイアス、法線方向へのオフセット)。一定のバイアスを浅い角度に合わせると、他の場所で影が浮く。 |
| 影が物体の接地点から離れて浮いて見える | バイアスが大きすぎる(ピーターパンニング)。値を下げるか、法線方向へのオフセットに切り替える。 |
| 縞は消えたが、影の輪郭がギザギザしている | バイアスでは直らない。シャドウマップの解像度を上げるか、影を落とす範囲を狭めるか、輪郭をぼかす処理を使う。 |
| 中身の詰まった閉じた形状が多い場面 | 光源から見た裏の面を深度に書き込むと、表の面にアクネが出にくくなる。ただし薄い板のような形状では影が抜けることがある。 |
こう見えたら
指示の例
光が当たっている床や壁に、細かい縞模様の影が出ている。シャドウアクネだと思うので、影のバイアスを少し上げて
バイアスを上げたら、影が物体の根元から浮いてしまった。バイアスを戻して、代わりに法線方向のオフセットで縞を消して
太陽が低い夕方の場面だけ縞が出る。面の傾きに応じてバイアスを変える設定にして
詳細
影は深度の比較で決まる
シャドウマップは、光源の位置から場面を描き、各画素に「光源からいちばん近い面までの距離(深度)」を記録した画像である。ある点が影かどうかは、その点の光源からの深度 と、シャドウマップの同じ位置に記録された深度 を比べて決める。 なら光源との間に何もないので照らされ、 なら手前に別の面があるので影になる。
面が自分の影を受ける理由
シャドウマップの 1 画素は、画素の中心で測った深度を一つだけ記録するが、実際には面のある広さを受け持っている。光に対して傾いた面では、その広さの中で深度が少しずつ変わる。記録された値より少しでも奥にある部分は と判定され、自分自身の影に入ったことになる。この誤判定が画素ごとに繰り返されるので、光の当たる面に縞や斑点の模様が並ぶ。これがシャドウアクネである。光が面をかすめるほど画素の中での深度の変化が大きくなるので、デモで光源を低くすると縞が強くなる。
比べるときに少しずらす
対策は、比べる前に深度を光源側へ少しずらすことである。ずらす量 をバイアスと呼び、 なら照らされていると判定する。 が小さすぎるとアクネが残り、大きすぎると本来の影まで消えて、影が物体の根元から離れる(ピーターパンニング)。
画素の中での深度の変化は、法線と光の方向のなす角 が大きいほど大きい。そこで、傾きに応じてバイアスを増やす方法がよく使われる。
比べる点そのものを法線の方向へ少し押し出す方法(法線方向へのオフセット)もあり、傾いた面でも影の浮きが出にくい。
float bias = max(0.0005, 0.005 * (1.0 - dot(n, l))); // 傾いた面ほど大きく
vec3 p = worldPos + n * normalOffset; // 法線方向へ押し出す方法
vec4 lp = lightViewProj * vec4(p, 1.0);
vec3 s = lp.xyz / lp.w * 0.5 + 0.5; // シャドウマップ上の位置と深度
float lit = (s.z - bias <= texture(shadowMap, s.xy).r) ? 1.0 : 0.0;
見分け方
シャドウアクネは、光が当たっている面の上に出る細かい縞で、光源やカメラが動くとうねるように動く。影の輪郭がギザギザになる現象とは別物で、こちらはシャドウマップの解像度が足りないことが原因なので、バイアスでは直らない。なお、このデモでは違いを見やすくするため、光源から見た表の面を深度に書き込む素朴な設定にしている。