效果
思路
使用高斯核,进行卷积计算。可简化为使用两个一维的高斯核,做为采样的权重点,对图像先后进行水平与竖直方向的滤波处理。
采样点为当前像素点的左右各扩展2个单位,上下各扩展2个单位。可自定义后乘以系数,缩放采样的范围。
采样的过程,一般是水平一次,竖直方向一次。可将这个过程进行多次,以达到加强模糊程度的效果。
实现
c#
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GaussianBlurTest : PostEffectBaseTest
{
public Shader gaussianBlurShader;
private Material gaussianBlurMatrial = null;
public Material material
{
get
{
gaussianBlurMatrial = CheckShaderAndCreateMaterial(gaussianBlurShader, gaussianBlurMatrial);
return gaussianBlurMatrial;
}
}
[Range(0, 4)] public int iterations = 3;
[Range(0.2f, 3f)] public float blurSpread = 0.6f;
[Range(1, 8)] public int downSample = 2;
//使用迭代系数,渲染出更模糊的效果
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material)
{
//降采样
int rtW = src.width / downSample;
int rtH = src.height / downSample;
RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);//得到与屏幕图像大小相同的缓存区
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);//获取屏幕图像缓存区
Graphics.Blit(buffer0,buffer1,material,0);//对纹理的竖直方向使用模糊渲染
RenderTexture.ReleaseTemporary(buffer0);//释放缓冲区
buffer0 = buffer1;
buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);//获取缓冲区
Graphics.Blit(buffer0,buffer1,material,1); //对纹理的水平方向使用模糊渲染
RenderTexture.ReleaseTemporary(buffer0);//释放屏幕缓冲区
buffer0 = buffer1;
}
Graphics.Blit(buffer0,dest);//多重渲染后,再计算总结果,渲染出最后的图形
}
else
{
Graphics.Blit(src,dest);
}
}
shader
Shader "Custom/GaussianBlurShader" {
Properties {
_MainTex("_MainTex",2D) = "white" {}
_BlurSize("_BlurSize",Float) = 1.0
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
float _BlurSize;
struct v2f {
float4 pos : SV_POSITION;
half2 uv[5] : TEXCOORD0;
};
v2f vertexBlurVertiacl( appdata_img v ){ //竖直方向需要处理的点
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
计算竖直方向上的需要处理的顶点的位置
o.uv[0] = uv;
o.uv[1] = uv + float2( 0.0, _MainTex_TexelSize.y * 1.0f) * _BlurSize;
o.uv[2] = uv - float2( 0.0, _MainTex_TexelSize.y * 1.0f) * _BlurSize;
o.uv[3] = uv + float2( 0.0, _MainTex_TexelSize.y * 2.0f) * _BlurSize;
o.uv[4] = uv - float2( 0.0, _MainTex_TexelSize.y * 2.0f) * _BlurSize;
return o;
};
v2f vertexBlurHorizontal( appdata_img v ){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
//计算水平方向上的需要处理的顶点的位置
o.uv[0] = uv;
o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0f,0.0) * _BlurSize;
o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0f,0.0) * _BlurSize;
o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0f,0.0) * _BlurSize;
o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0f,0.0) * _BlurSize;
return o;
};
fixed4 fragBlur(v2f i) : SV_Target{
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*2-1] ).rgb * weight[it];
sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
};
return fixed4(sum, 1.0);
};
ENDCG
ZTest Always Cull Off ZWrite Off
Pass{
NAME "GAUSSIAN_BLUR_VERTICAL"
CGPROGRAM
#pragma vertex vertexBlurVertiacl
#pragma fragment fragBlur
ENDCG
}
Pass{
NAME "GAUSSIAN_BLUR_HORIZONTAL"
CGPROGRAM
#pragma vertex vertexBlurHorizontal
#pragma fragment fragBlur
ENDCG
}
}
FallBack Off
}