先來張效果圖,Demo在這
這裏實現的是有無效半徑的徑向緩衝,並且能夠調節徑向緩衝的中心點,無效半徑即徑向緩衝不起效果的區域,是中心區。
然後上一張徑向模糊的像素採樣示例圖,方便代碼的理解,如圖所示,綠色的是中心點的uv位置,橙色的是片元的原uv位置,藍色的線是中心點uv到本身片元的uv的向量, 紫色是採樣的uv位置,橙色的片元的顏色是紫色點採樣顏色值加起來之後除以採樣次數求得的
上代碼:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]//在編輯器模式下也運行
[RequireComponent(typeof(Camera))]//必須有Camera組件
public class RadiusBlur : MonoBehaviour
{
public Material RadiaBlurMaterial;
[Range(1, 100)]
public float Level = 10;
[Range(0, 1)]
public float BufferRadius = 0.5f;
[Range(0, 1)]
public float CenterX = 0.5f;
[Range(0, 1)]
public float CenterY = 0.5f;
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (RadiaBlurMaterial != null)
{
RadiaBlurMaterial.SetFloat("_Level", Level);
RadiaBlurMaterial.SetFloat("_CenterX", CenterX);
RadiaBlurMaterial.SetFloat("_CenterY", CenterY);
RadiaBlurMaterial.SetFloat("_BufferRadius", BufferRadius);
Graphics.Blit(src, dest, RadiaBlurMaterial);
}
else
{
Graphics.Blit(src, dest);
}
}
}
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Custom/RadiusBlur" {
Properties {
_MainTex("紋理",2D)="while"{}
_Level("強度",Range(1,100))=10
_CenterX("中心X座標",Range(0,1))=0.5
_CenterY("中心Y座標",Range(0,1))=0.5
_BufferRadius("緩衝半徑",Range(0, 1))=1
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float _Level;
float _CenterX;
float _CenterY;
float _BufferRadius;
struct v2f{
fixed4 vertex:SV_POSITION;
fixed2 uv:TEXCOORD;
};
v2f vert(appdata_base v)
{
v2f o;
o.vertex=UnityObjectToClipPos(v.vertex);
//這裏不使用TRANSFORM_TEX函數使得
//材質上的scaling和offset應用於uv了
o.uv=v.texcoord;
return o;
}
fixed4 frag(v2f i):COLOR
{
fixed4 finalColor;
fixed2 center=fixed2(_CenterX,_CenterY);
//求中心點uv座標到片元所在uv的向量
//屏幕成像左下角的點的uv座標是零向量
//右上角的點的uv座標是一向量
//向量的方向是減數指向被減數
fixed2 uvDir =i.uv - center;
fixed3 tempColor = fixed3(0,0,0);
//求得兩點之間的距離
//根據uv座標的特點 這個距離值不會大於根號2 或小於0
fixed dis= distance(i.uv, center);
fixed blurRatio = 0;
//這裏遇到了個奇怪的bug
//之前編輯的時候 這部分代碼剪切後可以運行
//粘貼後不能運行
//但是重新打一次就可以了
//估計是在中文模式下重輸入了一些空白符
if(_BufferRadius == 0){
blurRatio = 1;
}else if(_BufferRadius == 1){
blurRatio = 0;
}else {
//saturate(dis / _BufferRadius)一定是小數
//在center不變即dis不變的時候
//_BufferRadius越大
//saturate(dis / _BufferRadius)越小
//pow是爲了讓blurRatio呈現兩極分化
//即靠近中心區域的片元是極小的
//越遠離則模糊效率越大
blurRatio = pow(saturate(dis / _BufferRadius), 4);
}
for(fixed j=0; j<_Level; j++){
//在blurRatio等於1時 每次採樣的uv座標剛好等於uvDir + center
//即等於i.uv 即每次採樣都是片元本身對應像素點的顏色然後疊加
//最後再除以採樣次數 結果相當於沒有開啓徑向模糊
//在blurRatio等於1時
//相當於沒有開啓徑向模糊的清晰半徑
//因爲每個像素點都按照註釋3進行徑向模糊
//在blurRatio介於兩者之間時
//其實是各個採樣點更加靠近了片元的原始像素點
//並且blurRatio越小 每次的採樣點更加靠近原始像素點
//越靠近原始像素點 模糊的效果越小
//註釋3
//下面的討論在blurRatio等於1的條件下進行
//當j = 0的時候 採樣的uv座標剛好等於uvDir + center
//即等於i.uv
//j = 1的時候 uvDir * (1 - 0.01 * j * blurRatio)的值等於
//uvDir * 0.99
//採樣的uv座標等於在i.uv的基礎上
//往 center 方向推移了
//uvDir與center距離乘以0.01再乘以1的長度
//j = 2的時候 uvDir * (1 - 0.01 * j * blurRatio)的值等於
//uvDir * 0.98
//採樣的uv座標等於在i.uv的基礎上
//往 center 方向推移了
//uvDir與center距離乘以0.01再乘以2的長度
//如此類推 這裏將這些位置的顏色值疊加起來
//在結尾部分再進行求平均值
tempColor += tex2D(_MainTex,
uvDir * (1 - 0.01 * j * blurRatio) + center).rgb;
}
//將疊加的值進行求平均值
//變成當前片元代表的顏色
finalColor.rgb = tempColor / _Level;
return finalColor;
}
ENDCG
}
}
FallBack "Diffuse"
}