shader实现毛玻璃效果解析

2020年01月30日 11:01 1 点赞 0 评论 更新于 2025-11-21 21:30

毛玻璃效果在许多项目中都有广泛应用。下面将详细解析如何使用Shader实现毛玻璃效果,并介绍如何使用Dotween插件控制模糊程度,以及如何通过增加CanvasGroup组件来控制整个场景的透明度。

毛玻璃效果的Shader代码

Shader "Custom/WaterBlur" {
Properties {
_blurSizeXY("BlurSizeXY", Range(0,15)) = 2
}
SubShader {
Tags {
"IgnoreProjector"="True"
"Queue"="Transparent"
"RenderType"="Transparent"
"CanUseSpriteAtlas"="True"
"PreviewType"="Plane"
}
// 获取屏幕纹理,这一步是必须的
GrabPass { }
// 用上面生成的纹理渲染对象
Pass {
CGPROGRAM
#pragma debug
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
// 屏幕纹理定义
sampler2D _GrabTexture : register(s0);
float _blurSizeXY;

struct data {
float4 vertex : POSITION;
float3 normal : NORMAL;
};

struct v2f {
float4 position : POSITION;
float4 screenPos : TEXCOORD0;
};

v2f vert(data i) {
v2f o;
o.position = UnityObjectToClipPos(i.vertex);
o.screenPos = o.position;
return o;
}

// 核心逻辑是多次采样纹理并混合,采样对象为屏幕纹理,以此实现高斯模糊
half4 frag(v2f i) : COLOR {
// 模糊原理是错开uv进行采样以实现模糊效果
float2 screenPos = i.screenPos.xy / i.screenPos.w;
float depth = _blurSizeXY * 0.0005;
screenPos.x = (screenPos.x + 1) * 0.5;
screenPos.y = 1 - (screenPos.y + 1) * 0.5;

half4 sum = half4(0.0h, 0.0h, 0.0h, 0.0h);
sum += tex2D(_GrabTexture, float2(screenPos.x - 5.0 * depth, screenPos.y + 3.0 * depth)) * 0.025;
sum += tex2D(_GrabTexture, float2(screenPos.x + 5.0 * depth, screenPos.y - 3.0 * depth)) * 0.025;
sum += tex2D(_GrabTexture, float2(screenPos.x - 4.0 * depth, screenPos.y + 2.5 * depth)) * 0.05;
sum += tex2D(_GrabTexture, float2(screenPos.x + 4.0 * depth, screenPos.y - 2.5 * depth)) * 0.05;
sum += tex2D(_GrabTexture, float2(screenPos.x - 3.0 * depth, screenPos.y + 2.0 * depth)) * 0.09;
sum += tex2D(_GrabTexture, float2(screenPos.x + 3.0 * depth, screenPos.y - 2.0 * depth)) * 0.09;
sum += tex2D(_GrabTexture, float2(screenPos.x - 2.0 * depth, screenPos.y + 1.5 * depth)) * 0.12;
sum += tex2D(_GrabTexture, float2(screenPos.x + 2.0 * depth, screenPos.y - 1.5 * depth)) * 0.12;
sum += tex2D(_GrabTexture, float2(screenPos.x - 1.0 * depth, screenPos.y + 1.0 * depth)) * 0.15;
sum += tex2D(_GrabTexture, float2(screenPos.x + 1.0 * depth, screenPos.y - 1.0 * depth)) * 0.15;
sum += tex2D(_GrabTexture, screenPos - 3.0 * depth) * 0.025;
sum += tex2D(_GrabTexture, screenPos - 2.5 * depth) * 0.05;
sum += tex2D(_GrabTexture, screenPos - 2.0 * depth) * 0.09;
sum += tex2D(_GrabTexture, screenPos - 1.5 * depth) * 0.12;
sum += tex2D(_GrabTexture, screenPos - 1.0 * depth) * 0.15;
sum += tex2D(_GrabTexture, screenPos) * 0.16;
sum += tex2D(_GrabTexture, screenPos + 3.0 * depth) * 0.15;
sum += tex2D(_GrabTexture, screenPos + 2.5 * depth) * 0.12;
sum += tex2D(_GrabTexture, screenPos + 2.0 * depth) * 0.09;
sum += tex2D(_GrabTexture, screenPos + 1.5 * depth) * 0.05;
sum += tex2D(_GrabTexture, screenPos + 1.0 * depth) * 0.025;
return sum / 2;
}
ENDCG
}
}
Fallback Off
}

代码解读

  1. 属性定义:在Properties块中定义了一个名为_blurSizeXY的属性,用于控制模糊的大小,其取值范围在0到15之间,默认值为2。
  2. SubShader标签:通过Tags设置了一些渲染相关的标签,如忽略投影器、渲染队列、渲染类型等。
  3. GrabPass:使用GrabPass获取屏幕纹理,这是实现毛玻璃效果的基础。
  4. 顶点着色器vert:将顶点从模型空间转换到裁剪空间,并将裁剪空间下的位置赋值给screenPos
  5. 片段着色器frag:核心逻辑是多次采样屏幕纹理并混合。通过错开uv坐标进行采样,实现高斯模糊效果。最后将所有采样结果相加并除以2,得到最终的颜色值。

使用Dotween插件控制模糊程度

可以使用Dotween插件来控制模糊的程度,以下是示例代码:

/// 主页按钮点击回调
private void HimebtnHundler()
{
_leftpic.transform.DOLocalMoveX(-746f, 1);
_rightpic.transform.DOLocalMoveX(787f, 1);
_material.DOFloat(0, "_blurSizeXY", 1);
_word.GetComponent<CanvasGroup>().DOFade(0, 1f);
StartCoroutine(CloseErrorView());
}

代码解读

  • _leftpic.transform.DOLocalMoveX(-746f, 1)_rightpic.transform.DOLocalMoveX(787f, 1):分别将_leftpic_rightpic在本地坐标系下沿X轴移动到指定位置,动画时长为1秒。
  • _material.DOFloat(0, "_blurSizeXY", 1):使用Dotween的DOFloat方法将材质球上的_blurSizeXY属性在1秒内从当前值渐变到0,从而控制模糊程度。
  • _word.GetComponent<CanvasGroup>().DOFade(0, 1f):获取_word对象上的CanvasGroup组件,并使用DOFade方法在1秒内将其透明度渐变到0。
  • StartCoroutine(CloseErrorView()):启动一个协程CloseErrorView

增加CanvasGroup组件控制场景透明度

这段代码主要是通过增加CanvasGroup组件,实现对整个场景的alpha控制。主要使用DOFade来控制CanvasGroup的透明度,使用DOFloat对材质球上的Shader属性进行控制。

通过以上步骤,我们可以实现一个具有毛玻璃效果的场景,并通过Dotween插件灵活控制模糊程度和场景透明度。