Appearance
第06回 レイマーチのライティング
今回の授業内容
- SDF の微小な差から法線を推定する
- Lambert 拡散反射と鏡面反射を追加する
- 光方向へレイを進めてソフトシャドウを作る
- 近傍の距離から Ambient Occlusion を近似する
- Smooth Min で形状を有機的に接続する
SDF から法線を推定する
表面の周囲で SDF を少しずらして評価し、距離が大きくなる方向を法線とします。
hlsl
float3 EstimateNormal(float3 p)
{
const float e = 0.001;
float3 offsetX = float3(e, 0.0, 0.0);
float3 offsetY = float3(0.0, e, 0.0);
float3 offsetZ = float3(0.0, 0.0, e);
float3 gradient = float3(
MapScene(p + offsetX) - MapScene(p - offsetX),
MapScene(p + offsetY) - MapScene(p - offsetY),
MapScene(p + offsetZ) - MapScene(p - offsetZ)
);
return normalize(gradient);
}1ピクセルの法線に複数回の MapScene が必要です。精度を上げるほど、計算回数と数値誤差の両方を考える必要があります。
光と視線を使って明るさを求める
hlsl
float3 n = EstimateNormal(hitPosition);
float3 l = normalize(lightPosition - hitPosition);
float3 v = normalize(cameraPosition - hitPosition);
float3 h = normalize(l + v);
float diffuse = saturate(dot(n, l));
float specular = pow(saturate(dot(n, h)), smoothnessPower);
float fresnel = pow(1.0 - saturate(dot(n, v)), fresnelPower);前期で Shader Graph のノードとして扱った内積、Normalize、Power が、HLSL の短い式に対応します。
ソフトシャドウを近似する
命中点からライトへ向けて、もう一度レイマーチします。レイ上で形状に近づいた程度を記録すると、影の輪郭をなめらかにできます。
hlsl
float SoftShadow(float3 origin, float3 direction, float maxDistance)
{
float visibility = 1.0;
float traveled = 0.02;
for (int i = 0; i < 48; i++)
{
float distanceToScene = MapScene(origin + direction * traveled);
visibility = min(visibility, 12.0 * distanceToScene / traveled);
traveled += clamp(distanceToScene, 0.01, 0.5);
if (distanceToScene < 0.001 || traveled > maxDistance) break;
}
return saturate(visibility);
}自己干渉を避けるため、レイの始点を表面から法線方向へ少しずらします。
Smooth Min で形状をつなぐ
hlsl
float SmoothMin(float a, float b, float k)
{
float h = saturate(0.5 + 0.5 * (b - a) / k);
return lerp(b, a, h) - k * h * (1.0 - h);
}min(a, b) では形状の境界が鋭くつながります。Smooth Min は境界に幅を持たせ、液体や粘土のように滑らかに合体させます。
実習:粘性のある SDF オブジェクトを作る
- 前回の球とボックスに法線を追加する
- Lambert で光源の向きを表す
- 鏡面反射とフレネルを追加する
- 光源の位置をプロパティで変更する
- ハードシャドウとソフトシャドウを比較する
- 球2個を Smooth Min で接続する
- 時間で球を移動し、接続部の変化を確認する
授業内課題:接地感を調整する
- ソフトシャドウの硬さを3段階で比較する
- Ambient Occlusion を有効・無効にして接地部を比較する
- 表面が暗くなりすぎない設定を決める
- 採用値と理由を記録する
完成条件
- SDF から法線を推定できる
- 拡散反射、鏡面反射、影を表示できる
- Smooth Min で複数形状を接続できる
- ライティングと影で
MapSceneが複数回実行されることを説明できる
今回のまとめ
SDF は形状の交差だけでなく、法線、影、近傍の閉塞、形状合成に使えます。複数の追加レイは見た目を豊かにしますが、それぞれが反復計算を増やすことも意識します。
次回: 第07回 ボリュームレイマーチ