§ pass · advanced
Seascape 海面
raymarch 程序化海面:octave 高度场构造波浪 + Fresnel 天空反射 + 琥珀落日反光。灵感来自 Shadertoy「Seascape」by TDM。
返回实验室§ params
浪高0.60
浪速0.80
太阳高度0.25
§ preview
- octave 海面高度场:4 层三角波叠加,每层频率翻倍振幅减半
- raymarch 海面:从相机沿视线步进,命中水面后差分求法线
- Fresnel 反射天空 + 琥珀落日高光(太阳越低反光越长)
§ fragment source
#version 300 es
precision highp float;
uniform float iTime;
uniform vec2 iResolution;
uniform float u_waveHeight;
uniform float u_waveSpeed;
uniform float u_sunHeight;
out vec4 fragColor;
#define uv (gl_FragCoord.xy / iResolution.xy)
// ---- octave 海面高度场(三角波叠加,Seascape 同款思路)----
// 每层频率翻倍、振幅减半,方向旋转,叠加成水面起伏
float seaHeight(vec2 p) {
float freq = 0.16;
float amp = 1.0;
float t = iTime * u_waveSpeed;
mat2 rot = mat2(0.8, 0.6, -0.6, 0.8);
float h = 0.0;
for (int i = 0; i < 4; i++) {
// 三角波:|fract(x)-0.| 的变体, Seascape 用 sin 的叠加正弦近似
vec2 q = p * freq + vec2(t * 0.5, 0.0);
float w = sin(q.x * 2.0 + sin(q.y * 3.0 + t)) * 0.5
+ sin(q.y * 2.7 - t * 0.7) * 0.3;
h += w * amp;
p = rot * p;
freq *= 2.0;
amp *= 0.5;
}
return h * u_waveHeight;
}
// 海面法线(差分)
vec3 seaNormal(vec2 p) {
vec2 e = vec2(0.01, 0.0);
float hL = seaHeight(p - e.xy);
float hR = seaHeight(p + e.xy);
float hD = seaHeight(p - e.yx);
float hU = seaHeight(p + e.yx);
return normalize(vec3(hL - hR, 2.0, hD - hU));
}
void main() {
vec2 uv2 = uv * 2.0 - 1.0;
uv2.x *= iResolution.x / iResolution.y;
// 相机在水面以上,向前看
vec3 ro = vec3(0.0, 1.2, -2.5);
vec3 rd = normalize(vec3(uv2, -1.5));
// raymarch 海面:水面 y = seaHeight(xz)
float t = 0.0;
bool hit = false;
vec3 hitPos = vec3(0.0);
for (int i = 0; i < RAYMARCH_STEPS; i++) {
vec3 p = ro + rd * t;
float h = seaHeight(p.xz);
if (p.y < h) { hit = true; hitPos = p; break; }
t += 0.06;
if (t > 20.0) break;
}
vec3 col;
if (hit) {
vec3 n = seaNormal(hitPos.xz);
vec3 v = -rd;
vec3 l = normalize(vec3(0.4, u_sunHeight, -0.5));
// 天空色:高处青绿,地平线琥珀(落日)
vec3 sky = mix(
vec3(0.31, 0.82, 0.78), // 青绿天
vec3(0.91, 0.69, 0.29), // 琥珀地平线
smoothstep(0.5, 0.0, l.y)
);
// 深水色
vec3 deep = vec3(0.02, 0.08, 0.10);
vec3 shallow = vec3(0.05, 0.18, 0.20);
// Fresnel:越平视角反射越强
float fres = pow(1.0 - max(dot(n, v), 0.0), 3.0);
// 漫反射水色(背光面深、向光面浅)
float ndl = max(dot(n, l), 0.0);
vec3 water = mix(deep, shallow, ndl);
// 太阳高光:视线反射方向接近太阳
vec3 r = reflect(-v, n);
float sun = pow(max(dot(r, l), 0.0), 60.0);
col = mix(water, sky, fres);
col += vec3(0.91, 0.69, 0.29) * sun * 1.5; // 琥珀落日反光
// 距离雾:远处淡化到天空
float fog = smoothstep(5.0, 18.0, t);
col = mix(col, sky * 0.8, fog);
} else {
// 未命中:天空
col = mix(
vec3(0.31, 0.82, 0.78),
vec3(0.91, 0.69, 0.29),
smoothstep(0.5, 0.0, uv2.y * 0.5 + 0.3)
);
col = mix(col, vec3(0.02, 0.025, 0.03), smoothstep(0.6, 1.0, uv2.y));
}
// gamma + tonemap
col = col / (col + 1.0);
col = pow(col, vec3(1.0 / 2.2));
fragColor = vec4(col, 1.0);
}