テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】リアルタイムな影がカクカクになる問題の対処

f:id:tsubaki_t1:20180413013847j:plain

今回はリアルタイムライトがカクカクになる場合の対処についてです。

 

ライトがカクカクになる問題

Unityでリアルタイムなライトを使用すると、ライトがカクカクになる事があります。例えば下の図のように、ジャギーが発生するなど。

f:id:tsubaki_t1:20180413014330j:plain

 この現象は端的に言えば「Shadow Mapの解像度が足りてない」から起こるのですが、そこに至るまでに何をどうすべきなのか…というのを今回は書いてみます。

 

Shadow Mapの解像度はどのようにして決まるのか?

まずリアルタイムなShadow Mapは各ライト毎にShadow Mapが生成されます。
つまり解像度が512のスポットライトを二つ用意した場合、512x512のテクスチャが2枚分レンダリングされる訳です。

f:id:tsubaki_t1:20180413020353j:plain

さて、各ライトにShadow Mapが作られるとしてどのように決まるのか?
Shadow Mapの解像度は画面の解像度と使用するライトのタイプ(あとGPUの性能)に依存します。
下の倍率はHighの状態で、Middleで1/2、Lowで1/4になります。

ディレクショナルライト 解像度の1.9倍
スポットライト 解像度の1倍
ポイントライト 解像度の0.5倍

ポイントライトがやたらと解像度が低く設定されているのは、ポイントライトは6面レンダリングする必要がある為だそうです。またポイントライトは6回バッファをクリアしなければいかんので、それもまたコストになりそうです。

 

ライトがカクカクになる問題の対策

さて、ライトがカクカクする原因は解像度として、どのように対策をするべきか考えてみます。実は色々とありますが、やり方はパフォーマンスや表現とのトレードオフになります。

Shadow Mapの解像度を上げる

即座に思いつくアイディアは「Shadow Mapの解像度を上げる」というものです。Shadow Mapには解像度が設定されており、それを上げれば良いという力技の考えです。

解像度はライトのResolutionで変更が可能ですが、それ以外にもLight.shadowCustomResolutionで直接値を設定することが出来ます。設定した値は2のべき乗に丸められます。

f:id:tsubaki_t1:20180413023024j:plain

gist.github.com

これは単純に綺麗にしやすいですが、パフォーマンスのボトルネックになる事があるかもしれません。またハードウェア上の制約で解像度が上がらない…という事もあります。例えば幾つかのモバイルは解像度が2048以上のテクスチャを使えないとか。

 

Scriptable Render Pipelineには影の解像度設定(数値)があるので、それを云々するのも良いアイディアかもしれません。

 

解像度を有効活用する

解像度を上げられない場合、解像度を可能な限り効率的に有効活用するべきです。これは対応するライトのタイプによって考えるべき設定が異なります。

 

ポイントライトの場合

まずポイントライトの場合、可能な限り影を映すモノの近くに配置するべきです。ポイントライトと対象との距離で、影の品質が大きく違います。

上の画像はライトを非常に遠くに置いた場合、下は近くにおいた場合です。


稀に太陽や広域照明としてポイントライトを使用する…というケースを見ますが、それはリアルタイムな影には非常に都合が悪いという訳です。

f:id:tsubaki_t1:20180413025714j:plain

f:id:tsubaki_t1:20180413025722j:plain

 

スポットライトの場合

ではスポットライトの場合はどうか?

スポットライトの場合は、Spot Angleが重要なポイントになります。この項目が広いと解像度が下がり、小さいと高解像度になります。Rangeや距離はそれ程重要では無いみたいです。

 

そのため、スポットライトを広げて広域照明に…みたいな使い方をすると、あっさりとジャギーが出現するかもしれません。

f:id:tsubaki_t1:20180413030421j:plain

f:id:tsubaki_t1:20180413030431j:plain

 

ディレクショナルライトの場合

ではディレクショナルライトの場合はどうか? これは単純に影の範囲が問題になります。Shadow Distanceの設定やCameraの描画範囲、Shadow Projectionの設定を見直すことで解決するかもしれません。

特にUnity 2017までの初期設定では、Shadow Distanceは120mというオープンワールドを意識したような範囲を想定しているので、近距離に使える解像度が非常に少なく、簡単にジャギーが起こります。

f:id:tsubaki_t1:20180413030804j:plain

またカスケードも活用すれば良い結果が得られる事があります。例えば通常の場合は単一の解像度で描画するので、遠距離の影の解像度は十分だが近距離は不足…といった事が起こります。

f:id:tsubaki_t1:20180413030851j:plain

これを、近距離と遠距離で解像度を分けて「近距離は高密度、遠距離は雑」なShadowMapを実現して回避します。

ただしカスケードはモデルを複数回描画します。GPU的には安いですが、CPU的には高くつくかもしれません

f:id:tsubaki_t1:20180413031108j:plain

どうせ遠距離にある物は静的な物だろ?という理屈でShadow Maskを活用すると、カスケードを使わず高解像度なShadow Mapを活用できますが、またそれは別の話です。

f:id:tsubaki_t1:20180413033925j:plain

f:id:tsubaki_t1:20180413033935j:plain

なおカスケードはプラットフォームによって無効にされてる事があります。PlayerSettingsのGrahicsとかで確認すると良いですが、有効にしても無効化されることがあります。

関連

Shadow Maskで遠距離の影は全部ベイクするアイディア(遠距離の影のみなので、ベイクした影の解像度は超低くても良い)

tsubakit1.hateblo.jpDirectional Lightを設定するときに有効

tsubakit1.hateblo.jpライトのマニュアル

docs.unity3d.com