シェーダー本4がようやく脱稿しました(宣伝エントリは後で作ります)。
「 #Unity シェーダープログラミングの教科書SRP[1] UniversalRP/Litシェーダー解説編」脱稿しました>< シェーダー本シリーズの4冊目になります。9月12日から始まるオンラインの #技術書典 でPDF版とPDF+紙版を頒布する予定です(紙版のみ頒布のオプションがなかった)。続報待て。 pic.twitter.com/WCqrGaLIIc
— 土屋つかさ (@t_tutiya) 2020年8月15日
ブログは独自ドメイン取ったりしたものの、最適化他の作業が後回しになってたりして(httpドメインが残ってるとか)宿題がたまってる(ツイートボタンを一時的に消したの4ヶ月ぶりに思い出した)んですが、ぼちぼち更新を再開したいと思っています。
記事の位置づけ
今回はMixed LightingのSubtractiveモードについて。毎回Subtractiveモードの挙動が良くわからなくなるので、ここで機能とサンプルをまとめておきます。
リアルタイムGI(Enlighten)が実装されたUnityでは、Subtractiveモードは互換性のために遺しているのだと勝手に思っていたのですが、そのEnlightenが廃止され(!)、その上URPではShadowMaskモードも非対応(!!)なので、Subtractiveモードがまた意味を持つようになりました*1。
なお、言及しているのは全てURPでの挙動になります。とはいえ、Unity組み込みレンダーパイプライン(≒Standardシェーダー)でもほぼ同じ処理になっていると思われます。
Subtractiveモードとはなにか
Subtractiveモードは、Unityで大域照明(GI)を使用する時に、Lighting Modeで選択できるモードの一つです*2。
Subtractiveモードでは、静的オブジェクトに対し「①ライトマップからサンプリングしたライトカラー値」を反映するのとは別に、「②メインライトによって動的オブジェクトからキャストされるシャドウ」も同時に反映します*3。通常、動的オブジェクトがキャストするシャドウは、静的オブジェクトには描画されないのですが、Subtractiveモードでは(メインライトに限り)それが出来るわけです。
ところで、この「②動的オブジェクトからキャストされるシャドウ」というのは(何らかのカラー値ではなく)減衰率でして、通常は元となるライトカラー値に乗算してシャドウのかかり具合を示します。しかし、「①ライトマップからサンプリングしたライトカラー値」には、シャドウの効果が既に反映されている(場合がある)ため、それに減衰率を乗算すると、シャドウが2重に反映されてしまい不自然な描画になってしまいます。そのため、Litシェーダーではこの現象を抑制するブレンド処理が内部的に行われています。
Subtractiveモードではリアルタイムシャドウ色の手動調整が必要
ここからが本題なのですが、先述したブレンド処理はURPが全自動で計算してくれるわけでありません。これは、ライトマップに記録された各ピクセルについて「シャドウによる減衰が発生しているのか否か」という判断と、「発生していた場合のRGB各チャンネルのシャドウ減衰率」を計算することが、システムには困難なためです。シェーダーコード内である程度の推測による自動化されているものの、手動による微調整が必要になります。
そのため、Subtractiveモードを使用する場合、ユーザーはLightingウィンドウのRealtime Shadow Colorプロパティを更新し、手作業で調整する必要があります。
以下、その手動調整作業についてのサンプルを上げておきます。
Subtractiveモードでのシャドウ手動調整サンプルA
サンプルA-1 白色光での補正前
サンプルシーンは「地面(静的)」「壁(静的)」「球体(動的)」から構成されています(ベースカラーはすべて(255,255,255))。シーンに配置されているライトはディレクショナルライト(Mixed)のみです(ライトカラーは(255,255,255))。ライトマップはベイク済みで、壁から地面に落ちたシャドウはライトマップからサンプリングした物、球体から地面に落ちたシャドウはリアルタイムに計算された物です。
見て分かる通り、2つのシャドウが重なった部分がおかしな事になっています。ここでRealtime Shadow Colorは(0,0,0)に設定しています。
サンプルA-2 白色光での補正後
Realtime Shadow Colorを(111,129,158)に設定しました。シャドウが馴染んで境界線が見えなくなりました(完全に消えたわけではなく、アップにすると境界がわかります)。
余談ですが、Realtime Shadow Colorの値を手作業で決定するのは(少なくとも土屋は)大分難しいのですが、シーンウィンドウで地面に落ちたシャドウ部分の色をスポイトで拾うと、適切な色が簡単に取得できて効果的でした。
Subtractiveモードでのシャドウ手動調整サンプルB
次のサンプルでは、ライトカラーが変わる場合にRealtime Shadow Colorの再調整が必要であることを示します。
サンプルB-1 ライトの色変更&再ベイク
まずディレクショナルライトのライトカラー値を(0,128,128)に変更して、ライトマップをベイクしなおしました。
サンプルB-2 ライトの色に合わせた補正
この状態で、Realtime Shadow Colorを(93,119,153)に設定し、影を馴染ませました(これもスポイトで色を決めています)。
サンプルB-3 白色光に変更&再ベイク
さらにこの状態でディレクショナルライトのカラーを(255,255,255)に戻して再度ライトマップをベイクしました。また描画が不自然になっているのがわかります。
このように、Realtime Shadow Colorの調整は、ベイクされたライトの色に合わせて行う必要があります。
まとめ
ライトマップとメインライトシャドウをブレンドする計算が(シェーダーコードを読めるとはいえ)微妙にブラックボックス化していることもあり、Subtractiveモードはとっつきが悪いかもしれません。とはいえ、静的オブジェクトにリアルタイムシャドウが投影されるのは演出効果としても非常に強力なので、使いこなして行きたい所です*4。