【Unity】HDRPでカスタムポストエフェクトを実装する

UnityのHDRPでカスタムポストエフェクトを実装する方法についてまとめました。

Unity2020.3.22f1
High Definition RP 10.7.0

はじめに

本記事ではUnityのHigh Definition RP (HDRP)でカスタムポストエフェクトを作成する方法についてまとめます。

HDRPおよびポストエフェクトの基本的な使い方については本記事では扱わないので、必要に応じてマニュアルを参照してください。

docs.unity3d.com

シェーダを作成する

それではまずシェーダから作成します。
HDRPではポストエフェクト用のシェーダのテンプレートが用意されているので、
Assets > Create > Shader > HD Render Pipeline > Post Processからシェーダファイルを作成します。
ファイル名はExampleにしておきます。

これをベースにエフェクトを作成します。
今回は簡単に画面を赤くするエフェクトを作成します。

Shader "Hidden/Shader/Example"
{
    HLSLINCLUDE

    #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/Color.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/FXAA.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/RTUpscale.hlsl"

    struct Attributes
    {
        uint vertexID : SV_VertexID;
        UNITY_VERTEX_INPUT_INSTANCE_ID
    };

    struct Varyings
    {
        float4 positionCS : SV_POSITION;
        float2 texcoord   : TEXCOORD0;
        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);
        output.texcoord = GetFullScreenTriangleTexCoord(input.vertexID);
        return output;
    }

    float _Intensity;
    TEXTURE2D_X(_InputTexture);

    float4 CustomPostProcess(Varyings input) : SV_Target
    {
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

        uint2 positionSS = input.texcoord * _ScreenSize.xy;
        float3 outColor = LOAD_TEXTURE2D_X(_InputTexture, positionSS).xyz;

        // ここだけ追加しました
        outColor *= lerp(float3(1, 1, 1), float3(1, 0, 0), _Intensity);

        return float4(outColor, 1);
    }

    ENDHLSL

    SubShader
    {
        Pass
        {
            Name "Example"

            ZWrite Off
            ZTest Always
            Blend Off
            Cull Off

            HLSLPROGRAM
                #pragma fragment CustomPostProcess
                #pragma vertex Vert
            ENDHLSL
        }
    }
    Fallback Off
}

コメントをつけた一行だけテンプレートから変えました。

スクリプトを作成する

さて次に必要なスクリプトを作成します。
こちらもテンプレートが用意されているので、Assets > Create > Rendering > C# Post Process Volume から作成します。
こちらもシェーダと同様に名前はExampleにしておきます。

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using System;

[Serializable, VolumeComponentMenu("Post-processing/Custom/Example")]
public sealed class Example : CustomPostProcessVolumeComponent, IPostProcessComponent
{
    // Volumeコンポーネントから設定してシェーダに渡すパラメータ
    // このようにVolumeParameterの派生クラスを使用する
    [Tooltip("Controls the intensity of the effect.")]
    public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f);

    Material m_Material;

    // 無駄に処理が走らないように有効状態を返す
    public bool IsActive() => m_Material != null && intensity.value > 0f;

    // このポストエフェクトをかけるタイミング
    public override CustomPostProcessInjectionPoint injectionPoint => CustomPostProcessInjectionPoint.AfterPostProcess;
    
    // シェーダ名
    const string kShaderName = "Hidden/Shader/Example";

    public override void Setup()
    {
        if (Shader.Find(kShaderName) != null)
            m_Material = new Material(Shader.Find(kShaderName));
        else
            Debug.LogError($"Unable to find shader '{kShaderName}'. Post Process Volume Example is unable to load.");
    }

    public override void Render(CommandBuffer cmd, HDCamera camera, RTHandle source, RTHandle destination)
    {
        if (m_Material == null)
            return;

        m_Material.SetFloat("_Intensity", intensity.value);
        m_Material.SetTexture("_InputTexture", source);
        HDUtils.DrawFullScreen(cmd, m_Material, destination);
    }

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

今回は中身は特に変える必要はありません。
説明のためにコメントだけ記述しました。

シーンに適用する

最後にこのエフェクトをシーンに適用していきます。
まずProject Settings > HDRP Default Settings > Custom Post Process Ordersの. After Post Processの+ボタンを押下して今回作ったExampleエフェクトを追加します。

f:id:halya_11:20211209173846p:plain:w600
Custom Post Process Orders

次にシーン上にPost Processingをセットアップし、VolumeコンポーネントAdd Overrideボタンから、
Post-processing > Custom > Exampleを選択します。
Intensityにチェックを入れてスライダーの値を大きくすると、以下のようにポストエフェクトが適用されることを確認できます。

f:id:halya_11:20211209174614p:plain:w600
適用

参考

docs.unity3d.com

docs.unity3d.com