
各位做地编和TA的朋友咱们今天聊一个特别实在的话题在UE4和UE5里怎么自己动手搓一个简单但够用的水材质。这个标题叫《UE4_UE5 自定义简单水材质UE材质图表大全1》听起来像是教程系列的第一篇实际上也确实值得作为系列开篇。因为水材质是材质图表里最典型的综合性案例它能把UV、时间动画、深度计算、半透明混合、反射折射这些东西一次性串起来你把这个学会了后面再做边缘发光、做坡道泛光、做路面湿滑思路都是通的。这个内容适合谁你如果是刚接触材质图表的小白可以把这篇文章当入门实战如果你是主要做模型或蓝图但一直没系统玩过材质的开发也可以照着这篇把水面搭出来。咱们要做的不是一个极致的写实大海而是一个能放进场景里、从正面侧面看都像水、同时性能还说得过去的湖面或泳池水面。我会把每一步怎么连节点、为什么这么连、连完踩过哪些坑都讲清楚保证你看完能直接照着做。1. 动手前的设计思路简单的“水”到底应该包含什么做材质和做菜很像你得先知道这盘菜应该是什么味道再开火。很多人一上来就找瀑布、海洋那种复杂工程文件结果打开一看几百个节点直接劝退。其实场景里我们用的最多的水面是湖面、池塘、泳池、水坑这类“平静水面”它们的效果核心不是浪花大小而是颜色、透明度、波纹细节和岸边过渡这几件事的组合。1.1 看起来像水靠的是哪几个基本要素我习惯把水材质拆成四个部分去看基础颜色、表面起伏、边缘过渡、光的反馈。基础颜色就是水的固有色调偏蓝、偏绿还是偏灰决定了水给人的第一印象表面起伏靠的是法线贴图随时间流动当法线变化时高光和反射会跟着变形水面才有了“动”的感觉边缘过渡解决的是水与岸边、与水底物体的衔接水浅的地方应该更透、更亮水深的地方应该更沉、更暗光的反馈则包含反射、折射和粗糙度水面越平、粗糙度越低反射就越清晰。这四个部分不是独立的它们会互相影响。比如你用法线扰动做波纹那么折射应该跟着法线走否则水底的石头和沙粒就会“纹丝不动”看起来像贴在玻璃下面的一张图片。再比如你调了深度阈值做边缘泡沫泡沫边缘最好也稍微被波纹噪声扰动一下不然就像画上去的一条白线。1.2 先想清楚你要的是哪种水半透明还是透明材质在UE的材质系统里水的Blend Mode一般只有两种选择Translucent和Opaque配合Semitransparent或Alpha Composite这类变体。场景里的普通水面也就是需要看到水底物体的那种我们选用Translucent因为它能正确处理透明度、折射和低动态光照。这里有个常见的认知误区有人图简单把不透明水面的材质拿过来在Base Color里手动混合出一个半透明的蓝色然后放在水面上。这样做的效果是从正面看颜色还行但俯视时水底完全看不出深度因为Opaque材质不透明水面之下什么都看不到更不可能产生折射变形。所以只要你的水下面有地形、鹅卵石、泳池底纹就必须用Translucent没有讨价还价的余地。1.3 工艺选择直接连节点还是依赖材质函数库做简单材质我的建议是先不引入任何材质函数库或第三方插件就用引擎自带的基础节点。原因有两个一是节点越少越容易排查问题你对每一条连线的含义都清楚二是材质函数库虽然方便但封装层级越深参数调整就越依赖别人的设计意图。等你把今天这个版本的逻辑吃透了再去看官方Water插件里的材质函数就很容易理解它为什么要那么封装。我在项目里见过不少美术同学直接拿官方水的材质函数改了半天只调了颜色和法线强度。一旦遇到水底深度显示不对、泡沫边缘闪烁的问题根本不知道怎么排查因为函数内部对她来说是黑盒。自己搭一遍的好处就是每个数值、每个节点都能掌控时间久了你会发现自己调试材质的速度快很多。2. 从零搭起第一个水面基础材质节点详解准备工作很简单。在Content Browser里右键选择Material命名为M_Water_Simple。双击打开材质编辑器先把Details面板里的材质属性设对再开始连线。2.1 第一步就卡住材质属性面板的设置顺序打开材质编辑器后先别急着拖节点先看Details面板。在Material属性区把Material Domain设置为SurfaceShading Model选择Default LitBlend Mode选择Translucent。这三个设置决定了后续许多通道的可用性比如Opacity、Refraction通道只有在Translucent下才会出现。然后记得把Two Sided打开并且勾选Cast Shadow As Masked这两个细节对后期调整很有帮助。为什么要开Two Sided因为水面从水下看是要透光的如果你不开双面渲染相机在水中或水岸边低角度时水面的背面会被裁掉画面直接穿帮。Cast Shadow As Masked则是让水在投射阴影时用透明度阈值来决定阴影避免半透明水产生奇怪的阴影条带。这两个开关在引擎默认情况下不开启做水材质时要手动打开。2.2 水的Base Color不要只给一个纯蓝色给一个蓝色的Vector3这是大多数人会做的第一步但其实Base Color单给纯色并不够。纯色缺乏层次水面会显得很“塑料”。比较稳的做法是给Base Color一个中等偏深的蓝绿色用Vector3节点大概取值R0.01G0.05B0.08左右然后后续通过深度计算把浅水区提亮深水区压暗。这样水面的颜色变化就有层次了。还需要注意的是如果场景里有方向光水的Base Color会被光照系统直接参与漫反射计算太亮的高饱和蓝色会让水看起来像荧光池。所以Base Color要偏暗偏灰高光和反射才是水面亮度的主要来源。一个调试小技巧把光照关掉看水此时能看到的应该是一个很暗的深色表面如果关灯后水还是亮堂堂的说明Base Color给得太亮了。2.3 Opacity与Depth Fade让边缘交接不再生硬做水岸边交接线时最容易出现的瑕疵是水面和河岸之间有一条清晰又生硬的边界线仿佛水面边缘被人用矩形选区切过。解决这个问题核心是Depth Fade节点。Depth Fade节点做的事情非常简单它比较当前水面的像素深度和场景里已渲染的不透明物体的深度两个深度越接近透明度越低距离越远透明度越高。把Depth Fade的输出连到Opacity通道上水面靠岸的部分就会自然变得透明产生一种“水爬到岸边土里”的渐隐效果。你可以在Depth Fade节点的Details里把Fade Distance调到50到100之间让过渡范围更宽。我刚才提到Overdraw和半透明排序是我们在项目里反复调整的两点。Translucent材质不写入深度缓冲所以引擎只能按物体中心距离排序渲染这意味着水里的石头、岸边的草和天空球都有可能和水面产生错误的遮挡关系。最简单的规避方案是把跟水面直接交接的物件尽量设成Opaque或Masked然后让水靠Depth Fade去过渡这样排序问题就不太容易暴露。3. 让水面动起来法线、UV流动与波纹的核心逻辑一个完全静止的水面不管颜色多好看看起来都是一块贴了图的大平板。水流动起来靠的是法线贴图的UV随时间平移所以这一节是整个水材质最核心的部分也是最容易出效果、最容易做过头的地方。3.1 法线贴图的选择一张普通的法线贴图就能干活做简单水面一张常规的石墙法线贴图或混凝土法线贴图就够了不一定非要找“水法线贴图”。因为法线贴图记录的是表面微表面的朝向它本身没有“水”的语义关键是平铺和流动的参数。我在项目里常用两张来自引擎内容库的默认纹理做法线源比如Concrete_Basalt和Ground_Cracked这类噪声型法线。如果你有Substance Designer也可以自己生成一张各向同性的小尺度噪波法线周期上多平铺几遍效果就出来了。注意一定要把Texture的Compression Settings设为Normalmap让引擎将纹理当作法线贴图来处理否则法线方向会不正确。3.2 UV流动的基础Pan节点加Time一秒搞定动画UV流动的原理特别简单。把一张纹理的UV坐标随时间平移纹理就在表面“滑”动起来。在材质图表里最直接的做法是拿TextureCoordinate节点和Time节点做乘法再塞进Texture Object或者直接作为Texture Sample的UV输入。我通常的连法是这样的TextureCoordinate节点默认输出UV0我把它乘以一个Tiling参数比如2.0到4.0之间得到平铺后的UV。接下来把Time节点乘以一个Speed参数Speed是一个Float2向量X分量控制U方向的流动速度Y分量控制V方向的流动速度这个乘积加给刚才的平铺UV最后输入到Texture Sample的UVs接口。这里要注意的是方向。Vector2X为正会让纹理向右流动如果要水从左向右流Speed的X取正如果要做河流往一个方向流动就把该方向的Speed单独加大。这种做法比用Panner节点更直观Panner虽然封装了同样的逻辑但参数多了几个对新手来说不如直接拆开看得明白。实际测试下来Speed取值在0.05到0.2之间比较合适太快的流动会让水面看起来像加了风扇的消毒池。3.3 两层法线混合为什么推荐BlendAngleCorrectedNormals只用一层法线做水面效果往往偏单薄水面波纹看起来像一整块胶带在平移。我建议叠加两层法线一层大尺度慢速波形一层小尺度快速细节。大尺度的Tiling小一点比如1.0到2.0流速慢一点小尺度的Tiling大一点比如4.0到8.0流速快一点。两层法线叠加后水面会有细微的重叠干涉效果波光粼粼的感觉就出来了。混合两层法线时不要用Lerp。法线必须归一化后混合Lerp会破坏法线长度导致光照方向出错。正确做法是用BlendAngleCorrectedNormals节点它内部会做角度修正把两层法线做加权混合后并且重新归一化保证结果始终是单位长度的法线。这个节点在材质编辑器的搜索框里输入Blend就能找到。混合比例我一般设在0.5左右如果某一层太强波纹会显得特别硬就像金属表面的拉丝太弱又几乎看不到多层叠加失去意义。加上法线之后记得从Normal通道引出两路一路接给材质Normal输入另一路可以接给Refraction通道做折射偏移。这个后面会细说。4. 岸边的过渡逻辑深度信息与泡沫效果的进阶玩法做普通水面做到上面那些已经可以在场景里看了。但要让它真正“融进”环境深度信息是关键。深度不仅是岸边过渡的触发器也是泡沫、深浅色分层的基础。4.1 SceneDepth和PixelDepth算清楚一片水到底多深UE里的半透明材质可以通过两个节点拿到深度信息。PixelDepth是当前像素的深度值SceneDepth则是场景中不透明物体比如水底地形的深度值。两者相减得到的差就是我们常说的水体深度正值越大说明该位置水越深值接近0或负值说明水很浅或者根本没有水体覆盖。实际操作中我通常用SceneTexture节点Scene Texture Id选择SceneDepth拿到场景深度值再拖一个PixelDepth节点。两者连到Subtract节点SceneDepth减PixelDepth得到WaterDepth。这里有个容易搞反的操作如果你的水面上方有荷叶或漂浮物这部分像素的SceneDepth体现的是叶子深度数值会跟水底不一致所以水体深度计算最好只用于没有漂浮物覆盖的区域。常见的解决方案是让漂浮物使用Unlit或者特殊材质标记屏蔽掉对深度采样的干扰。得到WaterDepth后后续的很多效果都可以用它来驱动。比如用一个RemapValueRange或Saturate把它映射到0到1的区间0表示无限深1表示岸边浅水。然后把这个值接到Base Color上浅水提亮、深水压暗接到Opacity上浅水更透明、深水更不透光还可以接到法线强度上浅水波痕小、深水波纹更平静。4.2 岸边的泡沫深度阈值加噪波扰动白线不再生硬泡沫是最能提升水面真实感的细节。简单版的泡沫逻辑就是当水体深度小于某个阈值时表面出现白色泡沫。可以先用一个Float参数定义泡沫出现的深度阈值比如20厘米注意这里深度单位是场景纹理的深度值不是厘米需要你根据自己的场景比例调整然后用下面这个方式处理FoamMask 1 - Saturate((WaterDepth - FoamDepth) / FoamFade)当WaterDepth小于FoamDepth时FoamMask接近1深度越大FoamMask越小。这里可以把WaterDepth的输出接到Saturate之后用RemapValueRange微调范围控制泡沫从白到无的过渡区宽窄。这样岸边就有一条白色泡沫带但边缘仍然比较硬。为了让泡沫边缘自然我通常加一张噪波纹理扰动它。用TextureSample取一张高平铺的小噪波材料叠加到上面的FoamMask上可以用Multiply乘一个扰动强度再用Saturate夹住。这样泡沫边缘会出现断裂和参差像是被水纹撕扯过。最终把泡沫结果接到底色上用Lerp在原始Base Color和白色比如0.8, 0.85, 0.9之间切换泡沫位置就显示白色泡沫了。这里有一个我在项目里踩到过的坑如果把FoamMask直接加到Base Color上而不是通过Lerp混合泡沫会随着材质原有亮度被放大或减小结果就是泡沫颜色不稳定。正确的做法是始终用Lerp控制泡沫层的显隐Lerp的Alpha就是泡沫遮罩。4.3 深水区颜色变暗深度驱动的深浅分层除了泡沫深度信息还能用来模拟水体对光线的吸收。水越深能到达的光越少所以颜色应该更暗更蓝。把这个逻辑接到Base Color上就不需要手动刷AO或调水面透明度了。具体的实现是在Base Color的输出前加一个Lerp浅水颜色用偏青绿、偏透明的亮色深水颜色用深蓝、偏黑的暗色Alpha通道接前面算出来的WaterDepth。当WaterDepth值较小时Lerp输出浅水颜色当WaterDepth值较大时输出深水颜色。这样就实现了俯视看水时岸边颜色较亮、向中心渐深的效果。如果场景里水体特别浅比如只有十厘米的浅坑你可能会希望即使深水区域也能看到部分色彩这时可以调整Lerp的两个颜色让深水颜色不要纯黑保留一定的饱和度。我一般取0.004, 0.02, 0.035这种暗蓝色既能保持水的辨识度又不会在接近底部时掩盖细节。5. 反射、折射招数与材质实例化让水面更抓眼到了这一步水面的基础框架已经有了接下来就是细节手感。反射和折射决定了水面的“高级感”材质实例化则决定了你在场景里调起来有多顺手。5.1 反射是水面的灵魂半透明材质里怎么做反射水面对反射的依赖比普通物体强得多。具体怎么做取决于你的项目是否开启了Lumen或使用屏幕空间反射SSR。在UE4或UE5中使用默认的Lit模式制作的Translucent材质在经过反射捕获盒Reflection Capture或Planar Reflection区域反射后水面上可以出现不错的光影反射。UE5里由于Lumen的存在半透明表面也可以接收部分Lumen反射信息但要注意性能开销。如果你在UE5里开启Lumen后发现水面反射闪烁优先检查Lumen Reflections里的Material Roughness设置让水面粗糙度不要过低。粗糙度参数对反射清晰度有直接影响。水面的粗糙度我给到0.01到0.05之间能产生镜面般的反射效果如果场景里有很强的太阳光可以稍微把粗糙度提高到0.1左右避免反射高光过曝。通过材质参数控制粗糙度在调试时特别重要。5.2 折射不只是IOR用Normal扰动做出水底扭曲水面的折射在UE里是通过Refraction通道控制的。只要Blend Mode选的是Translucent材质面板里就会出现Refraction通道。默认情况下引擎输出到Refraction的往往是IOR值也就是折射率。水的IOR约为1.33空气的IOR是1.0所以在Refraction节点里输入一个1.33左右的值效果会比较自然。但如果你想看到水底物体发生扭曲变形光有IOR还不够。做法是先把上面计算出的法线方向送给Refraction通道而不是IOR常量。具体连线为由法线纹理和Blend混合出来的Normal向量经过一个AppendVector或者直接输入到Refraction节点Refraction节点会自动把它解释为对UV方向的偏移。这个偏移强度可以用一个参数控制比如乘以0.02或0.03数值越大水底的扭曲越严重但太大就会像透过毛玻璃看东西那反而不适合普通水面。这里有个经验在工程里做近景水面时微弱折射加点法线扰动效果最佳如果水面面积很大而你在很远的地方观察折射细节根本看不出来此时可以略微降低折射强度来节省性能。5.3 材质实例化把参数全部暴露出来别再一遍遍重编译调试水材质的时候最忌讳的就是每个参数都在底层材质里改来改去。只要把常用的属性变成参数创建材质实例Material Instance后你就能在编辑器里直接滑动参数观察效果不用每次改完底层材质都等编译。我建议把以下参数全部变成Scalar Parameter或Vector ParameterTiling纹理平铺是Float2、Speed流动速度是Float2、NormalIntensity法线强度、DepthFadeDistance、FoamDepth、FoamFade、FoamDistortion、RoughnessValue、IORStrength。至于Base Color的浅水色和深水色可以用两个Vector Parameter单独暴露出来方便以后调不同场景的氛围。参数化还有一个额外好处同一个M_Water_Simple可以被不同模型复用。比如你做城市街道时可以拉一个参数调暗的水坑材质实例做个泳池又拉一个透明度低一点、泡沫少一点的水面板实例。同一套底层材质一个项目里能塞进四五种不同用途的水面。6. 常见问题排查与优化建议自己做水材质踩坑太正常了。这里我把我积累的排查思路和调整建议整理成速查按症状来直接对号入座。6.1 问题速查表故障现象可能原因排查方向水面不透底什么都看不到Blend Mode不是Translucent或Opacity输出成0全不透明检查Blend Mode、Opacity节点的值水底完全无折射扭曲Refraction通道没有接Normal或IOR设置为1.0接Normal到Refraction并调IORStrength参数边缘出现生硬的白色线没开Depth Fade岸边过渡FadeDistance太小增加DepthFadeDistance或检查Depth计算正负号泡沫边缘像锯齿泡沫Mask没有噪声扰动或材质本身的MSAA抗锯齿权重低给泡沫Mask加噪波扰动并开启材质抗锯齿水面像一层玻璃毫无波光法线强度太弱或法线贴图被压平提高NormalIntensity检查法线贴图Compression设置反射高光过曝一片白粗糙度太低高光反射过强把Roughness调到0.05到0.1之间水面波速看起来很“抖”流动速度过快或者法线Tiling过大导致高频噪声降低Tiling降低Speed值6.2 半透明排序与深度写入的坑半透明材质不写入场景深度这是机制上决定的但它带来的连带问题很多。最直观的表现是当你站在岸边往水里看时岸边的草、石头、人物可能出现在水面之上遮挡关系完全错误。我的处理方式分两步。第一步凡是靠近水面的静态物体比如小石子、浅滩、荷叶片优先使用Masked材质而不是Translucent让它们写入深度至少让水面裁切关系正确。第二步如果确实需要某些半透明物体跟水同时存在比如气泡或薄雾把它们放在不同的优先级范围内通过Translucent Sort Priority调整渲染顺序。水面的排序优先级我习惯设到10左右气泡设在20到30让气泡尽量晚渲染减少遮挡穿帮。6.3 性能优化水的开销变化与省略方案Translucent材质比Opaque材质贵这是必然的。在做大水面时有几个常见的性能取舍点。第一尽量减少深度的采样次数SceneTexture节点每用一次都会对场景深度做一次采样你可以只算一次WaterDepth后续所有效果都从这一个值派生不要在每个效果里都重新采样。第二法线贴图数量控制在两层两层以上在多数场景中已经感知不到差异了。第三如果水面上看不清反射的物体比如沙漠中的小水潭可以关闭SSR只保留反射捕获盒能省下至少一半的反射开销。我在实际项目里做过一个对比一个512x256大小的湖面用完整半透明材质开SSRDrawCall和Shader指令数大概是不开SSR版本的1.6倍左右。如果场景里同时有三四处水面整体性能还是有一定压力的。所以优先保质量的话主水面开SSR周围小水坑、水洼用简单版的材质实例只需关闭反射或降一档法线肉眼基本察觉不出来。最后分享一点我的制作习惯水材质这个东西做起来没有标准答案但有一条我比较坚持的习惯任何水材质第一版一定要把核心节点控制在20个以内。因为这个数量下你完全清楚每一条线在干嘛。等你把20个节点的版本跑通了再慢慢叠加泡沫、焦散、波浪、深水雾化这些高级效果。先让水面“看起来能用了”再继续雕琢细节。我个人在实际操作中最得意的经验是把两层法线中第二层的Speed和Tiling参数留成材质实例参数这样在不同地形上调试时完全不用碰底层材质。还有一个调试小技巧把Opacity通道临时接到一个永远输出1的常量上看水面的基础外观排除透明度干扰后再把Depth Fade接回去能省下不少排查时间。这个系列如果继续写下去下一篇可以聊聊如何用同样的节点组合做道路积水、泳池水面和低多边形风格的水体。你会发现基础的水材质逻辑学会了后面所有跟水有关的效果都能顺手拈来。