Water Rendering — Single Layer Water Shading & Pipeline水体渲染 — 单层水体着色与管线
Why a Dedicated Water Model?为何需要专用水体模型?
Water is optically unique: it simultaneously reflects (air–water interface with Fresnel), refracts (objects underwater are displaced by IOR ≈ 1.33), and absorbs (exponential extinction per wavelength — red dies fastest, blue persists longest). None of the standard shading models handle all three together. The Single Layer Water shading model was purpose-built to solve this, treating the water surface as a thin interface between air and a participating medium.
水在光学上是独特的:它同时反射(具有 Fresnel 的空气–水界面)、 折射(水下物体因 IOR ≈ 1.33 而发生位移),以及吸收 (每个波长的指数衰减——红色最先消失,蓝色持续最久)。 没有任何标准着色模型能同时处理这三者。 单层水体着色模型专为解决此问题而构建, 将水面视为空气与参与介质之间的薄界面。
Single Layer Water BRDF — The Math单层水体 BRDF — 数学原理
The water shading model evaluates four components and combines them into the final pixel color:
水体着色模型计算四个分量并将它们合并为最终像素颜色:
// SingleLayerWaterShading.ush — conceptual structure float4 EvaluateSingleLayerWater(FSingleLayerWaterData W, FGBufferData G, ...) { // 1. SURFACE SPECULAR — GGX reflection, same as Default Lit float3 SurfaceSpecular = SpecularGGX(G.Roughness, 0.02 /*F0 for water*/, Context); // 2. FRESNEL REFLECTANCE — how much light reflects vs refracts // Schlick approximation with IOR=1.33 float F0_water = ((1.33f - 1.0f) / (1.33f + 1.0f)); F0_water *= F0_water; // ≈ 0.02 float FresnelR = F0_water + (1 - F0_water) * pow5(1 - saturate(dot(N, V))); float Transmission = 1.0f - FresnelR; // complement: refracted fraction // 3. WATER ABSORPTION — Beer-Lambert law per channel // WaterDepth = distance from surface to bed (in cm) float WaterDepth = max(0, W.SurfaceWorldZ - SceneBedWorldZ); float3 Transmittance = exp(-W.AbsorptionCoeff * WaterDepth / 100.0f); // 4. SCATTER COLOR — in-water scattering (Mie: forward scatter, blue-ish) // Henyey-Greenstein phase function for directional scatter float ScatterPhase = HenyeyGreenstein(W.PhaseG, dot(L, V)); float3 ScatterColor = W.ScatterColor * ScatterPhase * WaterDepth; // 5. UNDERWATER COLOR — what's below, attenuated by absorption float3 RefractedUV = ComputeRefractedUV(PixelPos, G.Normal, IOR); float3 Underwater = SampleSceneColor(RefractedUV) * Transmittance; // 6. FINAL COMPOSITE return FresnelR * (SurfaceSpecular + ReflectionColor) + // reflected Transmission * (Underwater + ScatterColor); // refracted }
Key Physical Parameters Explained关键物理参数说明
| Parameter参数 | Physical Meaning物理含义 | Typical Values典型值 |
|---|---|---|
| WaterScatterColor | The color of light scattered within the water body — gives tropical water its teal/cyan, or murky water its greenish-brown tint水体内散射光的颜色——使热带水呈现蓝绿/青色,或浑浊水呈现绿棕色 | Tropical: (0.1, 0.6, 0.6) Ocean: (0.05, 0.3, 0.5)热带:(0.1, 0.6, 0.6) |
| WaterAbsorptionColor | Per-channel extinction coefficient (cm⁻¹). Higher = faster absorption. Red absorbs first in real water.逐通道消光系数(cm⁻¹)。越高吸收越快。真实水中红色最先被吸收。 | Clear: (0.3, 0.05, 0.01) Murky: (0.7, 0.4, 0.2)清澈:(0.3, 0.05, 0.01) |
| ColorAbsorptionDepth | Reference depth (cm) for absorption curve normalization吸收曲线归一化的参考深度(cm) | 200–500 |
| WaterPhaseG | Henyey-Greenstein phase anisotropy: 0=isotropic, +1=full forward scatter, -1=full back scatterHenyey-Greenstein 相位各向异性:0=各向同性,+1=完全前向散射,-1=完全后向散射 | 0.1–0.5 |
Water Rendering Pipeline — Per-Frame Passes水体渲染管线 — 逐帧通道
The critical insight is that water renders after all opaque geometry. At render time, the
SceneColor texture already contains the correctly-lit underwater floor. The water shader
simply reads it (with a refraction offset for distortion), applies the absorption/scatter math,
and adds the surface reflection — all in a single pass. No separate "underwater render" is needed.
关键洞察是水体在所有不透明几何体之后渲染。在渲染时,
SceneColor 纹理已经包含正确光照的水下地面。
水体 Shader 只需读取它(使用折射偏移进行扭曲),应用吸收/散射数学,
然后添加水面反射——全在单一通道中完成。不需要单独的"水下渲染"。
Refraction & Underwater Distortion折射与水下扭曲
Refraction in UE5 water works by offsetting the screen-space UV when sampling the underwater scene color. The offset direction and magnitude is derived from the surface normal: a flat calm water surface has zero offset (you see straight down); a normal-mapped choppy surface distorts the underwater view.
UE5 水体中的折射通过在采样水下场景颜色时偏移屏幕空间 UV 来实现。 偏移方向和大小从表面法线推导: 平静的水面偏移为零(你直视水下);法线映射的波涛汹涌的水面扭曲水下视图。
// Refraction UV offset (SingleLayerWaterShading.ush concept) float2 ComputeRefractionUVOffset(float3 WorldNormal, float3 V, float RefractStrength) { // Project normal deviation onto screen plane float2 NormalXY = WorldNormal.xy; // Scale by depth (closer to camera = larger apparent distortion) float DepthScale = 1.0f / max(0.01f, SceneDepth); return NormalXY * RefractStrength * DepthScale; } // Sample underwater with refraction: float2 RefractUV = PixelUV + ComputeRefractionUVOffset(N, V, RefractStrength); // Clamp to avoid sampling outside the screen RefractUV = clamp(RefractUV, float2(0.001f), float2(0.999f)); // Depth test: if refracted UV hits geometry above water, fall back to no-refract float RefractedDepth = SceneDepthTexture.SampleLevel(PointSampler, RefractUV, 0).r; if (RefractedDepth < SurfaceDepth) RefractUV = PixelUV; // fallback: no distortion for above-water pixels float3 UnderwaterColor = SceneColorTexture.SampleLevel(BilinearSampler, RefractUV, 0).rgb;
Water Info Texture — Per-Pixel Water Properties水体信息纹理 — 逐像素水体属性
The Water Info Texture (WaterInfoTextureRendering.cpp) is a
screen-space render target that stores per-pixel water properties: surface height, normal, and flow
velocity. It's generated before the main water pass by rendering the water surface to a dedicated
2D texture. Downstream effects (foam, depth fade, caustics) sample this texture to know whether
a given screen pixel is underwater and at what depth.
水体信息纹理(WaterInfoTextureRendering.cpp)是一个
存储逐像素水体属性的屏幕空间渲染目标:水面高度、法线和流速。
它在主水体通道之前通过将水面渲染到专用 2D 纹理来生成。
下游效果(泡沫、深度消退、焦散)采样此纹理以了解给定屏幕像素是否在水下以及深度如何。
Reflections — SSR, Lumen & Planar反射 — SSR、Lumen 与平面反射
Water integrates with all three reflection systems in UE5:
水体与 UE5 中的所有三种反射系统集成:
- Screen Space Reflections (SSR): Works well for calm, nearly-flat water. Fast, but only sees what's on screen. Enabled by default. Set
r.SSR.Quality=4for higher quality on water. - 屏幕空间反射(SSR):适用于平静、几乎平坦的水体。快速,但只能看到屏幕上的内容。默认启用。在水体上设置
r.SSR.Quality=4以获得更高质量。 - Lumen Reflections: Full scene reflections including off-screen objects. Best quality for rough/choppy water (needs cone-traced reflections). Enable in Post Process Volume: Lumen → Reflections → Allow Lumen Reflections.
- Lumen 反射:完整场景反射,包括屏幕外物体。最适合粗糙/波涛汹涌的水体(需要锥形追踪反射)。在后处理体积中启用:Lumen → Reflections → Allow Lumen Reflections。
- Planar Reflections: Renders the scene from a reflected camera. Perfect mirror-quality but 2× rendering cost. Best for very still, highly reflective water (ponds, pools).
- 平面反射:从反射相机渲染场景。完美镜面质量但渲染成本翻倍。最适合非常平静、高度反射的水体(池塘、泳池)。
Material Setup Guide — Creating a Water Material材质配置指南 — 创建水体材质
Step 1: Material Settings步骤 1:材质设置
- Material Domain: Surface
- 材质域:Surface
- Blend Mode: Opaque (yes, opaque — the transparency is handled inside the SingleLayerWater shader)
- 混合模式:Opaque(是的,不透明——透明度在 SingleLayerWater Shader 内部处理)
- Shading Model: Single Layer Water
- 着色模型:Single Layer Water
- Two Sided: Off (usually — water is one-sided from above)
- 双面:关(通常——水体从上方是单面的)
Step 2: Required Material Inputs步骤 2:必要材质输入
// After selecting SingleLayerWater shading model, these special pins appear:
Normal:
→ Use two Panner nodes on a Normal Map texture (different speed/scale)
→ Cross-fade between two tiling scales for convincing motion
→ Adjust amplitude with HeightLerp or Lerp(Flat, Normal, WaveHeight)
Water Scatter Color: float3
→ The "in-water" color. Tropical: (0.05, 0.5, 0.5). Deep ocean: (0.01, 0.2, 0.4)
Water Absorption Color: float3
→ How fast each RGB channel dies with depth
→ For realistic ocean: (0.3, 0.06, 0.01) — red dies fastest
→ Connected to: Multiply(BaseColor, AbsorptionDepthMask)
Water Phase G: scalar [−1, 1]
→ 0 = isotropic scatter, 0.3 = forward bias (sunlit underwater glow)
Specular: scalar [0, 1]
→ Leave at 0.5 (maps to F0=0.04 for water)
Roughness: scalar
→ 0 = dead calm (mirror), 0.05–0.2 = ocean chop, 0.5+ = storm
Refraction (implicit): controlled by Normal map strength
→ Stronger normals = more underwater distortion
Step 3: Foam & Shoreline步骤 3:泡沫与海岸线
// Shore fade using Water Body depth: // In the material, use the built-in WaterBodyDepth output // (available on Water Body component, or via WaterInfoTexture) float ShoreFade = saturate(WaterBodyDepth / ShoreTransitionDist); float3 FoamMask = 1 - ShoreFade; // white at shore, zero in deep // Blend foam texture using FoamMask float3 FoamColor = FoamTexture.Sample(Sampler, UV * 8) * FoamMask; // Add foam to Emissive or blend into BaseColor // Note: foam uses Emissive not BaseColor to avoid shadowing Emissive = FoamColor * SunLightColor;
Performance Tuning性能调优
| CVar | Effect效果 |
|---|---|
| r.Water.SingleLayer.Refraction | Enable/disable screen-space refraction (0 = no underwater distortion, saves ~0.5ms)启用/禁用屏幕空间折射(0 = 无水下扭曲,节省约 0.5ms) |
| r.Water.SingleLayer.Reflection | Enable/disable surface reflections (0 = no reflections, significant savings)启用/禁用水面反射(0 = 无反射,显著节省) |
| r.Water.SingleLayer.ShadowReceiving | Enable/disable shadow receiving on water (0 = no shadows cast on water)启用/禁用水体接收阴影(0 = 水体上无阴影) |
| r.SSR.Quality | SSR quality on water (0=off, 1–4, 4=best). Reduce to 2 on mobile.水体上的 SSR 质量(0=关,1–4,4=最佳)。移动端减为 2。 |
| r.Water.VisualizeWaterInfo | 1 = show the WaterInfo texture (depth + normal + velocity) for debugging1 = 显示 WaterInfo 纹理(深度 + 法线 + 速度)用于调试 |
Source Navigation源码导航
- Renderer/Private/SingleLayerWaterRendering.h/.cppMain water pass orchestrator: schedule, depth composite, reflection integration主水体通道协调器:调度、深度合成、反射集成
- Shaders/Private/SingleLayerWaterShading.ushWater BRDF: absorption, Fresnel, scatter, refraction UV computation水体 BRDF:吸收、Fresnel、散射、折射 UV 计算
- Renderer/Private/WaterInfoTextureRendering.h/.cppWater info texture generation (depth, normal, flow velocity)水体信息纹理生成(深度、法线、流速)
- Shaders/Private/WaterInfoMerge.usfMerge multiple water body layers into the info texture将多个水体层合并到信息纹理中