运动模糊

摄影常用的一招就是延时摄影,以使运动的物体产生运动模糊。

摄影技巧为: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);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/PangNanGua/article/details/85330156