今回はライトマッピングを使用した室内シーンの照明でゲーム実行中にライトマップデータを差し替えてライティングを変更する方法を試してみました。
ライトマップをベイクする Area Light のみを使用して明るい照明と暗い照明を配置したテスト用のシーンを用意します。ライトプローブとリフレクションプローブを配置しています。
明るい照明はややオレンジ色に調整してこんな感じになりました。
暗い照明は床に配置して色をグリーン調整。こんな感じになりました。
ライティング設定は WebGLビルドに合わせて調整。
メニュー:Window > Rendering > Lighting Settings を選択。[Lighting]ウィンドウ > [Scene]タブを選択。シーンは室内のみでライトプローブとリフレクションプローブを配置しているので環境ライティングは影響を失くし、WebGLビルドでサポートされていない Realtime Lighting のチェックは外します。
Lightmapping Settings > Directional Mode もWebGLビルドでサポートされていないので通常は Non-Directional に設定しますが。今回はスクリプト検証のために Directional に設定しました。
ライトマップ – Directional Mode – Unity マニュアル
Non-Directional ライトマップは平らな拡散を生成します。このモードはただ 1 つのライトマップを使用しており、光が純粋に拡散性であると仮定して、どれだけ多くの光を表面から放出するかという情報を保存します。オブジェクトはこの方法で照らされると、平らに見えます (法線マップは使用されません)。
明るい照明の状態から暗い照明に切り替えるにはライトマップデータを書き出し、ファイルを差し替える処理を行います。そこでシーンを暗い照明の状態にしておき、ライトマップデータをファイルに書き出します。
シーン名と同じフォルダが作成されて、ライトマップファイルが出力されました。
先程出力したライトマップデータにはキャラクターのように動くオブジェクトにライティングを反映するライトプローブファイル含まれていません。調べてみると、ライトプローブファイルはアセットファイルとして現在のシーンから個別に出力する必要があるということなので、エディタ用スクリプトを作成します。
ビルドの際にエディタ用スクリプトを除外するために新しく Editor フォルダを作成します。 [Project]ウィンドウ > [Create] > Folder を選択。名前をEditor に変更。フォルダをダブルクリックして階層を移動してから [Project]ウィンドウ > [Create] > C# Script を選択。
using System.Collections; using System.Collections.Generic; using UnityEngine; // シーン管理のライブラリを使用する宣言 using UnityEngine.SceneManagement; // エディタ関連ライブラリを使用する宣言 using UnityEditor; // システム入出力のライブラリを使用する宣言 using System.IO; public class ExportLightprobe { // エディタのメニューに項目を追加 [MenuItem("Export/Export LightProbe")] // ライトプローブファイルの出力処理 static void Export() { // 変数 scene を作成して現在開いているシーンを格納 Scene scene = SceneManager.GetActiveScene(); // 変数 path を作成してシーン名を含む階層情報を格納 string path = "Assets/" + Path.GetFileNameWithoutExtension(scene.name); // 変数 path の場所にシーン名のフォルダを作成 Directory.CreateDirectory(path); // ライトマップ設定のライトプローブファイルのアセットを作成して変数 path の場所に出力 AssetDatabase.CreateAsset(GameObject.Instantiate (LightmapSettings.lightProbes), path + "/Lightprobe.asset"); } }
メニュー項目に Export が追加されました。スクリプトファイルを作成するだけでメニューを拡張できるとは、ちょっと不思議な感じです。
追加されたメニュー:Export > ExportLightprobe を選択すると現在開いているシーンのライトプローブがシーン名と同じフォルダにアセット(拡張子 asset )として書き出されます。
出力したライトマップデータから法線ライトマップ、ライトマップ、リフレクションプローブを複製(Ctrl キー + D キー)し、別ファイルにしてからリネームします。
このようにリネームしました。
暗い照明のライトマップデータの出力ができたので、シーンを明るい照明に戻し、 [Lighting]ウィンドウ > [Scene]タブ > [Generate Lighting]ボタンをクリック。元のライトマップデータは上書きされます。リフレクションプローブだけ複製します。
メニュー:Export > ExportLightprobe を選択して明るい照明のライトプローブを出力します。
このようにリネームしました。
出力したすべてのライトマップデータを新しいフォルダ LightmapData を作成して移動します。必要なライトマップデータが揃いました。
ライトマップデータを切り替えるオブジェクトとスクリプトを作成します。
スクリプトの内容はこのようになっています。UIのトグルボタンでライトマップデータを切り替えるための処理を入れています。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Corevale_SwapLightmap : MonoBehaviour { // ライトマップファイルと法線ライトマップを格納する配列 [SerializeField] Texture2D[] lightMap; // 法線ライトマップを格納する配列 [SerializeField] Texture2D[] dir; // ライトプローブファイルを格納する配列 [SerializeField] LightProbes[] lightProbe; // ReflectionProbeファイルを格納する配列 [SerializeField] Cubemap[] reflectionProbe; // リフレクションプローブを格納する変数 [SerializeField] ReflectionProbe probeComponent; // 明るい照明のライトマップデータ LightmapData[] brightDatas; // 暗い照明のライトマップデータ LightmapData[] darkDatas; // 現在のライトマップデータが暗い照明か判定するフラグ public bool swapLight = false; // ゲームスタート時の処理 void Start() { // 現在のシーンのライトマップデータを変数 brightDatas に格納 //(明るい照明のライトマップデータに格納) brightDatas = LightmapSettings.lightmaps; // ライトマップファイル lightMap の長さを取得 //(暗い照明のライトマップデータ darkDatas に格納) darkDatas = new LightmapData[lightMap.Length]; // 繰り返し処理:暗いライトマップデータ、法線ライトマップのファイル数分 for (int i = 0; i < darkDatas.Length; i++) { // ライトマップデータ lightingData を作成 LightmapData lightingData = new LightmapData(); // 配列 light に格納したライトマップファイルをライトマップデータに格納 lightingData.lightmapColor = lightMap[i]; // 配列 dir に格納した法線ライトマップファイルをライトマップデータに格納 lightingData.lightmapDir = dir[i]; // ライトマップデータ lightingData に格納された // ライトマップファイルと法線ライトマップファイルを配列 lightingDatas に格納 darkDatas[i] = lightingData; } } // ライトマップデータの切替処理 public void SwapLightmapData() { // 変数 swapLight が false の場合 if (!swapLight) { // DarkLightmap 処理を実行 DarkLightmap(); // 変数 swapLight を true に変更 swapLight = true; } // 変数 swapLight が true の場合 else if (swapLight) { // BrightLightmap 処理を実行 BrightLightmap(); // 変数 swapLight を false に変更 swapLight = false; } } // 暗い照明のライトマップデータを現在のシーンに適用する処理 void DarkLightmap() { // 暗い照明のライトマップファイルを現在のライトマップ設定に適用 LightmapSettings.lightmaps = darkDatas; // 暗い照明のライトプローブファイルを現在のライトマップ設定に適用 LightmapSettings.lightProbes = lightProbe[1]; // 暗い照明のリフレクションプローブファイルを現在のリフレクションプローブに適用 probeComponent.customBakedTexture = reflectionProbe[1]; } // 明るい照明のライトマップ設定を現在のシーンに適用する処理 void BrightLightmap() { // 明るい照明のライトマップファイルを現在のライトマップ設定に適用 LightmapSettings.lightmaps = brightDatas; // 明るい照明のライトプローブファイルを現在のライトマップ設定に適用 LightmapSettings.lightProbes = lightProbe[0]; // 明るい照明のリフレクションプローブファイルを現在のリフレクションプローブに適用 probeComponent.customBakedTexture = reflectionProbe[0]; } }
スクリプトが完成したので設定を行います。
スクリプトを割り当てたオブジェクト SwapLightmap を選択して[Inspector]ウィンドウで確認するとこのように変数と配列のフィールドが表示されています。
配列数を変更して表示されたフィールドにそれぞれライトマップデータのファイルを割り当てます。Probe Component フィールドにはシーンに配置してあるライトプローブオブジェクトを割り当てます。
リフレクションプローブのコンポーネントの設定はデフォルトでType > [Bake]になっています。(シーンの静的オブジェクトをベイクしたキューブマップを使用)
スクリプトからリフレクションプローブのキューブマップファイルを切り替えるために設定を[Custom]に切り換え、明るい照明のリフレクションプローブ ReflectionProbe-Bright を割り当てておきます。
ライトマップデータを切り替えるためのトグルボタンを作成します。[Hierarchy]ウィンドウ > UI > Toggle を選択。
[Inspector]ウィンドウ > Toggle (Script) > On Value Changed (Boolean) にボタンの値が変わったときに実行される処理を設定。オブジェクトに SwapLightmap を割り当て、関数 Corevale_SwapLightmap > SwapLightmapData() を選択。
テスト用にmetallic の値を 1 に設定したマテリアルを割り当てた Sphere オブジェクトをシーンに配置して再生。トグルボタンを切り替えるとライトマップとリフレクションプローブのキューブマップが切り替わりました!(画像クリックで拡大)
動作確認したシーンにライトプローブの切り換えが確認できるようにプレイヤーキャラクター Robot Kyle を配置。リフレクションプローブの切り換えが分かりやすいように Capsule オブジェクトを頭に追加してみました。
以前の記事 プレイヤーキャラクターをマウスクリックした位置に移動させる の内容と同じ方法を使用してマウスクリックで操作できるようにしました。
画像クリックで再生(ファイルサイズ:約18MB)
今回の記事はこちらのWebサイトの記事を参考にしました。感謝!
【Unity】モバイル向けのライトマップTipsと、ライトマップを動的に更新するHack – テラシュールブログ
How to create lightmap for day and for night and switch them in runtime. – Unity Forum
View Comments