unity 相机后处理之径向模糊效果

作者:雨辰 发布于:2017-7-17 20:44 Monday 分类:Unity3D

径向模糊效果的原理

    径向模糊效果是后处理啦,后处理的原理就不多说了,就是通过渲染好的屏幕图像进行全屏操作。首先观察上面那张图,从中心部分,向外扩展,有一种画面向外拉伸的感觉。那么,我们在fragment阶段,就可以按照这个思路来处理,上一篇文章在写描边效果的后处理时,为了让轮廓向外扩展,我们使用了模糊效果,把像素和周围的像素进行加权平均;而对于径向模糊也是一样,我们针对每个像素点,首先要得到一个相对于中心(不一定就是屏幕正中心,可以自己指定)的方向,从中心指向该像素点的方向就是径向模糊的方向,然后取当前像素点,以及沿着径向模糊方向再取几个点作为采样点,采样点越靠近中心越密集,越远离中心越稀疏,最后,该像素点的输出就是这些采样点的均值。这样,在靠近中心点的位置,采样距离小,几乎为0,也就不会模糊;而越靠近边界的位置,采样的距离越大,图像也就会越模糊。
 
RadialBlurEffect
/********************************************************************
 FileName: RadialBlurEffect.cs
 Description: 径向模糊效果
*********************************************************************/
using UnityEngine;

public class RadialBlurEffect : PostEffectBase {

    //模糊程度,不能过高
    [Range(0,0.05f)]
    public float blurFactor = 1.0f;
    //模糊中心(0-1)屏幕空间,默认为中心点
    public Vector2 blurCenter = new Vector2(0.5f, 0.5f);

	void OnRenderImage (RenderTexture source, RenderTexture destination)
    {
        if (_Material)
        {
            _Material.SetFloat("_BlurFactor", blurFactor);
            _Material.SetVector("_BlurCenter", blurCenter);
            Graphics.Blit(source, destination, _Material);
        }
        else
        {
            Graphics.Blit(source, destination);
        }   
		
	} 
}
shader :
//径向模糊shader
Shader "ApcShader/PostEffect/RadialBlurShader1" 
{
	Properties 
	{
		_MainTex ("Base (RGB)", 2D) = "white" {}
	}

	CGINCLUDE
	uniform sampler2D _MainTex;
	uniform float _BlurFactor;	//模糊强度(0-0.05)
	uniform float4 _BlurCenter; //模糊中心点xy值(0-1)屏幕空间
	#include "UnityCG.cginc"
	#define SAMPLE_COUNT 6		//迭代次数

	fixed4 frag(v2f_img i) : SV_Target
	{
		//模糊方向为模糊中点指向边缘(当前像素点),而越边缘该值越大,越模糊
		float2 dir = i.uv - _BlurCenter.xy;
		float4 outColor = 0;
		//采样SAMPLE_COUNT次
		for (int j = 0; j < SAMPLE_COUNT; ++j)
		{
			//计算采样uv值:正常uv值+从中间向边缘逐渐增加的采样距离
			float2 uv = i.uv + _BlurFactor * dir * j;
			outColor += tex2D(_MainTex, uv);
		}
		//取平均值
		outColor /= SAMPLE_COUNT;
		return outColor;
	}
	ENDCG

	SubShader
	{
		Pass
		{
			ZTest Always
			Cull Off
			ZWrite Off
			Fog{ Mode off }

			//调用CG函数	
			CGPROGRAM
			//使效率更高的编译宏
			#pragma fragmentoption ARB_precision_hint_fastest 
			//vert_img是在UnityCG.cginc中定义好的,当后处理vert阶段计算常规,可以直接使用自带的vert_img
			#pragma vertex vert_img
			#pragma fragment frag 
			ENDCG
		}
	}
	Fallback off
}

径向模糊的优化

    径向模糊和高斯模糊,均值模糊等模糊效果一样,都是采样次数越高,模糊效果越好,但是采样次数高了,性能就下去了,尤其是在移动设备上,GPU不强,但是分辨率极高,后处理这种全屏纹理采样及其耗费性能。所以,优化很重要。我们在高斯模糊以及Bloom效果中使用了降分辨率的操作,对于径向模糊,一样实用。优化的思路如下:首先,我们将图像渲染到一张降低了分辨率的RT上,然后使用这个降低了分辨率的RT进行上面的模糊处理;最后再将这个RT与原始图像进行插值操作。这样会多一个Pass(一个Draw Call),但是由于降低了分辨率,仍然对性能会有较好的提升。下面附上更改后的径向模糊代码。
 
RadialBlurEffect2
/********************************************************************
 FileName: RadialBlurEffect.cs
 Description: 径向模糊效果
*********************************************************************/
using UnityEngine;

public class RadialBlurEffect2 : PostEffectBase {

    //模糊程度,不能过高
    [Range(0,0.1f)]
    public float blurFactor = 1.0f;
    //清晰图像与原图插值
    [Range(0.0f, 2.0f)]
    public float lerpFactor = 0.5f;
    //降低分辨率
    public int downSampleFactor = 2;
    //模糊中心(0-1)屏幕空间,默认为中心点
    public Vector2 blurCenter = new Vector2(0.5f, 0.5f);

