当前位置: 首页 > news >正文

Cesium 体积云进阶:从Perlin-Worley噪声到动态云区渲染

1. 从一团云到动态云区的技术跃迁

第一次在Cesium里用Perlin噪声做出那团棉花糖般的云时,我兴奋地截了十几张图发朋友圈。但很快发现一个问题——这团云放在城市上空像块棉花,放在山脉间又像团雾气,怎么看都不像自然界的云层。真正的云应该有绵延数百公里的蓬松感,有随风流动的形态变化,还有厚度不均带来的光影层次。

这里面的核心矛盾在于:基础Perlin噪声生成的"一团云"本质上是个均匀的噪波球。就像用相同大小的肥皂泡堆出来的云朵玩具,虽然单个泡泡的质感不错,但整体缺乏自然云层的分形结构和多尺度细节。后来在《地平线》游戏的体积云技术文档里找到了答案——需要引入Worley噪声作为"骨架",再用Perlin噪声填充"血肉"。

2. 噪声算法的交响乐:Perlin-Worley混合策略

2.1 为什么单独使用Perlin噪声不够

Perlin噪声的优势在于能生成连续的、自然过渡的随机图案。在Three.js的webgl2_volume_cloud示例中,我们看到通过调节以下参数可以控制云的密度分布:

float d = getData(p + halfdim); d = smoothstep(0.25 - 0.05, 0.25 + 0.05, d) * 0.1;

但这种均匀噪波就像把面粉撒进水里,只能形成模糊的雾状效果。实际云层需要:

  • 清晰的边界轮廓(积云的块状结构)
  • 细腻的内部纹路(卷云的丝状结构)
  • 多层次的密度变化(层云的渐变效果)

2.2 Worley噪声的细胞结构妙用

Worley噪声(又称Voronoi噪声)通过空间划分产生细胞状图案。我将二维算法改造成三维版本时,关键是要保持细胞边界的锐利度:

