カスタムGI メモ

画像6

複数のLightmapTextureをShaderによって切り替える方法をメモしておきます。

先に書いておきますが、アバターについては現状どうしようもありません。将来的にこういうことをしてLightProbeが触れるようになればいいのかもしれません(関連canny)。アバターのみに影響するDirectionalLightをアニメーションやUDONで制御するのが現状の誤魔化し策のひとつだと思います。もしくは素直にRealTimeGIを使いましょう。


LightmapTextureを用意する

例えば、ワールドの昼と夜、照明の点灯状況等によって複数の照明環境がある状態が想定されます。単純のため、ここでは照明のオンオフのみを想定します。

画像1

画像2

少し極端かもしれませんが、このように2つのLighting環境を切り替えてみます。カーテンの隙間から入り込む光がちゃんと反映されるので個人的に好きです。

Sceneを複製してもいいので、とりあえず2つの環境でそれぞれBakeを行い、生成されたLightmapTextureを複製して保持しておきます。このワールドではEnlightenでBakeしました。Lightmapが1枚に収まればその後の扱いが楽になりますが、複数枚になってもテクスチャの参照を変えるだけですので問題ありません。

画像4

画像5

こんな感じのLightmapTextureが生成されました。これを使います。

注意点として、LightmapTextureが扱うUV情報はUnityが自動生成するわけですが、該当オブジェクトの有無でUVの生成状況は異なってしまいます。特にオブジェクトの有無によって確実に変わってしまいます。

画像3

こういうやつ。Bakeするときにオブジェクトのオンオフも同時に切り替えたい場合、Standard Shader の Cutoutでオブジェクト全体を見えなくした状態でBakeすると上手くいくかもしれません。

また、SkinnedMeshRendererがアタッチされているオブジェクトはLightmapのBakeができません。シェイプキーがあるとMeshRendererに変更するのもできないので、何かしら対策を考える必要があります。レイヤー分け等によってアバターと同様の扱いをしてしまう、とかですかね。

Q.冒頭動画のカーテンはどうやって閉めてるの?
A.頂点シェーダーをエイッとしてます。


Standard Shaderを改変してみよう

一例として、Standard Shader を改変します。Autodesk Interactive も参照先はStandard Shader と同じですので、どちらも同じように使うことができます。
まずはStandard Shader をUnity公式からダウンロードします。こちらが詳しいです。ちなみにAutodeskInteractiveが使っているGUIScriptはここにあります。

今回改変するコードは以下の3つです。
・Standard.shader(またはAutodeskInteractive.shader)
・UnityStandardCore.cginc
・UnityStandardCoreForward.cginc

まずStandard.shaderを開き、shader名を適当な名前に変更します。今回はStandard_Customにしました。ほかの.cgincファイルも適当に_Customを追加します。そしてLightmapTexture等必要なパラメータを追加します。

//Standard_Custom.shader

