开放世界水体与海岸线程序化生成:基于距离场(SDF)与流动矢量的泡沫渲染

发布时间:2026/10/11 20:57:54
开放世界水体与海岸线程序化生成:基于距离场(SDF)与流动矢量的泡沫渲染
在开放世界游戏的地貌构建中水体系统蜿蜒河流、高山湖泊与壮阔海岸线是赋予世界生机与动态张力的核心要素。然而许多程序化生成的水体往往给人一种强烈的“塑料玩具感”水体几何网格与岸边地形相交处呈现出生硬的几何切线水流的方向与河床的弯曲曲率严重割裂波浪拍击沙滩与礁石时缺乏自然的浪花碎沫Foam与水渍消退效果。传统的简单解决方案是仅在水面片元着色器中利用相机深度缓冲与水面几何深度的差值Depth Fade来简单混合一层半透明白色。但这种方案存在天然缺陷在摄像机掠射角较小或水下坡度极陡时深度差剧烈变化导致泡沫在礁石边缘呈现出极不自然的断裂条带且完全无法表达波浪受岸线形状折射碎裂的物理动态。要生成具备次世代品质的动态海岸线与水流工业级标准在于构建基于二维符号距离场Signed Distance Field, SDF与程序化流动矢量场Flow Vector Field的综合水体着色体系。符号距离场SDF与流动矢量的数学拓扑在地形高度图生成完毕后我们在离线构建或流式加载阶段通过 Compute Shader 为每个地块烘焙一张紧凑的局部 2D 水岸 SDF 贴图与 Flow Map二维符号距离场Coastline SDF设陆地表面高度场为 $H(x, z)$水平面高度为 $W$。令水陆交界等值线为 $\Gamma {(x, z) \mid H(x, z) W}$。SDF 贴图的每个像素记录该点到最近水陆交界边缘 $\Gamma$ 的欧氏物理距离 $d(x, z)$在水体内部 $d 0$负值表示离岸距离在陆地内部 $d 0$。法向梯度场通过对 SDF 采样点求空间偏导 $\nabla d \left(\frac{\partial d}{\partial x}, \frac{\partial d}{\partial z}\right)$可以直接获得指向最近海岸线的单位法向矢量它代表了海浪拍击海岸的物理运动主方向。流动矢量场Flow Vector Field对于河流与海流流动方向由水体中心流线与地形高度梯度的负方向共同驱动以切线矢量 $(V_x, V_z)$ 记录在 Flow Map 的 RG 通道中用于驱动法线贴图的平移与时间性重混。[ Terrain Heightmap Water Mesh ] │ ▼ (Compute Shader 离线/实时跳点洪水算法 JFA) [ Coastline 2D SDF ] ─── 记录物理离岸距离与海岸线梯度向量 │ ▼ [ Water Surface Shader (水面着色器) ] ├── 1. Gerstner 波浪沿海岸梯度发生物理弯曲 (Wave Refraction) ├── 2. 基于 SDF 负距离解算动态浪花前锋与消退冲刷 └── 3. 采样 Flow Map 与噪声扰动混合生成非均匀细密泡沫网Jump Flooding 算法JFA快速烘焙水体 SDF在数十平方公里的巨型地图上若采用暴力循环计算每个像素到海岸线的距离复杂度将达到灾难性的 $O(N^4)$。我们利用 GPU Compute Shader 实现跳点洪水填充算法Jump Flooding Algorithm, JFA以 $\log_2(N)$ 步并在数毫秒内完成全图 SDF 的精准解算// JumpFloodingStepCS.hlsl - JFA 距离场跳步解算 Texture2Dfloat2 InNearestSeed : register(t0); // 记录当前像素已知最近边缘点的坐标 RWTexture2Dfloat2 OutNearestSeed : register(u0); cbuffer JFAParams : register(b0) { int StepOffset; // 步长: N/2, N/4, N/8 ... 1 int TextureSize; }; [numthreads(16, 16, 1)] void CSMain(uint3 id : SV_DispatchThreadID) { int2 currentCoord id.xy; float2 bestSeed InNearestSeed[currentCoord]; float bestDistSq 1e8; if (bestSeed.x 0.0) { float2 diff bestSeed - currentCoord; bestDistSq dot(diff, diff); } // 检索以 StepOffset 为跨度的 8 邻域候选点 for (int y -1; y 1; y) { for (int x -1; x 1; x) { if (x 0 y 0) continue; int2 neighborCoord currentCoord int2(x, y) * StepOffset; if (neighborCoord.x 0 neighborCoord.x TextureSize neighborCoord.y 0 neighborCoord.y TextureSize) { float2 neighborSeed InNearestSeed[neighborCoord]; if (neighborSeed.x 0.0) { float2 diff neighborSeed - currentCoord; float distSq dot(diff, diff); if (distSq bestDistSq) { bestDistSq distSq; bestSeed neighborSeed; } } } } } OutNearestSeed[currentCoord] bestSeed; }基于 SDF 距离与流动矢量的高保真泡沫片元着色器在片元着色器阶段水体利用烘焙好的 SDF 距离与波浪相位合成带有物理折射与自然消退的泡沫网络// WaterSurfaceFoam.frag - 海岸线动态泡沫着色器 #version 450 layout(location 0) in vec2 inUV; layout(location 1) in vec3 inWorldPos; layout(location 0) out vec4 outColor; layout(binding 0) uniform sampler2D uCoastlineSDF; // R 通道存储世界米数距离 layout(binding 1) uniform sampler2D uFoamNoiseTex; // 泡沫 Voronoi 噪声 layout(binding 2) uniform sampler2D uFlowMap; // 水流速度矢量 layout(push_constant) uniform WaterParams { float time; float waveSpeed; float foamWidth; // 岸边泡沫影响带宽 (如 5.0 米) } ubo; void main() { // 1. 采样海岸线物理距离 (米) float distToCoast texture(uCoastlineSDF, inUV).r; // 2. 采样水流矢量与时间偏置 vec2 flowDir texture(uFlowMap, inUV).rg * 2.0 - 1.0; // 3. 计算随时间向岸边推进的正弦冲浪波浪前锋 // distToCoast 越接近 0 代表越靠近陆地 float wavePhase (distToCoast * 0.5) - (ubo.time * ubo.waveSpeed); float waveCrest sin(wavePhase); // 周期波峰 [-1, 1] // 4. 泡沫强度因子只有当靠近海岸线 (distToCoast foamWidth) 且处于波峰时形成泡沫 float shorelineFactor 1.0 - clamp(distToCoast / ubo.foamWidth, 0.0, 1.0); float waveFoamMask clamp(waveCrest * 2.0 shorelineFactor, 0.0, 1.0); // 5. 结合 Flow Map 驱动的双层交错采样 Voronoi 噪声打碎规则色块 vec2 flowUV1 inWorldPos.xz * 0.2 flowDir * (ubo.time * 0.1); vec2 flowUV2 inWorldPos.xz * 0.2 flowDir * (ubo.time * 0.1 0.5); float noise1 texture(uFoamNoiseTex, flowUV1).r; float noise2 texture(uFoamNoiseTex, flowUV2).r; float combinedNoise mix(noise1, noise2, abs(fract(ubo.time * 0.2) * 2.0 - 1.0)); // 6. 硬切锐化与边缘消融利用 smoothstep 雕刻泡沫气泡的纤维状网状结构 float finalFoam smoothstep(0.4, 0.7, combinedNoise * waveFoamMask * shorelineFactor); // 基础深浅水颜色与泡沫混合 vec3 deepWaterColor vec3(0.02, 0.15, 0.28); vec3 shallowWaterColor vec3(0.1, 0.45, 0.55); vec3 foamColor vec3(0.95, 0.98, 1.0); vec3 waterBase mix(deepWaterColor, shallowWaterColor, shorelineFactor); vec3 finalRGB mix(waterBase, foamColor, finalFoam); outColor vec4(finalRGB, 0.85 finalFoam * 0.15); }礁石浸润水渍与潮汐消退Wetness Decal当海浪退潮退去时沙滩和礁石表面如果瞬间变干依然会暴露出明显的CG破绽。利用前面生成的同一套海岸线 SDF我们在地形着色器中加入了“潮汐浸润水渍Tidal Wetness”计算当潮汐波浪回落时将岩石表面的粗糙度参数Roughness临时压降至 0.05呈现光滑镜面反光将反照率Albedo乘以 0.7 变暗符合多孔湿润物理光照反射特性结合指数时间衰减模拟水分在阳光与风力下的自然蒸发干燥使潮起潮落展现出高度拟真的物理微气候细节。结合跳点洪水算法解算的距离场与流动矢量调制程序化水体不仅拥有了清晰坚固的边缘几何依据更在波浪起伏间流淌出大自然水波灵动而富有弹性的生命力。