尧图精选

Unity Shader Graph 2D电波特效制作全流程:从UV计算到噪声扰动

🕒 发布时间:2026/9/19 23:27:14 📁 来源:尧图网络
做2D游戏的时候我一直觉得“角色脚下冒出一圈圈往外扩散的电波”是个非常实用的表现手法。不管是角色登场、施法前摇、还是怪物预警这一下子就能把画面张力拉满。前阵子用Unity Shader Graph搓了一个2D电波效果踩了不少坑也把整个思路理顺了今天把完整过程记录下来。如果你正好想用Shader Graph做类似的2D特效这篇文章应该能帮你省下不少试错时间。先说清楚这个效果能干什么一个从指定中心点向外扩散的圆环带一点扭曲和衰减边缘柔和半透明可以叠加在2D场景里也可以挂在角色脚下当预警圈。它不吃贴图序列帧不依赖粒子系统纯Shader实时计算改参数就能调形态。适合用Unity做2D项目、想快速出效果又不想写HLSL的同学也适合刚接触Shader Graph、想搞懂UV和时间节点怎么配合的新手。1. 先把效果拆开看一个电波效果到底有哪些视觉层1.1 电波效果的三个核心视觉元素想用Shader Graph实现电波第一步不是连节点而是想清楚“电波”在视觉上由什么组成。我自己拆解下来核心就是三样东西扩散的圆环、边缘的扭曲、传播中的衰减。扩散的圆环很好理解——从中心点往外一圈圈变大这是整个效果的主干。边缘的扭曲是“电波感”的关键没有扭曲就是一个普通的放大圆环加上噪声扰动后才有那种能量波动的味道。传播中的衰减则决定了观感层次——离中心越远圆环应该越淡、越窄、甚至消失如果所有环都一样亮整体就会显得死板。这三层对应到Shader Graph里分别是距离计算与时间驱动、噪声扰动UV、透明度随距离衰减。把这三件事拆开做每一步的节点逻辑都会非常清晰调试的时候也不会一头雾水。1.2 为什么选Shader Graph而不是粒子系统或贴图序列帧做这个效果之前我也想过用粒子系统配一张环形贴图或者直接做序列帧。粒子系统的问题是“每次扩散都要起一个粒子”连续波动的时候要么调循环要么做SetActive很麻烦而且想做出扭曲感几乎只能靠贴图预烘焙。序列帧的问题更直接——如果你要改颜色、改半径、改扭曲强度就要重新出图迭代成本太高。Shader Graph的优势在于全部参数化。扩散速度、波纹宽度、噪点强度、颜色、透明度全都可以在材质面板上动态调甚至可以从脚本传值运行时改变发射中心点。对于2D项目里经常要反复调特效风格的情况这种“所见即所得地调参”比重新做一套素材高效太多。另外颗粒感——Shader Graph没有CPU开销执行全在GPU上对移动端2D游戏来说非常友好。只要不用高频噪声满天飞性能基本是几十条指令量级可以放心挂一堆在角色身上。2. 搭建前的准备材质、渲染管线与2D模板2.1 用哪个Render Pipeline差别很大做Shader Graph之前先确认你的项目用的是哪种渲染管线。这直接决定了你在Shader Graph里能看到哪些Master节点以及光照、混合模式这些选项的默认行为。如果你是Built-in管线也就是传统内置渲染管线创建Shader Graph时选Unlit Master节点就好2D电波不需要光照计算。如果你是URPUniversal Render Pipeline推荐使用Sprite Unlit Master节点它会主动兼容Unity 2D Sprite的渲染方式并且能自动处理Sprite纹理的采样。HDRP做2D有点大材小用不推荐在这里用。我这次是在URP项目里做的Sprite Unlit Master节点用起来非常顺手后面Blend模式、透明度这些参数都直接暴露在Master节点上不用额外去改Shader文件。如果你是在Built-in里做Unlit Master其实也够用只是Sprite相关项自己要注意一下。2.2 创建Shader Graph的正确打开方式创建方式很简单在Project窗口右键 - Create - Shader Graph - URP - Sprite Unlit Shader Graph如果是URP项目。建好后它会自动生成两个资产——一个Shader Graph文件和一个配套的Material文件。我习惯给它们取相同的名字放在同一个文件夹里免得之后工程大了找不到对应关系。打开Shader Graph编辑器你会看到一个默认的Sprite Unlit Master节点。这个节点有几个关键入口要考虑清楚Sprite Texture、Sprite Color、Alpha、Alpha Clip Threshold、Blend。电波效果不需要Sprite纹理所以把Sprite Texture入口留空即可颜色和透明度我们完全用程序化节点计算。做完第一步后在Graph Inspector里把Surface Type设为TransparentBlending Mode设为Additive这样渲染出来才是半透明叠加的效果。这一步不做后面连再多节点也可能什么都看不到。2.3 Sprite Unlit Master节点的参数怎么设Sprte Unlit Master节点里的参数项很多新手容易忽略。我的建议是Surface Type选Transparent半透明Blend Mode选Additive加法混合Render Face选Front。不要把Blend Mode设成Alpha混合电波这种发光类效果用Additive叠加后层次感好很多而且不会出现因为Alpha排序导致的前后穿插问题。另外在URP下如果后面发现Shader Graph材质在Sprite上渲染不出来最常见的就是Material的Surface Type还是Opaque或者Shader没被正确设置成透明。这个坑我后面会在排查章节里详细说这里先记住配置路径Graph Inspector - Graph Settings - Surface Type / Blend Mode。还有一个容易被忽略的点Shader Graph的Target设置里有个选项叫“Allow Material Override”如果你勾选了材质面板会暴露一大堆可覆盖属性不勾选的话属性列表会干净很多。我建议默认不勾自己需要暴露哪些参数就手动加哪些Property这样后续美术同学调起来也不会被多余参数干扰。3. 核心节点搭建从距离计算到扩散环3.1 第一步拿到UV算到中心的距离电波效果最底层的数学量是“当前像素离中心点有多远”。在2D精灵上UV范围一般是0到1如果我们想以精灵中心为圆心那就把UV减0.5再取长度就能得到一个以中心为0、边缘为0.5左右的距离值。Shader Graph里的节点分工是这样的UV节点UV坐标选UV0拿到当前采样点的坐标然后接一个Substract节点减去一个Vector2默认给0.5, 0.5再把减法结果接到Distance节点的两个输入之一——注意Distance节点还有另一个口也要接另一个口接Vector2的0,0这个节点就会输出两点之间的距离。实际上你也可以用Length节点直接对相减后的向量求模效果完全一样。这里有一个很关键但不容易注意的问题Sprite的UV不一定是标准的0到1。如果Sprite资源的Mesh Type是Full Rect那么UV确实铺满0到1但如果用了Tight紧密包围盒模式UV范围会缩到精灵实际像素范围的一部分导致圆心偏移。这也是为什么不少人照着教程做圆环却不在精灵正中间。所以记得去Sprite导入设置里把Mesh Type改成Full Rect或者干脆在Shader里用对象空间坐标算后面会讲。3.2 第二步让半径随时间往外跑有了静态的距离图下一步是让它动起来。电波扩散的本质是“当前时间对应的传播半径逐渐变大”再把“距离等于当前半径”的像素点亮。我用Time节点提供秒数乘以一个Speed速度值得到随时间线性增加的半径值。为了让半径循环变化我把它接到Frac节点上取小数部分Frac输出的结果会在0到1之间循环跳变正好对应“从中心扩散到边缘再回到中心”的视觉循环。这里有个细节如果你想要“扩散到一半重新开始”的感觉Frac是合适的但如果你想要“逐渐消失后隔一段时间再来”的断档感就需要用Floor或者Step做门控或者直接靠后期Alpha控制一般用不上。参数设置上我一般把Speed初始给0.5到1之间的值让一次扩散周期在1到2秒之间视觉上从容不迫也不会让人觉得迟钝。实际数值跟精灵大小有关大精灵需要适当降低速度小精灵则可以提高本质上人眼感知的是“半径增长速度”而不是速度绝对值。3.3 第三步把“扩散半径”变成“可见圆环”到这一步你已经有了“每个像素到中心的距离”和“当前传播半径”两个关键量。把距离减去半径得到的差值越小说明越接近当前波纹所在位置。为了画成一个有宽度的圆环我习惯用两个SmoothStep夹出一个环带。具体方法是先把距离减去当前半径得到一个差值负数代表在环内侧正数代表在环外侧。然后用SmoothStepFrom0.02To0算出环内侧的渐变再用SmoothStepFrom0To-0.02或类似的负数区间算出外侧的渐变两者相乘就是一个内外都柔和的环带。这个方法的本质是把“距离等于半径”这条数学线扩展成一条有厚度的带状区域通过SmoothStep的边缘过渡避免锯齿感。说得直白点就是在距离值上做一个上下限的裁剪让距离刚好等于传播半径的那一圈像素选中其他像素逐渐过渡为0。环带宽度就会对应“0.02”这个范围大精灵上可以适当调大一点。我踩过的一个坑是直接把两个SmoothStep结果相加而不是相乘那样会得到两条不相连的弧线看起来像半个环。相乘才是“既要满足内侧约束又要满足外侧约束”的逻辑。3.4 第四步平滑边缘与透明度衰减环带画出来了但直接看会发现边缘很生硬而且所有环亮度都一样没有扩散衰减的感觉。这时需要在透明度上做文章。我用的方法是把1减去当前传播半径或者用1减去Frac的小数结果得到一个大致的“离中心越远越弱”的系数再乘到Alpha上。这样电波在扩散初期亮度高、后期逐渐隐去视觉上更自然。另外一个增强层次感的操作是把环带的强度值通过Pow节点做一次Gamma调整。Pow值大于1会让亮部更集中小于1则让亮部更散。我常用1.5到2之间的值让环带中心更亮、边缘更快衰减模拟能量集中在波前的效果。到这里一个不会动的“静态波纹”已经很完整了接下来只要加噪声扭曲就是真正的电波效果了。3.5 第五步加一点噪声扭曲电波感就出来了没有扭曲的圆环在2D里看起来像一个教学演示的几何图形不太像“电波”。电波的那种抖动、撕裂、能量扰动的感觉来自对UV坐标的噪声扰动。我在UV坐标输入距离计算之前先给UV加一个偏移量用Simple Noise节点采样一个噪声值Multiply一个强度参数我通常给0.02到0.06之间然后把噪声加到原始的UV坐标上再把扰动后的UV用于距离计算。这样算出来的距离本身就被噪声“扭曲”了画出来的圆环边缘就会有自然的凹凸抖动。有一个比较重要的细节Simple Noise的输出是0到1之间的值如果直接加到UV上会让圆环整体偏向一边。更稳妥的做法是把Noise输出乘以2再减1用Remap节点或直接Subtract 0.5再Multiply 2都行让噪声正负对称这样扭曲只改变形状不会引起圆心漂移。时间维度上还可以把噪声的采样UV加上Time乘以一个流速让扭曲本身也在流动。这会让电波边缘的抖动看起来像是“有生命力的能量在跑”而不是一个固定的油漆刷边缘。这一步加完后整个效果已经非常接近我理想中的电波了。4. 让电波效果真正用起来材质参数、脚本驱动与性能控制4.1 材质面板的参数整理Shader Graph做好的效果最终是要给美术同事或自己后续反复调参的所以Property整理不能偷懒。我的习惯是把所有可变数值都暴露成Property命名清晰分组明确并且在默认值上放一个“看起来差不多”的数值让人一拖进去就能看到效果。我这次暴露的参数主要包括参数名类型作用我的推荐默认值CenterVector2电波中心偏移(0.5, 0.5)SpeedFloat扩散速度0.6RingWidthFloat环带宽度0.05NoiseStrengthFloat边缘扭曲强度0.03NoiseScaleFloat噪声缩放8.0NoiseFlowFloat噪声流速0.5IntensityFloat整体亮度1.0TintColorColor电波颜色青色系(0.2, 0.9, 1.0)这里特别说下Center。默认(0.5, 0.5)表示精灵中心但实际游戏里电波中心经常不是精灵中心——比如角色脚下。所以Center要暴露出来后续脚本会动态修改这个值让电波从任意位置发出。还有个很好的习惯在每个Property的Tooltip里写清楚它控制什么。Shader Graph支持给Property写描述鼠标悬停就会显示这个对后续交接项目帮助非常大。4.2 从脚本动态控制中心点与发射时机光有材质还不够很多时候电波是跟着游戏逻辑走的。我的做法是用一个C#脚本在需要的时候往Shader里传值。比如角色要释放技能时我拿到角色的世界坐标通过Camera.WorldToScreenPoint换算或局部坐标赋值给Shader的Center属性。注意这里Center的坐标空间必须和Shader里UV或对象空间的计算方式保持一致否则你会看到电波从奇怪的位置冒出来。更简单的一种做法是让电波跟随精灵自身运动每次Update时把Center设成(0.5, 0.5)然后通过做一个专门的“电波圆心跟随点”的空物体把它的局部位置映射到UV空间再传进去。这样电波就能固定在角色脚底来回了。触发器方面我没做发射间隔的代码逻辑而是让Shader自己用Frac无限循环。如果你需要“只在某个时刻发射一波、然后停了”两个思路一是用脚本控制全局属性Rate来控制Frac的可选分支二是把材质的Enabled开关当作发射开关播放完一整个周期后关闭。后一种做法在性能上更省因为你完全不更新了。4.3 性能上的三个注意点2D特效最担心的就是性能。Shader Graph虽然开发效率高但如果不注意也很容易写出密集的ALU指令。对于这个电波效果我有三点优化建议第一噪声节点是性能大头。Simple Noise在多数平台上会编译成好几条指令如果你叠加多层噪声比如两个噪声叠加起来制造复杂扭曲开销会翻倍。建议最多用一层噪声如果效果不够丰富可以通过增大NoiseScale来增加细节而不是叠加新节点。第二尽量用half精度。Shader Graph里很多节点的默认精度可能是float但2D特效在移动端用half就够了。做法是在Graph Inspector的Precision里改成Half或者对特定节点右键设置Precision为Half。这一项优化对移动端Shader性能影响非常明显。第三控制Overdraw。如果电波是Additive模式叠加的大面积高亮区域会在手机上造成不必要的像素填充开销。我通常会把电波的Sprite Size控制得比实际需要略小然后在Shader里用透明度让边缘更早消失减少无效填充。当然这是在效果达标的前提下做取舍不能为性能牺牲太多观感。4.4 Blend模式与层级合并Additive混合是电波这个效果的最佳选择因为它天生自带发光感而且叠加到任何背景上都只会变亮、不会压暗背景。这在2D游戏里几乎总是想要的效果——电波就应该是亮眼的。但Additive有个隐藏问题如果背景本来就很亮电波会非常不明显甚至“看不见”。这是它的物理特性决定的——加法混合本质上就是“当前屏幕颜色 本片元颜色”背景越亮加出来的差异越小。如果你的场景偏亮可以考虑改成Alpha混合把背景调暗一些但这会牺牲一点发光感。另外2D项目里要留意Sprite Renderer的Sorting Layer和Order in Layer。电波这种特效应该放到角色所在层之上、演出层之下否则会被普通场景元素遮挡或者盖住角色。我一般会给电波单独开一个Sorting Layer名字就叫Effect放在默认层和前景层之间这样层级关系永远是对的。5. 我踩过的坑常见问题排查一手实录5.1 效果只在精灵的右下角出现或者位置对不上这个问题我一开始也遇到过排查下来绝大多数是UV空间问题。前面提过Sprite的Mesh Type如果是Tight它的UV会被裁剪到实际像素范围导致Shader无法用标准的0,0到1,1坐标来判断中心。解决办法有两种。第一种是去Sprite导入设置里把Mesh Type改为Full Rect让整个精灵的UV满幅覆盖。缺点是如果你本身用了Tight模式是为了减少渲染面积改成Full Rect会增加一点点像素填充但对2D小元素来说基本无感。第二种是改用对象空间坐标计算把Object节点输出到Position入口用Position节点的xy分量减去Center再算距离。这样可以完全绕开UV裁切问题代价是需要处理坐标空间转换。我后面统一改用对象空间坐标了因为这样不依赖Sprite的导入设置换素材也不容易再出问题。但要注意如果你的精灵本身有缩放Position是基于模型空间的需要在Shader里除一下Scale或直接用归一化后的值否则电波会跟着Sprite的缩放被拉伸。5.2 叠加多个电波时看不清层次叠了多层电波在画面里结果全糊在一起看不出谁先谁后。这是Additive混合的老毛病多个半透明层相加透明度会快速饱和变成一片白色。我处理这个问题的思路是控制每层的峰值透明度。比如第一层MainWave的Alpha峰值给0.8第二层SubWave的峰值给0.4第三层Ripple的峰值给0.2。这样叠加起来虽然会变亮但层次依旧能从亮度上区分开。更进阶的做法是给电波加一个“距离衰减曲线”。越靠近中心的电波透明度高、越靠边缘透明度低这样即使叠了三层最外层依然保留一个渐隐的轮廓不容易糊成一片。衰减曲线我一般用1-saturate(radius)来实现简单有效但如果想要更精细的控制可以接一个AnimationCurve节点。5.3 锯齿和像素闪烁2D电波最让人头秃的就是锯齿。由于电波本身是一条细环叠加移动后特别容易出现闪烁和锯齿感。解决锯齿的思路不是“消除硬边”而是“让硬边变成渐变”。用SmoothStep做边缘过渡已经是标准做法了但如果还是不行检查一下MMAA或者RenderFeature有没有开。URP项目里如果开了MSAA或后处理抗锯齿对抗锯齿效果会有非常大的提升。另一种闪烁来自噪声的时间采样不稳定。如果Time节点直接驱动噪声采样噪声的采样坐标在某些平台上会因为浮点精度问题产生抖动。解决办法是把噪声采样坐标乘上一个较大的值比如100或者把时间做一次Frac循环让浮点数值不至于无限增长。这里有个小技巧把Time的输出做一次Modulo 10再参与后续计算可以有效避免长时间运行后的浮点精度问题。5.4 URP下电波显得“发灰”或者完全看不见在URP下做2D特效最常见的坑有两个一是材质没有正确设置为透明模式二是没有使用对应的Sprite Unlit Master节点。如果材质设为Opaque但Shader里又在输出半透明颜色结果就是整个Sprite按不透明方式渲染电波的颜色会把背景盖成一片灰。或者反过来如果Master节点用了一般的Unlit而没开透明混合Alpha完全不起作用效果也会“消失”。排查方式很简单选中材质看Inspector面板的Surface Type是否是Transparent如果不是就改过来再确认Shader Graph里的Blend模式是否设成Additive。这在绝大多数情况下能解决“看不到效果”的问题。另一个URP特有问题URP默认的Renderer Feature如果开了HDR那么Additive高亮很容易被Bloom或后处理接管导致电波过曝成一片白。解决方法是把材质的HDR Color属性调低一些或者在后处理配置里给特效层单独做曝光控制。电波这种本身就该亮的东西稍微过曝一点反而有味道但要控制在能看清细节的范围内。6. 还能怎么玩电波效果的几种扩展方向6.1 多波叠加与相位偏移一个电波环做出来后最自然的升级是叠多层波。我通常的做法是把核心的“扩散半径”计算复制两三份分别给不同的Speed、RingWidth和NoiseStrength再把它们的输出相加最后统一乘一个整体透明度。为了让多个波之间不会完全同步我给每一层加了一个Phase偏移量。具体实现是在Time进入Frac之前给某一层加一个固定的时间偏置这样它的相位就和其他层错开了。三层电波交替扩散视觉层次立刻丰富很多像波纹交错的能量场非常带感。不过要注意层数越多指令开销越大我一般控制在3层以内超过3层就建议用多材质或粒子系统配合了。6.2 围绕任意中心点发射前面讲到Center参数实际上配合脚本后可以让电波围绕2D场景中的任意点发射。比如Boss战时在玩家脚下发一波预警圈或者在拾取物周围显示“可交互”提示。这个扩展的核心是坐标空间转换。脚本里把世界坐标除以精灵World Scale再减去精灵Pivot偏移换算到对象空间坐标后传给Center属性。这部分虽然写起来有点绕但逻辑清晰一次写完后面所有场景都能复用。更推荐的做法是给Shader增加一个“WorldPosition模式”开关在Shader内部用Screen Position或World Position节点来计算距离脚本只管传世界坐标Unity帮我们完成坐标换算。6.3 收拢式电波向内收缩扩散的圆环看多了有时候需要“收拢”的效果比如角色在聚气、拉取能量。实现方法其实很简单把Frac的结果反过来用1减去Frac得到的值作为半径驱动其他节点全部保持不变。这样环就从边缘往中心收拢了。视觉上收拢式电波通常配合更快的速度和更窄的环宽营造“能量吸入”的感觉。如果你想让收拢和扩散交替发生可以再加一个开关属性用脚本控制方向。或者在Shader里结合正弦波做一个往返运动周期性扩散-收拢-扩散形成类似呼吸的效果。这部分就是纯数学调参耐心试几次就能找到适合自己项目的参数组合。6.4 和音效、打击感联动效果做出来之后真正让玩家“感觉到”电波的往往不是细节参数而是它和音效、打击感的同步。我的经验是电波首次扩散的那一帧刚好是音效“砰”的那一下。为了实现这个脚本在发送Center和发射指令时会同时触发音效两者靠代码同步不用靠Shader去猜时间。另外角色打击瞬间可以把NoiseStrength短暂调高再随动画恢复这样电波边缘会短暂“炸裂”一下打击感立刻上一个台阶。说到底Shader只是工具如何让它为玩法节奏服务才是做2D特效最有意思的地方。多站在玩家视角去感受“哪一帧最该亮、哪一帧最该停”比单纯调参数更能做出好效果。用了几次之后的几点心里话这个电波效果做完之后我最大的感受是Shader Graph真的是2D项目做特效的利器它把繁琐的GPU编程抽象成了可视化连线让策划、美术、程序之间沟通顺畅了很多。以前我可能要花半天写一个Shader再反复调试现在搭一套节点留好参数后续改风格基本就是拖滑动条的事。但也要说实话Shader Graph不是万能的。它调试复杂逻辑时比代码Shader难很多某些平台兼容性问题也很难从图上游刃有余地解决最终还是要会看Generated Code。不过对于2D电波这种中等复杂度的效果Shader Graph的效率和灵活度绝对是碾压级的。希望这篇记录能帮你快速搭出自己想要的电波效果少走一点我当初绕过的弯路。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →