
1. 项目概述与核心价值最近在做一个游戏项目UI界面里需要一个动态的、能持续滚动的进度指示器比如用来表示资源加载、技能冷却或者任务进度。静态的进度条太死板想加点“动感”。一个很自然的想法就是让进度条上的纹理动起来比如让条纹或者光点沿着进度方向循环滚动。这种效果在Unity里用ShaderGraph来实现可以说是“天作之合”既直观又高效。今天要聊的就是这个如何用ShaderGraph实现一个贴图UV的循环移动滚动效果。这个效果本身不复杂但它是理解ShaderGraph中UV操作和时间动画的绝佳入门案例掌握了它你就能举一反三做出各种动态纹理、流光、扫描线等酷炫的UI或场景特效。简单来说这个效果的核心就是让一张贴图在模型表面比如一个Quad平面上沿着某个方向通常是U或V轴持续、平滑地移动并且当它移出边界时能从另一边无缝地“循环”回来。这听起来是不是很像小时候看的走马灯没错原理上就是那样。在Shader中我们通过不断修改传给纹理采样节点的UV坐标来实现这个“移动”。而“循环”的特性则得益于纹理采样时的Wrap Mode循环模式。整个过程不需要写一行代码全部在ShaderGraph的可视化节点中完成。这个效果非常适合Unity的UI Image组件、3D物体的材质或者粒子系统的纹理动画。无论你是UI设计师、技术美术还是想丰富游戏表现的程序员这个技巧都能让你的项目增色不少。接下来我会从原理到实操一步步拆解并分享我在实际项目中积累的一些参数调整心得和避坑指南。2. ShaderGraph核心思路与节点选型解析在动手连接节点之前我们必须先理清思路。在ShaderGraph里实现UV滚动本质上是一个数学运算新的UV 原始UV (时间 * 速度)。然后我们将这个计算后的新UV坐标输入给Sample Texture 2D节点去采样纹理。2.1 核心节点链解析整个效果的核心节点链非常清晰主要涉及以下几类节点输入节点Time节点这是动画的“发动机”。它输出一个随着游戏时间不断增大的浮点数。我们通常使用Time节点的Time输出端口它提供的是自游戏开始以来的总秒数。UV节点提供模型原始的纹理坐标。对于UI Image这样的屏幕空间四边形其UV范围通常是(0,0)到(1,1)。运算节点Multiply乘法节点用Time乘以一个Speed速度标量得到累计的“偏移量”。速度值控制滚动的快慢。Add加法节点将原始UV的某个分量比如U与计算出的“偏移量”相加得到移动后的新UV坐标。这里需要注意UV是一个二维向量(Vector2)包含U和V两个分量。如果我们只想让纹理水平滚动就只修改U分量。纹理采样节点Sample Texture 2D节点这是最终出效果的节点。它将计算好的新UV坐标作为输入从指定的纹理贴图中取出对应位置的颜色。节点的Wrap Mode属性必须设置为Repeat循环这是实现无缝滚动的关键。如果设置成Clamp钳制纹理移动到边界就会卡住无法循环。输出节点Fragment片元或Base Color输出将Sample Texture 2D节点的RGBA输出连接到主节点的Base Color上纹理颜色就会显示在物体表面。2.2 为什么选择ShaderGraph而不是代码你可能会问用几行Shader代码o.uv TRANSFORM_TEX(v.uv, _MainTex) float2(_Time.y * _Speed, 0);也能实现为什么用ShaderGraph对于这个效果尤其是对于技术美术或非专业图形程序员来说ShaderGraph的优势非常明显可视化与快速迭代所有逻辑一目了然调整速度、方向甚至混合多个滚动效果都只需要拖拽节点和滑动参数条无需编译等待。这对于快速原型设计和美术自主调整至关重要。易于理解与教学UV移动的数学关系被具象化为节点连线对于初学者理解Shader工作原理有巨大帮助。集成度高参数如速度可以直接暴露为Material的Property方便在编辑器或运行时动态调整。当然对于极其复杂或性能要求苛刻的定制化效果手写Shader可能更有优势。但对于本文讨论的UV滚动这种标准操作ShaderGraph是更优解。3. 分步实操构建基础水平滚动Shader理论清晰了我们打开Unity新建一个ShaderGraphURP或Built-in管线均可节点名称可能略有差异但逻辑相通开始动手搭建。3.1 创建Graph与基本框架在Project窗口中右键 - Create - Shader - Universal Render Pipeline或你使用的管线- Lit Shader Graph对于UI使用Unlit Graph更合适。双击打开创建的ShaderGraph文件。在Blackboard黑板区域我们先创建一个控制滚动速度的参数。点击“”号选择Float命名为ScrollSpeed默认值设为0.5。这个参数之后会出现在材质球上。3.2 构建水平滚动节点网络现在在主Graph窗口中开始连接节点获取时间与速度在空白处右键搜索Time节点添加。使用其Time输出口。再添加一个Multiply节点。计算UV偏移将Time节点的输出连接到Multiply节点的A端口。将我们创建的ScrollSpeed参数从Blackboard拖拽出来连接到Multiply节点的B端口。此时Multiply节点的输出就是一个随时间线性增大的值其增长速度由ScrollSpeed控制。获取并修改UV添加一个UV节点。默认输出是模型的原始UV。添加一个Add节点。组合新UV将UV节点的输出连接到Add节点的A端口。这里有个关键操作我们只需要水平U方向滚动。所以我们需要构造一个二维向量其U分量是偏移量V分量是0。右键搜索Vector2节点并添加。将Multiply节点的输出连接到Vector2节点的X即U输入Vector2节点的Y即V输入保持为0或连接一个Float节点设为0。最后将这个Vector2节点代表偏移向量连接到Add节点的B端口。节点连接示意Time-Multiply(A)ScrollSpeed(B)-Multiply(Out)-Vector2(X)-Add(B)UV-Add(A)。采样纹理添加一个Sample Texture 2D节点。将Add节点的输出即移动后的新UV连接到Sample Texture 2D节点的UV输入端口。设置纹理与循环模式在Sample Texture 2D节点的属性面板中点击Texture字段的圆点可以关联一张Project中的贴图比如一张条纹图。最关键的一步确保其Sampler State下的Wrap Mode设置为Repeat。这是循环滚动的基石。输出到表面将Sample Texture 2D节点的RGBA输出端口连接到主节点Master Stack或PBR Master的Base Color输入端口。保存与应用点击Graph上方的Save Asset。在Project中右键该ShaderGraph文件选择Create - Material生成一个材质球。将这个材质球赋予给场景中的一个Quad3D物体或UI ImageCanvas下。现在运行游戏你应该能看到贴图在水平方向上持续滚动了。调整材质球上的ScrollSpeed参数可以实时改变滚动速度。注意如果你用的是UI Image确保其材质Material属性指向了你创建的ShaderGraph材质。有时UI Image默认使用Sprite Shader需要手动替换。3.3 实现垂直与对角线滚动水平滚动会了垂直和斜向滚动就是简单的变体垂直滚动在构造偏移向量时将Multiply的输出连接到Vector2节点的Y输入而X输入保持为0。即偏移向量为(0, time * speed)。对角线滚动这需要两个速度参数。一个控制U方向ScrollSpeed_U一个控制V方向ScrollSpeed_V。分别用两个Multiply节点计算U和V方向的偏移量然后将它们分别输入到Vector2节点的X和Y。最后将这个向量与原始UV相加。节点连接扩展添加第二个Float参数ScrollSpeed_V。添加第二个Multiply节点Time同时输入给两个Multiply分别乘以ScrollSpeed_U和ScrollSpeed_V。两个Multiply的输出分别给到Vector2节点的X和Y。4. 效果增强与高级技巧实录基础滚动有了但往往看起来有点“单调”。下面分享几个我常用的增强技巧让效果更出彩。4.1 使用Mask控制滚动显示区域很多时候我们不想让滚动纹理铺满整个物体。比如我们只想在一个圆形的进度条内部有滚动效果。这就需要用到遮罩Mask。准备遮罩纹理在Photoshop等工具中制作一张黑白遮罩图。白色区域表示显示滚动纹理黑色区域表示不显示通常显示为透明或底色。在ShaderGraph中混合添加第二个Sample Texture 2D节点来采样这张遮罩贴图。注意这个节点的UV可以使用原始UV不需要滚动。添加一个Lerp线性插值节点。Lerp节点需要三个输入A,B,T。将滚动纹理的颜色输出连接到A。将一个纯色比如黑色或透明色可以用Color节点设置连接到B。将遮罩纹理的R通道灰度图每个通道值一样通常用R连接到T。将Lerp的输出连接到Base Color。原理Lerp根据T的值在A和B之间混合。当遮罩为白色值为1时输出A滚动纹理当遮罩为黑色值为0时输出B底色。这样就实现了区域限制。4.2 实现“来回”滚动PingPong而非单向循环单向循环看久了可能会腻。有时我们需要纹理像钟摆一样来回运动。这需要用到PingPong节点。修改时间输入删除之前直接连接Time的线。添加PingPong节点搜索PingPong节点并添加。它有两个输入In和Length。连接将Time节点的输出连接到PingPong节点的In。创建一个Float参数PingPongLength例如设为2连接到Length。替换将原本连接Time的线改连接到PingPong节点的输出。效果PingPong节点会输出一个在0到Length之间来回平滑变化的值。这样UV偏移量就会在正负方向之间往复纹理也就来回滚动了。4.3 多层纹理混合滚动为了增加细节和丰富度可以叠加多层不同速度、不同方向甚至不同混合模式的滚动纹理。复制节点链将之前构建的从Time到Sample Texture 2D的整个节点网络复制一份。设置不同参数为第二套节点链设置不同的ScrollSpeed和滚动方向修改Vector2的输入。可以选择另一张细节纹理比如噪点或细线。混合模式使用Add、Multiply或Screen等节点来混合两个Sample Texture 2D的输出。Add增强亮度适合做光效叠加。Multiply变暗适合做污渍、磨损效果。Screen变亮与Add类似但效果更柔和。连接输出将混合后的颜色连接到Base Color。4.4 在UI中应用与性能考量将ShaderGraph材质应用于UI Image时有几点需要特别注意Canvas设置确保UI所在的Canvas的Render Mode不是World Space除非你需要3D空间中的UI。对于屏幕空间UI使用Screen Space - Overlay或Screen Space - Camera。材质与ShaderUI Image默认使用UI/DefaultShader它不支持ShaderGraph。你需要将Image的Material属性指定为你创建的ShaderGraph材质。同时将该材质的Shader改为你的ShaderGraph。性能ShaderGraph本身会带来一定的性能开销但对于简单的UV滚动开销微乎其微。然而如果在一个屏幕上同时使用成百上千个这样的动态UI元素Draw Call和Overdraw可能会成为瓶颈。优化建议尽可能将多个需要同样滚动效果的UI元素合并到一张大图Atlas中共享同一个材质。对于完全被遮挡的UI元素及时禁用其GameObject。评估是否真的需要每帧都滚动。对于某些次要提示可以降低更新频率。5. 常见问题、排查技巧与参数调优心得即使逻辑正确实操中也可能遇到各种“坑”。下面是我总结的一些常见问题及解决方法。5.1 纹理不滚动或滚动异常问题贴图完全不动。排查首先检查ScrollSpeed参数是否为0。其次检查Time节点是否正确连接。可以临时将Time输出直接连到Base Color需要先通过Fraction节点取小数部分避免颜色过亮看颜色是否随时间快速闪烁以验证Time节点工作正常。问题纹理滚动方向不对或扭曲。排查检查Vector2节点的输入。确认你想滚动的轴U或V连接了Multiply的输出另一个轴输入了0。如果两个轴都接了非零值就是对角线滚动。如果接反了方向就会相反。问题纹理滚动到边界时“卡住”或出现硬边。排查这是最经典的问题。百分之百确认Sample Texture 2D节点的Wrap Mode设置为Repeat。如果设置为Clamp当UV值超过[0,1]范围时就会被钳制在0或1导致纹理在边界拉伸而不是循环。5.2 性能与视觉优化参数调优速度ScrollSpeed速度值通常很小。对于UI上的细条纹0.1到0.5的范围比较舒适。值太大会导致滚动过快视觉上无法分辨细节显得混乱。建议根据贴图本身的纹素密度来调整。一个技巧是让纹理在1-3秒内完整循环一次观感比较自然。纹理尺寸与过滤用于滚动的贴图尺寸不宜过大通常512x512或更小足够并确保其导入设置中Max Size和Compression设置合理避免不必要的内存和带宽占用。Filter Mode选择Bilinear即可在动起来时锯齿感不明显。使用Tiling和Offset除了用节点计算ShaderGraph的UV节点本身就有Tiling和Offset输入端口。你可以将计算出的偏移量直接连接到UV节点的Offset端口效果是等价的有时节点图会更简洁。但注意Offset是直接加在UV上的而Tiling是乘数会缩放纹理。避免精度问题Time值会变得非常大。虽然UV计算通常使用浮点数精度很高但如果你将Time直接用于非常精细的纹理如4096x4096极小的UV变化可能因为浮点数精度导致纹理采样出现轻微的“跳动”或“闪烁”。一个常见的解决方案是在将Time用于UV计算前先使用Fraction节点取小数部分frac(time * speed)但这只适用于无限循环且不关心绝对位置的情况。对于我们的滚动效果直接用Time即可因为Wrap Mode为Repeat时GPU的纹理采样硬件会自动处理大数值的循环。5.3 在粒子系统中使用这个ShaderGraph材质同样可以用于粒子系统Particle System制造出流动的粒子轨迹效果。创建一个使用该ShaderGraph材质的材质球。在粒子系统的Renderer模块中将Material指定为该材质。关键点粒子系统每个粒子有自己的生命周期和UV。如果你希望所有粒子共享同一个滚动动画那么直接使用上述材质即可。如果你希望滚动动画与每个粒子的生命周期相关联则需要更复杂的设置可能涉及将粒子的Normalized Age通过Custom Vertex Streams传入Shader作为时间因子这超出了基础范围但思路是相通的。5.4 从Built-in管线迁移到URP/SRP如果你从旧的Built-in管线项目迁移到URPShaderGraph的创建路径和部分节点名称可能不同例如主节点从PBR Master变成了Lit或Unlit但核心节点逻辑完全不变。Time、UV、Multiply、Add、Sample Texture 2D这些节点在所有支持ShaderGraph的管线中都存在。迁移时只需在新管线中重新创建Graph并连接相同的逻辑即可。注意检查纹理采样器的状态设置。最后我个人最深的体会是ShaderGraph最大的魅力在于“所见即所得”的快速迭代能力。不要害怕尝试不同的节点组合。比如试着在UV偏移前加一个Sine节点让滚动速度呈波浪形变化或者用一张Gradient贴图来控制滚动纹理的颜色随时间变化。把这些基础技巧像积木一样组合起来你就能创造出无限丰富的动态视觉效果。这个简单的UV滚动项目正是打开ShaderGraph可视化创作大门的第一把钥匙。