【Unity】HDRPで単色のSky

Unity HDRPでは、VolumeのVirtual Environmentから背景(環境)を設定できます。現状、選べるSky TypeはGradient Sky、HDRI Sky、Physically Based Skyの3つですが、自分でカスタムSkyを作成することも可能です。この記事では単色のSkyを描画するカスタムSkyを作成してみます。

使用しているバージョンは以下のようになっています。

- **Unity:** 2021.2.9f1
- **High Definition RP:** 12.14 

この記事はUnity HDRP公式ドキュメントおよびHDRPのビルトインSky実装を参考にしています。

- 公式ドキュメント
    - [Creating a custom sky \| High Definition RP \| 12\.1\.4](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@12.1/manual/Creating-a-Custom-Sky.html)
- ビルトインSky実装の場所
    - `/Packages/High Definition RP/Runtime/Sky`
    - `/Packages/High Definition RP/Editor/Sky`


---

カスタムSkyを実装するには、以下の三種類のスクリプト(と必要であればSky Settingsのインスペクター拡張)を作成する必要があります。

- **Sky Settings:**
    - Volumeコンポーネントに表示され、カスタムSkyのプロパティを管理する
- **Sky Renderer:**
    - Sky Settingsのプロパティをもとに、Sky Rendering ShaderでカスタムSkyの描画を行う
- **Sky Rendering Shader:**
    - カスタムSkyの描画を行うシェーダー
- **(Sky Settings Editor):**
    - Sky Settingsのインスペクター拡張

まず、SkySettingsです。

```c#:UniformSky.cs
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;

[VolumeComponentMenu("Sky/Uniform Sky")]
[SkyUniqueID(UNIFORM_SKY_UNIQUE_ID)]
public class UniformSky : SkySettings
{
    const int UNIFORM_SKY_UNIQUE_ID = 782151;

    [Tooltip("Specifies the color of the sky.")]
    public ColorParameter color = new ColorParameter(Color.white, true, false, true);

    public override Type GetSkyRendererType()
    {
        return typeof(UniformSkyRenderer);
    }

    public override int GetHashCode()
    {
        int hash = base.GetHashCode();
        unchecked
        {
#if UNITY_2019_3
            hash = hash * 23 + color.value.GetHashCode();
#else
            hash = hash * 23 + color.GetHashCode();
#endif
        }
        return hash;
    }
}
```

次に、Sky Rendererです。

```c#:UniformSkyRenderer.cs
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;

public class UniformSkyRenderer : SkyRenderer
{
    public static readonly int _Color = Shader.PropertyToID("_Color");
    public static readonly int _SkyIntensity = Shader.PropertyToID("_SkyIntensity");
    Material m_UniformSkyMaterial;

    public override void Build()
    {
        m_UniformSkyMaterial = CoreUtils.CreateEngineMaterial(GetUniformSkyShader());
    }

    Shader GetUniformSkyShader()
    {
        return Shader.Find("Hidden/HDRP/Sky/Custom/UniformSky");
    }

    public override void Cleanup()
    {
        CoreUtils.Destroy(m_UniformSkyMaterial);
    }

    protected override bool Update(BuiltinSkyParameters builtinParams)
    {
        return false;
    }

    public override void RenderSky(BuiltinSkyParameters builtinParams, bool renderForCubemap, bool renderSunDisk)
    {

        var uniformSky = builtinParams.skySettings as UniformSky;
        float intensity = GetSkyIntensity(uniformSky, builtinParams.debugSettings);
        m_UniformSkyMaterial.SetColor(_Color, uniformSky.color.value);
        m_UniformSkyMaterial.SetFloat(_SkyIntensity, GetSkyIntensity(uniformSky, builtinParams.debugSettings));
        CoreUtils.DrawFullScreen(builtinParams.commandBuffer, m_UniformSkyMaterial, null, renderForCubemap ? 0 : 1);
    }
}
```

最後にSky Rendering Shaderです。`UniformSkyRenderer.cs`で`Shader.Find`を使用しているので、このシェーダーをビルドに含めるには`Resources`ディレクトリに置くか、`Always-included Shaders`に含める必要があります。


```hlsl:UniformSky.shader
Shader "Hidden/HDRP/Sky/Custom/UniformSky"
{
    HLSLINCLUDE

    #pragma vertex Vert

    #pragma editor_sync_compilation
    #pragma target 4.5
    #pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch

    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonLighting.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/SkyUtils.hlsl"

    float3 _Color;
    float _SkyIntensity;

    struct Attributes
    {
        uint vertexID : SV_VertexID;
        UNITY_VERTEX_INPUT_INSTANCE_ID
    };

    struct Varyings
    {
        float4 positionCS : SV_POSITION;
        UNITY_VERTEX_OUTPUT_STEREO
    };

    Varyings Vert(Attributes input)
    {
        Varyings output;
        UNITY_SETUP_INSTANCE_ID(input);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
        output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID, UNITY_RAW_FAR_CLIP_VALUE);
        return output;
    }

    float4 RenderSky(Varyings input)
    {
        return float4(_Color * _SkyIntensity, 1.0);
    }

    float4 FragBaking(Varyings input) : SV_Target
    {
        return RenderSky(input);
    }

    float4 FragRender(Varyings input) : SV_Target
    {
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
        float4 color = RenderSky(input);
        color.rgb *= GetCurrentExposureMultiplier();
        return color;
    }

    ENDHLSL

    SubShader
    {
        Tags{ "RenderPipeline" = "HDRenderPipeline" }
        Pass
        {
            ZWrite Off
            ZTest Always
            Blend Off
            Cull Off

            HLSLPROGRAM
                #pragma fragment FragBaking
            ENDHLSL
        }

        Pass
        {
            ZWrite Off
            ZTest LEqual
            Blend Off
            Cull Off

            HLSLPROGRAM
                #pragma fragment FragRender
            ENDHLSL
        }
    }
    Fallback Off
}
```

ここまで作成すると、Virtual EnvironmentのSky TypeでUniform Skyが選択できるようになり、かつVolumeコンポーネントのAdd OverrideでUniform Skyを追加できます。Uniform Skyを適用するとColorプロパティで選択した色でSkyが描画されます。
![Volume.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61569/c6be7fa2-87ac-74ff-43fb-67f9c7a17abc.png)

Uniform Sky自体はここまでのスクリプトで機能しますが、余分なプロパティが表示されているのでインスペクター拡張を作成して必要なプロパティのみ表示します。

```c#:UniformSkyEditor.cs
using UnityEditor;
using UnityEditor.Rendering;
using UnityEditor.Rendering.HighDefinition;

[CanEditMultipleObjects]
[VolumeComponentEditor(typeof(UniformSky))]
public class UniformSkyEditor : SkySettingsEditor
{
    SerializedDataParameter m_Color;

    public override void OnEnable()
    {
        base.OnEnable();
        m_CommonUIElementsMask = (uint)SkySettingsUIElement.UpdateMode
            | (uint)SkySettingsUIElement.SkyIntensity;
        var o = new PropertyFetcher<UniformSky>(serializedObject);
        m_Color = Unpack(o.Find(x => x.color));
    }

    public override void OnInspectorGUI()
    {
        PropertyField(m_Color);
        base.CommonSkySettingsGUI();
    }
}
```

以上でUnity HDRPで単色Skyの完成です。
![UniformSky04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61569/48e73e0c-4f82-d2c9-c321-cc4fe0009d94.gif)