UE材质图表大全:用节点从零搭建动态水材质
2026/9/16 20:35:16 网站建设 项目流程

在UE里做水,我第一次做出来的是巨大的果冻。后来做出来的是蓝色玻璃。再后来才意识到,问题不在于贴图不够清晰,也不在于节点懂得少,而是我没搞清楚一张UE水材质到底要同时负责几件事。这篇是我计划里的"UE材质图表大全"系列第一篇,核心目标只有一个:用材质编辑器里最常见的十几个节点,从零拼出一套不用任何付费插件、随便拖到Plane上就能出效果的自定义简单水材质,顺便把每个节点为什么这么连、数值为什么要这么调讲清楚。

这篇文章适合两类人:一类是刚接触材质系统,想摆脱蓝图默认Demo感、自己动手做场景元素的新手;另一类是已经能做出静态水面,但总觉得颜色、高光、深浅过渡哪里不对的开发者。阅读过程中你可以完全不懂代码,材质就是一张“节点图表”,我会把每一步连线的逻辑拆开讲,你照着连完,再去改参数,自然能感觉到哪些旋钮控制的是真实水体的哪个特征。

1. 拼水材质前,先搞清楚水在场景里到底靠什么"看着像水"

很多人在材质编辑器里卡住,不是因为不会连节点,而是不知道目标长什么样。做水的第一步不是找贴图,不是调BaseColor,而是先想清楚:一个平面如果要在场景里骗过眼睛,需要同时提供哪些视觉信息。

1.1 水的视觉特征拆解

我在实际项目里总结过,一张水材质需要控制的视觉要素至少有五个:

  • 底色:水体本身对光线的吸收,深水区偏暗偏蓝,浅水区偏亮偏绿。
  • 表面起伏:水面不是绝对平面,波浪和微澜会改变光线反射方向,这一步全靠法线贴图实现。
  • 高光和反射:水是电介质,不是金属,它的高光强度很低,但在掠射角(视线几乎平行于水面)时反射非常强。
  • 深度信息:岸边的水浅,能看到水底,所以颜色更亮;湖中央水深,光线被吸收,颜色更深。这个必须通过深度信息来计算。
  • 动态:水是流动的,即使风平浪静,水面也在微微颤动。完全静止的水面在游戏里极其可疑。

很多新手只做了前三件事中的一部分,比如给一个蓝色BaseColor,拉低Roughness,然后就觉得“差不多”。但这样的水面在场景里看起来就是一个彩色镜子,没有层次也没有生命力。真正的水材质是上述五件事叠加后的结果。

1.2 一张水材质的节点总览

在动手之前,我先给你一张“地图”,后面每一章都是这张图里的一条支路。这样连的时候心里不会慌。

用途核心节点作用路径
颜色基底Constant3Vector直接进BaseColor
深浅过渡SceneDepth与PixelDepth比较,得到水底距离
颜色混合LinearInterpolate在浅水色和深水色之间做插值
波浪流动Panner让UV随时间平移,驱动法线贴图
法线细节TextureSample多层法线叠加,控制高光形状
波浪高度World Position Offset让顶点随波峰起伏
高光Roughness + Specular控制反射亮度与锐利度
边缘光Fresnel让摄像机低角度时边缘更亮/更透

这张表不需要背,它是用来帮你在连错的时候定位问题的。比如“水面很暗”,你就去查Fresnel方向是不是反了;“水面像碎玻璃”,你就去查法线强度是不是太高。后面的内容全是围绕这张表展开的。

2. 从零搭出能用的静态水面:主材质配置与颜色控制

先建一个最基础的水材质跑通流程。很多人一上来就想做很炫的效果,结果节点一多,画面一花,根本不知道哪里出了问题。我的习惯是先把“静态水体”做出来,确认颜色、高光、视觉方向都对,再往里面加动态。

2.1 创建材质和主节点设置

