3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RenderFeature でフェードアウトする床反射

Last updated at Posted at 2023-01-13

はじめに

ScriptableRenderFeature の勉強に、フェードアウトする床反射を作ってみました。
Preview

謝辞

この記事の内容は 【Unity】URPを拡張して水面反射を作ってみた を参考・ベースにさせて頂いています。
「反射する3Dオブジェクトの描画」部分はほぼそのままなので、詳しくは参考記事をご覧ください。

Git Repository

環境

  • Unity 2021.3.16f1 (LTS)
  • Universal RP 12.1.8

Render 設定方法

HorizontalPlaneReflectionTextureRenderFeature を UnivarsalRenderData に追加して使用します。

Settings

Property 詳細
Property Description
Culling Mask レンダリング対象とするレイヤー
Plane Height 鏡面の高さ
Plane Thickness 鏡面の厚み
鏡像のレンダリング位置が厚みの分だけ下がります。
Fade Base Height フェードアウトが始まる高さ
Fade Range フェードアウトしきるまでの幅
Draw Opaque 不透明オブジェクトを描画するか
Draw Transparent 透明オブジェクトを描画するか
Render Pass Event Render Pass のタイミング
鏡面オブジェクトを Opaque で描画することを想定して、デフォルト値は Before Rendering Opaques.
Render Texture Scale Render Texture の大きさ倍率
Filter Mode Render Texture の Filter Mode
Render Texture Format Render Texture のフォーマット
HDR を使用する際は RGBAHalf などのアルファチャンネルが使えるフォーマットを指定することを推奨。
Render Texture Debug View Render Texture を表示する(デバッグ用)

フロー概要

  • RenderTexture に特定の高さで反転した3Dオブジェクトをフェードアウト状態でレンダリングする
    • フェードアウト描画用に専用の Pass を用意
    • 不透明パスは Depth 描画 → Color 描画の2ステップを踏むことで、前後関係の破綻を回避する
  • 上記 RenderTexture をサンプリングした床オブジェクトを描画する

実装

鏡像を描画する設定

鏡像の描画部分は 参考記事(【Unity】URPを拡張して水面反射を作ってみた) そのまま利用させて頂いています。
非常に丁寧に解説してくれているので、詳細はそちらをご覧ください。

フェードアウト描画

Plane Height などのパラメータを ShaderGlobalParameter で渡して、専用パスで描画します。
ScriptableRenderPass では LightMode を指定して描画できるため、 Opaque と Transparent にそれぞれ独自の LightMode の Tag を付けます。

  • Opaeque : HorizontalPlaneReflectionOpaque
  • Transpaernt : - Opaeque : HorizontalPlaneReflectionTransparent

Render Pass実装

HorizontalPlaneReflectionTextureRenderFeature.cs
cmd.SetGlobalFloat(FadeRangeId, Settings.fadeRange);
cmd.SetGlobalFloat(FadeBaseHeightId, Settings.fadeBaseHeight);
// 高さぴったりにすると床にピッタリ着く面も消してしまうため、少し余裕を持たせる
cmd.SetGlobalFloat(PlaneHeightId, Settings.planeHeight - 0.01f);

// Opaque Pass は Depth だけ先に描画する
context.DrawRenderers(cullingResults, ref depthDrawingSettings, ref FilteringSettings);
// Color を描画
context.DrawRenderers(cullingResults, ref drawingSettings, ref FilteringSettings);

Shader 実装

Opaque Shader

SampleLit.shader
// URP Lit Shader に Pass を追加
Pass
{
    Name "HorizontalPlaneReflectionOpaque"
    Tags{"LightMode" = "HorizontalPlaneReflectionOpaque"}

    Blend SrcAlpha OneMinusSrcAlpha
    Cull [_Cull]
    ZTest Equal  // 事前に Depth を描いているので Equal で描画
    ZWrite Off

    ...

    float GetReflectionAlpha(float heightWS)
    {
        half reflectionFadeAlpha = smoothstep(
            _ReflectionFadeBaseHeight + _ReflectionFadeRange,
            _ReflectionFadeBaseHeight,
            heightWS);
        reflectionFadeAlpha *= step(_ReflectionPlaneHeight, heightWS);

        return reflectionFadeAlpha;
    }

    half4 ReflectionLitPassFragment(Varyings input) : SV_Target
    {
        half4 outColor = LitPassFragment(input);

        half reflectionFadeAlpha = GetReflectionAlpha(input.positionWS.y);
        outColor.a *= reflectionFadeAlpha;

        return outColor;
    }
}

