Shader:
precision highp float;
uniform vec2 resolution;
uniform float time;
uniform vec2 mouse;
// 哈希 / 噪声函数
float hash12(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
vec2 hash22(vec2 p) {
p = vec2(dot(p, vec2(127.1, 311.7)),
dot(p, vec2(269.5, 183.3)));
return fract(sin(p) * 43758.5453);
}
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
float a = hash12(i);
float b = hash12(i + vec2(1.0, 0.0));
float c = hash12(i + vec2(0.0, 1.0));
float d = hash12(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
float fbm(vec2 p) {
float v = 0.0, a = 0.5;
mat2 m = mat2(1.6, 1.2, -1.2, 1.6);
for (int i = 0; i < 4; i++) {
v += a * noise(p);
p = m * p;
a *= 0.5;
}
return v;
}
// SDF 函数
float sdBox(vec3 p, vec3 b) {
vec3 q = abs(p) - b;
return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
}
float sdOcta(vec3 p, float s) {
p = abs(p);
return (p.x + p.y + p.z - s) * 0.57735027;
}
// 光塔(简化版)
float tower(vec3 p, vec2 id, float hMul) {
float d = 1e5;
float H = 18.0 + 30.0 * hMul;
float W = 2.8 + 0.9 * hash12(id + 1.7);
// 基座
d = min(d, sdBox(p - vec3(0.0, -10.0, 0.0), vec3(W + 2.2, 12.0, W + 2.2)));
// 主体(两段)
d = min(d, sdBox(p - vec3(0.0, H * 0.32, 0.0), vec3(W, H * 0.32, W)));
d = min(d, sdBox(p - vec3(0.0, H * 0.68, 0.0), vec3(W * 0.72, H * 0.28, W * 0.72)));
// 檐口
d = min(d, sdBox(p - vec3(0.0, H * 0.28, 0.0), vec3(W + 0.55, 0.25, W + 0.55)));
d = min(d, sdBox(p - vec3(0.0, H * 0.55, 0.0), vec3(W * 0.8 + 0.4, 0.22, W * 0.8 + 0.4)));
// 中央尖塔
d = min(d, sdBox(p - vec3(0.0, H + 3.0, 0.0), vec3(0.9, 4.5, 0.9)));
d = min(d, sdOcta(p - vec3(0.0, H + 9.0, 0.0), 3.4));
d = min(d, sdBox(p - vec3(0.0, H + 12.5, 0.0), vec3(0.2, 2.5, 0.2)));
// 4个角落的小塔楼(简化)
for (int i = 0; i < 4; i++) {
float a = float(i) * 1.5708 + 0.7854;
vec3 o = vec3(cos(a), 0.0, sin(a)) * (W * 0.95);
vec3 tp = p - o;
float tH = H * 0.55 + 5.0;
d = min(d, sdBox(tp - vec3(0.0, tH * 0.45, 0.0), vec3(0.95, tH * 0.45, 0.95)));
d = min(d, sdOcta(tp - vec3(0.0, tH + 1.8, 0.0), 1.8));
}
// 小支撑柱
for (int i = 0; i < 4; i++) {
float a = float(i) * 1.5708;
vec3 dir = vec3(cos(a), 0.0, sin(a));
d = min(d, sdBox(p - dir * (W + 1.6) - vec3(0.0, H * 0.28, 0.0),
vec3(0.55, H * 0.28, 0.55)));
}
return d * 0.9;
}
float city(vec3 p) {
float cell = 28.0;
vec2 id = floor(p.xz / cell);
vec3 q = p;
q.xz = mod(p.xz + 0.5 * cell, cell) - 0.5 * cell;
// 轻微抖动
q.xz -= (hash22(id) - 0.5) * 5.0;
float hm = pow(hash12(id), 0.6);
float d = tower(q, id, hm);
// 偶尔加个矮点的副塔
if (hash12(id + 9.0) > 0.72) {
vec2 o2 = (hash22(id + 3.1) - 0.5) * 9.0;
d = min(d, tower(q - vec3(o2.x, 0.0, o2.y), id + 20.0, hm * 0.7));
}
// 罕见的大教堂
if (hash12(id * 0.7) > 0.92) {
d = min(d, tower(q * vec3(1.1, 1.25, 1.1), id + 50.0, 1.05));
}
return d;
}
// 海洋
float ocean(vec2 xz) {
float t = time;
float h = 0.0;
h += 2.4 * sin(xz.x * 0.055 + t * 0.55) * cos(xz.y * 0.042 + t * 0.40);
h += 1.8 * sin(xz.y * 0.07 - t * 0.70 + xz.x * 0.02);
h += 1.3 * sin(dot(xz, normalize(vec2(0.75, 0.65))) * 0.10 + t * 0.95);
h += 0.9 * sin(dot(xz, normalize(vec2(-0.55, 0.8))) * 0.14 - t * 1.1);
h += 1.1 * (fbm(xz * 0.06 + t * 0.09) - 0.4);
h += 0.35 * fbm(xz * 0.32 + vec2(t * 0.2, -t * 0.15));
return h;
}
// 场景
float ma
Opus 4.8
查看英文原文
Shader:
precision highp float;
uniform vec2 resolution;
uniform float time;
uniform vec2 mouse;
// ------------------------------------------------------------
// Hash / noise
// ------------------------------------------------------------
float hash12(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
vec2 hash22(vec2 p) {
p = vec2(dot(p, vec2(127.1, 311.7)),
dot(p, vec2(269.5, 183.3)));
return fract(sin(p) * 43758.5453);
}
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
float a = hash12(i);
float b = hash12(i + vec2(1.0, 0.0));
float c = hash12(i + vec2(0.0, 1.0));
float d = hash12(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
float fbm(vec2 p) {
float v = 0.0, a = 0.5;
mat2 m = mat2(1.6, 1.2, -1.2, 1.6);
for (int i = 0; i < 4; i++) {
v += a * noise(p);
p = m * p;
a *= 0.5;
}
return v;
}
// ------------------------------------------------------------
// SDFs
// ------------------------------------------------------------
float sdBox(vec3 p, vec3 b) {
vec3 q = abs(p) - b;
return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
}
float sdOcta(vec3 p, float s) {
p = abs(p);
return (p.x + p.y + p.z - s) * 0.57735027;
}
// ------------------------------------------------------------
// Light tower (kept simple)
// ------------------------------------------------------------
float tower(vec3 p, vec2 id, float hMul) {
float d = 1e5;
float H = 18.0 + 30.0 * hMul;
float W = 2.8 + 0.9 * hash12(id + 1.7);
// foundations
d = min(d, sdBox(p - vec3(0.0, -10.0, 0.0), vec3(W + 2.2, 12.0, W + 2.2)));
// main shaft (two steps)
d = min(d, sdBox(p - vec3(0.0, H * 0.32, 0.0), vec3(W, H * 0.32, W)));
d = min(d, sdBox(p - vec3(0.0, H * 0.68, 0.0), vec3(W * 0.72, H * 0.28, W * 0.72)));
// cornices
d = min(d, sdBox(p - vec3(0.0, H * 0.28, 0.0), vec3(W + 0.55, 0.25, W + 0.55)));
d = min(d, sdBox(p - vec3(0.0, H * 0.55, 0.0), vec3(W * 0.8 + 0.4, 0.22, W * 0.8 + 0.4)));
// central spire
d = min(d, sdBox(p - vec3(0.0, H + 3.0, 0.0), vec3(0.9, 4.5, 0.9)));
d = min(d, sdOcta(p - vec3(0.0, H + 9.0, 0.0), 3.4));
d = min(d, sdBox(p - vec3(0.0, H + 12.5, 0.0), vec3(0.2, 2.5, 0.2)));
// 4 corner turrets (simple)
for (int i = 0; i < 4; i++) {
float a = float(i) * 1.5708 + 0.7854;
vec3 o = vec3(cos(a), 0.0, sin(a)) * (W * 0.95);
vec3 tp = p - o;
float tH = H * 0.55 + 5.0;
d = min(d, sdBox(tp - vec3(0.0, tH * 0.45, 0.0), vec3(0.95, tH * 0.45, 0.95)));
d = min(d, sdOcta(tp - vec3(0.0, tH + 1.8, 0.0), 1.8));
}
// tiny buttresses
for (int i = 0; i < 4; i++) {
float a = float(i) * 1.5708;
vec3 dir = vec3(cos(a), 0.0, sin(a));
d = min(d, sdBox(p - dir * (W + 1.6) - vec3(0.0, H * 0.28, 0.0),
vec3(0.55, H * 0.28, 0.55)));
}
return d * 0.9;
}
float city(vec3 p) {
float cell = 28.0;
vec2 id = floor(p.xz / cell);
vec3 q = p;
q.xz = mod(p.xz + 0.5 * cell, cell) - 0.5 * cell;
// slight jitter
q.xz -= (hash22(id) - 0.5) * 5.0;
float hm = pow(hash12(id), 0.6);
float d = tower(q, id, hm);
// occasional second lower tower
if (hash12(id + 9.0) > 0.72) {
vec2 o2 = (hash22(id + 3.1) - 0.5) * 9.0;
d = min(d, tower(q - vec3(o2.x, 0.0, o2.y), id + 20.0, hm * 0.7));
}
// rare big cathedral
if (hash12(id * 0.7) > 0.92) {
d = min(d, tower(q * vec3(1.1, 1.25, 1.1), id + 50.0, 1.05));
}
return d;
}
// ------------------------------------------------------------
// Ocean
// ------------------------------------------------------------
float ocean(vec2 xz) {
float t = time;
float h = 0.0;
h += 2.4 * sin(xz.x * 0.055 + t * 0.55) * cos(xz.y * 0.042 + t * 0.40);
h += 1.8 * sin(xz.y * 0.07 - t * 0.70 + xz.x * 0.02);
h += 1.3 * sin(dot(xz, normalize(vec2(0.75, 0.65))) * 0.10 + t * 0.95);
h += 0.9 * sin(dot(xz, normalize(vec2(-0.55, 0.8))) * 0.14 - t * 1.1);
h += 1.1 * (fbm(xz * 0.06 + t * 0.09) - 0.4);
h += 0.35 * fbm(xz * 0.32 + vec2(t * 0.2, -t * 0.15));
return h;
}
// ------------------------------------------------------------
// Scene
// ------------------------------------------------------------
float map(vec3 p) {
return min(city(p), p.y - ocean(p.xz));
}
float mapB(vec3 p) { return city(p); }
vec3 nBuild(vec3 p) {
vec2 e = vec2(0.02, 0.0);
return normalize(vec3(
mapB(p + e.xyy) - mapB(p - e.xyy),
mapB(p + e.yxy) - mapB(p - e.yxy),
mapB(p + e.yyx) - mapB(p - e.yyx)
));
}
vec3 nOcean(vec2 xz) {
float e = 0.12;
float h = ocean(xz);
return normalize(vec3(
h - ocean(xz + vec2(e, 0.0)),
e,
h - ocean(xz + vec2(0.0, e))
));
}
// ------------------------------------------------------------
// Lightning + sky
// ------------------------------------------------------------
float lightning(float t) {
float c = t * 0.4;
float id = floor(c);
float f = fract(c);
float r = hash12(vec2(id, 7.3));
if (r > 0.87) {
float b = exp(-f * 8.0) * (0.7 + 0.3 * sin(t * 50.0));
b += 0.4 * exp(-abs(f - 0.1) * 28.0) * step(0.4, hash12(vec2(id, 2.2)));
return clamp(b, 0.0, 1.4);
}
return 0.0;
}
vec3 sky(vec3 rd, float fl) {
float y = max(rd.y, 0.0);
vec3 col = mix(vec3(0.012, 0.018, 0.03),
vec3(0.055, 0.06, 0.075),
exp(-y * 3.0));
col = mix(col, vec3(0.07, 0.06, 0.055), exp(-abs(rd.y) * 8.0) * 0.6);
vec2 cuv = rd.xz / (rd.y + 0.15);
float clouds = smoothstep(0.3, 0.75, fbm(cuv * 1.6 + time * 0.03));
col = mix(col, vec3(0.025, 0.028, 0.04), clouds * 0.85);
col += fl * vec3(0.4, 0.45, 0.75) * (0.3 + 0.7 * clouds);
return col;
}
// ------------------------------------------------------------
// Main
// ------------------------------------------------------------
void main() {
vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
// mouse look (optional)
vec2 mo = mouse / max(resolution, vec2(1.0));
mo = mo * 2.0 - 1.0;
if (length(mouse) < 0.5) mo = vec2(0.0);
float t = time;
float fl = lightning(t);
// cinematic path
float path = t * 2.9;
float h0 = 8.5 + 4.0 * sin(t * 0.22);
vec3 ro = vec3(path,
h0 + ocean(vec2(path, 0.0)) * 0.35,
7.5 * sin(t * 0.14));
vec3 ta = ro + vec3(12.0,
-1.0 + 5.0 * sin(t * 0.16) + mo.y * 4.0,
5.0 * cos(t * 0.12) + mo.x * 6.0);
// thunder shake
ro.xy += (hash22(vec2(floor(t * 7.0), 1.1)) - 0.5) * fl * 0.3;
vec3 ww = normalize(ta - ro);
vec3 uu = normalize(cross(ww, vec3(0.0, 1.0, 0.0)));
vec3 vv = cross(uu, ww);
vec3 rd = normalize(uv.x * uu + uv.y * vv + 1.3 * ww);
// ---- raymarch (lighter) ----
float trav = 0.0;
float tmax = 160.0;
vec3 p;
for (int i = 0; i < 90; i++) {
p = ro + rd * trav;
float d = map(p);
if (d < 0.0015 * trav || trav > tmax) break;
trav += d * 0.8;
}
vec3 col = sky(rd, fl);
if (trav < tmax) {
p = ro + rd * trav;
float dB = mapB(p);
float h = ocean(p.xz);
bool water = (p.y - h) < dB * 0.9 + 0.02;
if (water) {
// ---- WATER ----
vec3 n = nOcean(p.xz);
// foam
float eps = 0.15;
float steep = length(vec2(
ocean(p.xz + vec2(eps, 0.0)) - h,
ocean(p.xz + vec2(0.0, eps)) - h)) / eps;
float foam = smoothstep(0.55, 1.4, steep);
foam = max(foam, smoothstep(1.6, 3.2, h + 0.6 * fbm(p.xz * 0.3 + t * 0.2)));
foam = clamp(foam, 0.0, 1.0);
float fres = pow(1.0 - max(dot(-rd, n), 0.0), 4.0);
// cheap reflection (sky only)
vec3 refl = sky(reflect(rd, n), fl);
vec3 waterCol = mix(vec3(0.005, 0.03, 0.04),
vec3(0.02, 0.055, 0.06),
max(dot(-rd, n), 0.0));
waterCol = mix(waterCol, vec3(0.7, 0.76, 0.85), foam * 0.9);
vec3 L = normalize(vec3(0.4, 0.8, 0.35));
float diff = max(dot(n, L), 0.0);
float spec = pow(max(dot(reflect(-L, n), -rd), 0.0), 70.0);
float lspec = fl * max(dot(n, vec3(0.0, 1.0, 0.0)), 0.0) * 1.2;
col = waterCol * (0.25 + diff * 0.4 + fl * 0.3);
col += vec3(0.9, 0.95, 1.0) * (spec * 0.8 + lspec * 0.5);
col = mix(col, refl, fres * (0.65 + 0.3 * foam));
col += foam * foam * 0.25;
} else {
// ---- STONE ----
vec3 n = nBuild(p);
vec2 id = floor(p.xz / 28.0);
vec3 albedo = vec3(0.12, 0.11, 0.105);
albedo *= 0.7 + 0.45 * hash12(id + floor(p.y * 0.2));
// algae lower down
albedo = mix(vec3(0.04, 0.06, 0.045), albedo,
smoothstep(-1.0, 20.0, p.y) * 0.9 + 0.1);
float wet = clamp(exp(-(p.y - h) * 0.5), 0.0, 1.0);
albedo *= mix(1.0, 0.3, wet);
// glowing windows
float fy = fract(p.y * 0.38);
float fxz = min(abs(fract(p.x * 0.5) - 0.5),
abs(fract(p.z * 0.5) - 0.5));
float win = step(0.22, fy) * step(fy, 0.7) * step(fxz, 0.09);
float lit = step(0.5, hash12(floor(p.xy * vec2(0.5, 0.38)) + id));
lit *= 0.7 + 0.3 * sin(t * 3.0 + hash12(id) * 10.0);
vec3 glowHue = mix(vec3(1.0, 0.5, 0.15),
vec3(0.3, 0.8, 1.15),
hash12(id + 5.0));
vec3 emission = glowHue * win * lit * 2.6;
// top lantern glow
emission += vec3(1.0, 0.7, 0.3) *
smoothstep(24.0, 40.0, p.y) * 0.35 * (0.5 + 0.5 * fl);
vec3 L = normalize(vec3(0.35, 0.8, 0.4));
float dif = max(dot(n, L), 0.0);
float amb = 0.18 + 0.16 * n.y + fl * 0.55;
float spec = pow(max(dot(reflect(-L, n), -rd), 0.0), mix(16.0, 40.0, wet))
* 0.4 * mix(0.2, 0.8, wet);
// cheap AO
float ao = clamp(mapB(p + n * 1.0) / 1.0, 0.4, 1.0);
col = albedo * (amb + dif * 0.65) * ao + vec3(0.95) * spec + emission;
}
// fog
float fog = 1.0 - exp(-trav * trav * 0.00004);
fog = clamp(fog + 0.12 * (1.0 - exp(-trav * 0.015)), 0.0, 1.0);
vec3 fogC = mix(vec3(0.04, 0.045, 0.06), sky(rd, fl) * 0.5, 0.4);
fogC += fl * 0.06;
col = mix(col, fogC, fog);
}
// ---- post ----
// rain
vec2 ruv = uv * vec2(48.0, 20.0) + vec2(0.0, -t * 25.0);
float rain = smoothstep(0.93, 0.995, noise(floor(ruv)));
ruv = uv * vec2(80.0, 35.0) + vec2(12.0, -t * 38.0);
rain += 0.5 * smoothstep(0.94, 0.998, noise(floor(ruv)));
col += rain * (0.1 + 0.05 * fl) * vec3(0.6, 0.68, 0.8);
// screen flash
col += fl * 0.07;
// bloom-ish
col += max(col - 0.7, 0.0) * 0.4;
// grade + tone
col = col * 1.15 / (1.0 + col * 0.5);
col = mix(col, col * vec3(0.82, 0.93, 1.15), 0.35);
// vignette
vec2 v = gl_FragCoord.xy / resolution.xy;
float vig = v.x * v.y * (1.0 - v.x) * (1.0 - v.y);
col *= mix(0.55, 1.0, pow(vig * 14.0, 0.25));
col = pow(max(col, 0.0), vec3(0.92));
gl_FragColor = vec4(col, 1.0);
}
Opus 4.8