Realistic Water 水体渲染实战:参数调优、场景搭建与性能优化

发布时间:2026/10/14 9:56:29
Realistic Water 水体渲染实战:参数调优、场景搭建与性能优化
做项目时碰到一滩水面最难受的不是水本身而是水怎么和光、阴影、角色交互融合。Realistic Water 是我在几个版本里反复对比后一直留在项目中的水体方案它把浮沫、折射、深度变色、波浪扰动这些本来要写半天的东西打包成一组参数你只需要按项目调到一个顺眼的状态就能获得接近真实的水面效果。这篇文章不聊官方文档里那些套话只讲我真正把它用进项目的完整流程包括安装准备、参数含义、场景摆放、性能取舍和实际踩过的坑。1. Realistic Water 是什么为什么值得上手1.1 它解决的核心问题Realistic Water 本质上是一套游戏引擎用的水面渲染方案。普通水面用一张平铺的蓝色半透明材质就能糊弄但换成真实感水面要处理的东西非常多远处的水面要有天空反射近处要有透明度渐变水下地形会改变水的颜色波浪会不断扰动法线岸边还要有泡沫圈。这些东西如果全从零开始写一个经验不太丰富的开发者可能要折腾几周而且写出来的效果还不一定经得起多角度观察。Realistic Water 把这些常见水体表现收拢到一张材质控制面板里用脚本、着色器和配套贴图组合让你在几分钟内获得一个可看的水面。这不是说它像一键生成那样完全不需要动脑而是把原本分散在多个模块里的逻辑统一成一个入口。你调的是水位的高度、波浪的高度、泡沫的宽度、反射的强度而不是纠结某个像素值该怎么计算。对我个人来说它最大的价值是“下限足够高”。即使默认参数不改扔进场景里也比自己临时拼的蓝色半透明面片强得多。如果你要做的是写实风格的室外湖面、海边、泳池或者河道这类资源能帮你把水面那部分工期压缩到一天以内。1.2 适合放到哪些场景里用我实际用过的项目类型主要有三类第一类是步行模拟和探索类场景。这类场景经常有很长的湖边栈道或者峡谷河道玩家会长时间盯着水看。水面的运动如果太规律一眼就能看出是贴图在滑如果太复杂性能又容易撑不住。Realistic Water 默认的波频叠加方式比较自然远处看着安静近处慢慢起波纹适合这种慢节奏观察。第二类是开放世界的交互水体。比如主角能下水游泳、船和木筏能浮在水面上或者怪物会从水里冒出来。这类需求光好看不够还需要有入水交互的波纹、深浅交界处的变色以及水面上物体浮力相关的参数。Realistic Water 配套的交互脚本就是做这个的我会在后面专门讲。第三类是可视化和建筑漫游。这类项目不需要很多玩法但画面要有“照片感”。水面作为场景里重要的反射源处理好了整个空间会显得很润。把实时反射开高一点配合烘焙过的环境贴图效果甚至比游戏里还稳。2. 安装前的准备与资源导入2.1 环境和渲染管线兼容性先说环境。Realistic Water 在大多数商业引擎的默认内置渲染管线下都能直接跑但如果你把项目切到通用渲染管线或者高清渲染管线导入时就要特别注意着色器兼容性。我踩过的最基础一个坑是旧版本资源没有针对新管线的着色器结果材质球显示成洋红色错误色之后整个水面都渲染不出来。所以导入之前先确认三件事当前项目的渲染管线用的是什么。如果项目还停留在默认管线上通常导入后就能用如果已经切到了通用渲染管线需要选用资源包中带“通用渲染管线”后缀的着色器或者到资源商店下载对应的兼容扩展包。项目的着色器编译目标是否够高。移动端如果开了很低的图形 API可能无法编译一些高级水面着色器功能导致水面显示为空白或者纯色。场景里有没有天空盒和环境反射源。真实感水面的反射很大程度靠场景环境贴图如果没有环境反射水面再怎么调都会显得像一块深色塑料。2.2 导入步骤与常见报错导入步骤本身很标准在资源商店窗口里下载包然后在导入面板里勾选全部内容点导入。但有几个细节我建议你务必手动勾选核心脚本文件夹包括水面的交互控制器和材质参数脚本。着色器文件夹不要只导入材质文件。纹理和立方体贴图文件夹很多效果取决于那些看起来不起眼的噪声图。示例场景文件夹可以勾选但尽量单独放导入后先打开示例场景看默认效果再对照自己场景调。导入完成后如果材质球是洋红色先不要急着删资源。去着色器列表里搜索这个体的着色器名称检查是否包含当前管线的变体。我遇到过的情况是着色器在列表里显示正常但材质球还是洋红原因出在工程里存在两个同名着色器系统优先选了一个不兼容的。把多余的同名着色器移出工程问题就解决了。还有一种是水面导入后变成了黑色方块。这种情况通常不是着色器问题而是场景里没有放置方向光或者反射探针没刷新。你先检查 Light Explorer 列表里有没有有效光源再检查反射探针的类型是不是实时并且已经烘焙。很多时候加一个简单的天空盒反射就能恢复正常。3. 核心参数与逐项调优3.1 水波法线与波浪模拟参数Realistic Water 的水面效果大体分两层表层细节是“法线贴图扰动”中层运动是“顶点波浪位移”。调节参数时如果只盯着一个数值容易顾此失彼我习惯按“远看-近看-交互”三个顺序来调。法线贴图方面最关键的参数是法线强度和UV平铺倍率。法线强度决定波纹对光照的折射效果调到很高时水面会出现大量刺眼的高光闪烁俗称“碎玻璃感”。我通常把整体法线强度控制在 0.2 到 0.4再配合一层较弱的细节法线细节法线用更大的平铺倍数比如基础法线平铺 4 倍细节法线平铺 12 倍。这样近看有颗粒远看不糊。顶点波浪部分要区分“浅水湖面”和“深海大浪”。浅水湖面用一到两层正弦波就够了波浪高度在 0.1 到 0.3频率偏低给人安静感。深海或者风浪场景可以开启更高阶的波函数比如基于 Gerstner 波的组合。Gerstner 波会让顶点真正围绕波峰旋转形成更尖锐的浪尖但代价是顶点位移计算量变大而且如果面数不够网格会明显穿帮。我的建议是先关闭所有额外波把法线贴图调到舒服的样子再一层一层把顶点波浪加回去。每次加一层就旋转视角看远中近三个距离不要一次性把所有参数拉满否则很难定位是哪一层导致的水面发乱。3.2 颜色、透明度、吸收与散射水面颜色不是随便给一个蓝而是由“浅水色”“深水色”和“吸收系数”共同决定的。真实世界里水质对光的吸收是有波长的蓝光穿透更强红黄光在深处衰减快所以深水偏蓝黑浅水偏青绿。在材质参数里你会看到类似“浅滩颜色”和“深水颜色”两个颜色拾取器。浅水色我习惯用偏青的颜色RGB 大约在 (0.2, 0.35, 0.45)深水色用偏蓝黑的颜色RGB 在 (0.05, 0.12, 0.25)。两个颜色之间的过渡靠深度图计算深度越深颜色越接近深水色。注意这里的“深度”不是模型空间内的 mesh 深度而是水下碰撞体或者地形造成的视线深度。如果你在场景里建了一个水面却没有给底部水体添加碰撞盒材质脚本无法计算深度水面就可能全程显示为深水颜色。为了让游戏角色能站在浅水区里你需要在水中或者水底放置一个能够作为深度参考的碰撞体或者使用脚本提供的水下地形高度采样。散射参数影响水在高光区域的柔和感。默认值都不太高你可以在正午强光场景下把散射强度微微调高让波峰附近出现一层淡淡的乳白色光晕这个光晕就是“体积感”的来源。如果调太高整片水会像牛奶那就成了毒化湖泊不是写实水。3.3 反射、折射与岸边泡沫反射是真实感水面的重头戏。Realistic Water 一般提供两种反射方式平面反射和立方体贴图反射。平面反射会基于水面平面镜像渲染场景精度高但代价是场景绘制两边非常吃显卡立方体贴图反射则是用一个静态或者近似动态的反射探针来采样环境效果好但移动水面变化有限。我个人在高端画质下会用平面反射并限制在 128 到 256 的分辨率在中低端情况下使用反射探针把更新频率降到每帧或者每两帧。反射强度不要拉到 1真实水面的反射率随观察角度变化水面材质里通常有“菲涅尔强度”或者“反射角度曲线”。把菲涅尔强度保持在默认附近只微调否则水面会像镜子一样完全没有水的质感。折射方面主要关注折射强度和扭曲噪声。如果场景水下有石头、水草或者地形纹理折射能把这些内容按波纹方向扭曲看起来更有立体感。但折射是典型的“远看没用、近看惊艳”效果大场景且镜头很少靠近水面时可以直接关闭折射换取性能。岸边泡沫也就是水体和陆地交界处的白色边缘通常是由深度差触发的。只要深度差小于一个设定阈值就开始生成泡沫噪声。调泡沫的时候我会分两步先调“泡沫范围”到只覆盖岸边一小圈再把“泡沫噪声”调到一个不太规则的形态。如果泡沫范围到 3 米整片湖都会像浑身起泡很假。3.4 交互与入水波纹如果你的游戏里有角色、船或者动物接触水面那就需要开启交互波纹。Realistic Water 的常见做法是维护一张交互贴图当物体落入水中时把碰撞产生的波形写入贴图然后水面材质采样这张贴图来产生位移或法线扰动。实际上网上的常见做法是给水面物体挂一个交互脚本脚本检测碰撞体然后把碰撞位置和物体的速度写入水面材质属性。我实际测试下来有几个参数格外重要交互分辨率推荐 512 或 1024太低会导致波纹变成像素方块。波纹衰减速度衰减太快会看不出交互太慢会让水面像果冻一样一直抖。波纹强度和波浪高度分开如果强度超过 1波纹会出现负值顶点导致水面凹陷穿帮。我踩过最大的坑是交互脚本只在物体“刚开始接触”水面时写入一次但游泳角色一直浸在水里时反而没有持续的圆形波纹。后来发现原因是脚本需要在持续触发阶段也写入波纹而不是只在 Enter 阶段触发。修改方式是在触发检测的 Stay 阶段每隔一小段时间把当前位置和速度写入交互纹理效果立刻自然了很多。4. 实操流程从空场景到一滩能用的水面4.1 搭建水面平面和网格空场景里新建一个平面网格当作水体。关键不是你建的平面有多大而是网格的细分数量。如果你要开启顶点波浪需要足够多的顶点来表现波形一个只有四个顶点的平面无论如何都模拟不出波峰。以一百米乘一百米的水面为例我建议至少给到 32 乘 32 的网格分段。如果是二十米乘二十米的小池塘可以给到 64 乘 64近处看起来更细腻。不要无脑上 256 分段五六个这样的大水面会把移动端直接拖垮。网格建好之后把平面的 Y 轴高度抬到水位线也就是水面和陆地接触的位置。水面平面不需要完全覆盖水域边界它稍微延伸到地下或者沙滩下方都可以因为岸边泡沫会遮住大多数边缘剩下的你可以用场景里的地形凹凸去挡住。4.2 材质参数按场景微调把 Realistic Water 的材质球拖到水面物体上材质肯定需要改成透明或者表面着色器的正确队列。这里有一个关键检查点材质的渲染队列必须低于场景中的半透明物体否则角色站在水里时身体会叠在水面后面出现奇怪的穿插。我一般先设置一套“白天湖面”参数波浪高度0.2波浪频率0.8波浪方向轻微偏移避免横平竖直法线强度0.3细节法线强度0.15浅水色青绿色深水色深蓝黑反射强度0.8菲涅尔强度1泡沫范围0.6泡沫噪声0.35这套参数在白天场景里很稳不会出大错。到了黄昏或者夜景需要把颜色整体压暗反射强度降低泡沫噪点要更少一点。夜景里高光反射容易过曝可以把反射探针的分辨率调低并在材质里把“镜头亮度影响”和“自发光强度”降到接近零。4.3 光照和反射源摆放水面对光照的敏感程度远超普通地形。如果你发现水面颜色很脏先不要怀疑水面参数看看方向光的角度。方向光与水平面的夹角越低水面反射天空区域的范围越大水面看起来会越像明亮的镜子。正午时太阳在头顶水面反而偏暗因为反射的是比较暗的地面或者天空阴影。所以调水面时最好先在场景里固定一个方向光确定好主光角度再动材质。反射源方面场景里要有一个天空盒并且反射探针要覆盖水面所在区域。如果反射探针位置离水面太远或者被地形遮挡水面反射会明显对不上周围环境。探针会捕捉最近的一些物体反射因此最好把探针放在水面正上方一到两米处让它的立方体贴图能拍到周围的岸和树。动态物体的反射如果能被水面捕捉到在实时反射开启时最准确如果使用静态反射探针船和角色不会出现在水面的倒影里玩家很容易发现。这种情况下建议开一个低分辨率的实时平面反射或者定期刷新反射探针。4.4 深度和地形配合要让水底渐变和岸边泡沫正确显现场景中需要给水下的地形添加碰撞或者深度计算支持。我习惯在水面材质不生效时先在场景里检查深度图是否开启。一个常见操作是把水底的地形碰撞体放到比水面稍微低一点点确保水面之下确实存在一个深度参考。如果水底地形和水面重合深度差为零会触发岸边泡沫大片生成或者水面直接显示成最浅色。如果你用的是程序生成地形需要注意水面和水底的层级关系。水面通常需要放在世界坐标同一高度而水底地形会随起伏变化深度差计算必须支持地形高度采样。Realistic Water 如果自带地形高度采样脚本就把脚本挂到水面物体上然后将地形对象拖进引用槽。实测下来用脚本采高地比单纯依赖碰撞盒更平滑尤其是地形有凹凸时岸边的泡沫会沿着地形轮廓自然过渡而不是整圈一个宽度。5. 性能优化与移动端适配5.1 关键性能开关水面效果好看但性能开销往往都很大。我优化时喜欢按开销从大到小排序实时平面反射开销最大开启后场景相当于多渲染一次分辨率越高越离谱。优化降分辨率、关闭反射层中的动态角色和树木、设置反射更新间隔。如果镜头基本在水面附近可以使用六面贴图反射代替。折射需要额外渲染深度和颜色开销也不小。优化关闭折射或改成采样一张静态水下纹理。顶点波浪顶点位移会打断静态合批导致水面无法被引擎合并。优化远处水面用无位移的简化材质近处水面用高细分带位移的材质。多层法线采样每个法线纹理采样都对应 GPU 带宽消耗。优化减少法线层把两到三层法线合并成一张更高分辨率的法线贴图。交互波纹更新交互纹理每一帧写入会占用少量 GPU 计算和绘制命令。优化只在有物体触及水面时更新无交互时冻结纹理。5.2 LOD 与多层水面方案大场景建议不要把一整片巨型水面挂同一个高精度材质而是拆成“近水层”和“远水层”。近水层覆盖角色周围 30 到 50 米使用高细分网格、完整交互和折射远水层覆盖更远区域使用简单的半透明材质加一张法线贴图甚至不加顶点波浪。两层水面在交界处会有轻微缝隙或者高度差我一般让远水层的 Y 轴比近水层低 0.01 到 0.02这样即便有接缝也不容易察觉。如果有一块海岸线非常远还可以再加一层纯色平面贴天空盒渐变色几乎零开销。移动端优化时最优先考虑的是把水面材质变体切到“移动版”或者“简化版”。这类变体通常会去掉顶点位移、实时反射、多层散射和交互波纹只保留两层法线加颜色渐变。看起来水会平面一点但帧率能保住。另一个被忽视的点是透明物体的渲染顺序移动端 GPU 过半透明物体负担很大尽量减少半透明水面的重叠。5.3 帧率波动排查的两个观察点如果开启水面后帧率上下跳不要只盯着水面本身。先检查相机近裁剪面离水面是否太近因为近裁剪面过小会导致深度缓冲区压力剧增水面深度计算所依赖的深度预通道也变慢。我遇到过一次水面不开启时 60 帧开启后掉到 30 帧找半天发现是相机近裁剪面设成了 0.01把水面网格切成大量极细的三角形。改成 0.3 后帧率提升了近一倍。另一个观察点是阴影。水面使用了透明材质时如果加入了实时阴影会让透明物体每帧都要重新写入阴影映射导致开销直线上升。水面一般不需要投射自身的实时阴影可以在材质设置里关闭投射阴影只接收来自场景其他物体的阴影。6. 常见问题与排查技巧实录6.1 问题速查表我把实际使用中遇到的高频问题整理成了一个表方便你快速定位现象可能原因解决方向水面整体发黑场景没有环境反射或反射探针未更新添加天空盒并放置可用的反射探针水面是洋红色着色器与当前渲染管线不兼容切换正确的着色器变体或更新资源包水面全是泡沫深度纹理缺失或泡沫范围过大开启深度预通道减小泡沫范围水面像镜子很亮菲涅尔没生效或反射强度过高降低反射强度检查菲涅尔开关水波高速闪动法线贴图平铺倍数过大或细节法线强度过高降低平铺倍数和强度水底看不到折射关闭且透明度深度计算失效打开深度图支持确认水底碰撞体存在交互波纹不持续脚本只在进入阶段写入一次在持续触发阶段补充波纹写入移动端掉帧严重实时反射或折射打开切换到移动变体关闭反射折射6.2 水面边缘出现白边怎么办水面和岸边交界处出现一圈生硬的白色边缘是新手最容易碰到的问题。尤其是地形比水面高一点点时深度差接近零泡沫阈值就会把白色泡沫当成越大越好的效果其实是泡沫数值过大。把“泡沫最小深度”从默认的 0 提高一点比如 0.05这样只有水深在某个范围内才会触发泡沫岸边的浅水部分就不会全部变白。同时把泡沫噪声对比度调高一点让泡沫看起来是一团一团而不是均匀铺开。如果边缘白边还是存在检查地面的碰撞深度是否包含在水面材质计算中。很多时候你看到的现象是白边但根本原因是地形没有参与深度计算导致材质认为所有地方都是 0 深度。6.3 水面黑块闪烁和阴影痤疮水面黑块闪烁通常发生在有实时反射的场景。反射相机渲染场景时影子贴图分辨率不一致或者反射相机的位置刚好被地形遮挡水面反射就会局部跳出黑块。解决办法有几种把反射相机的近远裁剪面调成和主相机一致给水面下方加一个碰撞体来挡住反射相机穿到水下如果黑块只在特定角度出现则可能让反射相机跟随主相机但在水面上方固定偏移防止进入水面下方。阴影痤疮则表现为水面在阳光直射下出现条纹状飘动阴影。这是因为透明材质在阴影计算时深度偏差不够导致阴影贴图产生自遮挡。解决方法可以在水面材质设置中关闭“投射阴影”或者将阴影偏移参数调低让透明物体不参与阴影生成。6.4 大水面UV拉伸导致波纹尺寸怪异如果你的水面像一整块大理石纹理一样每个波纹都巨大无比问题几乎都出在 UV 平铺。默认材质经常把水面纹理的 UV 设为自动当平面长度为 100 米时原本应该平铺几十次的纹理只在表面上铺了一次波纹自然大到失真。解决方式是给水面材质指定一个世界空间尺寸参数或者手动设置平铺数值。我习惯把主法线贴图的平铺设为 10 到 20细节法线贴图设为 30 到 50这样在百米级水面上波纹尺度接近真实水体。调整平铺时不要让两张法线的密度过于接近否则会出现明显的摩尔纹干扰。7. 最后再分享一点个人体会用 Realistic Water 这套资源的时间越长我越觉得它最大的价值不是“逼真”而是“可控”。你在项目里真正需要的是一个能跟着场景光照变、能承受交互反馈、能被性能预算限制的水面工具而不是一个静态的截图。我在项目里每次调水面都会经历一个循环先放默认参数观察远中近三个视角然后调颜色和泡沫让水位和地形贴合接着加交互和反射盯着角色入水的那一瞬间最后关掉一半效果让移动端也能跑。这个过程看起来很繁琐但一旦你对参数心里有数每次调整都不会超过五分钟。如果你刚开始接触它不要急着把所有特效都打开。先把一个最简单的小池塘调到顺眼再把同一套逻辑推广到湖面和海上。遇到水面发黑就先去检查环境和反射遇到性能问题就先压缩反射和折射遇到边缘奇怪就先看深度差。大部分问题都不是资源本身的问题而是使用它的人对渲染管线理解不到位。最后一个小技巧如果你打算让水面在多个关卡里复用建议把调好的材质参数保存成一个独立的材质资产并且不同场景共用这套基础参数。这样你只需要在某个关卡里微调颜色和波浪强度而不用从零开始重新设置。公用一套基础材质后期迭代会省下很多重复劳动。