高斯模糊

作者:追风剑情 发布于:2016-11-24 23:18 分类:Shader

      在本节,我们将会使用上述5x5的高斯核对原图像进行高斯模糊。我们将先后调用两个Pass,第一个Pass将会使用竖直方向的一维高斯核对图像进行滤波,第二个Pass再使用水平方向的一维高斯核对图像进行滤波,得到最终的目标图像。在实现中,我们还将利用图像缩放来进一步提高性能,并通过调整高斯滤波的应用次数来控制模糊程度(次数越多,图像越模糊)。

一、脚本代码


using UnityEngine;
using System.Collections;
/// <summary>
/// 高期模糊
/// </summary>
public class GaussianBlur : PostEffectsBase {

	//调整高斯模糊迭代次数
	[Range(0, 4)]
	public int iterations = 3;
	//模糊范围
	[Range(0.2f, 3.0f)]
	public float blurSpread = 0.6f;
	//缩放系数
	//downSample越大,需要处理的像素越少,同时也能进一步提高模糊程序,
	//但过大的downSample可能会使图像像素化。
	[Range(1, 8)]
	public int downSample = 2;

	public Shader gaussianBlurShader;
	private Material gaussianBlurMaterial = null;

	public Material material {
		get {
			gaussianBlurMaterial = CheckShaderAndCreateMaterial(gaussianBlurShader, gaussianBlurMaterial);
			return gaussianBlurMaterial;
		}
	}

	void OnRenderImage(RenderTexture src, RenderTexture dest) {
		if (material != null) {
			//利用缩放对图像进行降采样,从而减少需要处理的像素个数,提高性能。
			int rtW = src.width / downSample;
			int rtH = src.height / downSample;

			//分配一块缓冲区,这是因为,高斯模糊需要调用两个Pass,我们需要使用一块中间缓存来
			//存储第一个Pass执行完毕后得到的模糊结果。
			RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
			//将该临时渲染纹理的滤波模式设置为双线性。这样,在调用第一个Pass时,我们需要处理的
			//像素个数就是原来的几分之一。对图像进行降采样不仅可以减少需要处理的像素个数,提高性能,
			//而且适当的降采往往还可以得到更好的模糊效果。
			buffer0.filterMode = FilterMode.Bilinear;

			Graphics.Blit(src, buffer0);

			//高斯模糊迭代次数
			for(int i=0; i<iterations; i++){
				material.SetFloat("_BlurSize", 1.0f + i * blurSpread);

				RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

				//执行第一个Pass
				Graphics.Blit(buffer0, buffer1, material, 0);

				RenderTexture.ReleaseTemporary(buffer0);
				buffer0 = buffer1;
				buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

				//执行第二个Pass
				Graphics.Blit(buffer0, buffer1, material, 1);

				RenderTexture.ReleaseTemporary(buffer0);
				buffer0 = buffer1;
			}

			Graphics.Blit(buffer0, dest);
			//释放临时缓存
			RenderTexture.ReleaseTemporary(buffer0);

		} else {
			Graphics.Blit(src, dest);
		}
	}
}


二、Shader代码


Shader "Custom/Chapter12-GaussianBlur" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BlurSize ("Blur Size", float) = 1.0
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		//多个Pass需要共用的函数可以定义在CGINCLUDE里面。
		CGINCLUDE
		#include "UnityCG.cginc"
		
		sampler2D _MainTex;
		half4 _MainTex_TexelSize;
		float _BlurSize;
		
		struct v2f {
			float4 pos : SV_POSITION;
			half2 uv[5] : TEXCOORD0;
		};
		
		//竖直方向的顶点着色器代码
		v2f vertBlurVertical(appdata_img v) {
			v2f o;
			o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
			
			half2 uv = v.texcoord;
			
			//数组的第一个坐标存储了当前的采样纹理,而剩余的四个坐标则是高斯模糊中对邻域采样时使用的纹理坐标。
			//我们还和属性_BlurSize相乘来控制采样距离。
			o.uv[0] = uv;
			//_BlurSize越大,模糊程度越高,但采样数却不会受到影响。但过大的_BlurSize
			//值会造成虚影,这可能并不是我们希望的。通过把计算采样纹理坐标的代码从片元着色器中转移到
			//顶点着色器中,可以减少运算,提高性能。由于从顶点着色器到片元着色器的插值是线性的,
			//因此这样的转移并不会影响纹理坐标的计算结果。
			o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
			o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
			o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
			o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
			
			return o;
		}
		
		//水平方向的顶点着色器代码
		v2f vertBlurHorizontal(appdata_img v) {
			v2f o;
			o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
			
			half2 uv = v.texcoord;
			
			o.uv[0] = uv;
			o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.x * 1.0) * _BlurSize;
			o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.x * 1.0) * _BlurSize;
			o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.x * 2.0) * _BlurSize;
			o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.x * 2.0) * _BlurSize;
			
			return o;
		}
		
		//片元着色器
		fixed4 fragBlur(v2f i) : COLOR {
			float weight[3] = {0.4026, 0.2442, 0.0545};
			
			fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
			
			for(int it = 1; it < 3; it++) {
				sum += tex2D(_MainTex, i.uv[it]).rgb * weight[it];
				sum += tex2D(_MainTex, i.uv[2*it]).rgb * weight[it];
			}
			
			return fixed4(sum, 1.0);
		}
		ENDCG
		
		ZTest Always Cull Off ZWrite Off
		
		Pass {
			//为该Pass定义名字,以便在其他Shader中可直接通过它的名字来使用该Pass,
			//而不需要再重复编写代码。
			NAME "GAUSSIAN_BLUR_VERTICAL"
			
			CGPROGRAM
			#pragma vertex vertBlurVertical
			#pragma fragment fragBlur
			ENDCG
		}
		
		Pass {
			NAME "GAUSSIAN_BLUR_HORIZONTAL"
		
			CGPROGRAM
			#pragma vertex vertBlurHorizontal
			#pragma fragment fragBlur
			ENDCG
		}
	} 
	FallBack Off
}


效果截图(调整不同的参数值)

1.png

1_1.png


2.png

2_2.png


3.png

3_3.png


4.png

4_4.png

标签: Shader

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号