关于 threejs-volumetric-clouds
面向 Three.js 的体积云渲染设计与验证技能,指导图形开发者构建有界云体,并检验密度、光照、时间重建及误差与成本。
主要功能
- 01选择工作负载与表示方式
- 02定义光学和运动模型
- 03保守密度边界与光线步进
- 04云光照和阴影方法
- 05时间重建与合成
适用场景
- 01为局部或大范围云层选择渲染路径
- 02将密度跳跃与暴力步进结果对比
- 03验证云阴影和辐射误差
- 04随时间重建降分辨率云层
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ linegel/ threejs-complete-set-of-skill --skill threejs-volumetric-clouds安装后在 Claude Code 中输入/threejs-volumetric-clouds确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEagents/openai.yamlreferences/density-and-marching.mdlighting-and-shadows.mdtemporal-reconstruction.md详细介绍
体积云
构建一个有界体积,其主体由天气尺度的成因塑造,细节会侵蚀主体,并且其光学、阴影和时间误差均可测量。
流程
1. 选择声明和工作负载分支
先说明声明。程序化天气、覆盖率、形状和细节通常构成一种人为设计的外观模型。对于已声明的密度和系数,Beer-Lambert 衰减具有物理依据。在通过传输参考验证之前,双瓣相位拟合、倍频多重散射补偿、粉化效果和简单地面反弹仍然只是近似。
选择每个独立的工作负载分支:
| 决策 | 选择 | 证据 |
|---|---|---|
| 局部还是广域 | 投影范围较小时采用全分辨率裁剪步进;覆盖范围较广时采用降分辨率步进 | 完整分支的 GPU 成本和图像误差 |
| 全分辨率还是降分辨率当前网格 | 复用率低时采用全分辨率当前网格;宽广且连贯的云层采用降分辨率网格加重建 | 当前采样、带宽和重建误差 |
| 稠密还是稀疏 | 占用稠密时采用有界自适应步进;占用稀疏时采用保守宏单元 DDA | 节省的采样成本超过层级结构构建和遍历成本 |
| 接收器阴影 | 没有外部接收器查询云层阴影时不启用;地面/不透明接收器采用完整柱体 2D 光学深度;云内采样采用短距离太阳光步进或深度感知光照结果 | 已纳入的接收器查询和透射率误差 |
| 降水 | 仅用于外观的效果;或由 $threejs-rain-snow-and-wet-surfaces 消费的因果液态/冰态发射量 | 带单位的发射量、支持域、传输延迟和守恒误差 |
对于因果降水,发布以 kg m^-2 s^-1 为单位的液态和冰态质量通量,或以 kg m^-2 为单位的区间积分面质量,并明确标注采样时间、采样区间、物理帧/原点、物理支持域、面积/雅可比约定、下落延迟或传输模型、所有者、契约版本、代次、有效性、守恒门限和误差。将每个区间只发布一次到 $threejs-rain-snow-and-wet-surfaces。接收器将通量转换为区间质量一次,保留声明的支持域/雅可比,并在累积之前应用声明的延迟或传输模型。
密度/发射量配方、不兼容的物理帧/原点映射、支持域/雅可比约定或延迟模型发生变化时,开始新的代次。接收器会拒绝过期或不兼容的代次,绝不重新解释先前的区间质量。仅用于外观的分支仍然只是视觉效果,不发布接收器输入。
none 分支不分配接收器阴影结果。
完成条件: 五项决策均明确选定的表示方式及其可观测量;每种已纳入的工作负载均有可证伪的成本或误差门限;none 接收器阴影分支记录无接收器消费者且分配量为零;仅用于外观的降水不发布接收器输入;因果降水具有一个带单位的代次,并由一个兼容的消费者接受。
2. 定义统一的光学和运动模型
选择使用 beta_s 和 beta_a 表示无量纲形状密度,单位为
length^-1;或者选择使用质量比系数表示物理质量密度。始终保持
约定一致:
在下列方程中,beta 表示与 rho 匹配的系数:形状密度对应长度的倒数,物理质量密度对应单位质量的面积。
sigma_s = rho * beta_s
sigma_a = rho * beta_a
sigma_t = sigma_s + sigma_a
tau = integral sigma_t ds
T_step = exp(-sigma_t*ds)
DeltaL = T_acc * (j/sigma_t) * (1-T_step)
使用零光学深度极限 DeltaL = T_acc*j*ds,并依据
sigma_t*ds 而非单独依据系数来选择,同时使用稳定的 expm1/级数计算。这里的
j 是单位长度的源辐亮度。对于直射光,要区分需要进行立体角积分的有限圆盘辐亮度,与明确声明的准直辐照度约定。
将相函数归一化,使 2*pi*integral_-1^1 p(mu)dmu=1。令
rayDirection 指向从相机到采样点,toSun 指向从采样点到太阳;这样
mu=dot(toSun,rayDirection) 会使 mu=1 表示前向散射。双瓣权重必须非负且总和为 1。
定义统一的物理/渲染坐标系转换、米制比例、云状态时钟和宏观空气速度。按经过的模拟时间对速度积分;将相对天气、形状和细节运动视为相对于宏观平流的有界偏移。
**完成标准:**均匀薄层不受步长划分影响,并达到零消光极限;相函数数值积分具有正确的归一化和预期的前向方向;两种更新频率在容差范围内积分出相同的运动轨迹。
3. 构建有界且守恒的密度
将各活动层分开处理,包括高度/剖面、天气、形状、光学属性和运动。使用低频天气场和紧凑的垂直剖面确定云团质量。在可分辨尺度上应用形状。将细节用作随高度变化的侵蚀效果;它可以使占据区域的边界变得粗糙,但必须保留用于保守界限的天气/基础形状空集。
先积分一个宏观偏移,再添加有界的相对偏移:
macroOffset += integral u_air(t) dt
weatherOffset = macroOffset + relativeWeatherOffset
shapeOffset = macroOffset + relativeShapeOffset
detailOffset = macroOffset + relativeDetailOffset
将射线与选定的球形壳层、薄层、OBB 或稀疏体积域求交,然后将远端截断到最近的不透明场景深度。合并被占据的高度范围,仅跳过它们的补集空隙。对于水平稀疏性,构建最大密度宏单元,并纳入天气、剖面、形状、形变范围以及每一种增加密度的操作。平均 mip 是外观滤镜,不是占据界限。
请阅读 references/density-and-marching.md,了解密度方程、域求交、保守跳过界限和步长选择。
**完成标准:**调试视图能够证明每个跳过的区间/单元均为空,或处于声明的辐射忽略界限内;暴力步进与加速步进在透射率、HDR 辐射亮度和首次贡献深度上的结果一致。
4. 步进并照亮选定表示
写入当前场景线性云辐射、透射率,以及选定时间分支所需的深度数据。根据光学深度、已解析场带宽、单元格/层边界、非透明深度和剩余云区间限制步进。只有当剩余光源误差和背景衰减误差,以及已累积的跳过误差,都满足输出门限时,才终止。
在 Three.js r185 上,运行 await renderer.init(),并在分配或提交计算/存储工作前确认
renderer.backend.isWebGPUBackend === true。然后通过 renderer.compute() 提交
Fn().compute(count)。2D 当前/历史/阴影结果使用 StorageTexture,仅可写入的体积场使用 Storage3DTexture。将 computeAsync() 视为入队操作,而非完成栅栏。PassNode.setResolutionScale() 会缩放整个通道,因此应让宿主场景保持所需分辨率,并将缩小后的云放入独立通道/资源中。
仅根据云的光学深度计算云自遮蔽。地面接收点可以使用完整的太阳光线柱。云内采样点需要其自身位置的光学深度,可通过短距离太阳光线步进、深不透明度切片或其他深度感知表示提供。
如果任务涉及散射、大气衍生光照、相位拟合、云阴影或阴影滤波,请阅读 references/lighting-and-shadows.md。
**完成标准:**步长减半和增加光照采样数的控制符合线性 HDR 误差门限;关闭云的控制返回单位透射率和零云辐射;每个获准的阴影查询都能从实际接收点位置解码光学深度。
5. 重建大范围云层
对于缩小后的大范围覆盖分支,存储按不透明度加权的代表深度和深度离散度:
w_i = T_i * (1-T_step_i)
z_bar = sum(w_i*s_i)/sum(w_i)
variance_z = sum(w_i*(s_i-z_bar)^2)/sum(w_i)
仅当贡献分布为单峰时,才使用单个代表表面。对于宽广/多层贡献,使用前层深度加矩,或拆分历史。将受平流影响的代表物理点重投影到上一帧相机。记录不可变的当前渲染到物理空间,以及上一帧物理空间到渲染空间的变换;原点重置时,通过这些变换进行映射。宿主表面速度是另一种信号。
使用与帧率无关的当前响应进行混合:
alpha_current = 1-exp(-dt/responseTime)
resolved = alpha_current*current + (1-alpha_current)*clippedHistory
当历史数据位于视口之外,或存在深度/离散度不匹配、相机切换、投影变化、天气/拓扑不连续、编码变化、分辨率/档位变化,或缺少/不兼容的上一帧原点映射时,拒绝历史数据。对于遮挡显露和低置信度情况,提高当前响应。运算前拒绝无效样本。分别对预乘线性 HDR 辐射和透射率进行方差裁剪,然后仅对区间兼容的传输进行上采样;平均深度/前层深度无法消除隐藏的后层贡献。
如果历史数据、稀疏阶段、深度编码、重置策略或上采样属于任务范围,请阅读 references/temporal-reconstruction.md。
**完成标准:**平移密度控制能重投影到预期的上一帧像素;经过映射的浮动原点重置能保留该像素;相机切换/拓扑变化/不兼容原点控制会将历史置信度设为零;测得的鬼影衰减符合响应模型;深度边缘上采样不会越过非透明表面。
6. 集成光照与图像交接
选择一种入射光照约定:
- 编写一个光源,声明其所有者、修订版本、采样时间和时效、坐标系、物理量、单位、基准、方向或角度范围、滤波器、包含的衰减、有效性和误差;或
- 使用大气光照,并声明相同的元组。
对于大气分支,选择以下其中一种:已考虑大气衰减的直射光,或未衰减光源加上大气透射率。分别相乘每个符合条件的仅云层透射率因子和不透明度可见性因子。区分定向天空辐亮度与半球天空辐照度。
在宿主色调映射之前合成云层。此传递仅适用于其建模区间;重叠的大气/云层介质需要耦合求解或经过验证的交错式相机路径求解,不能对两个完整区段依次进行雾化合成:
C_out = L_cloud + T_cloud * C_scene
使用一个 WebGPURenderer 和一个宿主 RenderPipeline。为数据资源显式指定格式、滤波/缩略图策略,并设置为 NoColorSpace。由宿主的 renderOutput() 或 outputColorTransform 负责唯一一次显示转换;替换诊断输出节点后,将管线标记为需要更新。
**完成标准:**入射光源具有完整约定;衰减追踪能将每个纳入的大气、云层和几何因子各计算一次;云层缓冲区保持线性 HDR;关闭云层后,宿主图像路径完全不变。
7. 验证系统
验证以下内容:
- 均匀介质平板传递、零消光极限和相位函数归一化;
- 边界、不透明深度钳制、保守跳过和提前退出误差;
- 固定种子的天气质量、侵蚀、倍频滤波和平流连续性;
- 每种纳入的地面或云内阴影解码器、更新频率和过期产品拒绝机制;
- 对于因果降水,验证通量到区间的积分、在更新频率重新划分和物理支撑/Jacobian 面积分区下的质量守恒、声明的延迟到达映射,以及过期代次拒绝机制;仅外观控制不得发布接收器输入;
- 对于每个纳入的时间分支,验证平移密度、深度编码、历史拒绝机制、响应时间和深度感知上采样,包括映射后的原点重基准和不兼容的原点重基准;
- 将固定视角下的 HDR 辐亮度、透射率、轮廓和光晕与更高质量的参考结果进行对比;
- 验证创建、GPU 完成、释放,以及每种纳入的调整大小/档位切换或历史重置生命周期。
**完成标准:**每个选定分支都通过数值、时间、视觉和生命周期门槛;诊断信息能标识每一代保留的密度、阴影、历史和因果降水数据。
故障特征
| 现象 | 检查项 |
|---|---|
| 烟雾有孔洞或沸腾 | 天气质量原因、细节侵蚀、倍频滤波或共享平流 |
| 亮度随步数变化 | 光源单位或传递积分 |
| 成本随相机远平面增大 | 体积边界或不透明深度钳制 |
| 跳过时密度消失 | 过期/非保守主控值 |
| 相机运动拖影 | 代表性深度、云层速度或拒绝机制 |
| 地面阴影平坦/脱离 | 接收器表示、投影或时效 |
| 切换云层后颜色变化 | 重复色调映射或输出转换 |
路由边界
此技能负责天气塑形的云层密度、有界云层传输、仅云层光学深度阴影以及云层专用重建。分子/气溶胶传输和共享日光/天空光源请使用 $threejs-sky-atmosphere-and-haze;场景信号和最终输出请使用 $threejs-image-pipeline;因果降水传输和接收器累积请使用 $threejs-rain-snow-and-wet-surfaces;不透明几何体阴影贴图请使用 $threejs-scalable-real-time-shadows。