Shader "Standard_Custom"
{
Properties
{
    (略)
     //追加例
    _LightMap1("Baked Light Map 1", 2D) = "black" {}
    _LightMap2("Baked Light Map 2", 2D) = "black" {}
    _Mix0("Use Change Lightmap", Range(0.0, 1.0)) = 0.0
    _Mix1("Lightmap Change", Range(0.0, 1.0)) = 0.0
}
 
 (略)
 
Pass
{
    Name "FORWARD"
    Tags { "LightMode" = "ForwardBase" }

     (略)

    #pragma vertex vertBase
    #pragma fragment fragBase
    #include "UnityStandardCoreForward_Custom.cginc" //ここを変更する

    ENDCG
}
//他にも"UnityStandardCoreForward.cginc"と書かれている場所があるので
//忘れずに変更する
//UnityStandardCoreForward_Custom.cginc

(略)

#if UNITY_STANDARD_SIMPLE
   (略)
#else
   #include "UnityStandardCore_Custom.cginc" //ここを変更する
   VertexOutputForwardBase vertBase (VertexInput v) { return vertForwardBase(v); }
   VertexOutputForwardAdd vertAdd (VertexInput v) { return vertForwardAdd(v); }
   half4 fragBase (VertexOutputForwardBase i) : SV_Target { return fragForwardBaseInternal(i); }
   half4 fragAdd (VertexOutputForwardAdd i) : SV_Target { return fragForwardAddInternal(i); }
#endif

Noteのcode記述、使いづらいですね。

主に改変するのはUnityStandardCore_Custom.cgincです。上記によって改変したcgincを参照できるようになりました。

実装に関しては以下のリンクの一番下に書いてあることを参考にしました。

書いてあることはよくわかりませんが、つまり gi.light.color 等をどうにかすればLightmap情報を変更することができそうです。ではやってみましょう。

//UnityStandardCore_Custom.cginc
//463行目付近

// //追加部分
UNITY_DECLARE_TEX2D(_LightMap1);
UNITY_DECLARE_TEX2D(_LightMap2);
float _Mix0,_Mix1;

inline half3 ChangeLightmapTex(half3 i, float2 lightmapUV) {
   half3 bakedColor1 = DecodeLightmap(UNITY_SAMPLE_TEX2D(_LightMap1, lightmapUV));
   half3 bakedColor2 = DecodeLightmap(UNITY_SAMPLE_TEX2D(_LightMap2, lightmapUV));
   half3 bakedColor = lerp(bakedColor1, bakedColor2, _Mix1);

   return lerp(bakedColor, i, _Mix0);
}
// 追加終わり */

half4 fragForwardBaseInternal (VertexOutputForwardBase i)
{
   (略)
   UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight);

   //  //追加部分
   gi.light.color = ChangeLightmapTex(gi.light.color, i.ambientOrLightmapUV);
   #ifdef DIRLIGHTMAP_SEPARATE
       #ifdef LIGHTMAP_ON
           gi.light2.color = ChangeLightmapTex(gi.light2.color, i.ambientOrLightmapUV);
       #endif
       #ifdef DYNAMICLIGHTMAP_ON
           gi.light3.color = ChangeLightmapTex(gi.light3.color, i.ambientOrLightmapUV);
       #endif
   #endif
   gi.indirect.diffuse = ChangeLightmapTex(gi.indirect.diffuse, i.ambientOrLightmapUV);
   gi.indirect.specular = ChangeLightmapTex(gi.indirect.specular, i.ambientOrLightmapUV);
   // 追加終わり */
   
   half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect);
   
   (略)
}

i.ambientOrLightmapUV がLightmapテクスチャが参照するUVです。これを使ってテクスチャを読み込んで、lerpで変更しています。ちなみに正規にLightmapを参照している部分は UnityGlobalIllumination.cginc に書かれていますので、ぜひ読んでみてください。


さて、Shaderファイルを変更しただけではマテリアルのプロパティに反映されません。CustomEditor も改変する必要があります。元々あるStandardShaderGUI.csに、必要な部分をコピペするだけなので難しくないと思います。
面倒ならCustomEditor を使わない(コメントアウトする)という手もあります。

以下に解説がなされているサイト様を掲載します。

注意するべきは、「Editor」というフォルダ下にScriptを置いておかないとBuild時にエラーが起こります。

画像7

こんな感じです。

Scene内のLightmapStaticなオブジェクトすべてに、LightmapTextureをアタッチし、アニメーションなりUDONなりで _Mix0 や _Mix1 の値を変更すればOKです。

すべてのマテリアルにひとつひとつテクスチャをアタッチするのは面倒なので、そういうScriptも作ってみました。適当に改変して使ってみてください。


お疲れ様でした

解説、難しいです。メモ扱いです。

ちなみにCiANはいろいろあってこうなっていません。一般的なお話としては上記のような方法になると思います。