Unity Shader之運動模糊

參考自《Unity Shader入門簡要》

原理梳理

  1. 存儲上一幀渲染效果的成像矩陣和當前幀成像逆矩陣
  2. 通過深度值獲得當前NDC坐標
  3. 通過NDC坐標和當前幀逆矩陣獲得頂點世界坐標
  4. 頂點世界坐標與上一幀成像矩陣獲取上一幀NDC坐標,求取同一頂點在兩幀內(nèi)NDC距離的差值
  5. 根據(jù)距離計算疊加上一幀uv和像素值
  6. 片元返回疊加后采樣效果,即該幀顯示兩幀效果,實現(xiàn)運動模糊

實現(xiàn)效果

C#代碼

void OnEnable()
{
    //設(shè)置相機屬性,生成深度紋理
    camera.Main.depthTextureMode = DepthTextureMode.Depth;
}
/*
OnRenderImage為MonoBehaviour內(nèi)模板函數(shù),類似與Awake等
通過該方法獲取屏幕成像前的RenderTexture
該方法時后期屏幕效果處理的基礎(chǔ)
*/
 void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (material != null)
        {
            material.SetFloat("_BlurSize", blurSize);
            material.SetMatrix("_PreviousViewProjectionMatrix", previousViewProjectionMatrix);
            //通過透視投影矩陣*VP矩陣獲取成像矩陣,運用VP矩陣原因是因為在shader中我們將通過世界坐標運算
            Matrix4x4 currentViewProjectionMatrix = camera_.projectionMatrix * camera_.worldToCameraMatrix;
            //通過成像逆矩陣和深度獲取頂點世界坐標
            Matrix4x4 currentViewProjectionInverseMatrix = currentViewProjectionMatrix.inverse;
            material.SetMatrix("_CurrentViewProjectionInverseMatrix", currentViewProjectionInverseMatrix);
            previousViewProjectionMatrix = currentViewProjectionMatrix;
            //將material應(yīng)用到RenderTexture實現(xiàn)效果
            Graphics.Blit(src, dest, material);
        }
        else
        {
            Graphics.Blit(src, dest);
        }
    }

Shader 代碼

Shader "Youcai/Chapter13/MotionBlurDepth"
{
    Properties
    {
        _MainTex("Base (RGB)", 2D) = "white"{}
        _BlurSize("BlurSize", float) = 0.5
    }
    SubShader
    {
        CGINCLUDE
#include "UnityCG.cginc"
        sampler2D _MainTex;
        //獲取_MainTex貼圖紋素值
        half4 _MainTex_TexelSize;
        //深度紋理,設(shè)置對應(yīng)的相機屬性在Shader內(nèi)直接定義即可
        sampler2D _CameraDepthTexture;
        float4x4 _CurrentViewProjectionInverseMatrix;
        float4x4 _PreviousViewProjectionMatrix;
        half _BlurSize;

        struct v2f
        {
            float4 pos: SV_POSITION;
            half2 uv: TEXCOORD0;
            half2 uv_depth: TEXCOORD1;
        };

        v2f vert(appdata_img v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = v.texcoord;
            o.uv_depth = v.texcoord;
//處理平臺差異存在的圖像翻轉(zhuǎn)問題
#if UNITY_UV_STARTS_AT_TOP
            o.uv_depth.y = 1 - o.uv_depth.y;
#endif
            return o;
        }

        fixed4 frag(v2f i) :SV_Target
        {
            //獲取深度值
            float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);
            //通過深度紋理獲取NDC坐標
            float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);
            //通過NDC坐標和成像逆矩陣獲取世界坐標
            float4 D = mul(_CurrentViewProjectionInverseMatrix, H);
            float4 worldPos = D / D.w;
            float4 currentPos = H;
            //通過上一幀成像矩陣和世界坐標獲取上一幀NDC坐標
            float4 previousPos = mul(_PreviousViewProjectionMatrix, worldPos);
            previousPos /= previousPos.w;
            //疊加兩幀uv和像素值
            float2 velocity = (currentPos.xy - previousPos.xy);
            float2 uv = i.uv;
            float4 c = tex2D(_MainTex, uv);
            uv += velocity * _BlurSize;
            c += tex2D(_MainTex, uv);
            //平分像素值
            c /= 2;
            return fixed4(c.rgb, 1.0);
        }
        ENDCG

        Pass
        {
        ZTest Always Cull Off Zwrite Off
            CGPROGRAM
#pragma vertex vert
#pragma fragment frag
            ENDCG
        }
    }
    Fallback Off
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容