ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Unity Shader Logo流光效果实现:UV偏移与柔边技巧

Unity Shader Logo流光效果实现:UV偏移与柔边技巧 简介一份面向Unity开发者与初学者的Shader实战资源围绕“Logo流光效果”展示纯代码和流光图片叠加两种实现思路帮助读者理解ShaderLab语法、纹理UV动画及Shader Graph可视化编辑流程。压缩包共43个文件以asset、shader、mat、unity等Unity工程文件为主另含png贴图、json配置及项目设置文件整体仅92KB轻量易用适合直接在编辑器中打开学习。目前已有1420人浏览学习配套工程中包含了场景、材质和完整Shader源码可对照查看时间变量控制、颜色混合及UV偏移等关键实现便于快速复用并制作个性化流光效果。对于想入门Unity Shader或希望提升界面动效表现力的开发者这份资源能提供直观的范例与可运行的参考工程。1. 为什么说Logo流光效果是Shader入门最容易出成就感的一个案例做Unity开发的人多半都有过这种经历策划或UI同学指着一个静态Logo说“让它闪一下”你第一反应是叠几张序列帧图做UV动画或者直接调CanvasGroup的Alpha。结果做出来的效果要么发灰、要么边缘发硬怎么看都像P了条光带上去。真正能解决问题的路径是越过UI层直接写在Shader里——用像素着色器对采样坐标做一次随时间变化的偏移让光带在Logo的Alpha边界内反复扫过。这套做法的核心只有两个一个可循环的UV偏移量以及一个能约束光带形状的软边函数。本文就用“UV偏移法”作为主路线把Logo流光效果从能看做到耐看。适合三类人刚理清Shader基本结构的Unity开发者被UI特效反复折腾的客户端工程师以及想给数字孪生或WebGL项目加点“活气”却怕性能崩掉的技术美术。老手也能在参数边界、SRP兼容和验证手法上找到一点可带走的东西。2. 流光效果的本质是用UV偏移做时间动画而不是改贴图Alpha2.1 为什么“便宜”的UV偏移法能撑起Logo流光Logo流光效果的目标很明确一条有方向、有软边的亮带沿着Logo表面周期性滑过。最容易想到的实现是Shader里直接采样一张遮罩图用ScrollOffset挪动采样坐标。这个思路的复杂度低到几乎不需要额外贴图——因为Logo本身的Alpha通道就承担了遮罩的角色。UV偏移的精髓在于它改变的是“采样位置”而不是“像素颜色”。对同一张Logo贴图来说所有颜色信息都还在只是采样点沿着某个方向移动。配合上时间变量就能造成“光带在走”的视觉错觉。在经典的Surface Shader或Unlit Shader里核心代码只有几行// 输入结构体里定义了uvfloat2 uv : TEXCOORD0; float speed _Speed * _Time.y; float2 uv i.uv; uv.x frac(speed); // 只偏移X让采样位置周期性左移或右移 fixed4 col tex2D(_MainTex, uv);这段代码的意思是_Time.y是Unity内置的累计运行时间乘以速度系数后用它做X轴偏移值。frac()函数把偏移值截断在0到1之间保证光带循环往复不会越跑越远。此时Logo上的像素颜色会整体移动等于整张图在平移而不是只有一条光带在动——所以还差一个关键步骤用原始UV未偏移的UV做约束只让特定区域的颜色与偏移后的采样结果发生混合才能形成“光带”。这个“基于原始位置约束”的步骤看起来不起眼却是区分拙劣贴图平移和高级流光效果的分水岭。如果没有它光带扫过的地方颜色会被整体抹掉Logo主轴上的纹理细节也会丢失。2.2 从“能让它动”到“让它只在该亮的地方亮”流光效果如果不做区域约束整张图都会闪。更准确的做法是构造一条“亮度函数”它的值域只在Logo内部生效越靠近Logo边缘亮度越低。最常见的约束方式是拿采样UV和中心点做距离计算// 光带中心位置随时间变化这里把它限制在0.15~0.85之间循环 float centerX lerp(0.15, 0.85, (frac(_Time.y * _Speed) 1) * 0.5); float strip 1 - saturate(abs(i.uv.x - centerX) / _StripWidth);这行代码把UV的X坐标和光带中心位置做了绝对值差运算然后用_StripWidth控制光带宽度。saturate把负值截断为0意味着离中心越远亮度越低。如果再套一层smoothstep光带的边缘就能从硬边变成柔边这也是后面第三节要展开的参数化处理。要注意的是这套计算是基于原始UVi.uv的而不是偏移后的UV。为什么要用两个不同的UV分别服务于采样和遮罩核心原因是如果采样坐标已经偏移光带位置会随偏移方向漂移那你看到的光带就不是Logo上的光带而是一条“追不上”的虚影。所以正常做法是用一个offsetUV去采样贴图让颜色位置发生“被推着走”的变化用一个originUV去计算光带的位置和亮度保证光带在原点附近反复扫。这样两步交错在一起Logo上的光带既有了移动感又不会变成整张图在滑动。这个双UV思路往深了说可以演变成UV扰动、法线偏移等高级玩法但用在Logo流光是完全够用的。2.3 为什么说“改贴图Alpha”不是正经Shader做法有些教程会让美术把Logo的Alpha做成黑白渐变再用它当遮罩。这个做法在项目里能跑但代价是要额外维护一张贴图而且Alpha的渐变一旦不干净光带边缘就会出现严重的锯齿或灰边。更麻烦的是同一套Shader要复用到其他Logo你不得不重新做一张配套黑图。正确的做法是让Shader自己从主贴图的Alpha通道中提取遮罩。Logo的Alpha边界天然是封闭的直接用采样结果当mask即可// 采样主贴图得到Alpha作为区域的软边遮罩 fixed4 col tex2D(_MainTex, uv); half alphaMask col.a; // 直接取Alpha通道做边界 col.rgb _LightColor.rgb * strip * alphaMask;这样光带只会出现在Alpha大于0的地方而且Alpha本身的渐变会自然柔和边缘。如果Logo外圈带锯齿Shader层面可以做一步透明度混合但大多数UI Logo的贴图Alpha都是干净的直接用即可。这个取舍决定了你的Shader是“一套通吃”还是“每个Logo配一个变体”。3. 用Shader Graph和手写Shader把Logo流光拆成3个必调参数3.1 手写Shader的完整最小实现我先给出一份可直接挂到Unlit Shader上的完整代码。以Unity 2021及以上、Built-in渲染管线为例。用这个版本的原因是它不依赖任何URP或HDRP的节点模块方便新手直接上手排查。Shader Custom/LogoTicker { Properties { _MainTex (Logo Texture, 2D) white {} _Speed (Speed, Range(0.1, 5.0)) 1.0 _StripWidth (Strip Width, Range(0.05, 0.5)) 0.15 _LightColor (Light Color, Color) (1, 0.85, 0.5, 1) _Angle (Angle, Range(0, 6.283)) 0.0 _Softness (Softness, Range(0.001, 0.8)) 0.1 } SubShader { Tags { Queue Transparent RenderType Transparent } Blend SrcAlpha One // 直接叠加保留光感 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; float _Speed; float _StripWidth; float _Softness; fixed4 _LightColor; float _Angle; v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); o.uv TRANSFORM_TEX(v.uv, _MainTex); return o; } // 旋转UV坐标实现不同角度的流光 float2 RotateUV(float2 uv, float angle) { float s sin(angle); float c cos(angle); return float2(uv.x * c - uv.y * s, uv.x * s uv.y * c); } fixed4 frag (v2f i) : SV_Target { float2 originalUV i.uv; float2 rotUV RotateUV(originalUV, _Angle); // 光带走动范围 float t frac(_Time.y * _Speed); float center lerp(0.0, 1.0, t); // 柔边计算用距离与宽度的关系做出软边 float dist abs(rotUV.x - center); float strip 1 - smoothstep(0, _StripWidth _Softness, dist); // 提取Alpha做Logo遮罩 fixed4 col tex2D(_MainTex, originalUV); float mask col.a; // 光带的RGB加到主图上透明度不覆盖原图 col.rgb _LightColor.rgb * strip * mask * _LightColor.a; col.a mask; return col; } ENDCG } } }代码逻辑依次做了三件事。第一把输入的UV按角度做了旋转这直接改变流光的方向参数设为0代表水平扫过设为1.57π/2则是垂直扫过。第二用时间变量t在0到1之间往返移动配合center变量控制光带中心位置。第三在最后合成颜色时用strip * mask把光带约束在Logo的Alpha边界内。这个mask取自原贴图的透明通道所以哪怕Logo是圆形的、字母型的光带都能自动贴合外形。_LightColor.a这个参数往往被人忽略。把它调低比如0.5流光的高光会变弱适合暗色底Logo调高1.0光带会明显发亮适合科技感强的Logo演示。在UI场景中我一般会把主贴图的Alpha和流光的Alpha分开控制因为UI经常要做整体淡入淡出如果混在一起容易让光带半透明化。3.2 Shader Graph版的映射关系如果你项目用的是URP和Shader Graph不敢直接挂CGPROGRAM可以用下列节点完全等价地实现上述逻辑Texture2D节点采样LogoTexAdd节点 / Multiply节点组合时间轴用Time节点输出Time.y乘Speed再取Frac在Math下拉菜单里有Lerp节点把Frac结果作为TA设为0B设为1得到光带中心UV节点 → Split节点取R即U轴Subtract节点 (UV.R - Center) Abs节点取绝对值Smoothstep节点输入距离Edge0和Edge1分别填宽度的“内软边”和“外软边”SampleTexture2D后的Alpha通道直接连到Multiply作为遮罩Add节点把光带颜色与原图RGB相加最后输出Shader Graph里最容易被忽略的是节点的运算顺序。Unity的Lerp节点T是浮点数要求必须0~1之间才能正确插值。而Frac的输出天然在0~1之间直接接即可。很多人在Graph里做的效果“闪得乱”是因为没有对时间变量做Frac而是直接用了Time节点裸奔导致光带位置在0.1和3.7之间反复跳变。3.3 参数表以及“改了之后效果咋不对”的排查顺序参数名取值建议作用出问题时表现_Speed0.5 ~ 3.0控制流光每秒循环次数速度太快会导致视觉闪烁太慢像贴纸_StripWidth0.05 ~ 0.25控制光带宽度UV空间太小只剩一条线太大变成整图泛光_Softness0.001 ~ 0.3控制光纯度过大会让光带边缘糊成一片像起雾_Angle0 ~ 6.283控制扫射角度非45度时要注意贴图UV是否带旋转不然方向对不上_LightColor.a0.3 ~ 1控制流光强度调低后光带几乎看不见调高后过曝排错的第一件事先降速、加宽、加柔边确认光带本身在动之后再看是否贴合Logo轮廓。如果光带动了但Logo边缘没变化说明mask取值不对多半是贴图格式被压缩成了ETC2不带Alpha通道。把贴图格式调成RGBA32或ASTC且勾选Alpha Is Transparency才能读到干净的Alpha。如果光带是整条彩带横扫没有跟随Logo形状检查是否忘了在最终颜色里乘mask。4. 让Logo流光在UI和粒子中不出格需要解决透明排序和SRP兼容4.1 三种使用场景的适配方式同样是Logo流光挂在不同对象上问题完全不同。归纳下来有三种使用场景4.1.1 UI里的Image组件把Shader材质赋给Image组件的Material属性时最大的坑是Image自身的顶点颜色CanvasRenderer的Color会把Shader输出结果再乘一遍。这意味着如果你在Shader里把Alpha设成了mask而CanvasGroup整体透明度是0.5最终光带的明暗纹理仍能显示但透明度会被双重削弱。为了让流光效果在UI里明亮我一般会让Shader的顶点着色器做一步“顶掉”顶点色影响o.color fixed4(1, 1, 1, 1); // 在vert里强制顶点色为白色防止UI误乘这样改之后UI的CanvasGroup透明度仍可用但不会把光带的颜色压暗。另外UI节点如果Scale被压缩成负数UV会翻转流光方向会反向。排查光带方向时先检查RectTransform的Scale是否为负。4.1.2 3D平面上的Logo如场景大屏、虚拟展厅挂在Quad或玻璃材质上时主要问题变成了透视映射。Quad的UV范围是[0,1]和UI一致但当摄像机拉开视角后原来水平的流光会因为透视关系看起来变斜。此时调整_Angle就能修正。真正需要留意的是Logo贴图的Wrap模式。如果WrapMode是Repeat那么光带边缘纹理可能溢出到Logo背后造成奇怪的辉光。把Wrap Mode改为Clamp能避免这种边缘溢色。4.1.3 粒子系统里的Logo贴花在粒子流里用同一个Shader可能会出现半透不半透的奇怪混合。因为粒子默认Blend Mode为AlphaBlend如果底层的Shader用了Additive混合Blend SrcAlpha One再次叠加在粒子的半透明区域上会产生“二次发光”。我的建议是粒子场景单独做一个变体把Blend改回SrcAlpha OneMinusSrcAlpha然后光带亮度靠_LightColor.a控制而不是靠混合模式。4.2 在URP/HDRP里的迁移要点URP环境下内置管线的CGPROGRAM会出现两次警告一次提示UnityCG.cginc不推荐使用一次提示APPROPRIATE混合问题。我把常见的兼容写法整理成下表管线需要改的不用改的Built-in直接用上述代码无URP#include Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl顶点函数用TransformObjectToHClip替换UnityObjectToClipPos代码整体逻辑不变HDRP需要把Shader改成HLSL且带ShaderPass声明直接用CGPROGRAM基本报错若只是做预览可以在材质上勾选Disable SRP Batcher勉强跑但不推荐另外一个能看到但不能忽略的问题是URP里如果启用SRP Batcher你的Shader必须声明CBUFFER_START(UnityPerMaterial)包含所有材质属性否则SRP Batcher会把材质属性当成全局变量导致多个Logo材质互相串参数。这是新手最容易踩的暗坑。4.3 性能层面流光效果必须在意增量开销单纯从GPU指令数看一个Unlit的全屏像素Shader开销极低但在UI Canvas里面Shader的采样次数会成倍放大。对Logo流光来讲哪怕你的Shader写得再省只要Canvas开启了Mask或RectMask2DGPU就要多做一次裁剪计算再叠加上光带里的smoothstep和abs在旧款手机上可能超帧。常见做法是把流光的Shader只挂在一个独立Image组件上而不是让整个Canvas都挂带特效的材质。在性能调优时优先关注这两个指标# 用Unity Profiler的GPU Usage看Shader耗时 # 重点观察DrawCall数量是否因材质变体增多而增加 # 如果是把Shader内的属性都用[Toggle]做成Keyword减少变体数量[Toggle]加Keyword的做法是把双路面和光带参数分离。没有光带时渲染纯Logo不执行流光逻辑指令数直接少一半。这个方案在UI频繁重绘时能明显降本。5. 从“扫一下”到“扫得高级”两套变形玩法与验证手法5.1 双流光交叉与延迟扫射如果你不甘心只做一条横光带可以让两条光带以不同速度和方向错峰扫过制造出类似“呼吸”的高级感。具体只要让上一节的Shader从“单层流光”升级成“双层流光”。我建议的做法是复制一份光带计算函数把_Speed和_Angle各复制一个属性再在最后的加法步骤里多叠加一次。这样不需要改主贴图的采样逻辑只是对同一个主颜色增加一次增量。在参数上一条速度取1.2、宽度0.2另一条速度取0.7、宽度0.3第二条的角度旋转45度。两条流光叠加后会产生“无序而有序列”的闪烁感适合用于活动Logo或Splash画面。5.2 按Logo明暗局部提亮让流光更贴合内容另一种更细腻的做法是让光带只在Logo较暗的区域浮现避开高光区。这要求把主贴图的亮度采样值带进遮罩运算fixed3 texColor col.rgb; float lum dot(texColor, fixed3(0.299, 0.587, 0.114)); float darkMask 1 - saturate(lum * 3.0); col.rgb _LightColor.rgb * strip * mask * darkMask;这里的lum是亮度乘3是把亮度阈值压缩到0~0.33之间darkMask意味着“暗部亮亮部暗”。实际观感是光带像水流一样从暗色区域滑过而不是铺满整条Logo。用在白底Logo上尤其明显。5.3 怎么验证一条流光Shader效果是不是对的建议用三个步骤固定时间变量把_Time.y临时替换成0.3检查光带是否停在Logo中央且不跑动。如果此时光带颜色正常再动起来。若不动时就有问题说明Shader里光带叠加部分写错了。Frame Debugger检查DrawCall打开Window - Analysis - Frame Debugger找到处理该Logo的DrawCall查看该材质应用了哪些混合因子。如果发现RenderQueue不对导致UI被遮挡把Shader的Queue改成Transparent。用RenderDoc或Xcode GPU capture抓一帧查看该Shader在最终颜色输出时col.a的值是否等于贴图Alpha。如果不等于代表col.a被光带颜色污染了需要强制col.a mask。最后一个调试小技巧把_LightColor.a设为0如果此时LogoSprite的颜色和原始Logo完全一致说明流光叠加没有污染基础颜色如果颜色出现偏移检查加法运算是否不小心写在了采样之前。这是验证Shader整洁度的最有效的开关。本文还有配套的精品资源点击获取
返回列表