float getWorleyNoise(vec3 pos) { vec3 p = pos * 4.0; // 放大坐标增加细节 vec3 i = floor(p); vec3 f = fract(p); float F1 = 1.0; // 27邻域搜索确保细胞边界完整 for(int j=-1; j<=1; j++) { for(int k=-1; k<=1; k++) { for(int l=-1; l<=1; l++) { vec3 neighbor = vec3(float(j),float(k),float(l)); vec3 point = random(i + neighbor); float d = length(point + neighbor - f); F1 = min(F1, d); } } } return 1.0 - F1; // 反转使边界变亮 }

这个算法生成的细胞结构就像天空中的云块"骨架"。通过调节坐标缩放系数(示例中的4.0),可以控制云块的平均尺寸。

2.3 分形布朗运动(FBM)的叠加艺术

单一尺度的噪声仍然不够自然。我采用RGBA四通道分别存储不同频率的Worley噪声:

  • R通道:基础频率(大尺度云团分布)
  • G通道:2倍频率(中等云块细节)
  • B通道:4倍频率(细小云丝结构)
  • A通道:Perlin噪声(填充内部纹理)

在Shader中进行混合时,使用指数衰减权重:

float fbm = (texture.g * 0.625) + (texture.b * 0.25) + (texture.a * 0.125); float cloud = texture.r - fbm; cloud = remap(cloud, -1.0, 1.0, 0.0, 1.0); // 重映射到0-1范围

这种混合方式模拟了自然界中云层多尺度嵌套的特性。实测发现0.625:0.25:0.125的权重比能产生最自然的层次感。

3. 体数据存储的优化之道

3.1 从3D纹理到RGBA通道的进化

最初按照Bro_Of_Nagi的方案使用单通道(Alpha)存储密度信息时,发现几个瓶颈:

  1. 无法传递多频噪声数据
  2. 动态调整噪声混合比例需要重新生成纹理
  3. 光照计算缺乏材质变化信息

改用RGBA四通道后,体数据生成变为:

const data = new Uint8Array(size * size * size * 4); for(let z=0; z<size; z++) { for(let y=0; y<size; y++) { for(let x=0; x<size; x++) { const dx = x / size, dy = y / size, dz = z / size; // R通道:基础Perlin噪声 data[i] = perlinNoise(dx*10, dy*10, dz*10) * 128 + 128; // G通道:低频Worley data[i+1] = worleyNoise(dx*2, dy*2, dz*2) * 255; // B通道:中频Worley data[i+2] = worleyNoise(dx*4, dy*4, dz*4) * 255; // A通道:高频Worley data[i+3] = worleyNoise(dx*8, dy*8, dz*8) * 255; i += 4; } } }

这种存储方式带来三个优势:

  • 实时调整混合比例无需重新生成纹理
  • 各频段噪声可单独进行后期处理
  • 额外通道可用于存储光照相关参数

3.2 动态LOD策略解决性能瓶颈

当云区范围超过10km时,发现帧率明显下降。通过分析发现两个性能热点:

  1. 射线步进采样次数过多
  2. 大范围云区导致无效采样增加

改进方案是实施动态LOD:

float getLODLevel(vec3 worldPos) { float dist = length(worldPos - cameraPos); if(dist < 5000.0) return 1.0; // 全精度 if(dist < 10000.0) return 2.0; // 步长加倍 return 4.0; // 极远距离低精度 } void rayMarching() { float lod = getLODLevel(worldPos); for(float t=0.0; t<maxDist; t+=stepSize*lod) { // 根据LOD级别调整采样频率 vec3 samplePos = rayOrigin + rayDir * t; if(lod > 1.0) { // 低精度区域跳过细节计算 float density = getSimplifiedCloud(samplePos); } else { // 全精度计算 float density = getFullDetailCloud(samplePos); } } }

配合视锥体裁剪,在保持视觉效果的前提下,万米级云区的渲染性能提升3倍以上。

4. 动态云区的实现技巧

4.1 基于噪声的形态动画

要让云层自然流动,需要给噪声采样点加上时间变量。但直接修改UV会导致整体变形,我采用分层扰动策略:

vec3 animateNoise(vec3 pos, float time) { // 大尺度缓慢漂移 vec3 windDir = vec3(0.5, 0.0, 0.2); float largeScale = time * 0.1; // 中尺度形态变化 float midScale = time * 0.3; // 小尺度湍流 float smallScale = time * 1.0; return pos + windDir*largeScale + curlNoise(pos*2.0)*midScale + randomVec3(pos*10.0)*smallScale; }

其中curlNoise生成无散度噪声场,模拟真实气流效果。不同尺度的运动速度遵循1/f法则(大尺度运动慢,小尺度变化快)。

4.2 光照模型的优化

初期直接使用Three.js示例中的散射公式时,云层看起来像发光的棉花糖。参考大气散射模型改进后:

float phase(float cosTheta) { // 混合前向和后向散射 float g1 = 0.8; // 主散射方向性 float g2 = -0.3; // 次散射反向增强 float blend = 0.5; // 混合比例 return mix(henyeyGreenstein(cosTheta, g1), henyeyGreenstein(cosTheta, g2), blend); } vec3 calculateLighting(vec3 pos, vec3 viewDir) { float opticalDepth = 0.0; vec3 lightDir = normalize(sunPosition - pos); float cosTheta = dot(viewDir, lightDir); // 光线步进计算阴影 float shadow = 1.0; for(float s=0.0; s<shadowSteps; s+=shadowStepSize) { vec3 samplePos = pos + lightDir * s; shadow *= exp(-densityAt(samplePos) * shadowStepSize); } return sunColor * phase(cosTheta) * shadow; }

这个模型特别强调:

  • 双峰相位函数模拟云的多重散射
  • 基于密度的体积阴影
  • 考虑光线穿透性的光学深度计算

5. 实战中的避坑指南

在青海省地理信息项目里部署这套方案时,遇到过几个典型问题:

纹理内存爆炸:128^3的RGBA纹理需要128MB显存。通过以下方式优化:

  • 使用BC3压缩纹理(内存减少75%)
  • 动态加载可视区域纹理
  • 16位浮点替代32位存储

移动端兼容性问题:部分安卓设备对3D纹理支持不完善。回退方案:

const isMobile = /Android|iPhone/i.test(navigator.userAgent); if(isMobile) { // 使用2D纹理切片模拟3D纹理 texture = new Texture2DArray(...); } else { // 优先使用原生3D纹理 texture = new Texture3D(...); }

昼夜过渡不自然:直接切换光照参数会导致云层"跳变"。解决方案是:

uniform float dayNightProgress; // 0-1渐变参数 vec3 getFinalColor() { vec3 dayColor = calculateDayLighting(); vec3 nightColor = calculateMoonLighting(); return mix(nightColor, dayColor, smoothstep(0.3, 0.7, dayNightProgress)); }

现在这套系统已经稳定运行在多个智慧城市项目中,最远的视距达到50公里。有个意外收获是:通过调整噪声参数,还能模拟沙尘暴、火山灰等特殊天气现象。有次客户突然要求展示台风天气,我们只花了半小时调整Worley噪声的细胞结构就实现了逼真的螺旋云系效果。

http://www.jsqmd.com/news/843041/

相关文章:

  • Unity场景视图操作全解:从鼠标滚轮到Shift+左键,这些隐藏快捷键让你建模效率翻倍
  • HLK-V20语音模块的智能家居实战:如何用STM32控制灯、电机并连接ESP8266上云
  • SpringBoot+Vue校园活动管理平台:从零到一的实战开发与部署指南
  • 别再手动配对了!用STM32+ECB02蓝牙模块实现自动重连,打造稳定无线数据链路
  • ABAQUS 2023版渗流分析保姆级教程:从材料渗透系数到Soil分析步,手把手搞定多孔介质模型
  • ARM SVE2指令集:UABALB与UABALT指令详解与应用
  • 深入杰理AC701N芯片:拆解可视化SDK中蓝牙模式与消息分发的底层逻辑
  • AKShare:5分钟掌握Python金融数据获取的终极解决方案
  • 在银河麒麟V10 SP3上搞定MySQL 8.0.33:保姆级安装与避坑全记录
  • 毫米波雷达3D重建技术解析与工程实践
  • 别再死记硬背build.gradle了!从Groovy闭包到Kotlin DSL,彻底搞懂Gradle脚本的‘魔法’语法
  • Allegro PCB设计避坑指南:图解Margin、Delta、Tolerance,搞定DDR等长布线
  • 高通手机刷机救砖不求人:搞懂这10个关键分区,自己就能救活黑砖
  • 模数转换动态范围优化与无限采样技术解析
  • 开源阅读鸿蒙版:打造您的个性化无广告数字图书馆
  • USB HID键盘注入攻击:从微控制器模拟到物理安全防御
  • 3步掌握SRWE:Windows窗口分辨率自定义的终极指南
  • HT32 BFTM定时器实战:从基础配置到精准计时应用
  • ARTX中定时任务设计与实现问题解析
  • 别再问厂家了!手把手教你用变频器自学习功能获取PMSM磁链和转矩系数
  • 告别重复劳动:用这个Maya Mel脚本插件,5分钟搞定Arnold材质批量调节
  • 3分钟免费解决:Windows HEIC缩略图终极方案
  • 避坑指南:LVGL Bar控件在RTOS和低内存MCU上的5个常见问题与解决方案
  • [STM32U3] 【STM32U385RG 测评】+ PWM调节控制LED
  • 量子门分解技术:原理、优化与实践指南
  • 拆个汽车配件里的压电陶瓷片,用示波器和面包板实测它的‘发电’与‘震动’能力
  • 2026年热门的平度代理记账公司/胶州公司注销公司企业好评榜 - 品牌宣传支持者
  • 嘉立创EDA标准版新手避坑指南:从原理图到PCB制板的10个实用技巧
  • 甲骨文云 Ubuntu 系统更新后网络接口名称变了怎么办?
  • BMC投稿被要求用LaTeX?别慌!手把手教你用MikTeX+TeXStudio搞定论文格式(附中文配置)