unity shader 后处理实现运动模糊效果

记录用unity shader 后处理实现运动模糊效果(learn by 《unity shader 入门精要》)

运动模糊的实现有多种方式。这里的实现方式是利用一块累计缓存(accumulatioin buffer)来混合多张连续的图像

实现过程如下:

1、创建一个让摄像机移动的脚本 模拟运动效果。并将其拖至主摄像机

Translating.cs

using UnityEngine;
using System.Collections;

public class Translating : MonoBehaviour {

	public float speed = 10.0f;
	public Vector3 startPoint = Vector3.zero;
	public Vector3 endPoint = Vector3.zero;
	public Vector3 lookAt = Vector3.zero;
	public bool pingpong = true;

	private Vector3 curEndPoint = Vector3.zero;

	// Use this for initialization
	void Start () {
		transform.position = startPoint;
		curEndPoint = endPoint;
	}
	
	// Update is called once per frame
	void Update () {
		transform.position = Vector3.Slerp(transform.position, curEndPoint, Time.deltaTime * speed);
		transform.LookAt(lookAt);
		if (pingpong) {
			if (Vector3.Distance(transform.position, curEndPoint) < 0.001f) {
				curEndPoint = Vector3.Distance(curEndPoint, endPoint) < Vector3.Distance(curEndPoint, startPoint) ? startPoint : endPoint;
			}
		}
	}
}

2、创建继承自基类的运动模糊后处理脚本MotionBlur.cs,并将其拖拽至摄像机中

基类脚本见上文

unity shader 后处理实现边缘检测

MotionBlur.cs

using UnityEngine;
using System.Collections;

public class MotionBlur : PostEffectsBase {

	public Shader motionBlurShader;
	private Material motionBlurMaterial = null;

	public Material material {  
		get {
			motionBlurMaterial = CheckShaderAndCreateMaterial(motionBlurShader, motionBlurMaterial);
			return motionBlurMaterial;
		}  
	}

	//定义运动模糊在混合图像时使用的模糊参数 会有拖尾效果
	[Range(0.0f, 0.9f)]
	public float blurAmount = 0.5f;
	
	//用于保存之前图像叠加的效果
	private RenderTexture accumulationTexture;

	//脚本不运行时,即调用OnDisable函数时,立即销毁accumulation Texture
	void OnDisable() {
		DestroyImmediate(accumulationTexture);
	}

	void OnRenderImage (RenderTexture src, RenderTexture dest) {
		if (material != null) {
			// Create the accumulation texture
			if (accumulationTexture == null || accumulationTexture.width != src.width || accumulationTexture.height != src.height) {
				DestroyImmediate(accumulationTexture);
				accumulationTexture = new RenderTexture(src.width, src.height, 0);
				accumulationTexture.hideFlags = HideFlags.HideAndDontSave;//表示该变量不会显示在Hierarchy中,也不会保存到场景中
				Graphics.Blit(src, accumulationTexture);//将源纹理直接传给accumulationTexture作为初始化
 			}

			// We are accumulating motion over frames without clear/discard
			// by design, so silence any performance warnings from Unity
			accumulationTexture.MarkRestoreExpected();//进行一个渲染纹理的恢复操作

			material.SetFloat("_BlurAmount", 1.0f - blurAmount);

			Graphics.Blit (src, accumulationTexture, material);//将当前屏幕图像
			Graphics.Blit (accumulationTexture, dest);
		} else {
			Graphics.Blit(src, dest);
		}
	}
}

上文提到我们使用的方法是累计缓存 就是使用accumulationTexture变量不断地叠加新图像进行混合

逻辑如下:首先判断是否为空,是否与当前分辨率相等,如不满足重新创建该变量,用Blit进行初始化

然后进行一个渲染纹理的恢复操作,这样他会一直保存我们之前的混合结果

之后再用Blit将当前屏幕图像新叠加到accumulationTexture中

 

下面来实现Shader部分

MotionBlur.shader

Shader "MyShader/Motion Blur" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BlurAmount ("Blur Amount", Float) = 1.0 //混合图像时的混合系数
	}
	SubShader {
		CGINCLUDE //用CGINCLUDE复用代码 后面两个pass都能用
		
		#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;
		}
		
		//一个通道更新RGB通道,一个通道更新渲染纹理的A通道部分
		//之所以要两个通道 是因为更新RGB时我们需要设置它的A通道来混合图像,但又不希望A通道的值写入渲染纹理中
		fixed4 fragRGB (v2f i) : SV_Target {
			return fixed4(tex2D(_MainTex, i.uv).rgb, _BlurAmount);
		}
		
		half4 fragA (v2f i) : SV_Target {
			return tex2D(_MainTex, i.uv);
		}
		
		ENDCG
		
		ZTest Always Cull Off ZWrite Off
		
		Pass {
			Blend SrcAlpha OneMinusSrcAlpha //透明度混合 源alpha和 (1-源alpha)
			ColorMask RGB
			
			CGPROGRAM
			
			#pragma vertex vert  
			#pragma fragment fragRGB  
			
			ENDCG
		}
		
		Pass {   
			Blend One Zero
			ColorMask A
			   	
			CGPROGRAM  
			
			#pragma vertex vert  
			#pragma fragment fragA
			  
			ENDCG
		}
	}
 	FallBack Off
}

 

完成后返回编辑器,别忘了将shader拖拽至摄像机的MotionBlur.cs脚本中

实现效果如下

 

------------恢复内容结束------------

posted @   fjnloo  阅读(648)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示