
基于物理的水下效果
水底的效果主要是模拟水下光的吸收散射来实现的这样有着更加真实的效果,可以看到这个算法和SingleLayerWater很像,SLWUnderwater(SLW = Single-Layer Water)这个算法就是配合UE的SingleLayerWater去实现水底效果。
float DirScattLuminancePhase = PhaseG;
float3 AmbIsotropicPhase = AmbientPhase / (4.0f * PI);
float DirectionalLightShadow = 1.0;
float3 SunIlluminance = ResolvedView.DirectionalLightColor.rgb* DirectionalLightShadow * PI;
float3 ExtinctionCoeff = ScatteringCoeff + AbsorptionCoeff;
float3 ExtinctionCoeffSafe = max(ScatteringCoeff + AbsorptionCoeff, 1e-5);
float3 Transmittance = exp(-ExtinctionCoeff * BehindWaterDeltaDepth);
float3 LuminanceCoeff = (ScatteringCoeff / ExtinctionCoeffSafe) * (1.0 - Transmittance);
float3 ScatteredLuminance = LuminanceCoeff * (AmbientIlluminance + ((AmbIsotropicPhase + DirScattLuminancePhase ) * SunIlluminance));
FogAbsorption = Transmittance;
FogColor = ScatteredLuminance;//* ResolvedView.OneOverPreExposure;
FogAlpha = dot(ScatteredLuminance, 0.3333);
return 0;
可以一行一行的解释这个核心算法
float DirScattLuminancePhase = PhaseG;
- 直射散射的相函数值(来自 Schlick/Henyey–Greenstein 的 g)。越大=越“前向散射”,太阳方向更亮。
float3 AmbIsotropicPhase = AmbientPhase / (4.0f * PI);
- 环境光的各向同性相函数,对 4π 球面做了归一(平均分布)。
float DirectionalLightShadow = 1.0;
float3 SunIlluminance = ResolvedView.DirectionalLightColor.rgb * DirectionalLightShadow * PI;
- 主光强度(当前直接设 1.0,不做阴影衰减)。乘
PI相当于把辐照/亮度按朗伯关系做能量标定。
float3 ExtinctionCoeff = ScatteringCoeff + AbsorptionCoeff; // σ_t
float3 ExtinctionCoeffSafe = max(ScatteringCoeff + AbsorptionCoeff, 1e-5);
- 消光系数 σ_t = σ_s + σ_a。后者加了一个极小值,防止除零。
float3 Transmittance = exp(-ExtinctionCoeff * BehindWaterDeltaDepth); // T(d)=e^{-σ_t d}
- Beer–Lambert 透过率:穿过介质厚度
BehindWaterDeltaDepth后还能剩下多少直射光。
float3 LuminanceCoeff = (ScatteringCoeff / ExtinctionCoeffSafe) * (1.0 - Transmittance);
- 这是核心的“入射→单次散射”比例:
(1 - T)= 被介质“截获”的那部分光σ_s / σ_t= 单次散射反照率(被截获的光里有多少走向散射而不是被吸收)
float3 ScatteredLuminance = LuminanceCoeff * (
AmbientIlluminance + ((AmbIsotropicPhase + DirScattLuminancePhase) * SunIlluminance)
);
- 用上面系数去“分配”环境光与主光的散射:
- 环境光 → 各向同性(
AmbIsotropicPhase) - 主光 → 相函数加权(
DirScattLuminancePhase)
- 环境光 → 各向同性(
- 两者加起来得到体积里被散射出来的亮度。
FogAbsorption = Transmittance; // 透过率(给上层当吸收/透光因子用)
FogColor = ScatteredLuminance; // 体积里被散射出来的颜色
FogAlpha = dot(ScatteredLuminance, 0.3333); // 近似亮度当 Alpha(简单平均)
return 0;
-
Transmittance vs Depth
- 清澈水体(σ_t 小)下降慢;浑浊水体(σ_t 大)下降快。
- 这是
exp(-σ_t d)的形状:深度越大,直射光剩的越少。

-
Scattered Luminance vs Depth
- 起步随深度快速上升(因为
(1 - T)从 0 变大); - 再往深处趋于平台(因为
T→0,1 - T→1)。 - 浑浊水体因为 σ_s 大,散射亮度更高、更快饱和。

- 起步随深度快速上升(因为
-
Phase(g)对亮度的影响
- 相同水体,g 越大(越前向散射),直射方向的散射亮度越高。
- 这就是
DirScattLuminancePhase在乘主光照后的放大效果。

快速调参
- 变浑:增大
AbsorptionCoeff或ScatteringCoeff→Transmittance更快掉,FogColor更快到平台、整体更亮更“雾”。 - 更亮的太阳方向“体光柱”:增大
PhaseG(0.6→0.85),或提高SunIlluminance。 - 更蓝/更绿的水色:改
ScatteringCoeff/AbsorptionCoeff的 RGB 比例(通常水体蓝通道的 σ_s 比例更高,红通道吸收更强)。

我们的输入和SinglayerWater材质的其实是一样的。
散射与吸收

散射
散射光相对于太阳方向的各向异性方向性,PhaseG可以为正值或负值,其中正值会增加向(向前)太阳散射的光量,负值会增加从太阳(向后)散射的光量。根据当前视图和太阳在天空中的位置,这可以使水看起来更亮或更暗。



水的深度
计算的是水体的厚度,由入射点到出射点(终点)的长度

环境亮度