在内容浏览器右键,材质,命名为Water_Main。双击打开材质编辑器,先看右侧Details面板里的几个关键选项:

  • Material Domain:保持Surface,意思是这个材质用于表面渲染。
  • Shading Model:选Default。水中如果想要半透明背光效果可以选Subsurface,但第一步用Default最简单。
  • Blend Mode:Opague。不透明模式。这一条估计有人会疑惑:水不是透明的吗?这里先解释一下,后面的章节我会专门讲。
  • Two Sided:可以先不勾,等发现平面在水下看是黑色时再勾。

为什么第一步用Opaque而不是Translucent?因为半透明模式牵扯到渲染排序、透明物体深度写入、性能开销一堆问题,新手很容易在这里翻车。而Opaque模式下,你也可以通过SceneDepth去模拟“远处深、岸边浅”的观感,视觉上已经能骗过一大半人。先跑通,再升级。

2.2 第一版颜色和高光:让平面先有“水色”

创建三个常量节点:Constant3Vector打开自定义颜色,我这里用比较深的蓝绿色(0.02, 0.08, 0.12)作为主色调;一个Roughness常数给0.03;一个Metallic常数给0;一个Specular常数给0.5

连线方式如下:

  • BaseColor <- 主色调常量
  • Roughness <- 0.03
  • Metallic <- 0
  • Specular <- 0.5

这时候在场景里放一个Plane,把材质拖上去,你就能看到一块明显区别于普通地板的“水体底板”。这一步看着很简单,但有几个概念必须先立住:

Metallic必须是0。水是电介质,它不像金属那样直接反射环境色。一旦给金属度,高光颜色会跟着环境变,水面会像刷了漆的钢板。 Roughness低是为了让高光聚拢,但低到0会变成完美镜面,属于“果冻水”的常见成因。实际操作我给0.02-0.05,保留一点点粗糙度,高光边缘会自然一些。 Specular默认0.5其实是一个“中间塑料值”,真实水体垂直入射时的反射率非常低,理论上接近0.02-0.05。但这里我反而故意给你0.5,因为如果你的场景还没有做反射捕获和丰富环境光,Specular太低会让水面完全“瞎掉”,什么高光都看不到。这是一个视觉妥协,等后面把环境光和反射方案补齐了,你再慢慢往下压。

3. 让水动起来:UV流动与法线细节的叠加技巧

静态水已经能看,但它是“死”的。水面的生命力来自两个地方:一是UV一直在动,二是法线细节在高光区域产生不规则形状。这一章是整个水材质里最容易出效果、也最容易出问题的部分。

3.1 Panner实现水流方向的两种写法

Panner节点(中文版叫“平移”)是水的灵魂节点。它接收一个UV坐标,然后让这个坐标随时间匀速运动。连线方式:

  • TextureCoordinate(纹理坐标)-> Panner
  • Panner -> TextureSample的UVs
  • Panner的Speed输入 <- Constant2Vector,比如(0.05, 0.03)

Speed的第一个分量是U方向速度,第二个是V方向速度。(0.05, 0.03)表示UV每秒向右移动0.05、向上移动0.03。你把它加大到0.5以上,水就会变成急流;小到0.01,则几乎是静止的微澜。

