ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Unity Shader Graph 2D电波扩散效果实现详解

2026/9/26 12:15:53 拓冰建站 浏览量
Unity Shader Graph 2D电波扩散效果实现详解 在2D游戏里做电波扩散效果我第一个想到的是那种角色踩到机关、地面突然震开一圈能量涟漪的瞬间。如果用序列帧动画先不说美术那边要花多少时间导图光是循环对帧对齐就够头疼的。后来我改用Unity Shader Graph 2D直接写这个效果发现不仅省掉了美术资源还能实时调半径、调速度、调颜色整个迭代过程顺畅非常多。这篇内容我会从最底层的UV数学逻辑讲起再一路做到Shader Graph节点搭建、波纹动态化最后聊几个2D项目里尤其容易踩的坑。不管你是刚接触Shader Graph的新手还是已经在用但想做点更灵活的2D特效应该都能从中拿到一些可以立刻用的思路。1. 电波效果的本质一张UV坐标的数学游戏先说一个很多人初期容易绕进去的问题电波效果到底在改什么答案是它改的是每个像素的采样颜色。一个2D平面图贴上材质后每个像素都有对应的UV坐标坐标范围通常是(0,0)到(1,1)。电波效果就是让不同位置的像素显示出不同颜色——比如距离中心越远越亮或者离中心某个特定距离的像素才呈现高亮。所有波纹、扩散、衰减其实都是围绕这个像素离中心点有多远来组织的。从数学上拆解一个电波扩散需要三样东西一个中心点坐标用来定义波纹从哪里发起。每个像素到中心点的距离也就是距离场。一组随时间变化的波形函数控制波纹的亮暗交替与扩散速度。很多人习惯性地以为波纹效果需要复杂的纹理贴图或者粒子配合其实只用UV运算就能完成。贴图在这种情况下反而是锦上添花的事核心永远在数学函数上。举个例子假设你的屏幕分辨率是1920x1080屏幕左下角UV是(0,0)右上角是(1,1)。如果中心点在屏幕正中央坐标为(0.5, 0.5)那么左上角那个像素的UV是(0,0)它到中心点的距离大约是0.707。这个距离值经过正弦函数变换、再映射成颜色就形成了一圈一圈的波纹。这个思路在2D游戏里非常好用因为2D场景本身就是平面UV坐标和屏幕位置天然对应不需要像3D那样考虑相机朝向、法线方向之类的问题。这也是为什么Shader Graph 2D做这种特效特别适合——它的输入输出更直接逻辑链条更短。你可以在心里画一条简单链路UV坐标 → 算出到中心点的距离 → 套正弦波 → 映射颜色 → 输出到屏幕建好这个基本概念之后后面所有节点连接都只是给这条链路的具体环节做替换和增强。2. Shader Graph里的波纹核心链路到这一步我建议你直接打开Unity新建一个Shader Graph文件亲自动手把下面这几个节点连一遍。光看我写的东西没有用这个效果真正值钱的地方在你亲手连线之后对节点行为的理解。2.1 创建Shader Graph并设置基础属性在Project窗口里右键 → Create → Shader Graph → URP Lit Shader Graph或者如果项目用的是Built-in渲染管线就选Built-in的选项。文件名我习惯叫W_RippleEffect方便后期在材质球里直接搜。打开Shader Graph编辑器后先看左上角的Graph Inspector面板把Surface Type改成Transparent。不做这一步的话就算你后面把透明度算成半透明渲染出来也看不到任何透底效果。2D贴图很多带Alpha通道比如角色脚下的光圈贴图、场景里的光晕贴图用Transparent类型才能让透明区域真正透出背景。在URP项目里还需要确认一下Active Targets里勾选了Universal Render Pipeline/Unlit选项。电波效果不需要场景光照用Unlit就够了性能上更省钱渲染逻辑也更直接。2.2 连接距离计算与基础波纹形状核心思路是UV坐标分两路一路用来采样贴图对应原图和纹理的映射关系另一路用来算出距离参与波纹形状的计算。先用UV节点连接到一个Add节点方便后面给UV坐标加偏移。这样波纹的中心点就不是固定的(0.5, 0.5)而是可以暴露到外部变成一个Center属性以后你要把波纹从角色脚下扩散开来直接改这个Center属性就行。接下来是距离计算。你会需要用到Add节点给UV坐标加偏移得到以中心点为中心的新坐标。Multiply节点把坐标分量乘以某个值调整波纹的密度和形状。Dot节点算坐标的点积。注意这里如果用Length节点波纹形状是标准圆形如果用Dot可以做出椭圆甚至更扁平的形状。实际项目中波纹不总是正圆比如斜向扩散的冲击波用Dot配合非均匀缩放就比用Length灵活。假设你把UV减去中心点坐标后得到了一个以中心为原点的新坐标把这个坐标的x和y分量分别乘以一个频率系数再送入Dot节点得到的结果就是该点到中心点的加权距离。这个加权距离已经包含了波纹密度的影响不需要再额外乘其它参数。2.3 用Sine节点让波纹动起来上面算出来的距离值是静态的。要让波纹随时间扩散就要把时间变量加进来。在Shader Graph里用Time节点获取时间乘以一个速度值比如Speed属性再把它加到距离值上。然后将加完的结果输入Sine节点的输入端口。这里有个关键细节正弦函数是周期性函数它的输入每增加2π输出就会重复一次。这意味着随着时间不断增加距离值也不断变化Sine输出值就像水波一样在-1到1范围内来回振荡。离中心点近的像素先亮然后远处的像素依次亮起来产生的视觉效果就是波纹一圈一圈往外扩散。如果到这里停下来你会得到一个黑白相间的条纹环动态来看就是波纹一圈圈往外推。但这个条纹的边缘太硬了直接拿来用会显得很生硬像是测试图案而不是游戏特效。所以接下来必须要做柔化处理。2.4 用One Minus和Smoothstep做柔化边缘Sine节点的输出范围是-1到1。你可以先把它经过One Minus节点取反这样原本波峰的地方变成波谷波纹的视觉重心会跟着调整。结合你想要表现的效果这一步可以灵活处理不是必选项。真正重要的是Smoothstep节点。Smoothstep接收三个输入Edge1、Edge2和In。它的效果是把输入值在Edge1到Edge2之间做平滑插值小于Edge1输出0大于Edge2输出1中间过渡是平滑的曲线。用这个节点波纹的边缘就能变成渐变过渡而不是硬切。实际操作里我经常这样组合一组Smoothstep控制波纹内圈到外圈的浅变深过渡。另一组Smoothstep控制波纹透明度从中心到边缘的衰减。两组结果相乘就是既有清晰波纹轮廓、又不会在中心处突兀爆炸的视觉效果。连完这些基础节点你已经能在材质球预览窗口看到一圈圈柔和的波纹图案了。这时候记得把Master Stack里的Alpha端口接上你算出来的透明度值——不接的话整个效果会直接变成不透明色块前面所有的柔化工作全白搭。3. 让波纹动起来时间参数与多图层叠加基础链路跑通之后你会发现这个波纹的形态还是太单一。真实游戏里电波效果不是只有一个单纯的正弦环它通常会有多层波纹叠加、中心高亮、边缘衰减、甚至颜色渐变等多种层次。这就像你在后期软件里做特效一样不可能靠一个图层解决所有视觉需求。3.1 用Offset属性实现攻击点定位先解决位置问题。在Graph Inspector里新建两个Float属性分别命名为CenterX和CenterY。把UV节点连到Add节点之前先加入一个Vector2节点用CenterX和CenterY组合成偏移值。这样你能通过修改材质参数把波纹中心从屏幕中心挪到任意位置。更进一步的话还可以在代码里用MaterialPropertyBlock动态修改这两个参数。比如角色踩中陷阱时从C#侧传入角色脚下的世界坐标对应的UV坐标波纹就会从那个点冒出来。这个做法比在材质面板手动调参数灵活很多尤其在多敌人、多触发点同时出现时每人一个MaterialPropertyBlock互不干扰性能也远好过复制材质球。3.2 多层波纹与颜色渐变单层波纹的动态效果很单薄但你也不一定要写很多个独立Shader。一个技巧是复用同一个距离值用不同的频率、速度和相位参数生成多组Sine结果再分别赋予不同颜色最后叠加输出。举个例子第一层波纹频率较低速度较慢负责整体扩散氛围颜色用淡蓝色。第二层波纹频率较高速度略快负责内圈细节纹理颜色用亮白色。第三层波纹频率介于两者之间相位偏移0.5颜色用半透明青色。三层结果通过Add节点叠加再经过Smoothstep和透明度控制最终颜色就会从单调的白色环变成有层次感的能量波动。这个方法比用多层材质球做混合更简单而且Shader内部的计算开销并不比单层大多少节点数量增加带来的GPU负载完全在可接受范围内。颜色部分如果要更丰富的渐变可以引入Gradient节点配合Sample Gradient节点将距离值映射到渐变色上。比如内圈暖色到外圈冷色的过渡就能让波纹看起来更立体、更有设计感而不是单纯的单色环。我是强烈建议你在做正式项目时用Gradient因为调整颜色范围太方便了美术同学直接拖渐变条就行不用找你要代码参数。3.3 时间变量的速度与方向控制Shader Graph的Time节点有Time、SinTime、CosTime、DeltaTime等输出端口。做波纹效果最常用的就是Time和DeltaTime。Time绝对值时间从场景开始到当前的总时长适合做从起始点持续扩散的波纹。DeltaTime每帧间隔时间适合做需要严格基于帧间隔计算的动画比如受击反馈的短促波纹。我自己做电波效果时速度控制通常用一个Speed属性为了调试方便还会加一个Direction属性取值为1或-1控制波纹向内聚拢还是向外扩散。这在实际游戏里有用比如吸收类技能需要波纹往中心收缩爆发类技能需要波纹向外扩散两个效果共用一套Shader切换方向只需要把Direction设成-1。有几个调试期内特别常见的翻车点顺手提醒一下Speed属性在0附近时波纹可能会看起来静止不动这是正常的但容易误以为Shader挂了。Direction为负时波纹会向内收缩如果和中心的衰减函数冲突可能出现中心点高亮闪烁的情况需要调整衰减参数匹配。多层波纹速度不同时叠加到一起可能出现干涉条纹这种视觉上很像摩尔纹不是Bug但如果影响观感就把频率值调开。4. 波纹的淡入淡出与循环消除很多人在做电波效果时只关注波纹扩散和形状却忽略了一个极其重要的细节波纹消失时怎么办。这个问题在Shader里很直接。如果波纹扩散到屏幕外它自己就会因为像素超出范围而自然消失不需要额外处理。但在2D游戏里波纹往往还没跑出屏幕就被角色挡住了或者波纹本身就发生在屏幕内扩散一圈之后回到中心点又在屏幕上重新出现。这时候如果不做淡出处理波纹就会变成永久循环闪烁看起来非常灾难。4.1 距离衰减函数与边缘柔化在Shader Graph里实现淡出最常见的是把距离值输入一个OneMinus节点再经过Smoothstep衰减。例如把距离值除以最大半径归一化到0到1。OneMinus取反得到1到0。用Smoothstep设置衰减起点和曲线硬度。把这个结果作为Alpha输出。这个处理能让波纹在靠近边缘时不那么突兀视觉上是扩散出去后逐渐消失在空气里而不是硬生生撞到屏幕边界。如果你的波纹开场时不需要满屏显示而是从中心点逐渐扩展开那么还需要在距离值较小的区域做一个相反的衰减让中心处先暗、中间亮、边缘再暗整体呈能量脉冲的感觉。我的经验是距离衰减函数多做几层平滑完全没有坏处。波纹的观感很大程度取决于衰减曲线够不够软节点里多加两个Smoothstep往往比调半天波形频率有效得多。4.2 波纹循环使用的等价思路游戏项目里电波效果通常不是播一次就完事的。比如某个地面机关每5秒触发一次触发时波纹从机关中心扩散。如果你在Shader里写死了随时间无限扩散的逻辑那第二次触发时波纹会从上一个位置继续扩散而不是从中心重新开始。这时候有两个常用的解法方法一用DeltaTime做计时器配合脚本每次触发时重置时间参数。这个方案需要一点C#配合比较直接。方法二巧妙利用周期的整除性质。假设波纹扩散周期是T你让Shader内部使用的时间值是time % T这样每过T秒波纹状态会自动归零重来完全不用脚本参与。方法二在Shader Graph里也能实现用Fraction节点对时间取小数部分再乘上周期长度得到的就是一个反复循环的周期时间。缺点是波纹的相位会被强制周期化某些场景下波纹起点的过渡可能不够自然。我的建议是如果你的触发时机完全由游戏逻辑决定那就用方法一可控性最高如果你只是想要背景环境里一直有微弱的电波涌动效果方法二更省事。4.3 透明度混合模式的坑刚才所有的计算最终都要通过Alpha端口输出到Master Stack。这一步有两个地方特别容易出问题。第一Blend Mode要选对。做电波效果我一般用Additive混合也就是加法混合。黑色会直接透出背景波纹的高亮区域会让画面更亮这种混合模式很适合能量、电波、光效类效果。如果你希望波纹能遮挡背景而不是单纯叠加发光那就用Alpha混合。第二Additive混合下要注意颜色通道不能满。如果波纹颜色是白色(1,1,1)Additive混合后叠加区域会直接过曝变成一片找不到细节的白。日常处理是把主线颜色控制在0.8以下或者把波纹分成多层内圈颜色偏亮、外圈偏暗这样加出来的效果既亮又不丢失细节。我之前在项目里调一个电弧效果时遇到的就是这个问题。白色波纹叠加在亮色地板上画面完全过曝人物都看不清。降低主色亮度并改用淡蓝色阴影层之后效果立刻自然了这条经验希望对你有用。5. 2D项目中Shader Graph电波效果的整合与调优到这一步你的Shader Graph已经能输出比较完整的波纹动态效果了。但在真正放进游戏场景前还有几个必须考虑的整合问题这里一并说一下。5.1 Shader Graph与2D光照的叠加如果你的项目用URP的2D Renderer场景里很可能有2D Light组件。电波这类效果本身不需要光照因为它是自发光类特效。但要注意Shader Graph的材质类型选择如果选Lit那么场景光会影响到波纹颜色甚至让亮部变暗如果选Unlit波纹始终保持自身颜色和场景光照完全无关。我的建议是电波、能量、激光类特效一律用Unlit。一是不受光照影响颜色输出稳定二是少算光照可以让材质在大量实例化场景里更省性能。反过来如果你就是想做一种受光影响的波纹比如水面反光泛起的涟漪那就选Lit但那种效果需求完全不一样不是这篇重点。5.2 材质球与脚本参数的联动要让波纹效果在游戏里正常触发材质参数的控制是逃不掉的。简单场景里你可以直接拿到材质球引用在脚本里修改CenterX、CenterY、Speed这些属性。但遇到大量敌人同时放技能的场景材质球复制会造成大量材质实例内存和带宽都扛不住。更好的做法是用MaterialPropertyBlock。用法很简单获取Renderer组件。调用SetPropertyBlock传入包含你自定义参数的MaterialPropertyBlock。后续要修改某个参数先GetPropertyBlock再SetFloat再SetPropertyBlock放回去。MaterialPropertyBlock的好处是它不会创建新的材质实例完全通过渲染层的属性覆盖机制工作。多个敌人共用同一个材质球只是每个Renderder的PropertyBlock不同性能开销小得多也避免了材质球的重复创建。这个方案在2D项目里处理大量符文、法阵、脚印特效时非常好用。5.3 性能与Shader变体Shader Graph生成的Shader在打包时会包含多种变体。如果你在Shader里把多个功能都做进去比如又支持极坐标、又支持Gradient、又支持多层波纹那么Shader变体的数量会增加。在Editor里常常感觉不出来打包后在一些中低端移动设备上却能明显感觉到加载和运行的吃力。我自己的习惯是Shader Graph里用到的自定义属性数量控制在10到15个以内能用简单数学解决的问题不额外引入复杂节点。像波纹这种规律性强、数学逻辑清晰的效果最好能控制在40个左右的节点太多基本意味着逻辑复杂过头了。还有一个性能细节波纹效果的渲染区域其实不需要覆盖全屏。如果你的波纹始终围绕某个点扩散可以给材质设置具体的渲染范围比如在Renderer的Material中调整缩放或者在Sprite Renderer里用局部缩放限制波纹显示区域。范围小了像素计算量自然少移动端尤其划算。5.4 后期效果与多材质协同如果你需要把电波效果做得更精致纯Shader Graph一条路走到黑不一定是最优选择。把Shader Graph的输出和Unity后处理栈稍微结合一下效果层次会完全不一样。例如在URP里加一个Bloom后处理波纹的高亮部分会被自然扩散成柔和光晕这让电波的冲击感强很多。对比一下不加Bloom时波纹只是一个清晰的环形亮带加上Bloom之后边缘闪耀的感觉就出来了。画质的提升非常直观。另一个可以是结合粒子系统。Shader Graph管波纹的图案和动态粒子系统管波纹的数量和生命周期。比如你做一个全屏震荡波可以让Shader Graph负责单次波形的形态然后用Particle System控制波形的发生次数、随机位置和生命周期。两者各司其职调试起来也清晰。我真心建议你先把纯Shader Graph版做到可看再加Bloom再加粒子。每加一样东西都确认它确实提升了视觉质量同时性能消耗在自己能接受的范围内。步骤分得清楚项目后期排查问题会好受很多。6. 进阶思路从圆形波纹到任意形状波完成了圆形波纹之后你会发现这个效果可以延伸出大量变种有的甚至完全不像是波纹了但底层逻辑仍然是你前面搭好的那条链路。6.1 用距离场搭配遮罩纹理把算好的距离值乘以一张遮罩纹理的采样结果就能让波纹只在特定区域内显示。比如一张三角形的遮罩贴图配合电波扩散视觉上就像是三角形法阵的能量波动。这个做法很适合技能冷却圈、地面符文、传送门激活这类场景。遮罩纹理不需要美术额外制作大部分情况下用程序生成的噪声图就能满足需求。Shader Graph里提供了多种噪声节点像Simple NoiseRadius值调小一点就能得到细腻的颗粒纹理乘到电波上会让波纹的边缘变得更有质感不那么塑料。6.2 极坐标转换生成放射状波纹同样是距离和角度的计算如果转换坐标系从直角坐标变成极坐标波纹就能从环形变成放射状。做法是用Atan2节点算出UV坐标对应的角度值再把这个角度值输入Sine节点。这样生成的就是从中心向外发射的一圈圈辐射线常用于法阵的放射光效、雷达扫描效果。极坐标的思路看似复杂其实关键就是换了一种坐标映射方式。你不需要改整个链路只需要把前面算距离的那一步换成算角度后面的Smoothstep和透明度处理流程完全复用。这也是Shader Graph做效果最舒服的地方节点化之后整条链路是可复用可修改的。6.3 交互式波纹鼠标点击生成这是最能在项目里让人眼前一亮的应用。通过脚本将一个世界坐标点转换成UV坐标点传给Shader作为波纹中心然后触发波纹动画。如果你的项目的手势系统或点击反馈正好缺一种可视化表现这个效果非常适合。转换方式不复杂把屏幕坐标通过Camera.ScreenToWorldPoint转换到世界坐标再用WorldToUV这类函数映射到材质坐标。不同项目画布设置不一样这层转换代码略有差异但原理都是屏幕坐标 → 世界坐标 → UV坐标的流水线。做成交互式之后你会发现Shader Graph的威力不止于做出好看的过场特效它还能替你省掉很多序列帧动画的资源和人工成本。动态的、可交互的视觉反馈在游戏体验上是质的提升玩家也会觉得底层的表现力很强。用Shader Graph做2D电波效果这条路我从第一次尝试到现在走了不少弯路但确实收获很大。这个效果把数学、渲染和游戏体验结合得非常紧密学会一个链路等于同时打开了波纹、脉冲、光圈、法阵、雷达波等一系列效果的实现思路性价比实在太高。希望这篇内容能帮你少踩几个我踩过的坑也期待你做出更好看的作品。