	void OnRenderImage (RenderTexture source, RenderTexture destination)
    {
        if (_Material)
        {
            //申请两块降低了分辨率的RT
            RenderTexture rt1 = RenderTexture.GetTemporary(source.width >> downSampleFactor, source.height >> downSampleFactor, 0, source.format);
            RenderTexture rt2 = RenderTexture.GetTemporary(source.width >> downSampleFactor, source.height >> downSampleFactor, 0, source.format);
            Graphics.Blit(source, rt1);

            //使用降低分辨率的rt进行模糊:pass0
            _Material.SetFloat("_BlurFactor", blurFactor);
            _Material.SetVector("_BlurCenter", blurCenter);
            Graphics.Blit(rt1, rt2, _Material, 0);

            //使用rt2和原始图像lerp:pass1
            _Material.SetTexture("_BlurTex", rt2);
            _Material.SetFloat("_LerpFactor", lerpFactor);
            Graphics.Blit(source, destination, _Material, 1);

            //释放RT
            RenderTexture.ReleaseTemporary(rt1);
            RenderTexture.ReleaseTemporary(rt2);
        }
        else
        {
            Graphics.Blit(source, destination);
        }   
		
	} 
}
RadialBlurEffect2.shader:
//径向模糊shader 
Shader "ApcShader/PostEffect/RadialBlurShader2" 
{
	Properties 
	{
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BlurTex("Blur Tex", 2D) = "white"{}
	}

	CGINCLUDE
	uniform sampler2D _MainTex;
	uniform sampler2D _BlurTex;
	uniform float _BlurFactor;	//模糊强度(0-0.05)
	uniform float _LerpFactor;  //插值的强度(0-1)
	uniform float4 _BlurCenter; //模糊中心点xy值(0-1)屏幕空间
	float4 _MainTex_TexelSize;
	#include "UnityCG.cginc"
	#define SAMPLE_COUNT 6		//迭代次数

	fixed4 frag_blur(v2f_img i) : SV_Target
	{
		//模糊方向为模糊中点指向边缘(当前像素点),而越边缘该值越大,越模糊
		float2 dir = i.uv - _BlurCenter.xy;
		float4 outColor = 0;
		//采样SAMPLE_COUNT次
		for (int j = 0; j < SAMPLE_COUNT; ++j)
		{
			//计算采样uv值:正常uv值+从中间向边缘逐渐增加的采样距离
			float2 uv = i.uv + _BlurFactor * dir * j;
			outColor += tex2D(_MainTex, uv);
		}
		//取平均值
		outColor /= SAMPLE_COUNT;
		return outColor;
	}

	//定义最后插值使用的结构体
	struct v2f_lerp
	{
		float4 pos : SV_POSITION;
		float2 uv1 : TEXCOORD0; //uv1
		float2 uv2 : TEXCOORD1; //uv2
	};
	
	v2f_lerp vert_lerp(appdata_img v)
	{
		v2f_lerp o;
		o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
		o.uv1 = v.texcoord.xy;
		o.uv2 = v.texcoord.xy;
		//dx中纹理从左上角为初始坐标,需要反向(在写rt的时候需要注意)
		#if UNITY_UV_STARTS_AT_TOP
		if (_MainTex_TexelSize.y < 0)
			o.uv2.y = 1 - o.uv2.y;
		#endif
		return o;
	}

	fixed4 frag_lerp(v2f_lerp i) : SV_Target
	{
		float2 dir = i.uv1 - _BlurCenter.xy;
		float dis = length(dir);
		fixed4 oriTex = tex2D(_MainTex, i.uv1);
		fixed4 blurTex = tex2D(_BlurTex, i.uv2);
		//按照距离乘以插值系数在原图和模糊图之间差值
		return lerp(oriTex, blurTex, _LerpFactor * dis);
	}
	ENDCG

	SubShader
	{
		//Pass 0 模糊操作
		Pass
		{
			ZTest Always
			Cull Off
			ZWrite Off
			Fog{ Mode off }

			//调用CG函数	
			CGPROGRAM
			//使效率更高的编译宏
			#pragma fragmentoption ARB_precision_hint_fastest 
			//vert_img是在UnityCG.cginc中定义好的,当后处理vert阶段计算常规,可以直接使用自带的vert_img
			#pragma vertex vert_img
			#pragma fragment frag_blur 
			ENDCG
		}

		//Pass 1与原图插值操作
		Pass
		{
			ZTest Always
			Cull Off
			ZWrite Off
			Fog{ Mode off }

			//调用CG函数	
			CGPROGRAM
			//使效率更高的编译宏
			#pragma fragmentoption ARB_precision_hint_fastest 
			//vert_img是在UnityCG.cginc中定义好的,当后处理vert阶段计算常规,可以直接使用自带的vert_img
			#pragma vertex vert_lerp
			#pragma fragment frag_lerp 
			ENDCG
		}
	}
	Fallback off
}

标签: Unity3D

发表评论:

雨辰 joyimp|@2011-2017 京ICP备16030765号