Alpha Blend Shader

SampleAlphaBlend.shader
Pass
{
    Name "HorizontalPlaneReflectionTransparent"
    Tags { "LightMode"="HorizontalPlaneReflectionTransparent" }
    Blend SrcAlpha OneMinusSrcAlpha
    Cull [_CullMode]
    ZTest LEqual
    ZWrite Off

    HLSLPROGRAM
    #pragma vertex vert
    #pragma fragment fragReflection

    ...

    half4 fragReflection (Varyings input) : SV_Target
    {
        // sample the texture
        half4 outColor = frag(input);
        half reflectionFadeAlpha = GetReflectionAlpha(input.positionWS.y);

        outColor.a *= reflectionFadeAlpha;

        return outColor;
    }
    ENDHLSL
}

Additive Shader

SampleAdditive.shader
Pass
{
    Name "HorizontalPlaneReflectionTransparent"
    Tags { "LightMode"="HorizontalPlaneReflectionTransparent" }
    Blend One One, Zero One  // アルファチャンネルは書き込まない
    Cull [_CullMode]
    ZTest LEqual
    ZWrite Off

    HLSLPROGRAM
    #pragma vertex vert
    #pragma fragment fragReflection

    ...

    half4 fragReflection (Varyings input) : SV_Target
    {
        half4 outColor = frag(input);
        half reflectionFadeAlpha = GetReflectionAlpha(input.positionWS.y);

        outColor.rgb *= reflectionFadeAlpha;

        return outColor;
    }
    ENDHLSL
}

RenderTexture を鏡面に合成

RenderTexture を P-MAP ブレンド手法で合成します。
Textures

SamplePlane.shader
Pass
{
    Name "ForwardUnlit"
    Tags { }
    ZWrite On

    HLSLPROGRAM
    #pragma vertex vert
    #pragma fragment frag

    // Includes
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/UnityInput.hlsl"
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"

    struct Attributes
    {
        float4 positionOS : POSITION;
        float2 texcoord : TEXCOORD0;
    };

    struct Varyings
    {
        float4 positionCS : SV_POSITION;
        float2 uv : TEXCOORD0;
        float4 positionSS : TEXCOORD1;
    };

    TEXTURE2D(_MainTex);  SAMPLER(sampler_MainTex);
    TEXTURE2D(_HorizontalPlaneReflectionTexture);  SAMPLER(sampler_HorizontalPlaneReflectionTexture);

    CBUFFER_START(UnityPerMaterial)
    half4 _BaseColor;
    float4 _MainTex_ST;
    float _ReflectionTextureIntensity;
    CBUFFER_END

    Varyings vert (Attributes input)
    {
        Varyings output = (Varyings)0;
        output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
        output.uv = TRANSFORM_TEX(input.texcoord, _MainTex);
        // Screen Space Position
        output.positionSS = ComputeScreenPos(output.positionCS);
        return output;
    }

    half4 frag (Varyings input) : SV_Target
    {
        half4 outColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
        outColor *= _BaseColor;
        half4 reflectionColor = SAMPLE_TEXTURE2D(
            _HorizontalPlaneReflectionTexture,
            sampler_HorizontalPlaneReflectionTexture,
            input.positionSS.xy / input.positionSS.w);

        // SrcColor + DstColor * (1 - SrcAlpha) で合成
        outColor.rgb = reflectionColor.rgb * _ReflectionTextureIntensity
            + outColor.rgb * (1.0 - reflectionColor.a* _ReflectionTextureIntensity);
        
        return outColor;
    }
    ENDHLSL
}

サンプルでは Unlit ですが、もちろん PBR などの Lit Shader にも合成できます。

課題:ビルボードと相性が悪い

ビルボードと相性が悪い
反転後のビルボードメッシュの向きがカメラ方向を向かない課題があります。
ScriptableRenderPass からメッシュの向きを調整できれば良いのですが、良い方法が思いついてない……:innocent:

ユニティちゃんライセンス

ユニティちゃんライセンス
この記事はユニティちゃんライセンス条項の元に提供されています。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?