实际操作里有一种更好用的写法:先把TextureCoordinate乘上Tiling值,再做Panner。很多新手直接把贴图的Tiling设置在TextureSample节点里,再单独连Panner,结果流速和密度互相干扰。我一般这样连:

  • TextureCoordinate -> Multiply(乘一个Tiling值,比如(4, 4)
  • Multiply -> Panner
  • Panner -> TextureSample UVs

这样UV的平铺密度和流动速度彼此独立,调节时互不干扰,调参效率高很多。你可以理解为:先把水面分成格子,再让格子里的图案整体流动。

3.2 两层法线不同方向叠加

只用法线贴图本身是不够的,因为单层法线的流动太规律,一眼就能看出贴图在重复移动,非常假。我的做法是两层法线叠加,一快一慢、一正一反:

  • 第一层法线贴图,UV方向设为(0.04, 0.02),强度Scale=1。
  • 第二层法线贴图,UV方向设为(-0.03, 0.06),强度Scale=1。
  • 两层的法线输出都连到一个加法节点Add,然后接一个Normalize,最后进Material的Normal引脚。

这里有个非常关键的细节:法线贴图的压缩格式。你导入UE后,必须把纹理的Compression设置为Normalmap(法线贴图),否则法线数据会被错误压缩,采样结果发灰发暗,水面会像贴了一张皱巴巴的纸。怎么检查?选中纹理,在Details面板的Texture Group里选“NormalMap”,或者直接看贴图预览是不是那种蓝紫色。如果是灰白色,说明格式不对。

法线强度通常不直接用TextureSample的输出,而是用ConstantBiasScale节点(常量偏置缩放)控制。把Bias设为0,Scale从0.05到0.2之间调整是安全范围。Scale太大,水面会像碎玻璃一样高光碎成无数块;Scale太小,水面又像油面,完全没有波纹感。我在不同场景里的习惯是:远景大水面用0.05左右,近景角色脚下的池塘用到0.15左右,因为离摄像机越近,表面细节需要越明显。

3.3 波浪高度:World Position Offset的轻量用法

如果只靠法线贴图,水面即使“看起来”有波浪,轮廓依然是绝对平整的。当摄像机压低、视线贴近水面时就会穿帮——水面像一块透明的果冻皮,下面的波纹全是假的。

这时候需要World Position Offset(世界位置偏移)。这个引脚直接修改顶点位置,也就是说它让Plane的几何体本身发生起伏。

一个最简单的波浪算法:用TextureSample采样一张简单的噪点/法线贴图的红色通道,加上Panner驱动,乘以一个小幅度值,连到World Position Offset的Z方向。

需要注意三点:第一,幅度不要大,我一般给1-3(厘米级别),大幅度的WPO容易和岸线、物体产生穿插,看起来像水面在抽搐;第二,WPO变化频率由Panner速度控制,幅度和速度要匹配,如果速度快幅度大,水面会变成沸腾的锅;第三,WPO会让阴影也发生抖动,如果你的水面在场景里投下了明显阴影,建议在静态网格体上关闭Cast Shadow。

这里还要坦白一个最常见的坑:WPO修改了顶点位置,但法线贴图并不会自动跟着变。也就是说,顶点虽然在上下动,但你依然用原来那套Panner法线,视觉上会出现“法线起伏方向”和“几何起伏方向”不一致的问题。解决方式有两种:要么把WPO幅度做得非常小(比如1-2),让它只是一个微妙的轮廓变化,法线负责主要的视觉波浪,两者不至于打架;要么用Gerstner波同时计算WPO和法线,那个数学量会大不少,我用在写实海水项目里,简单水不需要做到那一步。

4. 岸边与深水区的颜色过渡:Scene Depth的完整用法

水面颜色一深到底,是很多新手水最明显的破绽。真实水域有一个非常显著的规律:岸边浅,颜色亮;湖心深,颜色暗。这个过渡如果做不出来,不管法线多漂亮,水都像一缸墨水。好消息是,UE材质系统里做这件事只需要SceneDepth一个节点。

4.1 SceneDepth节点为什么能让水在岸边变浅

SceneDepth节点输出的是当前像素在场景深度缓冲里的距离值,直白说,就是摄像机到“第一个被挡住的物体表面”的距离。在水的场景里,水这个平面本身有一个像素深度(PixelDepth),而水面背后的河床、岸底会记录在场景深度里。

关键公式是:

SceneDepth - PixelDepth = 水底到水面的距离

当这个值很小时,说明水很浅;当这个值很大时,说明水很深。把这个值映射到0到1,就能做成一个“浅水系数”。它其实是把水的深度信息转换成了我们可以用来混合颜色的遮罩。

节点连线顺序如下:

  • SceneDepth节点输出 -> 减法节点Subtract,减去PixelDepth
  • Subtract -> 除以一个最大深度值(比如Divide,除以200)
  • 结果 -> Clamp(约束到0到1),防止超出范围
  • 结果 -> Power,给一个1.5左右的指数,控制过渡的软硬
  • 最后用这个结果作为LinearInterpolate的Alpha

LinearInterpolate三个输入分别是:A=浅水颜色,B=深水颜色,Alpha=上面的系数。Alpha接近0的地方显示浅水色,接近1的地方显示深水色。这里的“最大深度值”你可以理解为“多少距离内算深水”,场景里如果河床很平,给300也能接受;如果水面深浅差异很大,那么取一个中间值让过渡舒缓一些,比如150。

4.2 深度插值的参数调试经验

深度值调起来有个小技巧:先在材质编辑器里连一个静态Switch到测试分支上,方便随时对比“有深度过渡”和“没有深度过渡”的差别。然后调整最大深度值,看岸边和湖心过渡是否自然。

我常用的参数组合是:

参数数值说明
最大深度150-250超过这个值都视为深水
Power1.5-2.0越大过渡越陡,越小岸边渐变更柔和
浅水色偏亮蓝绿色大概在(0.05, 0.25, 0.25)
深水色偏暗蓝黑色大概在(0.01, 0.05, 0.08)

还有一个很容易忽略的点:在Opaque模式下,颜色混出来之后可能偏暗。原因是浅水色本身不够亮。如果你想要那种海边浅滩的透明感,除了提高浅水色亮度,还可以给BaseColor加上一小块高光色,模拟阳光穿透浅水后的散射。不过这一步对整体画面影响较大,建议先把基本颜色过渡调舒服,再考虑加散射。

4.3 用DepthFade节点简化操作

除了手写SceneDepth-PixelDepth这一套,UE还提供了一个内置节点叫DepthFade(淡入淡出)。它的作用内核其实和我们手算的逻辑一致,只是把公式封装了,你只需要连入Opacity或者颜色,再给一个FadeDistance参数就行。

DepthFade在两种情况下特别好用:一是做透明水的边缘淡化,让水和岸边的衔接不穿帮;二是快速做“浅水透明”效果。缺点是可控性不如手写深度链路,它直接输出的是一段渐变值,你不太容易拿到具体的“水底距离”去做更复杂的颜色分层。

我的建议是:如果是做需要精细控制的写实水,一定用SceneDepth手写链路,因为后面加泡沫、加水位线、加岸线渐变都依赖同一个深度值;如果只是赶工期做一个远处场景的水面,直接拖一个DepthFade节点,快速省事。

5. 水面光效:菲涅尔、高光与反射方案怎么选

水面真正让人感觉到“湿”的,不是颜色,而是光。同一个水体,白天看是亮蓝色的镜面,黄昏看是金色的碎光,夜里看是暗色的绸缎。光效部分主要靠三个东西协同完成:菲涅尔、Specular高光参数、反射来源。

5.1 Fresnel在UE里的使用逻辑

Fresnel节点输出的是一个0到1之间的值,它表示“当前像素的法线方向与视线方向之间的夹角关系”。当你从正上方垂直看水面时,输出接近0;当你的视线几乎平行于水面时,输出接近1。这正好对应真实物理现象:水在低角度时反射能力极强,几乎变成一面镜子。

用法很直接:把Fresnel输出乘上一个天空颜色或者浅蓝色常量,叠加到BaseColor上。这样从高处看水面颜色深,从低处看水面颜色亮,体现出“倒影”的错觉。Fresnel节点默认的Exponent(指数)是5,这个值我一般保持在4到6之间:值越大,边缘变亮的速度越快,适合表现风浪大的水面;值越小,过渡越缓,适合平静湖面。

这里要提醒一点:很多新手把Fresnel乘到一个大数值直接连到Emissive Color上,想让水面“边缘发光”。这样就变成灯光材质了,看起来极其不自然。正确思路是把Fresnel当作一个混合系数,它告诉材质“这个角度下,反射光和透射光的比例各是多少”,而不是直接产生光。

5.2 Specular高光参数到底该给多少

回到Specular这个参数。UE材质节点的Specular输入并不是物理上的反射率,而是经过引擎缩放后的一个近似值。材质系统里默认0.5是塑料,0.0到0.1之间比较接近真实电介质。水在垂直视角下的反射率大约2%到5%,对应的节点值大概在0.02到0.05附近。

但实际操作时,如果你的场景没有布置反射捕获(SphereReflectionCapture),那么这个值给低了,水面高光会非常微弱,看起来像一块深色地板。所以我会分两种情况:

  • 场景里有反射捕获或者明确的环境光:Specular给0.02-0.05,靠真实的低反射率配合反射捕获呈现水感。
  • 场景里没有任何反射来源:Specular可以临时给到0.2-0.5,先把高光点撑起来,后续再调整。

Roughness这边,水面需要的不是“零粗糙度”的完美镜面,而是非常低的粗糙度配合法线细节,让高光形状呈现不规则破碎感。我给Roughness最小值设为0.02,最大值可以到0.3,用一张简单的噪声纹理连接到Roughness上,模拟水面的涟漪强弱分布。这样水面不是所有地方都同样光滑,显得更活。

5.3 平面反射与反射捕获:倒影效果好坏的决策点

水的倒影是水材质里最吃项目配置的部分。材质节点本身解决不了反射来源的问题,它只能决定“反射强度多少”。反射来源有三种常用方案,我列个表对比:

方案质量开销适用场景
SphereReflectionCapture中等,静态大多数中小型场景,一次烘焙全局可用
PlanarReflection高,实时准确高,额外渲染一次场景湖面特写、倒影很关键的关卡
屏幕空间反射SSR中高,实时但依赖屏幕内容写实场景里作为补充,需要项目设置开启

我见过不少项目把PlanarReflection拖进场景后性能直接掉一截,然后开始怀疑材质写错了。其实不是材质的问题:PlanarReflection组件会让引擎以反射面为视角再渲染一遍整个场景,相当于这个区域的渲染成本直接翻倍。所以我一般只在主角能近距离看到水平面的关键关卡里用PlanarReflection,其他水面统一靠反射捕获加上SceneDepth那套岸边渐变来撑着。

热搜里出现“平面反射倒影渐变”这个词,我多说一句:PlanarReflection解决的是“倒影内容从哪里来”,它告诉水面把天空、山、树的样子画上去;而岸边颜色的深浅渐变,仍然靠材质里SceneDepth那一套深度链路来算。两者是叠加关系,不是替代关系。很多人看到用了PlanarReflection后岸边颜色不对,第一反应是去检查反射,其实应该回到深度链路里调浅水色。

6. 实机测试与避坑清单:我踩过的具体数值坑

这一章专门写给那些“按教程连完节点但效果依然不对劲”的人。我早期做水材质踩过的坑,后来发现大部分不是偶然,而是若干典型的参数和设置问题。逐个排查一遍,基本能解决九成以上的问题。

6.1 法线贴图没有设置成NormalMap格式

这是“水像碎纸”的元凶。当你把一张贴图直接拖进材质作为法线输入时,如果它没有在导入设置里标记为法线贴图,引擎就不会用正确的解包方式去读取它。你看到的结果是法线信息被当作普通颜色采样,然后被Normalize,最后形成一种无数小折面乱跳的视觉噪声。

排查方式:选中贴图,在Details面板查看Compression Settings,如果是默认的TC默认压缩,要改成Normalmap。改完后重进材质编辑器,对比一下法线预览,效果立竿见影。

做法线叠加时还有一个细节:两层法线相加之后一定要接Normalize(归一化),把向量长度约束回单位长度,否则法线方向会偏,高光位置会怪。好多新人顺手把Add结果直接连到Normal引脚,结果高光在某个角度突然爆亮。

6.2 透明水模式下排序问题导致穿帮

透明(Translucent)水确实能做得很漂亮,能看到水下物体,但它有一个绕不开的麻烦:半透明物体在引擎里按距离排序,一个透一个水,或多个水面包裹同一个物体时,渲染顺序错乱会让水下物体忽隐忽现,或者水面叠加得一团黑。

如果你做的是池塘、河流、湖泊,我强烈建议先在Opaque模式下把效果做好看,利用SceneDepth做深浅颜色过渡来模拟“透明度变化”。只有一种情况我会建议切Translucent:你需要水下物体真实可见,并且水面上没有多个半透明层叠加。遇到这种情况,可以先把Blend Mode切换为Translucent,再加一个Opacity参数,把Opacity用深度的渐变来驱动——水越浅的部分越透明,水越深的部分越不透明,这样比固定透明度自然得多。

但排序问题并不会自动消失。如果发现穿帮,一个常用的补救方法是把水面的TranslucencyPass设置为AfterDOF,或者用单独的透明排序通道,但这已经属于半透明渲染的深水区了,等后续系列文章单独展开。

6.3 材质性能与指令数量的软性上限

水材质是场景里最容易堆指令数的地方,因为法线采样、WPO、深度计算、反射混合全都集中在一张材质里。我自己做项目时有一个经验值:

  • 中低端移动设备:水材质指令数控制在200以下,纹理采样控制在6次以内。
  • PC端中高画质:水材质指令数控制在300到400之间,纹理采样可以到8到10次。
  • 超过这个范围,建议拆节点做成Material Function,或者用材质LOD(根据距离切换低配水材质)。

一个非常重要的性能杀手是WPO。World Position Offset在渲染管线的几何阶段生效,它会打断一些静态网格体的预计算,导致引擎对水面网格体不能再使用GPU烘焙的加速结构。所以如果你的水满屏大片,只用WPO做近景的少量波浪就行,远景部分可以通过材质LOD直接关闭WPO,肉眼几乎分辨不出来。

6.4 材质编辑器里的小技巧:编译卡顿怎么避免

材质编辑器默认是“改动实时编译”模式,做复杂材质时机子会卡得连鼠标都拖不动。我习惯在左侧工具栏找到“Stats”旁边的编译模式按钮,切换成手动编译,这样你可以先把一堆节点连完、参数摆好,再按一下编译键看结果。别小看这个操作,做水材质这种需要反复调参的内容,手动编译能省一半时间。

还有一个小技巧:使用材质实例(Material Instance)来调节参数。做法是把材质里会频繁调整的值,比如深浅水色、Roughness、法线强度,全部选中后在Details里点“Convert to Parameter”转换成参数。然后在内容浏览器右键这个材质创建材质实例,场景里调实例参数时实时反馈,不用每次都进材质编辑器改。这个方法在调水的深度过渡时尤其好用,我几乎每次做水面都会顺手把主要颜色和强度都转成参数。

7. 从这套图表继续延伸:透明水与系列后续方向

做到这里,你已经拥有一张可用的简单水材质:它有颜色深浅过渡、有法线流动、有WPO起伏、有菲涅尔边缘光、有高光控制。如果要把它升级成真正的透明水,需要做的改动其实很小:材质混合模式切到Translucent,新增Opacity和OpacityMask引脚,把深度计算的结果乘进透明度里。但伴随而来的是半透明排序和性能问题,这块我会找机会单独写一篇。

泡沫是另一个非常实用的方向。泡沫的本质就是:用SceneDepth计算出的深度值,设定一个阈值,把水特别靠近岸边的区域显示成白色,再叠加上一张泡沫贴图做细节。这套思路在材质图里大概只需要十个节点,却能让水岸交接的地方一下子“活”起来。如果当前项目只是需要一块远景的海面,那么本文这套已经能应付;如果要继续深入做礁石激浪、卡通风格海、角色踩水交互,每一块都值得单独拆开讲。

我在实际带人做项目时发现,最快的学习路径永远是先把不透明版本的完整链路跑通,理解每一条线在干什么,再往里面加特性。别因为网上那些炫酷水材质就激进地直接上八层法线叠加、三维噪声、程序化波浪函数——入手难不说,出了问题根本没法排查。把本文这套基础链路吃透,你后面看任何复杂水材质图表都不会再发怵。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询