§ pass · advanced
体积云
raymarch 穿过 fbm 云密度场,云本体青绿、薄云边缘前向透射偏琥珀,天空随太阳高度从深青绿渐变到琥珀地平线。灵感来自 Shadertoy「Raymarched Volumetric Clouds」by bigwings。
返回实验室§ params
云量0.55
风速0.50
太阳高度0.25
§ preview
- fbm 5 层 value noise 构造云团多尺度结构,风漂流偏移采样坐标
- 沿射线累加云散射 + transmittance 指数衰减实现体积吸收
- 薄云边缘前向透射(视线朝太阳时)偏琥珀,厚云本体青绿,模拟 beer's law 透光
- 天空:深青绿顶 → 琥珀地平线,太阳越低地平线琥珀越强(日落因子)
§ fragment source
#version 300 es
precision highp float;
uniform float iTime;
uniform vec2 iResolution;
uniform float u_cloudCoverage;
uniform float u_windSpeed;
uniform float u_sunHeight;
out vec4 fragColor;
#define uv (gl_FragCoord.xy / iResolution.xy)
// ---- value noise 基础 hash ----
float hash(vec3 p) {
p = fract(p * 0.3183099 + 0.1);
p *= 17.0;
return fract(p.x * p.y * p.z * (p.x + p.y + p.z));
}
float noise(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
f = f * f * (3.0 - 2.0 * f);
return mix(mix(mix(hash(i + vec3(0,0,0)), hash(i + vec3(1,0,0)), f.x),
mix(hash(i + vec3(0,1,0)), hash(i + vec3(1,1,0)), f.x), f.y),
mix(mix(hash(i + vec3(0,0,1)), hash(i + vec3(1,0,1)), f.x),
mix(hash(i + vec3(0,1,1)), hash(i + vec3(1,1,1)), f.x), f.y), f.z);
}
// 5 层 fbm value noise:构造云团多尺度结构(大尺度形状 + 小尺度细节)
float fbm(vec3 p) {
float v = 0.0;
float a = 0.5;
for (int i = 0; i < 5; i++) {
v += a * noise(p);
p *= 2.0;
a *= 0.5;
}
return v;
}
// 云密度场:云层集中在 y∈[2,4] 高空带,受风漂流
// u_cloudCoverage 越大阈值越低 → 云越多
float cloudDensity(vec3 p) {
float heightMask = smoothstep(2.0, 2.6, p.y) * smoothstep(4.0, 3.2, p.y);
vec3 wind = vec3(iTime * u_windSpeed * 0.25, iTime * u_windSpeed * 0.04, 0.0);
float n = fbm(p * 0.7 + wind);
float threshold = 1.0 - u_cloudCoverage * 1.1;
float d = smoothstep(threshold, threshold + 0.18, n) * heightMask;
return d;
}
void main() {
vec2 p = uv * 2.0 - 1.0;
p.x *= iResolution.x / iResolution.y;
// 相机在地面,视线略仰视看云层
vec3 ro = vec3(0.0, 0.0, 0.0);
vec3 rd = normalize(vec3(p.x * 0.7, p.y * 0.8, -1.0));
vec3 sunDir = normalize(vec3(0.5, u_sunHeight, -0.4));
// ---- 天空:深青绿顶 → 琥珀地平线,太阳越低地平线琥珀越强 ----
float upFactor = clamp(rd.y, 0.0, 1.0);
float horizonFactor = clamp(1.0 - abs(rd.y) * 2.2, 0.0, 1.0);
// 日落因子:太阳低时为 1(强琥珀),太阳高时为 0
float sunsetFactor = 1.0 - smoothstep(-0.2, 0.6, u_sunHeight);
vec3 deepSky = vec3(0.02, 0.025, 0.03);
vec3 tealSky = vec3(0.31, 0.82, 0.78);
vec3 amber = vec3(0.91, 0.69, 0.29);
vec3 sky = mix(deepSky, tealSky, pow(upFactor, 0.5));
sky = mix(sky, amber, horizonFactor * (0.3 + 0.7 * sunsetFactor));
// 视线朝下时回到深底
sky = mix(sky, deepSky, smoothstep(0.0, -0.3, rd.y));
// ---- raymarch 体积云:沿射线累加散射 + transmittance 衰减 ----
vec3 cloudCol = vec3(0.0);
float trans = 1.0;
float t = 0.2;
float stepSize = 0.15;
for (int i = 0; i < RAYMARCH_STEPS; i++) {
vec3 pos = ro + rd * t;
float d = cloudDensity(pos);
if (d > 0.001) {
// 云本体青绿(偏暗,受云顶照亮)
vec3 body = vec3(0.31, 0.82, 0.78) * 0.35;
// 薄云边缘前向透射偏琥珀:视线越朝太阳、云越薄 → 越偏琥珀
float thin = clamp(1.0 - d * 2.2, 0.0, 1.0);
float sunFacing = max(dot(rd, sunDir), 0.0);
float forward = pow(sunFacing, 3.0);
vec3 edgeGlow = vec3(0.91, 0.69, 0.29) * (0.25 + 0.75 * forward);
vec3 c = mix(body, edgeGlow, thin);
// 云顶被太阳照亮(太阳高时云顶亮,太阳低时整体偏暗)
float topLit = clamp(dot(sunDir, vec3(0.0, 1.0, 0.0)) * 0.5 + 0.5, 0.0, 1.0);
c *= 0.55 + 0.45 * topLit;
cloudCol += c * d * trans * 0.18;
trans *= exp(-d * 0.55);
}
t += stepSize;
if (trans < 0.02) break;
}
// 合成:云散射 + 透射天空
vec3 col = cloudCol + sky * trans;
// tonemap + gamma
col = col / (col + 1.0);
col = pow(col, vec3(1.0 / 2.2));
fragColor = vec4(col, 1.0);
}