案例 19.5菲涅尔与天空反射进阶
目标:水面反射天空;低头看时几乎透明,看向远处时像镜子。
水的 F0
第 9 章讲过,电介质在垂直入射时的反射率 F0 由折射率决定:
水的折射率约 1.33,F0 只有 2%:正对着看,98% 的光都透进水里,所以脚下的水清澈见底。随着视线变平,Schlick 近似 F = F0 + (1 − F0)(1 − N·V)5 迅速上升,掠射时接近 100%。这条曲线就是水面“近处透、远处亮”的全部原因。
Assets/Cookbook/Ch19/WaterCore.hlsl(节选:fresnel)
// Schlick 菲涅尔。水的折射率约 1.33,F0 = ((1.33-1)/(1.33+1))² ≈ 0.02:正看几乎全透,掠射角几乎全反 half WaterFresnel(half3 normalWS, half3 viewWS) { half NdotV = saturate(dot(normalWS, viewWS)); return 0.02h + 0.98h * pow(1.0h - NdotV, 5.0h); }
天空反射:GlossyEnvironmentReflection
反射颜色先用环境反射:反射方向 reflect(-V, N) 去查天空盒或反射探针。URP 14 的 GlobalIllumination.hlsl 提供了 GlossyEnvironmentReflection,它会根据粗糙度选择 mip 级别,并处理反射探针混合与盒体投影。这里有一个 URP 14 特有的坑:
// 摘自 URP 14 ShaderLibrary/GlobalIllumination.hlsl(节选) half3 GlossyEnvironmentReflection(half3 reflectVector, float3 positionWS, half perceptualRoughness, half occlusion, float2 normalizedScreenSpaceUV) { ... } #if !USE_FORWARD_PLUS half3 GlossyEnvironmentReflection(half3 reflectVector, float3 positionWS, half perceptualRoughness, half occlusion) { ... } #endif
4 参数的版本被 #if !USE_FORWARD_PLUS 包住了。Forward+ 下反射探针是按屏幕分块(cluster)查找的,必须传入屏幕 UV。如果你只写了 4 参数调用,在 Forward 路径下能编译,切换到 Forward+ 就会报 “no matching 4 parameter function”。所以本书统一使用 5 参数版本。
反射与水下颜色按菲涅尔混合:color = lerp(。注意 alpha 也同时向 1 插值——在没有折射的简单版本里,强反射处的水面不应该再透明。

提示风格化水面常常故意“作弊”:把菲涅尔的指数从 5 调小(比如 3),让反射更早出现;或者给反射乘一个大于 1 的强度。物理正确是起点,不是终点——但先有正确的版本,你才知道自己在偏离什么。