好久不写了,简单做个记录。
需求是在UI的数字上做个扫光效果,文字使用了TextMeshPro,需要自定义shader。
复制了“FairyGUI/TextMeshPro/Distance Field”,然后AI生成代码,就是方便。
// === 扫光参数(无贴图版) ===
_SweepColor ("Sweep Color", Color) = (1,1,1,1)
_SweepAngle ("Sweep Angle (Rad)", Range(0, 6.2831853)) = 0.0
_SweepPos ("Sweep Position", Range(0,1)) = 0.0
_SweepWidth ("Sweep Width", Range(0,1)) = 0.2
_SweepIntensity ("Sweep Intensity", Range(0,2)) = 1.0
// 计算屏幕空间 uv:NDC(-1~1) -> 0~1
float2 ndc = vPosition.xy / vPosition.w;
output.sweepUV = ndc * 0.5 + 0.5;
// === 扫光叠加:颜色 + 角度 + 位置(0~1) + 宽度,使用屏幕空间 sweepUV ===
{
float2 uv = input.sweepUV;
float2 dir = float2(cos(_SweepAngle), sin(_SweepAngle));
float coord = dot(uv - 0.5, dir) + 0.5;
float dist = abs(coord - _SweepPos);
float w = max(_SweepWidth, 1e-4);
float intensity = saturate(1.0 - dist / w);
intensity = smoothstep(0.0, 1.0, intensity);
intensity *= _SweepIntensity;
faceColor.rgb = lerp(faceColor.rgb, _SweepColor.rgb, intensity * _SweepColor.a);
}
这里有个小坑,就是文本的atlas很大,直接取uv是不能用的。
这里是用屏幕空间计算的,但这样当文本移动时,扫光的位置不准确,好在我的文本是固定位置,能用就得呗。
然后就是替换材质,tween一个参数让它扫过去就行了:
var mat = new Material(this.numberTitle.displayObject.material)
{
shader = Shader.Find("FairyGUI/TextMeshPro/Distance Field Light Sweep")
};
this.numberTitle.displayObject.material = mat;
this.UI_Enter.SetHook("LightSweep", LightSweep);
private void LightSweep()
{
var m = this.numberTitle.displayObject.material;
m.SetColor("_SweepColor", Color.white);
m.SetFloat("_SweepAngle", 5.5f);
m.SetFloat("_SweepWidth", 0.02f);
GTween.ToDouble(0.2, 0.6, 2f).SetDelay(0.7f).OnUpdate((tweener =>
{
m?.SetFloat("_SweepPos", (float)tweener.value.x);
}));
}