Unity 径向模糊 简易解决方案
本文欢迎转载,转载请标明出处!
什么是径向模糊
径向模糊,这个名字咋一看很陌生,不知道是什么东西,但是看了下面这对对比图也许你就知道是什么了。
(PID:77789607)
可见图2中出现了一个由定点扩散型的模糊,这个就是径向模糊。
径向模糊的运用非常广泛。由于这种扩散型的模糊可以带来很强的速度感和压迫感,常用于竞速游戏或者高速移动的对象上,也有用于Boss的登场画面特效等等。
一、原理分析和算法简介
既然是一个定点扩散型的模糊,那就需要有一个扩散的起点。在我们的Shader渲染过程中,这个起点可以用UV坐标属性来确定。
其次就是这个扩散的效果,先贴上核心的算法:
(来源:https://qianmo.blog.youkuaiyun.com/article/details/105350519)
- 首先是我们需要计算出模糊的方向Offset,后面要用这个Offset值偏移UV持续采样。这个通过扩散定点的坐标减去当前像素的UV坐标就可以得出。得到方向后,通过一个额外的参数来控制这个方向带来的扩散效果。
- 既然是模糊,那就避免不了多次采样。径向模糊需要多次采样来保证模糊效果,并在每次采样后,采样坐标继续与扩散方向进行累加,并累加采样后的颜色。最终,输出这几次采样颜色的平均值。整体来看,径向模糊的主要逻辑相对于高斯模糊要简单不少,而且可以在一个Pass中就处理完毕。
- 核心代码中的unroll关键字的意义在于告诉着色器可以安全展开多少次遍历。这样一来,在Shader编译成OpenGL或者其他着色器语言的时候会把这个循环展开,最终效果就等同于我们把10,11行所在的代码重复写30次。
二、具体实现
新建一个项目,保存场景,创建一个Canvas,并修改Canvas的RenderMode为ScreenSpace-Camer。笔者这里图个方便,就挂主摄像机上去了。在实际项目中,一定要严格区分场景和UI摄像机。
创建一个RawImage,放上一张图片。
新建一个Shader,命名为RadiusBlur,Shader的代码如下:
Shader "SaberShad/RadiusBlur"
{
Properties
{
[HideInInspector]_MainTex ("Texture", 2D) = "white" {
}
// 径向模糊数据 xy分量代表径向中心点 z分量代表偏移
_RadiusData("Radius Data", Vector) = (0.5, 0.5, 0.0, 1.0)
_RadiusIteration("Radius Iteration", Range(1, 30)) = 1.0
}
SubShader
{
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
half3 _RadiusData;
half _RadiusIteration;
struct a2f_rb
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f_rb
{
float4 vertex : SV_POSITION;
float2 texcoord: TEXCOORD0;
};
v2f_rb RadiusBlurVertex(a2f_rb v)
{
v2f_rb o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
half4 RadiusBlurFragment(v2f_rb i): SV_Target
{
half2 radiusRange = (_RadiusData.xy - i.texcoord.xy) * _RadiusData.z;
half4 color = 0.0;
[unroll(30)]
for(int j = 0; j < _RadiusIteration; j++)
{
color += tex2D(_MainTex, i.texcoord);
i.texcoord += radiusRange;
}
return color / _RadiusIteration;
}
ENDCG
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex RadiusBlurVertex
#pragma fragment RadiusBlurFragment
ENDCG
}
}
}
参数方面,把中心点坐标和偏移强度参数整合到了一个Vector4参数中来传递。
新建一个材质球RadiusMat,把材质球的shader改成我们刚刚处理的Shader。
接着是写一个摄像机可以用的后处理类RadiusBlur,这个类的作用在于控制摄像机的渲染输出前处理我们的模糊效果,代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class RadiusBlur : MonoBehaviour
{
[SerializeField]
public Material radius_material;
[SerializeField]
public Vector3 radius_data{
get{
if(radius_material != null)
{
return radius_material.GetVector("_RadiusData");
}
return new Vector3(0.5f, 0.5f, 0.01f);
}
set{
if(radius_material != null)
{
radius_material.SetVector("_RadiusData", value);
}
}
}
[SerializeField]
public int iteration{