UnityのSkyboxを動的に弄る
会話の流れで、「Skyboxって動的にいじれるのかな」と思って調べたやつ。
マテリアルを準備しよう
なにはともあれSkyboxに使うマテリアルを新規作成。
今回は MySkybox
という名前で新規マテリアルを作った。
使用するシェーダは、未設定時に使われている default-skybox
と同じ Skybox/Procedual
をつける
window->lighting からLightingのメニューを開いて、 Environments Lighting
の SkyboxにMySkyboxを指定する。
下準備はOK
Skyboxの情報はどこにあるの?
RenderSettings.skybox
でMaterialクラスのインスタンスが取得できます。シーンのライティング情報はこの辺に格納されている模様。
じつはこれを見つけるのに一番時間かかった。
どうやって色を変えるの?
RenderSettings.skybox.color
の色を変える・・と思いきやそれだとエラーが出る。
Material doesn't have a color property '_Color' UnityEngine.Material:get_color()
Skyboxは _Color プロパティーを使用していないのでこれは使えない。
というか、 Color
プロパティて GetColor("_Color");
をやっていたのね・・。
シェーダが _Color を持っていないので、ちゃんと正しい名前を指定する必要がある。
Skybox/Procedualシェーダは以下のプロパティを用意している
- _SunSize
- _AtmosphereThickness
- _SkyTint
- _GroundColor
- _Exposure
Unity上で表示される場合は右側の 「_」無しだけど、シェーダ側では左側の名前で持ってるので間違えないように注意。
この中で空の色を扱っているのは _SkyTint
なので、これを取り出して値を弄ればよさそう。
実際にいじろう
以下は、毎フレーム空の色を -0.03して最終的に真っ暗にする処理です。
UniRXを使ってますが、あまりそこは気にしなくていいです。
void Start () { Material m = new Material(RenderSettings.skybox); RenderSettings.skybox = m; Observable.EveryUpdate(). Subscribe(_ =>{ var c = m.GetColor("_SkyTint"); c.r -= 0.03F; c.g -= 0.03F; c.b -= 0.03F; m.SetColor("_SkyTint", c); }); }
実行結果は記事トップのgifのようになります。
マテリアルのインスタンスからGetHoge、SetHogeでシェーダの任意の変数にアクセスできるので、
今回は _SkyTint
のColor情報を取ってきて、黒くして、またセットを行っている。
ちなみにcolorが0~1の範囲超えてもエラー吐かなかったのでどこかで丸めてくれている模様。
一つ注意として、最初にインスタンスを新規で作ってSkyboxに設定している部分がある。
Material m = new Material(RenderSettings.skybox);
RenderSettings.skybox = m;
なぜわざわざ差し替えているのかというと、SkyboxはProjectフォルダのマテリアルを直接参照してるので、シェーダに値を入れたりすると、エディタ上でゲームを再生して終了しても 値が反映されたまま になってしまう。
一度直接やってしまったので、Projectフォルダの MySkybox
の SkyTintの値が真っ黒のままになってしまっていて辛い思いをした。
なので、ゲーム開始時にその場で新しいマテリアルを生成してスワップすることで大元に変更を加えないようにしましょう。
GameObjectのInstantiate時と同じ考え方で大丈夫です。
Skyboxと銘打ったが、別にSkyboxに限らない話になってしまったことに気づいた。