「16-全实时雪地渲染技术-巴比伦JS」
/~1a003ZscWu~:/
链接:https://pan.quark.cn/s/4d5472d3a776
SNOWFLOW
一个实时雪地渲染技术演示。基于 WebGPU + Babylon.js,全程手写 WGSL。
仓库中没有任何纹理、网格、HDRI 或动画数据——你在屏幕上看到的一切,都是在加载时由 GPU 实时生成的。
▶ 在线演示 snowflow-lilac.vercel.app
运行要求: 支持 WebGPU 的桌面浏览器(Chrome/Edge 113+、Firefox 141+、Safari 26+)以及独立显卡或较新的集成显卡。
设计上不提供 WebGL 回退:若检测到navigator.gpu缺失,页面会明确提示并停止运行。
操作控制
操作 | 功能 |
|---|---|
点击画面 | 捕获指针 |
W / A / S / D | 相对摄像机方向移动 |
鼠标 | 视角转动 · 滚轮缩放 |
Shift | 冲刺 |
右键长按 | 雪地冲浪 — 在雪面上滑行并掀起雪浪 |
1 – 5 | 五个法术(2 号为持续施法) |
F1 / ` | 设置面板与性能叠加层 |
叠加层将所有美术参数暴露为实时滑块——太阳角度、风向、次表面散射半径、形变深度、色调映射曲线、曝光等——并附带帧时间图(中位数 / 95% / 1% Low)、Draw Call、三角形数与各系统 CPU 耗时分解。任意系统均可单独关闭,并提供法线、深度、级联覆盖、形变缓冲区与原始阴影贴图的调试视图。
核心功能
地形
采用嵌套环形几何 Clipmap:
8 个环,内层间距 8.5 cm,半径约 870 m,33.3 万三角面
仅一个静态网格,一次 Draw Call
顶点仅携带
(gridIndex, ringLevel);世界坐标放置、CDLOD 过渡与位移全部在顶点着色器中完成,CPU 无需重建,每帧无数据上传
底层高度场由分层梯度噪声 + 解析导数生成,围绕单一主导风呈各向异性:
宽阔的横卧沙丘脊线
长距离低缓起伏
沿风向剪切的中尺度漂移(迎风/背风不对称)
稀疏岩石露头
高度场一次性烘焙为4096² RG32F 纹理并镜像回 CPU,角色接地采样的是与绘制完全一致的表面,而非其重实现版本。
雪地着色
多尺度法线:烘焙宏观坡度 + 解析雪脊与波纹 + 三层平铺细节,陡坡面三平面投影
包裹漫反射 +深度相关蓝调次表面散射(背散射) + GGX 高光
SH 环境光(包含求解出的雪地反弹光)
程序化视角相关闪光(受掠射角门控)
压缩度、湿度与结冰作为表面状态通道被材质读取,而非独立材质。
阴影为手写三级级联,带世界空间 PCSS:
遮挡物搜索 → 半影估计 → 旋转泊松滤波
世界空间纹素对齐,基于旋转不变包围球稳定
Babylon.js 内置级联生成器在此不可用:地形没有与绘制匹配的 CPU 几何,因此每个投射物必须注册其实际使用的顶点程序。
形变系统(Deformation)
持久化、累加式地形状态缓冲区:
两个2048² RGBA16F Target,覆盖 80 m(3.9 cm/纹素)
每帧一次全屏 Pass(Ping-Pong),在同一 Dispatch 中完成滚动、松弛与泼溅
环面寻址(Toroidal):UV =
fract(worldXZ / size),视口跟随玩家而无需拷贝缓冲区,新暴露纹素由同一 Pass 检测并清零
四个通道:凹陷深度、位移质量、压缩度、结冰。
第二个通道(位移质量)区分了“带隆起雪埂的轨迹”与“扁平脚印贴花”。
回填逻辑:
各向异性扩散(松散雪埂坍塌速度是压实沟底的 3 倍)
雪埂向凹陷处塌陷
上风向风驱填充
缓慢指数衰减:约71% 的轨迹深度在一分钟后保留,期间持续扩散、软化
形变为真实几何,在最终渲染与三级阴影级联中通过同一 Include 生效,因此轨迹自投影、雪埂破坏轮廓。
脚掌、冲浪雪浪、五个法术均通过同一个brush()调用写入同一缓冲区。
角色系统
完全程序化生成 —— 无骨骼文件、无动画片段、无手工网格。
18 根骨骼,绑定姿势来自数值表
几何体(兜帽、躯干、手臂、裤腿、靴子、腰带)在加载时从该表放样生成
运动由运动状态求解,而非播放动画
脚部踩踏:距离驱动的站立/摆动状态机在触地瞬间精确写入一次脚的全局位置并绝对锁定,双骨骼 IK 朝向该点拉伸——已踩住的脚不可能滑动,因为代码中没有任何逻辑能移动它。步态相位随地面行进距离推进,步幅与地面速度在数学上天然一致。
衣物:Verlet 布料,4 个布片,带距离、弯曲与形状记忆约束,9 个身体碰撞胶囊体,下摆贴合雪面。褶皱存在于静止形状而非法线贴图中。
36×12 的布料求解结果在顶点着色器中通过 Catmull-Rom 重建渲染为 72×32 表面,曲面细分与模拟开销完全解耦。
兜帽边缘与袖口的壳毛发(Shell Fur)为局部圆环体,发射 22 次,通过哈希发丝场进行 Alpha Test。
仅一张小纹理承载所有数据:第 0–3 行存骨骼矩阵,第 4 行起存模拟布料节点。每帧一次上传,无额外分配。
雪地冲浪(Snow-Surf)
雪浪为扫掠网格(Swept Mesh),而非粒子特效。
脊柱为滑板路径,每 30 cm 重采样一次,存入 96×3 数据纹理
网格本身是
(column, row, side)的静态晶格,所有顶点在 VS 中放置19 米与 2 米的雪浪消耗相同的 Buffer 与相同的 4.6 KB 上传带宽
横截面为基于转向切线积分的破波浪:
切线从底部略低于水平到顶端 284°,一个卷曲参数即可连续控制从低矮堆积到悬垂唇缘的形态
振幅与卷曲按两侧分别解析,转弯外侧承担绝大部分积雪
峰值墙高在全速刻划时达 2.4 m,0.88 秒后坍塌,雪浪长度 = 寿命 × 速度,无需第二个常数
法线从与几何完全相同的wakePoint差分得出,二者永不冲突。
两类喷溅源自同一脊柱:
紧贴波峰的密集慢速水帘
飞离的弹道颗粒
附加屏幕空间速度拖尾与满载边缘的镜头抖动
五个法术
一种水体材质、一个网格、一次 Draw Call、八股水流。
其中四个法术移动同一连贯水体,结构上属于同一种对象:沿脊柱的扫掠曲面,带半径、平行输运标架与泡沫通道——与冲浪雪浪构造相同。未启用的股流通过清零行关闭,Draw Call 数量不随法术数量变化。
法术 | 描述 |
|---|---|
Sweep(横扫) | 新月形雪泥从地面升起向外推进,犁出沟壑并抛起雪埂 |
Ribbon(缎带) | 持续施法,追踪手部与准星,绘制进动的 8 字,在掠过的雪面刻下细曲线;释放后头部锁定准星加速,水流以释放时的弯曲形态弧线击中目标 |
Bloom(绽放) | 定点喷发:带隆起边缘的陨石坑 + 沿自身轴线升降的束腰柱体 + 4 秒底部照亮的坠落帷幕 |
Crystallise(结晶) | 沿黄金角螺旋生长的六棱柱,Alpha 混合 + 深度写入,透过冰层可见雪地但棱柱互不穿透;切面法线来自屏幕空间导数,边缘绝对锐利 |
Vortex(漩涡) | 环绕玩家的三股螺旋上升雪流,空中质量沿螺旋以切向速度发射;唯一写入负凹陷的系统 |
折射:无需场景拷贝与二次不透明 Pass。天空 LUT 已存储地平线下求解出的雪地反弹光,沿折射射线单次采样即可物理估算任意方向后方颜色;三次不同折射率采样得到色散,路径长度吸收得到色调。
每帧声明4 个池化动态光源,全部运行与太阳相同的snowSubsurface逻辑——法术通过雪埂顶部照亮雪地,而非仅在近面打亮。
雪地、长袍、雪浪、空中喷溅、水体与冰层全部从同一 Include 读取同一光照池。
天空与大气
Nishita 单次散射积分 + 多重散射近似 + 迭代求解雪地反弹,烘焙为等距柱状 LUT + SH 辐照度 + Mip 层级高光。
采用解析大气而非捕获 HDRI:整体观感高度依赖 10–15° 仰角的太阳——
调整太阳高度时,地平线暖调、天顶渐变、环境色调与直射阳光颜色同步变化
远景为在天空盒上光线步进的高度场:
无几何,天然位于一切之后
解析法线,山脊相互遮挡
朝向太阳进行二次短步进以生成自身投射阴影
使用雪地材质逻辑照明,受同一大气雾化,与近景雪地在颜色上无缝衔接
后处理管线
以相机空间深度预 Pass(线性视图深度作为 Varying 传递 + 高光掩码)驱动整条链路:
TAA:Halton(2,3) 抖动直接写入投影矩阵并整帧冻结,确保预 Pass 与最终 Pass 亚像素对齐;基于深度的重投影、方差裁剪、五点 Catmull-Rom 历史采样
Bloom:三级,阈值基于曝光值,仅太阳盘、闪光与受照喷溅参与;预过滤使用 Karis 平均
体积光(God Rays):沿射线向太阳方向在预 Pass 中积分天空可见性;太阳仰角高时自动淡出
景深:刻意轻微,焦平面跟踪 Spring Arm 自身长度,按各采样点的模糊圈加权
屏幕空间反射:仅作用于冰面,由预 Pass 掩码门控,Crystallise 未施放时整帧跳过
AgX / ACES 色调映射 → 对比度自适应锐化 → 颗粒感 → 暗角
性能数据
测量环境:WebGPU Timestamp Queries,2560×1440,Chrome / Win11 / RTX 5070 Ti,全系统开启:
指标 | 数值 |
|---|---|
GPU 帧时间 | 3.22 ms |
— 基础场景(Clipmap、雪地、3 级联、天空、角色、形变、预 Pass) | 1.64 ms |
— 后处理链 | ~1.1 ms |
— 远景 | ~1.2 ms |
— 角色(骨骼、布料、毛发、喷溅) | < 0.02 ms |
Draw Calls | 15–19 |
三角形数 | ~353,000 |
90 FPS 预算余量 | 7.9 ms |
渲染循环零分配:所有 Buffer 构造时定长,每帧写入均进入预分配的 Typed Array;所有材质、程序化纹理与渲染管线均在加载界面显式isReady()门控并预热,确保首个法术施放不会在帧中间编译管线。
显存占用约 350 MB:
4096² 高度纹理
两个 2048² 形变 Target
三个 2048² 阴影级联
天空与细节 LUT
本地运行
npm install npm run dev # Vite 开发服务器,端口 :5173 npm run build # 生产构建至 dist/ npm run preview # 预览生产构建目录结构
src/ ├── main.js # 入口与帧调度 ├── core/ # 设置、输入、相机臂、性能、加载、GPU 辅助 ├── terrain/ # 高度场、Clipmap 网格、形变状态缓冲 ├── render/ # 天空 + IBL、阴影级联、深度预 Pass ├── character/ # 骨骼、程序化几何、布料求解、雪面接触 ├── vfx/ # 池化粒子、雪地冲浪雪浪 ├── spells/ # 五个法术、共享水体、光源池 ├── post/ # 后处理链 ├── ui/ # 设置面板与性能叠加层 └── shaders/ # 全部 WGSL,lib/ 存放共享 Include资产与许可证
无任何第三方美术资产。
运行中看到的每一张“纹理”、每一个环境贴图、每一个网格,均由 GPU 在加载时实时生成:
天空 = 大气积分
雪粒与地形 = 噪声
角色 = 数值表放样
织物编织与毛发 = 片元着色器实时求值
运行时依赖:@babylonjs/core、@babylonjs/materials(Apache-2.0)
构建依赖:Vite(MIT),不参与最终输出分发。
本项目源码基于 MIT 许可证发布。