单文件实时极光水面 · three.js + 自定义 GLSL · 断网双击即可运行
aurora-curtain.html?t=0.8 实机截图 · 1920×1080 · 无后期合成,浏览器原生渲染
一个跑在浏览器里的实时极光场景:夜空中多条垂直极光帘幕,绿色下边缘渐变到品红拖尾, 近黑色的山脉剪影挡在帘幕前,脚下是一面近乎静止、却把极光撕成碎条的湖。
整件作品是一个 HTML 文件(约 940 KB):three.js r160、后期处理 addons、lil-gui 全部内联,
没有任何外链。双击打开、断网可用、也可以直接丢进 file:// 或任意静态服务器。
画面里的东西没有一样是贴图或模型资产:极光、星空、山脉、水面波纹法线、 天穹渐变全部在运行时程序化生成(GLSL 噪声 / 自实现值噪声 / 整数频率正弦叠加)。 因此整份作品只有源码,没有二进制素材。
- 单文件交付 — three.js + addons + lil-gui 内联,无 CDN、无网络请求、无构建步骤即可运行
- 全程序化 — 极光帘幕(3 阶 fbm 值噪声 + 域扭曲)、星空、山脉高度场、水面法线贴图,零素材
- 七层合成 — 星空 → 山脉 → 极光(加性) → 水面 → 倒影(Screen) → Bloom → HUD,顺序与混合模式逐层可控
- 真实镜像倒影 — 用
scale.y = -1的几何镜像 + 法线扰动 UV + 逐片元距离衰减, 不依赖Reflector/CubeCamera,成本远低于实时平面反射 - 自发光主导的光影 — 无三点布光:水面与山脊由倒影层、山脊自发光染色与 Bloom 间接照亮
- 可调面板 — lil-gui 四个常驻滑块 + 彩虹色谱条(色相偏移)+ 一键恢复默认(小箭头 /
R) - 确定性 — 画面内容不含
Math.random()生成项:星空与水面波纹用固定种子 LCG, 噪声是纯哈希函数,同一台机器上重复打开画面稳定 - 时间相位开关 —
?t=<秒>把动画停在指定时刻,方便截图与取帧
| 帘幕铺展 | 帘幕收拢 |
|---|---|
![]() |
![]() |
?t=3.6 |
?t=5.2 |
?t=7.0 · 极光减弱、星空开始显现
上图为浏览器渲染的原始帧(含画面内 HUD)。
docs/里 4 张为 1920×1080 静帧。
方式零:在线直接看(GitHub Pages,无需下载)
https://d-onexxx.github.io/aurora/ # 短链:自动跳转到作品页
https://d-onexxx.github.io/aurora/aurora-curtain.html # 作品页直链
方式一:直接打开(推荐)
双击 aurora-curtain.html
无需服务器、无需联网、无需安装任何依赖。
方式二:起一个本地静态服务器(可选,行为完全一致)
python -m http.server 8080
# 然后访问 http://127.0.0.1:8080/aurora-curtain.html取某一刻的静帧
aurora-curtain.html?t=5.2 # 本地:时间相位停在第 5.2 秒并暂停
https://d-onexxx.github.io/aurora/aurora-curtain.html?t=5.2 # 在线同理,HUD 显示 PINNED t=5.2
| 操作 | 说明 |
|---|---|
H |
显示 / 隐藏 HUD 与调试面板 |
R |
恢复默认参数(等同点击右下角旋转箭头) |
空格 |
暂停 / 继续时间 |
| 鼠标拖动滑块 | 实时调节流速、亮度、辉光、曝光、倒影等 |
| 彩虹色谱条 | 拖动游标旋转整体色相(0–1),右侧数值框可直接输入 |
?t=<秒> |
固定时间相位(截图 / 取帧用) |
flowchart LR
S["① 星空<br/>Points 800"] --> M["② 山脉剪影<br/>噪声高度场 3600×900"]
M --> A["③ 极光帷幕 ×2<br/>AdditiveBlending"]
A --> W["④ 水面<br/>MeshStandard · roughness 0.15"]
W --> R["⑤ 水面倒影 ×2<br/>scale.y = -1 · Screen"]
R --> P["⑥ 后期<br/>UnrealBloom → OutputPass (ACES)"]
P --> H["⑦ HUD<br/>lil-gui + 彩虹色谱条"]
| # | 图层 | 实现 | 混合 | renderOrder |
|---|---|---|---|---|
| 1 | 天穹 | SphereGeometry(1800) 内壁渐变 Shader |
Normal | −100 |
| 2 | 星空 | Points 800 点,size 0.05、opacity 0.6、固定种子 LCG |
Additive | −90 |
| 3 | 山脉 | PlaneGeometry(3600×900, 420×190) 顶点噪声位移,#0A0E0A、roughness 1.0 |
Normal | −10 |
| 4 | 极光帷幕 | 双层平面(z = −820 / −1250),自定义 GLSL |
Additive | 10 |
| 5 | 水面 | 水平 Plane,#05080A、roughness 0.15、metalness 0.0、滚动 normalMap |
Normal | 20 |
| 6 | 水面倒影 | 同一份 shader 的镜像副本 + 法线扰动 + 距离衰减 | Screen | 30 |
| 7 | 后期 | UnrealBloomPass → OutputPass |
— | — |
| 8 | HUD | 底部控制条 + lil-gui 面板(DOM 叠加) | Normal | — |
- 垂直拉伸:噪声在
v方向用极低频率(0.20 / 0.42 / 0.25 / 0.10), 在u方向用高频(8.0 / 16.0 / 26.0 / 34.0),于是得到竖向帘纹而不是云团。 - 三阶 fbm:自实现值噪声(五次平滑 + 双线性)+ 3 阶叠加(lacunarity 2.03、gain 0.5)。
- 域扭曲:两级低频噪声偏移采样坐标,让帘幕整体左右摆动。
- 暗隙:
gap项把低噪值压掉,切出多条独立帘幕与它们之间的黑缝。 - 配色:
mix(#39FF8A, #FF2D6F, f(噪声值, 世界高度))—— 底部荧光绿、顶部品红拖尾, 底部再叠一条绿色亮核(真实极光的亮绿下边缘)。 - 羽化:全部由世界高度驱动(底缘 smoothstep + 向上指数拖尾 + 贴地羽化),
而不是
uv,这样帘幕底边能被山脊自然挡住,不会出现一条硬切的直线。
float low = smoothstep(uLowY, uLowY + uLowFade, yA); // 贴地羽化
float edge = smoothstep(uBandY - 24.0, uBandY + 2.0, yA); // 底缘
float tail = exp(-max(0.0, yA - uBandY) / uBandW); // 向上拖尾
float vmask = low * (edge * tail + haze * 0.03);
float alpha = min(body * vmask * xf * hf, 1.0); // 必须 clamp 到 1512×512 的 DataTexture,由 22 条整数频率正弦叠加求导得到法线,保证可无缝平铺:
cyc = max(2, round(3 * 1.34 ** (i % 11))) // 每条的周期数
ang = PI/2 + (rand() - 0.5) * 0.62 // 波矢主要沿 v → 屏幕上呈横向条带
kx = round(cos(ang) * cyc); kv = round(sin(ang) * cyc) || cyc
amp = 1 / 1.30 ** i // 幅度衰减
// 法线 = normalize(-dhdu * 0.010, -dhdv * 0.010, 1)贴图 repeat 4×4、各向异性拉满,每帧滚动 offset.y += 0.08 * dt。
PlaneGeometry(3600×900, 420×190) 绕 X 轴 −90° 躺平后平移 z = −410,
逐顶点用 ridged 噪声位移(ramp = smoothstep(150, 430, dist) 让近处沉入水下),
再按 |x| 压低中间形成峡谷走廊。材质 #0A0E0A、roughness 1.0(无高光),
用 onBeforeCompile 往 totalEmissiveRadiance 里注入山脊染色
(pow(hn, 2.4) * 0.09,随高度衰减)——是自发光项,不是灯光。
不用 Reflector / CubeCamera。把同一份极光 shader 再挂一份到 scale.y = -1 的镜像组里,
几何本身就是关于水面的真实镜像,然后:
- 只保留水面以下的片元:
if (vWorld.y > -0.02) discard; - 用同一张滚动 normalMap 扰动 UV:
uv += nrm.xy * uRipple; - 按“水深”分条随机横移,把镜像打碎;
- 距离衰减取视线与水面交点到相机的水平距离(逐片元求解,不是顶点插值):
fade = 1 - clamp(dist / 30, 0, 1); - 混合用 预乘颜色 + Screen(
OneMinusDstColorFactor / OneFactor),比普通加性更通透。
EffectComposer → RenderPass → UnrealBloomPass(threshold 0.6 / strength 0.8 / radius 0.4) → OutputPass。
色彩空间统一 sRGB,输出前过 ACESFilmicToneMapping(exposure 1.0)。
composer.setSize() 内部会按 pixelRatio 同步各 pass 尺寸,不要再单独调 bloomPass.setSize()。
低粗糙度(0.15)水面 + 任何可观的平行光 ⇒ GGX 镜面峰值约 1/(π·α²) ≈ 14 倍,
画面里会出现一条贯穿地平线的“探照灯”白色光柱,与“自发光主导”的质感完全冲突。
因此这里只给一个微弱半球环境光,水面与山脊靠倒影层 + 山脊自发光染色 + Bloom间接照亮。
aurora/
├─ aurora-curtain.html ← 成品:单文件,双击即看(~940 KB)
├─ PROMPT.md ← 参数与实现说明(完整提示词 / 真实数值表 / 翻车点 / 自检清单)
├─ README.md
├─ LICENSE ← MIT
├─ docs/
│ └─ preview-01..04.jpg ← 1920×1080 实机静帧
├─ src/
│ ├─ app.js ← 场景与着色器(~830 行)
│ └─ shell.html ← HTML / CSS / HUD 外壳
├─ build.ps1 ← Windows 打包(调用 esbuild 原生二进制)
└─ build.mjs ← Node 打包(esbuild JS API,环境允许时可用)
| 项 | 值 |
|---|---|
| 极光主色 → 顶部色 | #39FF8A → #FF2D6F |
| 极光流速 / 发光强度 | 0.22 / 0.50 |
| 水面 基色 / roughness / metalness | #05080A / 0.15 / 0.0 |
| 波纹 normalMap | tile 4×4,滚动 0.08 |
| 倒影 强度 / 衰减 / 扰动 / 混合 | 0.75 / 30 / 0.07 / Screen |
| 星空 数量 / 尺寸 / 不透明度 | 800 / 0.05 / 0.6(种子 20240930) |
| 山脉 颜色 / roughness / 山脊染色 | #0A0E0A / 1.0 / 0.09 |
| 天穹 天顶 / 地平线 / 辉光 | #000104 / #04090E / 0.16 |
| 后期 Bloom | threshold 0.6 · strength 0.8 · radius 0.4 |
| 色调映射 / 曝光 | ACESFilmic / 1.0 |
| 相机 位置 / FOV | (0, 1.35, 14) / 55° |
| 运镜周期 | 8.0 s(dolly + pan,ease-in-out) |
| 环境光 / 方向光 | 半球 0.30 / 0.0 |
完整数值表(含每一项的源码行号)见 PROMPT.md 的 C 段。
修改 src/ 后重新打包成单文件:
# 1) 准备打包依赖(只需一次;运行作品本身不需要任何依赖)
mkdir .build ; cd .build
npm init -y
npm i three@0.160.0 esbuild
cd ..
# 2) 打包
powershell -NoProfile -ExecutionPolicy Bypass -File .\build.ps1
# → 输出 aurora-curtain.htmlbuild.ps1 直接调用 esbuild 原生二进制,
读取 src/app.js 打包为 IIFE,再把 src/shell.html 里的占位符 /*__APP_BUNDLE__*/
替换成打包结果,写回单文件。
| 环境 | 分辨率 | 帧率 |
|---|---|---|
| Chromium + SwiftShader(纯软件光栅) | 1920×1080 | ≈ 47 FPS |
| 任意独显 / 集显(硬件 WebGL2) | 1920×1080 | 60 FPS(vsync 上限) |
setPixelRatio 上限 2;Bloom 与主渲染在 resize 时同步;几何量极小
(两张 1×1 段帘幕平面 + 一块高度场 + 800 点星空),开销集中在片元着色器。
PROMPT.md 是从这个成品源码里逐行取值写成的实现说明:
| 段 | 内容 |
|---|---|
| A | 完整提示词 —— 可以把整段交给 AI 智能体,按同一套参数把作品重做一遍 |
| B | 生图参考提示词(质感与构图参考) |
| C | 真实数值速查表,每项标注源码行号,可回查 |
| D | 7 个翻车点(症状 → 病因) |
| E | 自检清单与 ?t= 截图开关 |
MIT © 2026 Dai Yihao


