ShaderTips

シェーダーTips

主にUnityシェーダーについての記事を書いています。

【Unity】【URP】 Chromatic Aberration (色収差)

Chromatic Aberration (色収差) エフェクト

光の屈折率は色によって異なるため、レンズを通過した際に焦点距離に差が発生し、ずれて見えます。

実装

実装方法としてはRチャンネルとBチャンネルを_Intensityに応じて拡大しています。

Shader "Hidden/ChromaticAberration"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Intensity ("Intensity", Range(0.0, 1.0)) = 0.1
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            half _Intensity;

            fixed4 frag (v2f i) : SV_Target
            {
                half4 col = tex2D(_MainTex, i.uv);
                // uvを-0.5〜0.5にする
                half2 uvBase = i.uv - 0.5h;
                // R値を拡大する
                half2 uvR = uvBase * (1.0h - _Intensity * 2.0h) + 0.5h;
                col.r = tex2D(_MainTex, uvR).r;
                // G値を拡大する
                half2 uvG = uvBase * (1.0h - _Intensity) + 0.5h;
                col.g = tex2D(_MainTex, uvG).g;

                return col;
            }
            ENDCG
        }
    }
}

色収差なし

色収差あり


SwapBuffer

今回URPのプロジェクトでポストエフェクトを実装するにあたってURP12の新機能であるSwapBufferを使って実装したので、解説します。

従来のやり方だとカメラのカラーバッファをテンポラリーなRenderTextureに描画しておき、ポストエフェクトをかけながらRenderTextureをカメラのカラーバファーに適応させます。

SwapBufferではURPが裏でテンポラリーなRenderTextureを用意してくれて、ユーザーがマテリアルを渡すだけでBlit処理を行ってくれます。

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public sealed class ChromaticAberrationRenderPass : ScriptableRenderPass
{
    private const string RenderPassName = nameof(ChromaticAberrationRenderPass);
    private readonly Material _material;

    public ChromaticAberrationRenderPass(Shader shader)
    {
        if (shader == null)
            return;

        _material = new Material(shader);
        // このレンダーパスをポストプロセスのタイミングで実行
        renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData data)
    {
        if (_material == null)
            return;

        var cmd = CommandBufferPool.Get(RenderPassName);

        // 一回Blitするだけで良くなりました。
        Blit(cmd, ref data, _material);

        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }
}
using System;
using UnityEngine;
using UnityEngine.Rendering.Universal;

[Serializable]
public sealed class ChromaticAberrationRendererFeature : ScriptableRendererFeature
{
    [SerializeField] private Shader _shader;

    private ChromaticAberrationRenderPass _postProcessPass;

    public override void Create()
    {
        _postProcessPass = new ChromaticAberrationRenderPass(_shader);
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        renderer.EnqueuePass(_postProcessPass);
    }
}

参考

light11.hatenadiary.com

light11.hatenadiary.com

youtu.be