Unity HDRPをカスタマイズしてアウトラインパスを追加する

HDRPを弄れる環境はこちらで既に整えました

nanka.hateblo.jp

今回は実際にカスタマイズして周作に最適なアウトラインパスを追加してみます。(Render Graphがリリースされたらここの情報は無になります(多分...))

使用するバージョン

  • Unity 2019.2.0f1
  • HDRP 6.9.0-preview

今回のプロジェクトはこちらです。 github.com

Lit.shaderをカスタマイズしてアウトラインパスを追加する

大元となるのはHDRPのLit.shaderなので丸々コピーしてきてアウトラインパスを追加しましょう。

今回使うバージョンである6.9.0-previewLit.shader はこちらです。

github.com

Lit.shader にはいくつかのパスが既に記述されているので、それを参考にアウトラインを描画するパスを追加します。

Pass
{
    Name "Outline"
    Tags { "LightMode" = "Outline" }

    // アウトライン用のパスのためカリング設定を反対にする
    Cull Front

    ZTest On
    ZWrite On

    HLSLPROGRAM

    #define SHADERPASS SHADERPASS_FORWARD
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Material.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/Lit.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/ShaderPass/LitSharePass.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Lit/LitData.hlsl"

    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/VertMesh.hlsl"

    PackedVaryingsType Vert(AttributesMesh inputMesh)
    {
        VaryingsType varyingsType;

        // オブジェクトスペースで法線方向に頂点を膨らませる
        inputMesh.positionOS += inputMesh.normalOS * 0.02;

        varyingsType.vmesh = VertMesh(inputMesh);
        return PackVaryingsType(varyingsType);
    }

    #ifdef TESSELLATION_ON

    PackedVaryingsToPS VertTesselation(VaryingsToDS input)
    {
        VaryingsToPS output;
        output.vmesh = VertMeshTesselation(input.vmesh);
        return PackVaryingsToPS(output);
    }

    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/TessellationShare.hlsl"

    #endif // TESSELLATION_ON

    void Frag(  PackedVaryingsToPS packedInput,
                out float4 outColor : SV_Target
                )
    {
        // アウトラインは黒色
        outColor = float4(0.0, 0.0, 0.0, 1.0);
    }

    #pragma vertex Vert
    #pragma fragment Frag

    ENDHLSL
}

該当箇所 https://github.com/kaneta1992/HDRPExperiments/blob/6b432dbe8ed167e9315ef1fce6f2fc16a75e6953/Assets/Outline/Shaders/outline.shader#L767-L818

HDRPのコードを書き換えてOutlineパスを実行する

最近HDRPのコードを読んでいて描画の大部分は HDRenderPipeline.cs に記述されていそうな感じがします。

少なくともパスを追加して呼び出すということがしたいだけであれば、ここを起点に読み進めればできそうです!

https://github.com/Unity-Technologies/ScriptableRenderPipeline/blob/6.9.0-preview/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.cs

というわけでOutlineを呼び出す処理を書いていきます。

Outlineパス差し込み

実装は置いておいて HDRenderPipeline.cs にパスを差し込みます。

今回はアウトラインということなので、DefferdやForwardのライティング処理が終わってから実行したいです。

空描画の次あたりに差し込んでみます。

RenderForwardEmissive(cullingResults, hdCamera, renderContext, cmd);

RenderSky(hdCamera, cmd);
++ RenderOutline(cullingResults, hdCamera, renderContext, cmd);

RenderTransparentDepthPrepass(cullingResults, hdCamera, renderContext, cmd);

RenderOutlineを実装する

差し込みましたが実装がまだないのでその他のRender関数を参考に作ります。

今回は RenderForwardOpaque を参考にしました。

void RenderOutline(CullingResults cullResults, HDCamera hdCamera, ScriptableRenderContext renderContext, CommandBuffer cmd)
{
    bool debugDisplay = m_CurrentDebugDisplaySettings.IsDebugDisplayEnabled();
    using (new ProfilingSample(cmd, "Render Outline", CustomSamplerId.Outline.GetSampler()))
    {
        bool msaa = hdCamera.frameSettings.IsEnabled(FrameSettingsField.MSAA);

        RenderTargetIdentifier[] renderTarget = null;

        renderTarget = mMRTSingle;
        renderTarget[0] = msaa ? m_CameraColorMSAABuffer : m_CameraColorBuffer;

        HDUtils.SetRenderTarget(cmd, renderTarget, m_SharedRTManager.GetDepthStencilBuffer(msaa));
        var rendererList = RendererList.Create(CreateOpaqueRendererListDesc(cullResults, hdCamera.camera, HDShaderPassNames.s_OutlineName));
        DrawOpaqueRendererList(renderContext, cmd, hdCamera.frameSettings, rendererList);
    }
}

CustomSamplerIdとHDShaderPassNamesにOutlineを追加

RenderOutlineで使用する変数等を追加します。

HDCustomSamplerId.cs にプロファイリング用に使用する列挙体を追加します

ForwardPassName,
++ Outline,
ForwardTransparentDepthPrepass,

HDStringConstants.cs にパス名の文字列とIDを追加します。

public static readonly string s_ForwardStr = "Forward";
++ public static readonly string s_OutlineStr = "Outline";
public static readonly string s_DepthOnlyStr = "DepthOnly";
public static readonly ShaderTagId s_ForwardName = new ShaderTagId(s_ForwardStr);
++ public static readonly ShaderTagId s_OutlineName = new ShaderTagId(s_OutlineStr);
public static readonly ShaderTagId s_DepthOnlyName = new ShaderTagId(s_DepthOnlyStr);

動作確認

これでアウトラインパスが動作するHDRPとシェーダーの準備ができたので、作成したシェーダーをアサインしたマテリアルをメッシュに割り当てて動作確認します。 f:id:kaneta1011:20190804023946p:plain

問題なく動作していそうです!

単純なパスなら意外とさっくりと追加できました、もっと良い方法や考慮すべきことが残っていそうな気がするので何か情報をお持ちの方は是非教えていただきたいです。