UGUI磨砂玻璃效果的Shader
平面设计里经常用到这种类似磨砂玻璃的排版效果,这在游戏里比较适合用来做独特的UI质感。
原理:
- 通过GrabPass获得背景截图
- 将截图内容做模糊算法处理
- 最后与自身颜色相加
这个做法的问题:
- GrabPass是个挺耗性能的方式,需要谨慎使用这个效果。
- 经测试,多个对象的状况下,对象之间不互相重叠的情况下会共用一个DrawCall,这方面还有优化空间。
- 多个对象时必须遮罩住同一个对象,否则会出现采样错误,这是由于在UI上用GrabPass会出现这种采样问题,我目前没找到解决方案。
当然了,我们为了追求独特的画面表现,可以通过一些设计上的取巧方式来规避以上问题。 这东西做法本身很简单,所以我就多搞了一些效果,可通过参数调整:
模糊程度
透明度
颜色叠加
去色
完整Shader代码:
Shader "WalkingFat/Sprite/TransparentBlur"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} // sprite自带的贴图
_Color ("Tint", Color) = (1,1,1,1) // 混合预设的颜色
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 // 用于sprite像素对齐
// blur采样贴图尺寸
// 因为shader取值都是百分比,所以我们要定出贴图的尺寸来进行计算
_TextureSize ("_Texture Size",Float) = 256
// 模糊半径
_BlurRadius ("_Blur Radius",Range(1,25) ) = 1
// 模糊效果的透明度,0是完全模糊,1是完全清晰
_Alpha ("_Alpha", Range(0.0,1.0)) = 0.5
// 去色效果的透明度,0是原来颜色,1是完全黑白
_GrayRate ("_Gray Rate", Range(0.0,1.0)) = 0
// required for UI.Mask
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
Fog { Mode Off }
ColorMask RGB
Blend One OneMinusSrcAlpha
// required for UI.Mask
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
ColorMask [_ColorMask]
GrabPass
{
"_BgColor"
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile DUMMY PIXELSNAP_ON
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
half2 texcoord : TEXCOORD0;
float4 grabPos : TEXCOORD1; // for _BgColor UV
};
fixed4 _Color;
v2f vert(appdata_t IN)
{
v2f OUT;
// set display shape
OUT.vertex = UnityObjectToClipPos(IN.vertex);
// set uv
OUT.texcoord = IN.texcoord;
// for _BgColor UV
OUT.grabPos = ComputeGrabScreenPos(OUT.vertex);
// output vertex color
OUT.color = IN.color * _Color;
// set pixelsnap
#ifdef PIXELSNAP_ON
OUT.vertex = UnityPixelSnap (OUT.vertex);
#endif
return OUT;
}
sampler2D _MainTex;
sampler2D _BgColor;
float _TextureSize;
float _BlurRadius;
// 高斯模糊算法-------------------
float GetGaussianDistribution( float x, float y, float rho ) {
float g = 1.0f / sqrt( 2.0f * 3.141592654f * rho * rho );
return g * exp( -(x * x + y * y) / (2 * rho * rho) );
}
float4 GetGaussBlurColor( sampler2D blurTex, float2 uv )
{
float space = 1.0/_TextureSize;
float rho = (float)_BlurRadius * space / 3.0;
float weightTotal = 0;
for( int x = -_BlurRadius ; x <= _BlurRadius ; x++ )
{
for( int y = -_BlurRadius ; y <= _BlurRadius ; y++ )
{
weightTotal += GetGaussianDistribution(x * space, y * space, rho );
}
}
float4 colorTmp = float4(0,0,0,0);
for( int x = -_BlurRadius ; x <= _BlurRadius ; x++ )
{
for( int y = -_BlurRadius ; y <= _BlurRadius ; y++ )
{
float weight = GetGaussianDistribution( x * space, y * space, rho )/weightTotal;
float4 color = tex2D(blurTex, uv + float2(x * space,y * space));
color = color * weight;
colorTmp += color;
}
}
return colorTmp;
}
//-----------------------------
float _Alpha;
float _GrayRate;
fixed4 frag(v2f IN) : SV_Target
{
// set texture color
fixed4 c = tex2D(_MainTex, IN.texcoord);
// get screen uv
float2 sceneUVs = (IN.grabPos.xy / IN.grabPos.w);
// 获得高斯模糊的结果
float4 blurCol = GetGaussBlurColor(_BgColor, sceneUVs);
// 模糊与清晰的比重
c = (c * _Alpha) + (blurCol * (1-_Alpha));
// 与顶点色和输入颜色混合
c *= IN.color;
// 去色效果的比重
float gray = dot(c.xyz, float3(0.299, 0.587, 0.114));
c.xyz = float3(gray, gray, gray) * _GrayRate + c.xyz * ((1-_GrayRate));
return c;
}
ENDCG
}
}
}