運動模糊【Unity Shader入門精要12.6】
阿新 • • 發佈:2018-11-15
實現方法:
【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
不得不說,當個小白也挺好,學習資源不愁找,哈哈~