1. 程式人生 > >運動模糊

運動模糊

攝影常用的一招就是延時攝影,以使運動的物體產生運動模糊。

攝影技巧為:1,三角架固定相機;2,調長曝光時間;3,物件有運動物體和靜止物體參照

用了延時攝影,照片會產生藝術感,見下圖(2015年1月 拍攝於上海陸家嘴)

 

遊戲方面可喜的是Unity3d也可以實現類似效果,先看效果圖

第一張為無運動模糊

第二張為有運動模糊:主體人物清晰,場景運動

第三張為第二張gif中的一幀

第四張為有運動模糊:攝像機不動,人物動的效果(凌波微步)

原理:

累積緩衝區:允許在渲染到顏色緩衝區之後,不是把結果顯示到視窗上,而是把內容複製到累積緩衝區,這樣就可以把顏色緩衝區與累積緩衝區中的內容反覆進行混合,可以用來進行模糊處理和抗鋸齒。

我們程式碼的實現是利用了一塊累積快取來混合多張連續的影象。我們不斷的不當前影象疊加到之前渲染好的影象中。

 

shader程式碼

Shader "mgo/motion_blur" 
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_BlurSize("_BlurSize", range(0.001, 0.999)) = 0.9
	}

	SubShader
	{
		Tags{ "RenderType" = "Opaque" }
		
		ZTest Off
		cull Off
		ZWrite Off
		Blend SrcAlpha OneMinusSrcAlpha

		Pass
		{
			Name "Main"
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

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

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

			uniform sampler2D _MainTex;
			uniform half _BlurSize;

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

			fixed4 frag(v2f i) : SV_Target
			{
				fixed4 color = tex2D(_MainTex,i.uv);
				//降低中心人物的運動模糊start
				float r = sqrt(pow(i.uv.x-0.5,2) + pow(i.uv.y-0.6,2));
				float a = _BlurSize * pow((1 - r + 0.01), 5);
				if (a < 1 - _BlurSize)
				{
					a = 1 - _BlurSize;
				}
				color.a = a;
				//降低中心人物的運動模糊end

				//color.a = 1 - _BlurSize;
				return color;
			}
			ENDCG
		}
		
	}
}

 c#程式碼

using UnityEngine;

namespace GameBase.Effect
{
    public class MotionBlurEffect : ImageEffectBase
    {

        [SerializeField]
        [Range(0.001f, 0.999f)]
        private float _blurSize = 0.9f;


        private void OnEnable()
        {
            material.SetFloat("_BlurSize",  _blurSize);
        }

        protected override void OnDisable()
        {
            base.OnDisable();
            RenderTexture.ReleaseTemporary(_accumulationRT);
            _accumulationRT = null;
        }

        private RenderTexture _accumulationRT;

        void OnRenderImage(RenderTexture source, RenderTexture destination)
        {
            if(_accumulationRT == null || _accumulationRT.width != source.width || _accumulationRT.height != source.height)
            {
                if(_accumulationRT != null)
                    RenderTexture.ReleaseTemporary(_accumulationRT);
                _accumulationRT = RenderTexture.GetTemporary(source.width, source.height, 0, source.format);
                _accumulationRT.hideFlags = HideFlags.HideAndDontSave;
                Graphics.Blit(source, _accumulationRT);
            }
            _accumulationRT.MarkRestoreExpected();//效能開銷很大,看下官方文件
            Graphics.Blit(source, _accumulationRT, material);
            Graphics.Blit(_accumulationRT, destination);
        }
    }
}