
最近在准备一个卡通渲染风格的原型项目其中水面是最难啃的一块。翻了很多资料后发现网上关于风格化水面的内容要么只讲原理不给操作要么直接丢一个写好的 Shader 让你复制完全没讲透为什么这么连节点。本文就以《塞尔达传说风之杖》的水面为目标完整拆解用 Unity Shader Graph 复刻这套风格化水面特效的整个过程包含 URP 工程配置、双层噪声波浪、顶点起伏、卡通高光、Fresnel 边缘光和浪花白沫的完整实现思路。无论你是刚接触 Shader Graph 的新手还是已经写过不少 Shader 想尝试卡通风格的老手这篇文章都会有一定的参考价值。文中所有节点连接都给出了具体参数也把每个节点“为什么这样连”解释了一遍方便你在自己的项目里二次改造。1. 先从“风之杖的水面”说起它为什么值得复刻《塞尔达传说风之杖》是 2003 年发售的 GameCube 游戏它最标志性的视觉风格就是用卡通渲染Toon Shading / NPR即 Non-Photorealistic Rendering营造出“可游玩动画电影”的感觉。整个海拉鲁世界被大片海洋包围玩家大部分时间都在海面上航行所以水面几乎成了游戏里出现频率最高的材质。用现在的技术眼光看它其实并没有使用多么复杂的物理模型没有真实光线追踪没有超精细法线贴图也没有逐像素海浪模拟。水面主要由几层动画噪声驱动顶点起伏配合色阶化着色和卡通形状的高光让人一眼就能认出“这是塞尔达的水”。它的高明之处在于大面积波浪节奏缓慢视觉上舒服不刺眼。细碎波纹让水面有“流动感”而不是一张静止贴图。颜色分区干净利落深浅蓝过渡明确不追求真实反射。卡通高光非常简洁不会像 PBR 高光那样出现大量微细亮点。从工程角度来看这套效果非常适合用 Unity Shader Graph 实现。因为 Shader Graph 是可视化节点编辑工具你可以非常直观地把“UV 流动”“噪声叠加”“颜色阈值”等概念用节点表达出来。后面排查问题时把节点连线看一遍基本就清楚问题出在哪里。本文的最终目标是在 URP 渲染管线下用 Shader Graph 制作一个可以放到场景里直接使用的风格化水面材质包含以下能力水面会持续波动不是静态贴图。有深蓝和浅蓝两层颜色过渡。高光是卡通的有明确阈值边界。水面边缘有类似浪花泡沫的亮色效果。所有核心参数都暴露在 Material 面板上方便调参。2. 开始之前环境准备与工程配置2.1 推荐的 Unity 与渲染管线组合先说明一点Shader Graph 不是独立运行的插件它依赖具体的渲染管线。内置渲染管线Built-in RP、URPUniversal Render Pipeline通用渲染管线、HDRPHigh Definition Render Pipeline高清渲染管线都可以使用 Shader Graph但同一套节点在不同管线下的光照变量名称和输出节点会有差别。本文选择 URP原因很简单URP 是当前 Unity 新项目的主流默认管线。它对移动端和 PC 端支持都比较好风格化游戏项目里非常常用。Shader Graph 在 URP 下的文档和社区案例最多踩坑时容易搜到解决方案。版本方面没有必须锁定的要求建议使用 Unity 2021.3 LTS 或更高的 LTS 版本。URP 包可以直接在 Package Manager 中搜索 “Universal RP” 安装Unity 会自动选择与当前版本兼容的 URP 版本。如果你的项目已经创建可以打开Window Package Manager在左上角包来源里选择 “Unity Registry”搜索 “Universal RP” 后点击 Install。2.2 创建 URP 工程并调整管线配置如果你是从零开始最省事的方式是直接在 Unity Hub 里新建工程时选择 “Universal 3D” 模板。这样 Unity 会自动帮你创建好 URP 管线资源并且已经在 Graphics Settings 中配置完成。如果要在现有工程中接入 URP则需要到Project Settings Graphics中把 Scriptable Render Pipeline Settings 指定为你创建好的 URP AssetUniversalRenderPipelineAsset。这里容易漏掉的是如果场景里原有的材质使用的是内置管线的 Standard Shader切换 URP 后它们往往会变成紫红色或无法正常显示需要替换为 URP 管线下的材质。创建好工程后在 Project 窗口右键选择Create Rendering URP Assetwith Universal Renderer可以生成一套最基本的 URP 配置资源后面如果需要调抗锯齿、阴影距离、颜色分级等效果直接在这个资源上修改即可。2.3 准备水面测试场景测试水面不需要复杂的场景简单一点反而方便观察在 Hierarchy 中新建 CubeScale 设置为10, 1, 10作为水面下面的“海底”或地面。新建 PlaneScale 设置为10, 1, 10这个 Plane 就是我们的水面。把场景默认的平行光保留再创建一个 Directional Light 作为补光强度控制在 0.3 左右。需要注意的是Shader Graph 水面并不一定要求物体是 Plane只要是带有 UV 的网格都可以。水面的顶点数量会影响波浪的细腻程度后面做顶点波浪时建议把 Plane 的网格段数调高一些。如果是 Unity 自带的 Plane它的默认分段数比较少做顶点起伏后棱角会比较明显。你可以在建模软件中生成一个 20×20 分段的平面导出 FBX也可以直接在场景里叠加多个不同大小和旋转的水面片来模拟层次。3. Shader Graph 基础从创建到第一个能用的水面材质3.1 认识 Shader Graph 主界面在 Project 窗口右键选择Create Shader Graph URP Lit Shader Graph创建一个 Lit 类型 Shader Graph。名字可以命名为Water_WindWaker.shadergraph方便和普通材质区分。打开后会看到几个核心区域Main Preview右上角的实时预览窗口可以旋转场景观察材质效果。Graph Inspector右侧属性面板可以设置 Surface、Blend Mode 等渲染状态。Blackboard左侧活动列表用来创建暴露在材质面板上的参数Property。Node 搜索窗口在空白处右键可以搜索节点或者直接按空格键呼出搜索框。一开始建议先熟悉这几个面板的位置因为后面调参数会频繁用到。3.2 设置 Surface Options在 Graph Inspector 中选中最顶部的 Shader Graph 文件节点把 Surface 设置为 Transparent。这个设置决定了材质的渲染方式。Transparent 会让水面拥有半透明能力可以从侧面透出一些下方海底的颜色这是风格化水面“通透感”的重要来源。如果保持默认的 Opaque后面给 Alpha 通道赋值也不会生效。同时把 Blend Mode 选择为 Alpha 或 Premultiply。Alpha 模式比较常用适合普通的半透明水面。如果你希望水面边缘更干净可以测试 Premultiply具体差异可以在预览窗口里直观看到。3.3 构建最基础的水面颜色输出目前 Shader Graph 默认的 Fragment 输出中可以看到 Base Color、Normal、Metallic、Smoothness、Emission 等端口。我们需要做的是把 Base Color 连到一个简单的颜色节点上比如深蓝色 RGB(0.1, 0.3, 0.55)。把 Smoothness 设置为 0.6这个值影响后面卡通高光的锐利程度。把 Metallic 设置为 0风格化水面不需要金属感。Alpha 端口先连一个 Float 值 0.9等后面实现浪花白沫时再替换。这样你就得到了第一版“能看的水面”。不过它还是静止的下一节开始加入动画和噪声。4. 让水面动起来UV 流动与双层噪声波浪4.1 UV 流动是什么在 Shader 中模型的每个顶点都带有一组 UV 坐标取值范围通常是 0 到 1。纹理采样就是通过 UV 坐标去查贴图颜色。如果我们在采样噪声之前先让 UV 坐标随着时间持续改变那么采样结果就会产生一种“贴图在流动”的视觉效果。最常用的做法是把 Time 节点输出的秒数乘以一个速度系数然后加到 UV 上。Shader Graph 中通过 Tiling And Offset 节点可以同时处理“缩放”和“偏移”。其中 Offset 接收二维向量我们就用(Time * speed, Time * speed2)这样的形式驱动。从视觉原理上讲UV 流动等同于让整张噪声贴图沿着某方向移动。水面波浪往往不是单一方向直线漂移所以我们会让两层噪声分别沿着不同方向流动再叠加起来看起来就会更自然。4.2 第一层大波浪Gradient Noise在 Shader Graph 节点库中搜索Gradient Noise它的输入有两个关键端口UV采样的坐标。Scale噪声的整体缩放数值越小噪声范围越大。Gradient Noise梯度噪声的特点是变化连续、起伏圆滑没有明显的方格块状感非常适合做水面大面积波浪。相比之下Value Noise 会出现明显网格状走势做水面时要谨慎使用。连线思路如下新建 UV 节点。将 UV 连入 Tiling And Offset 节点Tiling 设为 (0.3, 0.3)Offset 仍为 (0, 0)这个 Tiling 值越小波浪幅度跨度越大。再新建一个 Tiling And Offset接收上一个 Tiling And Offset 的输出Offset 连接Time × 0.05。将结果输入 Gradient Noise 的 UVScale 设置为 0.5 左右。为了调节振幅在 Gradient Noise 后接一个 Multiply 节点乘以振幅值 0.2。这一层的效果是水面缓慢的大范围起伏视觉节奏比较慢。你可以在 Main Preview 里播放预览观察大浪是否在移动。4.3 第二层细碎波纹大波浪只能提供基础形态真正让水面生动起来的是第二层“碎波”。它的思路和第一层完全一致只是 Tiling 更大、速度更快、振幅更小新建第二个 UV 节点。通过一个 Tiling And Offset 把 UV 放大到 (2.5, 2.5)。Offset 连接Time × 0.3让碎波快速流动。采样 Gradient NoiseScale 设置为 2.0。输出乘以振幅 0.03。这一层相当于在大波浪上叠加高频细节。两层噪声最终相加可以得到一个带有多层次细节的高度值。这个高度值有两个作用叠加到模型顶点位置的 Y 分量上让网格顶点真实起伏。驱动颜色分区和浪花泡沫的阈值形成风格化颜色变化。4.4 顶点波浪让网格本身起伏如果只改变颜色不改变顶点位置水面看起来就是在平面上流动的“油漆”缺少立体感。要让水面真正凹凸不平需要在 Vertex 阶段处理 Position。在 Shader Graph 中选中 Fragment 模块下方的 Vertex 选项展开后可以看到 Position、Normal、Tangent 三个输出端口。我们需要修改的是 Position。具体做法获取 Object 空间下的 Position 节点类型选择 Object。使用 Split 节点把 Position 拆成 R、G、B 三个分量分别代表 X、Y、Z。把 R 和 B 重新组合成二维向量作为水面平面上的输入坐标。用这个坐标参与多层噪声计算也就是前面大波浪和细碎波纹的计算。把最终噪声高度值加到 Y 分量上。使用 Combine 节点重新组合 XYZ连到 Vertex Position。这里有个容易被忽略的细节顶点位置修改必须发生在 Vertex 阶段而不是 Fragment 阶段。Shader Graph 里需要在顶部切换到 Vertex 模式。如果你只在 Fragment 阶段修改位置预览窗口里水波形体不会发生任何变化。实际制作中水面网格的顶点密度决定了波浪的形状精度。Unity 默认 Plane 只有 10×10 的分段做顶点位移后会出现明显的“折纸感”。建议使用高分段平面或者在 Maya/Blender 中导出 50×50 分段的平面 FBX 使用。5. 风格化着色卡通高光与边缘光5.1 色阶渐变代替物理 PBR 颜色《风之杖》水面颜色最重要的特征是“分区明确”。它不像真实水面那样有连续的环境反射渐变而是很直接地分成了深蓝、中蓝和亮蓝几个区域。这种阶梯状颜色过渡就是典型的 Toon Ramp 思路。在 Shader Graph 中实现方式有很多最简单也最容易理解的是用Smooth Step做阈值处理使用前面计算好的噪声高度值。用 Smooth Step 节点设置最小值 0.45、最大值 0.55将高度值映射为 0 到 1。用 Lerp 节点在深蓝色 RGB(0.06, 0.25, 0.5) 和浅蓝色 RGB(0.2, 0.6, 0.85) 之间插值。把 Lerp 结果连到 Base Color。Smooth Step 的 Min 和 Max 控制的其实就是色阶过渡的软硬度。如果想让风格更硬朗可以把 Min 和 Max 拉近比如 0.48 和 0.52这样过渡边缘更锐利。如果想让水面柔和一些可以把 Min/Max 拉开比如 0.4 和 0.6。实际项目中我建议用Sample Gradient节点配合 Gradient 参数来做颜色分区。Gradient 可以定义多个颜色标点用同一个高度值采样能实现深蓝、浅蓝、白沫三层甚至四层颜色比单次 Lerp 更灵活。例如在高度 0 到 0.3 时取深蓝0.3 到 0.7 时取中蓝0.7 到 1.0 时取带青白色的亮蓝。5.2 卡通高光真实 PBR 高光往往是一个或大或小的连续高光斑而卡通渲染的高光通常是边界清晰的亮面。在 Shader Graph 中最常用的做法是使用Main Light节点获取场景主光源的方向。使用Normal Vector节点获取当前像素法线。使用Dot Product计算主光方向和法线的点积得到光照强度。使用Smooth Step设置一个较高阈值例如 0.85 到 0.95让点积结果二值化。二值化结果乘以高光颜色通常是白色或淡青色加到 Base Color 上。这里使用的是经典的卡通光照模型也就是 NdotL。它与 PBR 的 Blinn-Phong 高光不同不会依赖视角方向所以无论玩家从哪个角度看高光都稳定出现在同一个光照区域内。如果你希望高光形状更集中在某个范围可以在二值化后接一个Power节点把指数调大。例如把二值化输出提高到 8 次方以上高光范围会明显缩小边缘更硬。5.3 Fresnel 边缘光水面有一种很自然的视觉现象视线与水面夹角越接近水平我们看到的水面反射越强颜色越亮垂直向下看时则相对较暗。这个效果可以用 Fresnel 节点模拟。在 Shader Graph 中搜索并创建Fresnel Effect节点。把 Power 设置为 2.0 左右控制边缘光的衰减速度。把 Fresnel 输出乘以一个亮度系数例如 0.6。将结果叠加到 Base Color 上。Fresnel Effect 节点内部会根据法线与视线方向的夹角计算强度所以我们不需要额外连接法线和视线方向节点。它默认使用 Fragment 阶段的 Normal Vector 和 View Direction。加上 Fresnel 后当相机高度较低时水面边缘会呈现一圈明显的亮蓝色很接近《风之杖》里海平线附近水面反射阳光的感觉。这个值和上面卡通高光叠加时需要注意总亮度不要超过 1否则会丢失颜色层次。5.4 浪花白沫浪花是水面最具识别度的元素之一。它不需要所有波浪都变成白色只需要在高振幅区域出现一些“泡沫”一样的亮色块。实现方法很简单用噪声高度值做一个阈值判断。取最终噪声高度值两层噪声相加后的结果。使用Step节点设置阈值 0.75高度超过 0.75 的部分输出 1否则输出 0。Step 结果乘以白色 RGB(1, 1, 1)。将结果叠加到 Base Color同时把结果连到 Alpha 端口让泡沫区域更不透明。为了不让白沫边缘太硬可以用 Smooth Step 替代 Step例如 Min 0.7、Max 0.75这样泡沫边缘会有一个小小的过渡带。白沫过多时把阈值调高白沫过少时降低噪声振幅或调低阈值。如果你打算做成更高级版本还可以加入“水下深度”因素在水面比较浅的区域泡沫更明显深海区域则很少泡沫。这需要额外采样场景深度暂不作为本文的基础内容。6. 完整 Shader Graph 连线结构与参数清单6.1 核心连接逻辑参考把前面的节点组合起来整个 Shader Graph 的连线逻辑可以总结成以下过程UV → TilingAndOffset(波浪UV) → GradientNoise → 大波浪高度 UV → TilingAndOffset(碎波UV) → GradientNoise → 碎波高度 大波浪高度 碎波高度 最终高度 最终高度 → Vertex Position.y顶点起伏 最终高度 → SmoothStep → Lerp(深蓝, 浅蓝) → Base Color 最终高度 → SmoothStep(硬阈值) → 白色 → Base Color叠加 Alpha MainLight方向 · Normal → SmoothStep → 卡通高光 → Base Color叠加 Fresnel → 系数 → Base Color叠加实际连节点时建议从下往上搭先做顶点位移再做颜色再做高光和边缘光最后加入泡沫。这样每一步都能在预览窗口看到清晰结果定位问题也容易。6.2 参数暴露与命名建议Shader Graph 左侧 Blackboard 中创建的 Property 会自动显示在材质 Inspector 上。为了后续调整方便建议要把这些核心参数暴露出来并设置合理的默认值WaveStrengthFloat默认 0.2大波浪振幅。WaveSpeedFloat默认 0.05大波浪流动速度。WaveTilingFloat默认 0.3大波浪尺度。DetailStrengthFloat默认 0.03碎波振幅。DetailSpeedFloat默认 0.3碎波速度。DeepColorColor默认 RGB(0.06, 0.25, 0.5)深水颜色。ShallowColorColor默认 RGB(0.2, 0.6, 0.85)浅水颜色。FoamThresholdFloat默认 0.75浪花白沫阈值。SpecularPowerFloat默认 8卡通高光指数。FresnelPowerFloat默认 2边缘光强度控制。养成暴露参数的习惯很重要。如果你把所有数值都写成固定节点常量后面美术同学调整时会非常痛苦。Shader Graph 的一大优势就是可以让美术在不打开 Shader 的情况下直接调材质参数。6.3 在 Inspector 中调节材质参数创建好 Shader Graph 后在 Project 窗口中找到它右键选择Create Material然后把材质拖到场景中的 Plane 上。选中材质在 Inspector 中可以看到刚才暴露的参数。一个比较实用的调参顺序是先调WaveTiling和WaveStrength找到你想要的波浪尺度。再调DeepColor和ShallowColor确定整体色调。之后调FoamThreshold控制泡沫范围。最后调FresnelPower和SpecularPower补足高光和边缘光的强度。在调整时建议打开 Scene 视图的“材质预览”模式让灯光从侧面打来方便观察高光形状。7. 常见问题与排查思路7.1 材质全部变成紫红色这是使用 URP 后最常见的现象。紫红色通常意味着 Shader 没有成功编译或者当前 Shader 与渲染管线不兼容。可能的原因有工程还没有切换为 URP但 Shader Graph 创建的是 URP Lit Shader。Shader Graph 文件存在节点报错导致编译失败。当前 GPU 不支持 Shader 所需的特性。解决办法是先确认Project Settings Graphics是否已经指定 URP Asset再打开 Shader Graph 看是否有红色报错节点最后检查 Console 窗口的具体报错信息。如果 Shader Graph 里某些节点版本不兼容Unity 通常会提示你升级或替换。7.2 打开工程出现 D3D11 GPU 错误在很多 Unity 新版本中启动时会看到类似 “A D3D11-compatible GPU (Feature Level 11.0, Shader Model 5.0) is required to run the Unity Graphics” 的报错。这个报错并不一定说明你的显卡坏了而是当前环境比如虚拟机、远程桌面、老显卡无法提供支持 Shader Model 5.0 的 D3D11 硬件能力。常规排查思路更新显卡驱动。在Player Settings Graphics APIs中尝试删掉 D3D12只保留 D3D11或者反过来尝试。如果是虚拟机或远程桌面环境考虑切换到本机物理显卡运行。如果只是预览工程也可以把渲染 API 改为 OpenGL 或 Vulkan 试试。注意这不是项目代码的问题更多是环境兼容性问题。Shader Graph 的某些节点要求 SM5.0在低端设备上可以改用 Simple Noise 替代 Gradient Noise降低硬件要求。7.3 水面波浪没有动画如果材质已经正常显示但水面完全静止优先检查两处Time 节点是否连接到了 Tiling And Offset 的 Offset 输入。很多人会忘记把 Time 放大或变小这里如果直接连 Time速度可能会太快让人误以为没动。顶点位移部分是否在 Vertex 阶段操作。如果你只在 Fragment 阶段修改了 Position预览里颜色可能变了但网格不变。另外Shader Graph 的预览窗口依赖于编辑器运行态时间在某些情况下需要点击播放按钮或手动移动场景相机才能看到动画更新。7.4 水面过暗或过亮水面过暗通常是因为场景中没有足够的光照或者材质使用了 Lit Shader 但主光源被阴影遮挡。可以先用 Unlit Shader Graph 快速验证噪声和颜色本身是否正常再切回 Lit 查看光照效果。水面过亮则通常是因为泡沫和 Fresnel 叠加过了。颜色输出值一旦超过 1就会进入 HDR 范围产生刺眼的高光。建议把 Base Color 的最终结果控制在 0.6 到 0.9 之间给高光预留余地。7.5 浪花白沫块状感明显泡沫阈值过于生硬时白沫会像一块块白色瓷砖而不是自然的碎浪。解决思路有两个把 Step 换成 Smooth Step增加过渡宽度。在泡沫阈值判断前对噪声增加一次模糊或平滑可以用 Posterize 节点降低灰度级让高度变化更柔和。另外噪声 Scale 越大白沫分布越碎Scale 越小白沫越呈片状。想要自然海面的泡沫密度需要多试几次。8. 最佳实践与渲染性能建议8.1 节点数量与性能平衡Shader Graph 用起来方便但如果无节制地堆节点编译出来的 Shader 性能可能会很差尤其在移动平台上。对水面来说两到三层噪声通常是性价比最高的方案第一层 Gradient Noise 负责大波浪。第二层 Gradient Noise 负责碎波。如果还不够可以第三层用 Simple Noise 做细节扰动。层数越多码率越高GPU 压力越大。手机上建议不超过三层PC 上可以适当增加。如果你的目标机型是低端安卓优先考虑用 Simple Noise 替换 Gradient Noise虽然视觉效果稍微生硬一点但性能改善明显。8.2 移动端兼容性设置发布到移动端时要注意 Surface 设置为 Transparent 会带来额外的 Overdraw 和混合开销。水面面积大、半透明层级多时尤其明显。建议水面的 Render Queue 设置在 Transparent 范围内。尽量减少水面的重叠层数例如不要用多个半透明水面贴片叠在一起。在 Player Settings 中开启 Multithreaded Rendering对部分平台有帮助。如果项目有前后景分层需求尽量用深度写入技巧替代多张半透明网格。8.3 与角色交互的水面涟漪《风之杖》里林克的小船划过水面时船尾会拉起一道白色的浪痕。这个效果严格来说不是水面材质本身能独立完成的它需要与角色/小船进行交互检测。常见实现方案有UV 偏移法当小船移动时在船尾位置持续向水面 Shader 写入一组 UV 坐标让泡沫噪声在指定位置增强。世界位置法传递小船的世界坐标到 Shader 中在世界空间下计算一个距离场距离小船越近的片元泡沫阈值越低。Renderer Texture 法把实时绘制的浪花图案渲染到 Render Texture水面采样这张贴图。这三种方法各有优缺点。第一种最简单适合原型第三种最灵活但要多一个相机或绘制 pass。如果你准备做完整的航海玩法建议从方案二开始它的可控性和复用性都比较好。8.4 规范化参数管理风格化水面的参数很多如果不集中管理美术调起来会觉得“每个材质都要单独调一遍”。实际项目中可以用一个 C# 脚本把多个水面材质的相同参数统一驱动。例如下面的脚本可以放在场景中的空物体上用来按全局速度播放水面动画// 文件路径Assets/Scripts/WaterFlowController.cs using UnityEngine; public class WaterFlowController : MonoBehaviour { [Range(0f, 5f)] public float globalSpeed 1f; public Material[] waterMaterials; private readonly int waveSpeedID Shader.PropertyToID(_WaveSpeed); private readonly int detailSpeedID Shader.PropertyToID(_DetailSpeed); private void Update() { foreach (Material mat in waterMaterials) { if (mat ! null) { mat.SetFloat(waveSpeedID, globalSpeed * 0.05f); mat.SetFloat(detailSpeedID, globalSpeed * 0.3f); } } } }这样项目里所有用到水面材质的地方只需要改一个globalSpeed全局水面动画速度都会同步变化。类似的思路也可以用于颜色、泡沫阈值等参数的批量管理。9. 下一步学习路线本文完成的水面只是塞尔达风格化渲染的一个起点。如果你想把这套水面真正放到一个完整的游戏场景中还应该继续深入以下几个方面深度与水面交互读取场景深度让水面在水下地形较浅的地方颜色更浅甚至可以模拟角色落水时溅起水花。风格化天空与光照《风之杖》的水面好看很大程度依赖于它那套明亮干净的卡通天空盒和高光环境。URP 中的 Ambient 和 Reflection Probe 对水面影响明显值得单独调优。手写 Shader 对比学习Shader Graph 对理解“效果由哪些节点组成”很有帮助但想深入优化性能、理解底层逻辑建议再用 HLSL 手写一遍简化版水面 Shader。例如下面这段代码展示的是高度噪声的核心思路// 文件路径Assets/Shaders/WaterNoise.hlsl示意片段供理解思路 #ifndef WATER_NOISE_INCLUDED #define WATER_NOISE_INCLUDED float hash21(float2 p) { p frac(p * float2(123.34, 456.21)); p dot(p, p 45.32); return frac(p.x * p.y); } float noise2D(float2 uv) { float2 i floor(uv); float2 f frac(uv); float2 u f * f * (3.0 - 2.0 * f); float a hash21(i); float b hash21(i float2(1.0, 0.0)); float c hash21(i float2(0.0, 1.0)); float d hash21(i float2(1.0, 1.0)); return lerp(lerp(a, b, u.x), lerp(c, d, u.x), u.y); } void WaterHeight_float(float2 uv, float time, out float height) { float h1 noise2D(uv * 0.5 float2(time * 0.05, 0.0)); float h2 noise2D(uv * 2.5 float2(0.0, time * 0.3)); height (h1 h2 * 0.3) * 0.5; } #endif这段 HLSL 的函数可以放到 Shader Graph 的 Custom Function 节点中使用也可以直接在手写 Shader 中调用。它的价值在于让你看清噪声和时间的组合方式。下一步的实践任务可以这样安排在本文水面基础上添加一个简易的小船模型实现“船尾拖浪花”的效果然后尝试把颜色从两层改成四层 Gradient 色带观察画面风格变化最后把水面放到昼夜循环场景中配合 URP 的灯光组件验证不同光照下的表现。风格化渲染最有趣的地方就在于“少即是多”——不需要追求完全真实的物理计算而是用几个关键节点组合出高识别度的视觉印象。塞尔达风之杖的水面正是这种思路的典型范例希望这篇文章能帮你打开更多自定义风格的大门。如果本文对你有帮助记得收藏备用后面我会继续更新角色交互涟漪和风格化天空相关的内容。