WebGL水波纹效果核心是GPU加速的顶点位移、法线扰动与反射/折射光照模拟:用多频正弦叠加驱动顶点Y向起伏,dFdx/dFdy实时计算扰动法线,再结合屏空间反射贴图与菲涅尔混合实现通透感。

WebGL 实现水波纹效果,核心是用 GPU 加速的顶点位移 + 法线扰动 + 反射/折射光照模拟,而不是逐像素计算物理波形。关键在于用简单的正弦叠加或噪声纹理驱动顶点高度,并实时更新法线以影响光照和反射。
用顶点着色器做动态高度偏移
水表面不是静态平面,需要让每个顶点沿 Y 轴(或法线方向)产生周期性起伏。常用方式是组合多个不同频率、振幅、相位的正弦函数:
- 在顶点着色器中,把顶点原始位置
position输入,用sin(time * freq + uv.x * scale)等表达式生成高度偏移量 - 叠加 2–3 组不同参数的正弦波(例如:主波 + 次波 + 微扰),避免单一频率带来的机械感
- 时间变量
u_time由 JavaScript 每帧传入,确保动画连续;UV 坐标决定空间变化规律
实时计算扰动后的法线向量
单纯抬高顶点还不足以产生真实水光效果——光照依赖法线方向。若不更新法线,水面会像“有起伏的塑料板”,没有光泽流动感:
- 对高度函数
h(uv)分别沿 U 和 V 方向求偏导(可用中心差分近似),构成切向量 - 用叉积算出新法线:
normal = normalize(cross(dFdx, dFdy)) - WebGL 中可直接在顶点着色器里用
dFdx()/dFdy()获取导数,无需手算解析式
叠加反射与折射实现通透感
真实水面同时呈现环境反射(上层)和水下折射(下层)。WebGL 中常用屏空间技巧模拟:
立即学习“Java免费学习笔记(深入)”;
- 先渲染一次场景到纹理(环境贴图),作为“天空/背景”反射源
- 用扰动后的 UV 坐标采样该纹理,模拟反射偏移(如
uv + normal.xy * reflectFactor) - 再叠加一层轻微模糊或颜色衰减的折射效果(例如降低饱和度、加蓝调),混合比例随视角角变化(Schlick 近似)
- 简单项目可跳过真正折射,只用带偏移的反射+半透明+菲涅尔系数控制边缘反光强度
优化与实用建议
水波纹容易卡顿或闪烁,注意这些细节:
- 避免在片元着色器里重复计算复杂噪声;把高度/法线逻辑尽量放在顶点着色器,减少 fragment 计算量
- 使用低分辨率水面网格(如 64×64 顶点),靠着色器插值保证视觉平滑,比高模省 GPU 带宽
- 时间 uniform 推荐用
performance.now() / 1000,而非requestAnimationFrame时间戳,避免跳帧导致波速突变 - 加入风向扰动项(如
sin(uv.x * windSpeed + u_time))能让波纹更自然,比纯同心圆更可信


















