資源鏈接
資源鏈接:https://github.com/andydbc/unity-frosted-glass,注意:下載時,最好將防護軟件都關掉,不然有可能出現:壓縮包損壞無法解壓的問題。
這是我下載再來的資源地址,大家可以用:https://download.csdn.net/download/f_957995490/12504874。
參考
效果圖
思路
- 利用
CommandBuffer
可以在攝像機渲染的幾個節點中插入,並執行一些操作。
這裏的實現就是把CommandBuffer
指定在渲染Transparent隊列前, 也就是此時已經渲染了天空盒,不透明物體 (opaque) 等渲染*render queue* < Transparent (3000)
的物體,把此時攝像機的顏色緩衝區 複製到申請的一塊render texture中。
然後把這個原始的rt的設置到shader全局紋理屬性中(CommandBuffer.SetGlobalTexture
)。 - 把render texture用模糊的算法處理 , 比如:高斯模糊。此時這塊rt是全屏的模糊,但我們只需要局部區域要這個模糊效果。
然後把這個模糊的rt的設置到shader全局紋理屬性中 (CommandBuffer.SetGlobalTexture
)。 - 在想要模糊的地方丟一個box或平面模型,然後渲染時,指定渲染隊裏爲Transparent+(只要在
CommandBuffer
之後就行)。 獲取模型頂點在屏幕空間的位置值 (也就是 [0, 1] 區間內),用這個值去採樣原始的rt和模糊後的rt,在用一個遮罩圖去插值指定哪些地方是需要鏤空的。
延伸:
除了CommandBuffer
可以在指定的渲染節點內獲取攝像機的顏色緩衝區, 也可以用在渲染某個物體前用GrabPass
的形式抓取攝像機當前的顏色緩衝區到一個rt上, 然後再用屏幕空間的位置去採樣這個rt。可以參考:Unity Shader-熱空氣扭曲效果。
從顯存抓數據到內存都是比較耗性能的操作。
源碼分析
CommandBufferBlur
.cs,這個腳本一定要掛在含有camera
組件的go上,因爲要在攝像機渲染場景前回調OnPreRender
函數。
using UnityEngine;
using UnityEngine.Rendering;
[ExecuteInEditMode]
[ImageEffectAllowedInSceneView]
[RequireComponent(typeof(Camera))]
public class CommandBufferBlur : MonoBehaviour
{
Shader _Shader;
Material _Material = null;
Camera _Camera = null;
CommandBuffer _CommandBuffer = null;
Vector2 _ScreenResolution = Vector2.zero;
RenderTextureFormat _TextureFormat = RenderTextureFormat.ARGB32;
public void Cleanup()
{
if (!Initialized)
return;
_Camera.RemoveCommandBuffer(CameraEvent.BeforeForwardAlpha, _CommandBuffer);
_CommandBuffer = null;
Object.DestroyImmediate(_Material);
}
public void OnEnable()
{
Cleanup();
Initialize();
}
public void OnDisable()
{
Cleanup();
}
public bool Initialized
{
get { return _CommandBuffer != null; }
}
void Initialize()
{
if (Initialized)
return;
if (!_Shader)
{
_Shader = Shader.Find("Hidden/SeparableGlassBlur");//模糊的shader
if (!_Shader)
throw new MissingReferenceException("Unable to find required shader \"Hidden/SeparableGlassBlur\"");
}
if (!_Material)
{
_Material = new Material(_Shader);
_Material.hideFlags = HideFlags.HideAndDontSave;
}
_Camera = GetComponent<Camera>();
if (_Camera.allowHDR && SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.DefaultHDR))
_TextureFormat = RenderTextureFormat.DefaultHDR;
_CommandBuffer = new CommandBuffer();
_CommandBuffer.name = "Blur screen";
int numIterations = 4;
Vector2[] sizes = {
new Vector2(Screen.width, Screen.height),
new Vector2(Screen.width / 2, Screen.height / 2),//降低分辨率,可以提高性能,和提高模糊效果,我的習慣是用>>1位移
new Vector2(Screen.width / 4, Screen.height / 4),
new Vector2(Screen.width / 8, Screen.height / 8),
};
for (int i = 0; i < numIterations; ++i)
{
int screenCopyID = Shader.PropertyToID("_ScreenCopyTexture");
_CommandBuffer.GetTemporaryRT(screenCopyID, -1, -1, 0, FilterMode.Bilinear, _TextureFormat);//申請攝像機分辨率大小的rt
_CommandBuffer.Blit(BuiltinRenderTextureType.CurrentActive, screenCopyID);//將攝像機當前的rt複製給screenCopyID
int blurredID = Shader.PropertyToID("_Grab" + i + "_Temp1");
int blurredID2 = Shader.PropertyToID("_Grab" + i + "_Temp2");
_CommandBuffer.GetTemporaryRT(blurredID, (int)sizes[i].x, (int)sizes[i].y, 0, FilterMode.Bilinear, _TextureFormat);//申請臨時的rt1 rt2,用來做模糊效果
_CommandBuffer.GetTemporaryRT(blurredID2, (int)sizes[i].x, (int)sizes[i].y, 0, FilterMode.Bilinear, _TextureFormat);
_CommandBuffer.Blit(screenCopyID, blurredID);
_CommandBuffer.ReleaseTemporaryRT(screenCopyID);//釋放screenCopyID的rt
_CommandBuffer.SetGlobalVector("offsets", new Vector4(2.0f / sizes[i].x, 0, 0, 0));//橫向模糊
_CommandBuffer.Blit(blurredID, blurredID2, _Material);
_CommandBuffer.SetGlobalVector("offsets", new Vector4(0, 2.0f / sizes[i].y, 0, 0));//縱向模糊
_CommandBuffer.Blit(blurredID2, blurredID, _Material);
_CommandBuffer.SetGlobalTexture("_GrabBlurTexture_" + i, blurredID);//模糊效果完成後,將其設置到全局紋理_GrabBlurTexture_1234,其他的FrostedGlass.shader中可以直接訪問
}
_Camera.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _CommandBuffer);//在渲染Transparent隊列之前執行,確保渲染FrostedGlass(Transparent)的時候可以使用_GrabBlurTexture_1234
_ScreenResolution = new Vector2(Screen.width, Screen.height);
}
void OnPreRender()
{
if (_ScreenResolution != new Vector2(Screen.width, Screen.height))
Cleanup();
Initialize();
}
}
- FrostedGlass.shader
Shader "Effects/FrostedGlass"
{
Properties
{
_FrostTex ("Fross Texture", 2D) = "white" {}
_FrostIntensity ("Frost Intensity", Range(0.0, 1.0)) = 0.5
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue" = "Transparent" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uvfrost : TEXCOORD0;
float4 uvgrab : TEXCOORD1;
float4 vertex : SV_POSITION;
};
sampler2D _FrostTex;
float4 _FrostTex_ST;
float _FrostIntensity;
sampler2D _GrabBlurTexture_0;
sampler2D _GrabBlurTexture_1;
sampler2D _GrabBlurTexture_2;
sampler2D _GrabBlurTexture_3;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uvfrost = TRANSFORM_TEX(v.uv, _FrostTex);
o.uvgrab = ComputeGrabScreenPos(o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float surfSmooth = 1-tex2D(_FrostTex, i.uvfrost) * _FrostIntensity;
surfSmooth = clamp(0, 1, surfSmooth);
half4 refraction;
half4 ref00 = tex2Dproj(_GrabBlurTexture_0, i.uvgrab);
half4 ref01 = tex2Dproj(_GrabBlurTexture_1, i.uvgrab);
half4 ref02 = tex2Dproj(_GrabBlurTexture_2, i.uvgrab);
half4 ref03 = tex2Dproj(_GrabBlurTexture_3, i.uvgrab);
float step00 = smoothstep(0.75, 1.00, surfSmooth);
float step01 = smoothstep(0.5, 0.75, surfSmooth);
float step02 = smoothstep(0.05, 0.5, surfSmooth);
float step03 = smoothstep(0.00, 0.05, surfSmooth);
refraction = lerp(ref03, lerp( lerp( lerp(ref03, ref02, step02), ref01, step01), ref00, step00), step03);
return refraction;
}
ENDCG
}
}
}
參考鏈接
參考鏈接:https://blog.csdn.net/yangxuan0261/article/details/90348851
測試
條件
- 在場景攝像機上掛一個叫
CommandBufferBlur
的Component
。如圖:
- 然後,創建一個Cube作爲測試,然後將shader:FrostedGlass的材質球賦到Cube上,即可。
效果
之後便可以看到響應的效果,如圖: