1. 程式人生 > >運動模糊【Unity Shader入門精要12.6】

運動模糊【Unity Shader入門精要12.6】

實現方法:

【1】利用一塊 累積快取(accumulation buffer)來混合多張連續的影象。當物體快速移動產生多張連續的影象後,去取他們之間的平均值作為最後的運動模糊影象,然而這種辦法消耗很大

【2】建立和使用 速度快取(Velocity buffer)這個快取中儲存了各個畫素當前的運動速度,然後利用該值來決定模糊的方向和大小

 

本節使用的是第一種,不過不需要再一幀中把場景渲染多次,但需要儲存之前的渲染結果,不斷把當前的渲染影象疊加到之前的渲染影象中,從而產生一種運動軌跡的視覺效果,這種方法與原始 的利用累積快取的方法相比效能更好,但模糊效果可能會略有影響

 

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//12.6 運動模糊
public class MotionBlur : PostEffectsBase {
    public Shader motionBlurShader;  //宣告shader
    private Material motionBlurMaterial = null;//宣告材質

    public  Material material//建立材質
    {
        get
        {
            motionBlurMaterial = CheckShaderAndCreateMaterial(motionBlurShader, motionBlurMaterial);//把shader給新建的材質
            return motionBlurMaterial;//返回材質
        }
    }
    //定義運動模糊在混合影象時使用的迷糊引數
    [Range(0.0f, 0.9f)]//為了防止拖尾效果完全替代前幀的渲染結果,所以把值控制在0到0.9範圍內
    public float blurAmount = 0.5f;

    private RenderTexture accumulationTexture;//定義一個RenderTexture型別的變數,儲存之前影象疊加的結果

    //在該指令碼不執行時,即呼叫OnDiable時,立即銷燬accumulationTexture,這是因為希望在下一次開始應用運動模糊時重新疊加影象
    private void OnDisable()
    {
        DestroyImmediate(accumulationTexture);
    }
    //定義迷糊使用的OnRenderImage函式
    private void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        //確認材質可用
        if (material !=null)
        {
            //判斷用於混合影象的accumationTexture是否為空,是否與當前解析度不相等,
            if (accumulationTexture ==null ||accumulationTexture .width !=src .width||accumulationTexture .height !=src .height )
            {
                //立即銷燬
                DestroyImmediate(accumulationTexture);
                //建立一個符合大小的得變數
                accumulationTexture = new RenderTexture(src.width, src.height, 0);
                //HideAndDontSave:保留物件到新場景,與DontSave類似,但不會顯示在Hierarchy面板中
                accumulationTexture.hideFlags = HideFlags.HideAndDontSave;
                Graphics.Blit(src, accumulationTexture);//使用當前的幀影象初始化accumulationTexture
            }
            //恢復操作(restore operation):發生在渲染到紋理而該紋理又沒有被提前清空或銷燬的情況下

            //呼叫函式 【accumulationTexture.MarkRestoreExpected】來表明需要進行一個渲染紋理的恢復操作
            accumulationTexture.MarkRestoreExpected();

            //每次呼叫OnRenderImage時都需要把當前的幀影象和accumulationTexture中的影象混合,accumulationTexture紋理不需要提前清空,因為她儲存了我們之前的混合結果
            
            material.SetFloat("_BlurAmount", 1.0f - blurAmount);//將引數傳給材質

            Graphics.Blit(src, accumulationTexture, material);//把當前螢幕影象src疊加到accumulationTexture中
            Graphics.Blit(accumulationTexture, dest);//把結果顯示到螢幕上
        }else
        {
            Graphics.Blit(src, dest);
        }
    }

}
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

//12.6運動模糊
Shader "Unlit/Chapter12-MotionBlur"
{
	Properties
	{//對應輸入的渲染紋理
		_MainTex ("Base(RGB)", 2D) = "white" {}
	//混合係數
		_BlurAmount("Blur Amount" , Float) = 1.0
	}
	SubShader
	{
			CGINCLUDE
	#include "UnityCG.cginc"

			sampler2D  _MainTex;
			fixed _BlurAmount;

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

			v2f vert (appdata_img v){
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				return o;
			}

			//定義兩個片元著色器,為了保護A通道不受到混合的影響
			//渲染rgb通道
			fixed4 fragRGB(v2f i) : SV_Target{
				//對影象進行取樣,把A通道的值設定為_BlurAmount,以便在後面混合時可以使用它的透明通道進行混合
			return fixed4(tex2D(_MainTex,i.uv).rgb,_BlurAmount); 
			}
			//渲染A通道
			half4 fragA(v2f i) : SV_Target{
				//直接返回取樣結果
					return tex2D(_MainTex,i.uv);
			}

				ENDCG

				ZTest Always Cull Off ZWrite Off

	//之所以把把A通道和RGB通道分開,是因為在更新RGB時我們需要設定它的A通道的來混合影象,但又不希望A通道的值寫入渲染紋理中

				//用來更新渲染RGB通道
			Pass
			{
				//混合 A 1-A
				Blend SrcAlpha OneMinusSrcAlpha
					//顏色遮罩 RGB
					ColorMask RGB


					CGPROGRAM
#pragma vertex vert
#pragma fragment fragRGB
					ENDCG 
			}

			//用來更新渲染A通道
			Pass {
				//混合 1 0
				Blend One Zero
					//顏色遮罩 A
				ColorMask A

				CGPROGRAM
#pragma vertex vert
#pragma fragment fragA
				ENDCG
			}
	}
			Fallback Off
}

關於shader的Blend參考:https://blog.csdn.net/baicaishisan/article/details/79072127

關於C#的HideFlags類參考:https://blog.csdn.net/Dylan_Day/article/details/80350420

 

不得不說,當個小白也挺好,學習資源不愁找,哈哈~