Unity - URPのShader Graphで雲が流れるSkyboxを作る

UnityのURPでProcedural skyboxに雲を足したものが欲しくて作り方を調べたが、情報が少ない感じだったので書く。

環境

  • Unity: 2020.3.26f1 Personal
  • Universal RP: 10.8.1
  • Shader Graph: 10.8.1

できあがりイメージ

f:id:bifutek:20220122221744g:plain
空と雲と太陽

f:id:bifutek:20220123122857p:plain
雲の質感の例。GIFだとファイルサイズが肥大化してしまうので静止画。

  • 太陽 … 割と情報ありそうだったのでこの記事ではおまけ。
  • … 天頂付近・中空・地平線付近・地面の4層で個別に色を指定し、各層の間でグラデーションさせる。
  • … Noiseノードを使って生成する(テクスチャファイルを使わない)。

ShaderGraph

(画像のリンク先はGoogleDrive*1。700KBくらい。)

ShaderGraph全体

プロパティ設定例

f:id:bifutek:20220123210620p:plain
プロパティ設定例

完成品.unitypackage

手元の環境で動くのか手っ取り早く確認するための完成品パッケージ。
チュートリアルをやり切ってから動かないことが分かるとつらい。

CloudySkyboxShaderSample.unitypackage (47KBくらい) - Google ドライブ

各要素の補足

太陽

  • 実装は最低限。
  • Bloomで光り、Directional LightRotationで傾きを制御できる。
  • 日の傾きによる光の強弱や色の変化などには対応していない。
    参考リンクでそれらを含めて実装できるチュートリアルを紹介している。
  • 太陽を描画する方角と色を求めるためにCustom Functionノードを使う。
    中身はこんな感じ。

f:id:bifutek:20220117230130p:plain
MainLight(Custom Function)のGraph Inspector

HLSL部分:

#ifdef SHADERGRAPH_PREVIEW
  Direction = float3(0.5, 0.5, 0);
  Color = 1;
#else
  Light light = GetMainLight();
  Direction = light.direction;
  Color = light.color;
#endif

  • 天頂付近で空が深く、濃くなるようにしたかったのでグラデーションを4層にした。
    4層だとLerpが使えないので、MinimumSubtractで各層の差分を取って色を付け、重ね直すというやり方をしている。
    もっといい方法があるかもしれない。

  • 雲はSimple NoiseGradient Noiseで生成する。
    Gradient Noise(1つめ)で雲の発生パターン、Simple NoiseGradient Noise(2つめ)で雲の表面パターンを作る。
    Gradient Noise(2つめ)は雲の位置とずらして生成することで、雲が移動するにつれて形を変えていく感じにした。
    ただ、雲の流れを早くしたり、ずっと同じ場所を見つめたりしているとNoiseのパターンが見えてしまう。

  • 雲の移動はC#スクリプトで制御する。
    ShaderGraph側のClouds Offsetプロパティに_CloudsOffsetというReferenceを割り当て、それをC#スクリプト側から制御するようにした。

f:id:bifutek:20220117230127p:plain
`Clouds Offset`プロパティのGraph Inspector

MoveClouds.cs:

using UnityEngine;

public class MoveClouds : MonoBehaviour
{
    public Vector4 _cloudsSpeed = new Vector4(0.5f, 0.5f);

    private const string CLOUDS_OFFSET = @"_CloudsOffset";

    private void Update()
    {
        var skybox = RenderSettings.skybox;

        Vector4 current = skybox.GetVector(CLOUDS_OFFSET);
        Vector4 next = current + (_cloudsSpeed * Time.deltaTime);
        skybox.SetVector(CLOUDS_OFFSET, next);
    }
}
  • C#スクリプトの代わりにTimeノードを使えば、すべてをShaderGraph内で完結できるので散からなくてよかったのだが、PC(UHD630)がうなりを上げてしまうのでやめた。

参考リンク

  • Unity ShaderGraph Procedural Skybox Tutorial Pt.1 – Coster-Graphics
    ShaderGraphでSkyboxを作るチュートリアル
    ShaderGraphの作り方から、空、太陽、テクスチャを使った雲や星空など、SkyboxShaderの作成に必要な知識を体系的に学べる。

    また、Pt.2ではC#スクリプトを使ってDirectional Light(太陽)を回転させ、その傾きに応じて光の色や強度を変化させたり、朝や深夜など特定のタイミングでUnityEventを発生させるといった、より踏み込んだ内容も含まれている。

    Unity2019時点で執筆されたものだが、Unity2020.3でも動作した。
    (ShaderGraph上のノードのプレビューイメージがやや異なるが)

*1:このブログだと大きい画像は自動で縮小されるのでフォントがつぶれてしまう