1. 项目概述五分钟的动态水面魔法最近在做一个独立游戏项目里面有个小池塘美术同学希望水面能有点动态效果比如随着游戏内时间或玩家靠近产生涟漪。如果让美术去手绘序列帧或者用粒子系统模拟不仅资源开销大调整起来也麻烦。这时候ShaderGraph的优势就体现出来了。我直接用Unity的ShaderGraph配合最基础的几个Input节点花了不到五分钟就搓出了一个基础但效果不错的动态水面材质原型。这听起来有点夸张但当你理解了ShaderGraph处理这类问题的逻辑后真的就是这么高效。这个材质的核心思路就是利用时间Time和空间坐标UV作为输入驱动一些简单的数学函数比如正弦Sine来模拟水波的起伏和流动感完全不需要写一行Shader代码。这个实战案例非常适合刚接触ShaderGraph的开发者或者想快速为场景添加一些动态视觉细节的同学。你不需要是图形学专家只要跟着节点连一连就能直观地看到效果变化。最终我会分享完整的节点连接图你可以直接“抄作业”应用到自己的项目中或者以此为起点衍生出更复杂的水面效果。接下来我们就一步步拆解这个“五分钟魔法”背后的每一个环节。2. 核心思路与节点选型解析2.1 为什么选择ShaderGraph和Input节点首先得明确我们想要什么一个会动的水面。在传统Shader编写中这意味着我们需要在顶点着色器或片元着色器中手动编写算法来根据时间和位置修改顶点位置或颜色。这对新手门槛很高。ShaderGraph将这个过程可视化我们把“时间”、“纹理坐标”这些输入数据通过一个个代表数学运算或纹理采样的节点连接起来最终输出到材质的最终颜色和表面偏移上整个过程像搭积木一样直观。而“Input”节点在ShaderGraph里特指那些从外部如引擎、材质实例、顶点数据获取信息的节点。对于动态水面我们最依赖的几个Input节点是Time节点这是动态效果的引擎。它提供一个随着游戏时间不断递增的值我们用它来驱动波纹的移动。UV节点代表纹理坐标是确定波纹在模型表面哪个位置出现的“地图”。通过对UV进行各种变换如平铺、扭曲我们可以控制波纹的密度、方向和形态。Position节点可选但推荐获取模型空间或世界空间的位置坐标。用它来代替UV可以让波纹效果基于物体的实际位置和大小进行计算效果更稳定尤其适用于不规则水面模型。选择这些节点的理由很简单动态效果的本质是“随时间变化”Time的“空间图案”UV/Position。用它们作为起点逻辑最直接也最容易控制。2.2 水面效果的核心算法正弦波的叠加我们如何用数学来模拟水波最经典且高效的方法就是使用正弦函数Sine。想象一下正弦波的图像它那平滑的、周期性的起伏和水波的波形非常相似。单个正弦波可以模拟一种规律、单调的波浪。但真实的水面是复杂的由不同频率、不同振幅、不同方向的小波纹叠加而成。因此我们的核心策略是使用多个经过不同参数调整的Sine节点将它们的结果叠加Add起来。这样就能打破单一波形的机械感创造出更自然、更丰富的动态细节。具体到节点操作流程是这样的Time和UV首先进入一个Multiply节点相乘这决定了波纹的移动速度。这个结果再输入到Sine节点生成基础的波形数据。我们通常会创建两到三个这样的波形链赋予它们不同的频率通过乘以不同的系数和移动方向通过使用UV的不同通道如U和V最后将它们加在一起。注意Sine节点的输出范围是[-1, 1]。直接用于偏移顶点或作为颜色可能会产生不理想的效果。通常我们会通过Remap节点或MultiplyAdd节点组合将其映射到更合适的范围比如[0, 1]或[-0.1, 0.1]。2.3 材质表现的关键输出法线扰动与高度偏移有了计算出的波形数据我们如何让它真正影响水面材质的外观呢主要通过两个输出通道顶点偏移将叠加后的波形数据连接到Master Stack在URP中或PBR Master节点的Vertex Position的“Offset”输入上。这会在顶点着色器阶段沿着模型法线方向或自定义方向移动顶点位置从而让水面模型在视觉上产生真实的起伏几何变形。这是实现水面波动的关键。法线扰动这是增强水面质感如波光粼粼更常用且性能更好的方法。我们不直接移动顶点而是用波形数据去扰动法线贴图。通常我们会将波形数据作为高度图Height通过一个Normal From Height节点或者Normal Reconstruct Z等节点网络生成一张动态的法线贴图然后将其连接到主节点的Normal输入。这样光照计算时就会根据动态法线产生高光移动的效果模拟水面的光泽变化。在实际项目中为了性能和效果的平衡我通常会以法线扰动为主辅以轻微的高度偏移。法线扰动负责大部分的高光细节和颜色变化而轻微的高度偏移则用于表现近处或较大波浪的轮廓变化两者结合效果最佳。3. 完整节点图拆解与分步实现下面我将基于Universal RP (URP) 的ShaderGraph详细构建这个五分钟水面的每一个步骤。你可以新建一个Unlit Graph或Lit Graph开始这里以Lit Graph为例因为它能更好地展示光照和法线效果。3.1 第一步搭建基础波形网络首先我们创建第一个也是最基础的波形。创建节点在空白处右键依次创建Time节点、UV节点、两个Multiply节点、一个Sine节点和一个Remap节点。连接与设置将Time节点的Time输出口连接到第一个Multiply节点的A输入端。将UV节点的Out输出口连接到第一个Multiply节点的B输入端。这样Time * UV的结果使得波纹的相位会随着时间和空间位置变化。将这个Multiply节点的输出连接到Sine节点的In输入端。此时Sine节点会输出一个基于时间和UV的正弦波。Sine节点的输出范围是[-1,1]。我们希望将其映射到一个更适合控制偏移或法线的范围比如[0, 1]。将Sine节点的输出连接到Remap节点的In。设置Remap节点的In Min为 -1In Max为 1Out Min为 0Out Max为 1。现在我们得到了一个在0到1之间起伏的波形信号。我们将其命名为Wave1。这个Wave1已经可以产生动态效果了但比较单一。我们复制这个网络选中相关节点CtrlD来创建第二个波形。3.2 第二步叠加多层波形以增加细节复制并修改选中Wave1的整个网络从第一个Multiply到Remap复制一份。我们通过修改参数来让第二个波形和第一个有所不同。制造差异在第二个波形网络的第一个Multiply节点前我们可以插入一个Split节点来处理UV。将UV连接到Split然后分别使用Split输出的R(U) 和G(V) 通道。例如让第一个波形主要用U通道乘以时间第二个波形主要用V通道乘以时间这样两个波的运动方向就垂直了叠加起来更复杂。更简单的方法是直接修改乘数。在第二个波形的Time * UV的Multiply节点后再串联一个新的Multiply节点并设置其B端为一个大于1的常数如1.5。这相当于Time * UV * 1.5增大了频率让第二个波的波纹更密集。也可以调整Time的输入例如将Time节点输出乘以一个小于1的系数如0.7再使用让第二个波移动得慢一些。叠加波形创建一个Add节点。将Wave1的Remap输出和Wave2的Remap输出一起连接到Add节点的输入。Add节点的输出就是两个波形的叠加结果。你可以继续添加第三个波形原则是让它们的频率、速度有所区别。控制整体强度叠加后的数值可能会超出预期范围。我们用一个最终的Multiply节点来控制整体强度。将Add节点的输出连接到一个新的Multiply节点的A端B端设置一个较小的系数比如0.1。这个系数决定了水面波动的剧烈程度。我们把这个最终输出命名为Combined_Wave。3.3 第三步将波形数据应用于材质现在我们有了Combined_Wave这个动态数据需要让它影响材质。方案A应用于顶点偏移实现几何起伏创建一个Combine节点将其模式设置为Vector3。将Combined_Wave数据连接到Combine节点的Y(Green) 通道。这是因为在Unity中Y轴通常是向上的。我们让波形数据只影响高度方向。将Combine节点的输出连接到PBR Master节点的Vertex Position端口下的Offset输入。这时将材质球赋给一个平面Plane你应该就能看到平面随着时间上下起伏了。调整之前Multiply节点的强度系数可以控制起伏的高度。方案B应用于法线扰动实现波光细节——更推荐用Combined_Wave数据来模拟高度图。创建一个Normal From Height节点可能需要通过Create Node搜索添加。将Combined_Wave连接到Normal From Height节点的Height输入。这个节点会根据高度差计算出对应的法线向量。将Normal From Height节点的Normal输出连接到PBR Master节点的Normal输入。同时为了水面有基础的反光确保PBR Master节点的Smoothness调高一些比如0.8Metallic可以设为0或一个很低的值。现在给平面打上灯光你就能看到水面上有动态移动的高光斑点了质感立刻提升。方案C结合使用你可以创建一个Lerp线性插值节点用另一个参数如一个Slider节点来控制顶点偏移和法线扰动的混合权重。或者用Combined_Wave同时驱动一个很微弱的顶点偏移和一个较强的法线扰动。3.4 第四步优化与增强效果基础效果有了但可能看起来还有点“假”。我们可以通过一些技巧来优化添加颜色水不是灰色的。创建一个Color节点选择一个深蓝色或湖绿色连接到PBR Master的Base Color。为了模拟水深处颜色更暗可以将Combined_Wave通过一个Power节点处理让对比更强然后与基础颜色相乘后再输出。模拟边缘泡沫水波在“波峰”处往往颜色更浅泡沫。我们可以利用Combined_Wave的值。使用一个Step或SmoothStep节点设定一个阈值如0.7当波形值高于这个阈值时输出白色否则输出透明或水的颜色。将这个结果与基础颜色混合Add或Lerp就能在波峰处看到白色的泡沫线。使用纹理增加细节单一的数学波纹缺乏细节。我们可以引入一张噪波纹理Noise Texture。将UV加上Combined_Wave对UV进行小幅偏移然后用这个偏移后的UV去采样一张Tileable的噪波图将采样结果与Combined_Wave再进行一次混合Multiply或Add可以打破数学波形的规律性让水面细节更自然。控制参数暴露将关键的系数如波速 (Time乘数)、波强度 (Combined_Wave乘数)、泡沫阈值等通过Float类型的Property节点创建并暴露给材质面板。这样美术或设计师可以在不打开ShaderGraph的情况下动态调整水面效果适配不同的场景平静的池塘 vs 汹涌的大海。4. 完整节点图参考与参数设置由于无法直接嵌入图片我将用文字描述最终节点图的核心链路和关键参数值你可以据此重建[输入侧] Time Node - (Output: Time) UV Node - (Output: Out) [波形生成链A] Multiply Node A1: - A: Time Node (Time) - B: 0.5 (Constant) - Output - Multiply Node A2 (B) Multiply Node A2: - A: UV Node (Out) - Split (R通道) - B: Multiply Node A1 (Output) - Output - Sine Node A (In) Sine Node A - Remap Node A: - In: Sine A (Out) - In Min/Max: -1 / 1 - Out Min/Max: 0 / 1 - Output: Wave_A [波形生成链B] Multiply Node B1: - A: Time Node (Time) - B: 0.3 (Constant制造速度差) - Output - Multiply Node B2 (B) Multiply Node B2: - A: UV Node (Out) - Split (G通道改变方向) - B: Multiply Node B1 (Output) - Output - Sine Node B (In) Sine Node B - Remap Node B: - In: Sine B (Out) - In Min/Max: -1 / 1 - Out Min/Max: 0 / 1 - Output: Wave_B [波形叠加与处理] Add Node: - A: Wave_A - B: Wave_B - Output - Multiply Node (Final Intensity) Multiply Node (Final Intensity): - A: Add Node (Output) - B: 0.15 (Property命名为“Wave Strength”) - Output: Combined_Wave [法线生成] Normal From Height Node: - Height: Combined_Wave - Output - PBR Master Node (Normal Input) [颜色与泡沫模拟] Color Node (Base): - Color: 深蓝色 (Hex: #0A2E5C) Power Node: - In: Combined_Wave - Power: 2 (增强对比) - Output - Multiply Node (Color Mod) Multiply Node (Color Mod): - A: Color Node (Base) (Output) - B: Power Node (Output) - Output - Lerp Node (A) Color Node (Foam): - Color: 浅蓝白色 (Hex: #B0E0E6) Step Node: - Edge: 0.65 (Property命名为“Foam Threshold”) - In: Combined_Wave - Output - Lerp Node (T) Lerp Node: - A: Multiply Node (Color Mod) (Output) - B: Color Node (Foam) (Output) - T: Step Node (Output) - Output - PBR Master Node (Base Color Input) [主节点] PBR Master Node: - Base Color: Lerp Node (Output) - Normal: Normal From Height Node (Output) - Metallic: 0.1 - Smoothness: 0.85 - (可选) Vertex Position Offset: 将 Combined_Wave 连接到 Combine (Vector3) 的Y再连接到Offset强度系数约0.02。这个结构实现了两个不同速度、不同方向的基础波叠加生成动态高度图用于法线扰动同时根据波高模拟了基础的深浅色和边缘泡沫效果。所有关键参数都已暴露便于调节。5. 常见问题、优化技巧与性能考量在实际应用这个Shader时你可能会遇到一些问题下面是一些排查思路和进阶技巧。5.1 效果不动态或没变化检查Time节点确保使用的是Time节点而不是Delta Time。Time是累计时间Delta Time是上一帧时间前者才能产生持续变化。检查连接确保Time节点的输出确实连接到了波形计算链的乘法器上。有时候连线可能意外断开。检查材质球确保场景中使用了你创建的ShaderGraph材质并且材质球已赋给对应的模型。5.2 波纹有明显的重复图案Tiling这是使用UV和正弦波时最常见的问题因为UV在平面上是重复的正弦波也是周期函数。解决方案1使用世界空间Position用Position节点设置为世界空间World代替UV节点。这样波纹是基于物体在世界中的实际位置计算的在移动相机或物体时重复感会大大降低。但要注意模型缩放的影响。解决方案2UV扭曲在UV进入正弦计算前先用一个复杂的噪波纹理对UV进行轻微扰动。创建一个Tiling And Offset节点用Combined_Wave去影响Offset再用这个结果作为采样噪波纹理的UV然后用噪波纹理的值去扭曲原始UV。这能有效打破规律性。解决方案3多层不同频率波叠加这是最根本的方法。如我们之前所做叠加多个频率、速度、方向不同的波。层数越多通常3-4层图案就越不可预测重复感越弱。但要注意性能开销。5.3 水面边缘穿帮或效果突兀当水面模型有边界比如一个圆形水池时波纹可能会在边界处突然截断很不自然。解决方案使用顶点颜色或遮罩纹理让美术在模型边缘靠近岸边的部分绘制顶点颜色如Alpha通道或者在Shader中使用一张遮罩纹理。在ShaderGraph中采样这个遮罩信息然后用它来逐步减弱LerpCombined_Wave的强度直到边缘降为0。这样波纹会自然衰减消失。5.4 性能优化考量ShaderGraph虽然方便但节点过多或计算复杂也会影响性能尤其在移动平台。精简节点合并可以合并的数学运算。例如连续的Multiply和Add可以合并计算。减少纹理采样噪波纹理采样很耗。如果用了多张噪波图考虑能否用程序化噪波如Simple Noise节点替代或者将多张图合并到一张图的RGBA通道中。降低波形层数在移动设备上先尝试2层波形叠加看效果是否可接受。法线扰动对视觉效果的贡献远大于顶点偏移可以考虑只保留法线扰动关闭顶点偏移。使用Shader LOD为ShaderGraph创建不同的LOD级别在远处使用简化版本比如只保留一层波关闭泡沫计算。5.5 从“玩具”到“产品”的进阶思路这个五分钟Shader是一个完美的起点。要让它更真实可以考虑引入菲涅尔效应水在视线与水面夹角较小看远处水面时反射更强夹角较大看脚下水面时透射更强。可以用Fresnel Effect节点轻松实现用于混合反射颜色和基础色/深度色。反射使用Scene Color节点或反射探针为水面添加环境反射。对于开阔水域屏幕空间反射SSR或平面反射Planar Reflection是更佳选择但性能开销较大。折射模拟水下物体的扭曲。可以用Combined_Wave对屏幕空间UV进行轻微偏移然后采样Scene Color来实现简单的折射效果。流动图用一张RGBA纹理流动图来指导波纹的方向和强度其中R通道代表X方向流速G通道代表Y方向流速B/A通道可存储其他信息。这能实现非常定向和艺术可控的水流效果如河流、漩涡。这个基于Input节点的动态水面材质其核心价值在于展示了ShaderGraph快速原型化的能力。它可能不是最物理真实的水但它用极低的成本和极高的效率为游戏场景注入了生命力。当你掌握了这种“输入驱动数学数学驱动外观”的思维模式后就能举一反三创造出风中的草地、燃烧的火焰、飘动的旗帜等无数动态效果。
ShaderGraph五分钟实现动态水面:UV与Time节点驱动正弦波叠加
1. 项目概述五分钟的动态水面魔法最近在做一个独立游戏项目里面有个小池塘美术同学希望水面能有点动态效果比如随着游戏内时间或玩家靠近产生涟漪。如果让美术去手绘序列帧或者用粒子系统模拟不仅资源开销大调整起来也麻烦。这时候ShaderGraph的优势就体现出来了。我直接用Unity的ShaderGraph配合最基础的几个Input节点花了不到五分钟就搓出了一个基础但效果不错的动态水面材质原型。这听起来有点夸张但当你理解了ShaderGraph处理这类问题的逻辑后真的就是这么高效。这个材质的核心思路就是利用时间Time和空间坐标UV作为输入驱动一些简单的数学函数比如正弦Sine来模拟水波的起伏和流动感完全不需要写一行Shader代码。这个实战案例非常适合刚接触ShaderGraph的开发者或者想快速为场景添加一些动态视觉细节的同学。你不需要是图形学专家只要跟着节点连一连就能直观地看到效果变化。最终我会分享完整的节点连接图你可以直接“抄作业”应用到自己的项目中或者以此为起点衍生出更复杂的水面效果。接下来我们就一步步拆解这个“五分钟魔法”背后的每一个环节。2. 核心思路与节点选型解析2.1 为什么选择ShaderGraph和Input节点首先得明确我们想要什么一个会动的水面。在传统Shader编写中这意味着我们需要在顶点着色器或片元着色器中手动编写算法来根据时间和位置修改顶点位置或颜色。这对新手门槛很高。ShaderGraph将这个过程可视化我们把“时间”、“纹理坐标”这些输入数据通过一个个代表数学运算或纹理采样的节点连接起来最终输出到材质的最终颜色和表面偏移上整个过程像搭积木一样直观。而“Input”节点在ShaderGraph里特指那些从外部如引擎、材质实例、顶点数据获取信息的节点。对于动态水面我们最依赖的几个Input节点是Time节点这是动态效果的引擎。它提供一个随着游戏时间不断递增的值我们用它来驱动波纹的移动。UV节点代表纹理坐标是确定波纹在模型表面哪个位置出现的“地图”。通过对UV进行各种变换如平铺、扭曲我们可以控制波纹的密度、方向和形态。Position节点可选但推荐获取模型空间或世界空间的位置坐标。用它来代替UV可以让波纹效果基于物体的实际位置和大小进行计算效果更稳定尤其适用于不规则水面模型。选择这些节点的理由很简单动态效果的本质是“随时间变化”Time的“空间图案”UV/Position。用它们作为起点逻辑最直接也最容易控制。2.2 水面效果的核心算法正弦波的叠加我们如何用数学来模拟水波最经典且高效的方法就是使用正弦函数Sine。想象一下正弦波的图像它那平滑的、周期性的起伏和水波的波形非常相似。单个正弦波可以模拟一种规律、单调的波浪。但真实的水面是复杂的由不同频率、不同振幅、不同方向的小波纹叠加而成。因此我们的核心策略是使用多个经过不同参数调整的Sine节点将它们的结果叠加Add起来。这样就能打破单一波形的机械感创造出更自然、更丰富的动态细节。具体到节点操作流程是这样的Time和UV首先进入一个Multiply节点相乘这决定了波纹的移动速度。这个结果再输入到Sine节点生成基础的波形数据。我们通常会创建两到三个这样的波形链赋予它们不同的频率通过乘以不同的系数和移动方向通过使用UV的不同通道如U和V最后将它们加在一起。注意Sine节点的输出范围是[-1, 1]。直接用于偏移顶点或作为颜色可能会产生不理想的效果。通常我们会通过Remap节点或MultiplyAdd节点组合将其映射到更合适的范围比如[0, 1]或[-0.1, 0.1]。2.3 材质表现的关键输出法线扰动与高度偏移有了计算出的波形数据我们如何让它真正影响水面材质的外观呢主要通过两个输出通道顶点偏移将叠加后的波形数据连接到Master Stack在URP中或PBR Master节点的Vertex Position的“Offset”输入上。这会在顶点着色器阶段沿着模型法线方向或自定义方向移动顶点位置从而让水面模型在视觉上产生真实的起伏几何变形。这是实现水面波动的关键。法线扰动这是增强水面质感如波光粼粼更常用且性能更好的方法。我们不直接移动顶点而是用波形数据去扰动法线贴图。通常我们会将波形数据作为高度图Height通过一个Normal From Height节点或者Normal Reconstruct Z等节点网络生成一张动态的法线贴图然后将其连接到主节点的Normal输入。这样光照计算时就会根据动态法线产生高光移动的效果模拟水面的光泽变化。在实际项目中为了性能和效果的平衡我通常会以法线扰动为主辅以轻微的高度偏移。法线扰动负责大部分的高光细节和颜色变化而轻微的高度偏移则用于表现近处或较大波浪的轮廓变化两者结合效果最佳。3. 完整节点图拆解与分步实现下面我将基于Universal RP (URP) 的ShaderGraph详细构建这个五分钟水面的每一个步骤。你可以新建一个Unlit Graph或Lit Graph开始这里以Lit Graph为例因为它能更好地展示光照和法线效果。3.1 第一步搭建基础波形网络首先我们创建第一个也是最基础的波形。创建节点在空白处右键依次创建Time节点、UV节点、两个Multiply节点、一个Sine节点和一个Remap节点。连接与设置将Time节点的Time输出口连接到第一个Multiply节点的A输入端。将UV节点的Out输出口连接到第一个Multiply节点的B输入端。这样Time * UV的结果使得波纹的相位会随着时间和空间位置变化。将这个Multiply节点的输出连接到Sine节点的In输入端。此时Sine节点会输出一个基于时间和UV的正弦波。Sine节点的输出范围是[-1,1]。我们希望将其映射到一个更适合控制偏移或法线的范围比如[0, 1]。将Sine节点的输出连接到Remap节点的In。设置Remap节点的In Min为 -1In Max为 1Out Min为 0Out Max为 1。现在我们得到了一个在0到1之间起伏的波形信号。我们将其命名为Wave1。这个Wave1已经可以产生动态效果了但比较单一。我们复制这个网络选中相关节点CtrlD来创建第二个波形。3.2 第二步叠加多层波形以增加细节复制并修改选中Wave1的整个网络从第一个Multiply到Remap复制一份。我们通过修改参数来让第二个波形和第一个有所不同。制造差异在第二个波形网络的第一个Multiply节点前我们可以插入一个Split节点来处理UV。将UV连接到Split然后分别使用Split输出的R(U) 和G(V) 通道。例如让第一个波形主要用U通道乘以时间第二个波形主要用V通道乘以时间这样两个波的运动方向就垂直了叠加起来更复杂。更简单的方法是直接修改乘数。在第二个波形的Time * UV的Multiply节点后再串联一个新的Multiply节点并设置其B端为一个大于1的常数如1.5。这相当于Time * UV * 1.5增大了频率让第二个波的波纹更密集。也可以调整Time的输入例如将Time节点输出乘以一个小于1的系数如0.7再使用让第二个波移动得慢一些。叠加波形创建一个Add节点。将Wave1的Remap输出和Wave2的Remap输出一起连接到Add节点的输入。Add节点的输出就是两个波形的叠加结果。你可以继续添加第三个波形原则是让它们的频率、速度有所区别。控制整体强度叠加后的数值可能会超出预期范围。我们用一个最终的Multiply节点来控制整体强度。将Add节点的输出连接到一个新的Multiply节点的A端B端设置一个较小的系数比如0.1。这个系数决定了水面波动的剧烈程度。我们把这个最终输出命名为Combined_Wave。3.3 第三步将波形数据应用于材质现在我们有了Combined_Wave这个动态数据需要让它影响材质。方案A应用于顶点偏移实现几何起伏创建一个Combine节点将其模式设置为Vector3。将Combined_Wave数据连接到Combine节点的Y(Green) 通道。这是因为在Unity中Y轴通常是向上的。我们让波形数据只影响高度方向。将Combine节点的输出连接到PBR Master节点的Vertex Position端口下的Offset输入。这时将材质球赋给一个平面Plane你应该就能看到平面随着时间上下起伏了。调整之前Multiply节点的强度系数可以控制起伏的高度。方案B应用于法线扰动实现波光细节——更推荐用Combined_Wave数据来模拟高度图。创建一个Normal From Height节点可能需要通过Create Node搜索添加。将Combined_Wave连接到Normal From Height节点的Height输入。这个节点会根据高度差计算出对应的法线向量。将Normal From Height节点的Normal输出连接到PBR Master节点的Normal输入。同时为了水面有基础的反光确保PBR Master节点的Smoothness调高一些比如0.8Metallic可以设为0或一个很低的值。现在给平面打上灯光你就能看到水面上有动态移动的高光斑点了质感立刻提升。方案C结合使用你可以创建一个Lerp线性插值节点用另一个参数如一个Slider节点来控制顶点偏移和法线扰动的混合权重。或者用Combined_Wave同时驱动一个很微弱的顶点偏移和一个较强的法线扰动。3.4 第四步优化与增强效果基础效果有了但可能看起来还有点“假”。我们可以通过一些技巧来优化添加颜色水不是灰色的。创建一个Color节点选择一个深蓝色或湖绿色连接到PBR Master的Base Color。为了模拟水深处颜色更暗可以将Combined_Wave通过一个Power节点处理让对比更强然后与基础颜色相乘后再输出。模拟边缘泡沫水波在“波峰”处往往颜色更浅泡沫。我们可以利用Combined_Wave的值。使用一个Step或SmoothStep节点设定一个阈值如0.7当波形值高于这个阈值时输出白色否则输出透明或水的颜色。将这个结果与基础颜色混合Add或Lerp就能在波峰处看到白色的泡沫线。使用纹理增加细节单一的数学波纹缺乏细节。我们可以引入一张噪波纹理Noise Texture。将UV加上Combined_Wave对UV进行小幅偏移然后用这个偏移后的UV去采样一张Tileable的噪波图将采样结果与Combined_Wave再进行一次混合Multiply或Add可以打破数学波形的规律性让水面细节更自然。控制参数暴露将关键的系数如波速 (Time乘数)、波强度 (Combined_Wave乘数)、泡沫阈值等通过Float类型的Property节点创建并暴露给材质面板。这样美术或设计师可以在不打开ShaderGraph的情况下动态调整水面效果适配不同的场景平静的池塘 vs 汹涌的大海。4. 完整节点图参考与参数设置由于无法直接嵌入图片我将用文字描述最终节点图的核心链路和关键参数值你可以据此重建[输入侧] Time Node - (Output: Time) UV Node - (Output: Out) [波形生成链A] Multiply Node A1: - A: Time Node (Time) - B: 0.5 (Constant) - Output - Multiply Node A2 (B) Multiply Node A2: - A: UV Node (Out) - Split (R通道) - B: Multiply Node A1 (Output) - Output - Sine Node A (In) Sine Node A - Remap Node A: - In: Sine A (Out) - In Min/Max: -1 / 1 - Out Min/Max: 0 / 1 - Output: Wave_A [波形生成链B] Multiply Node B1: - A: Time Node (Time) - B: 0.3 (Constant制造速度差) - Output - Multiply Node B2 (B) Multiply Node B2: - A: UV Node (Out) - Split (G通道改变方向) - B: Multiply Node B1 (Output) - Output - Sine Node B (In) Sine Node B - Remap Node B: - In: Sine B (Out) - In Min/Max: -1 / 1 - Out Min/Max: 0 / 1 - Output: Wave_B [波形叠加与处理] Add Node: - A: Wave_A - B: Wave_B - Output - Multiply Node (Final Intensity) Multiply Node (Final Intensity): - A: Add Node (Output) - B: 0.15 (Property命名为“Wave Strength”) - Output: Combined_Wave [法线生成] Normal From Height Node: - Height: Combined_Wave - Output - PBR Master Node (Normal Input) [颜色与泡沫模拟] Color Node (Base): - Color: 深蓝色 (Hex: #0A2E5C) Power Node: - In: Combined_Wave - Power: 2 (增强对比) - Output - Multiply Node (Color Mod) Multiply Node (Color Mod): - A: Color Node (Base) (Output) - B: Power Node (Output) - Output - Lerp Node (A) Color Node (Foam): - Color: 浅蓝白色 (Hex: #B0E0E6) Step Node: - Edge: 0.65 (Property命名为“Foam Threshold”) - In: Combined_Wave - Output - Lerp Node (T) Lerp Node: - A: Multiply Node (Color Mod) (Output) - B: Color Node (Foam) (Output) - T: Step Node (Output) - Output - PBR Master Node (Base Color Input) [主节点] PBR Master Node: - Base Color: Lerp Node (Output) - Normal: Normal From Height Node (Output) - Metallic: 0.1 - Smoothness: 0.85 - (可选) Vertex Position Offset: 将 Combined_Wave 连接到 Combine (Vector3) 的Y再连接到Offset强度系数约0.02。这个结构实现了两个不同速度、不同方向的基础波叠加生成动态高度图用于法线扰动同时根据波高模拟了基础的深浅色和边缘泡沫效果。所有关键参数都已暴露便于调节。5. 常见问题、优化技巧与性能考量在实际应用这个Shader时你可能会遇到一些问题下面是一些排查思路和进阶技巧。5.1 效果不动态或没变化检查Time节点确保使用的是Time节点而不是Delta Time。Time是累计时间Delta Time是上一帧时间前者才能产生持续变化。检查连接确保Time节点的输出确实连接到了波形计算链的乘法器上。有时候连线可能意外断开。检查材质球确保场景中使用了你创建的ShaderGraph材质并且材质球已赋给对应的模型。5.2 波纹有明显的重复图案Tiling这是使用UV和正弦波时最常见的问题因为UV在平面上是重复的正弦波也是周期函数。解决方案1使用世界空间Position用Position节点设置为世界空间World代替UV节点。这样波纹是基于物体在世界中的实际位置计算的在移动相机或物体时重复感会大大降低。但要注意模型缩放的影响。解决方案2UV扭曲在UV进入正弦计算前先用一个复杂的噪波纹理对UV进行轻微扰动。创建一个Tiling And Offset节点用Combined_Wave去影响Offset再用这个结果作为采样噪波纹理的UV然后用噪波纹理的值去扭曲原始UV。这能有效打破规律性。解决方案3多层不同频率波叠加这是最根本的方法。如我们之前所做叠加多个频率、速度、方向不同的波。层数越多通常3-4层图案就越不可预测重复感越弱。但要注意性能开销。5.3 水面边缘穿帮或效果突兀当水面模型有边界比如一个圆形水池时波纹可能会在边界处突然截断很不自然。解决方案使用顶点颜色或遮罩纹理让美术在模型边缘靠近岸边的部分绘制顶点颜色如Alpha通道或者在Shader中使用一张遮罩纹理。在ShaderGraph中采样这个遮罩信息然后用它来逐步减弱LerpCombined_Wave的强度直到边缘降为0。这样波纹会自然衰减消失。5.4 性能优化考量ShaderGraph虽然方便但节点过多或计算复杂也会影响性能尤其在移动平台。精简节点合并可以合并的数学运算。例如连续的Multiply和Add可以合并计算。减少纹理采样噪波纹理采样很耗。如果用了多张噪波图考虑能否用程序化噪波如Simple Noise节点替代或者将多张图合并到一张图的RGBA通道中。降低波形层数在移动设备上先尝试2层波形叠加看效果是否可接受。法线扰动对视觉效果的贡献远大于顶点偏移可以考虑只保留法线扰动关闭顶点偏移。使用Shader LOD为ShaderGraph创建不同的LOD级别在远处使用简化版本比如只保留一层波关闭泡沫计算。5.5 从“玩具”到“产品”的进阶思路这个五分钟Shader是一个完美的起点。要让它更真实可以考虑引入菲涅尔效应水在视线与水面夹角较小看远处水面时反射更强夹角较大看脚下水面时透射更强。可以用Fresnel Effect节点轻松实现用于混合反射颜色和基础色/深度色。反射使用Scene Color节点或反射探针为水面添加环境反射。对于开阔水域屏幕空间反射SSR或平面反射Planar Reflection是更佳选择但性能开销较大。折射模拟水下物体的扭曲。可以用Combined_Wave对屏幕空间UV进行轻微偏移然后采样Scene Color来实现简单的折射效果。流动图用一张RGBA纹理流动图来指导波纹的方向和强度其中R通道代表X方向流速G通道代表Y方向流速B/A通道可存储其他信息。这能实现非常定向和艺术可控的水流效果如河流、漩涡。这个基于Input节点的动态水面材质其核心价值在于展示了ShaderGraph快速原型化的能力。它可能不是最物理真实的水但它用极低的成本和极高的效率为游戏场景注入了生命力。当你掌握了这种“输入驱动数学数学驱动外观”的思维模式后就能举一反三创造出风中的草地、燃烧的火焰、飘动的旗帜等无数动态效果。