Cesium中实现全球体积云效果的一种方案

原生 Cesium 提供了一种积云的效果,云的物理特征和渲染性能都还不错,这种方案适合表达小范围相对离散的云朵,但是用来实现全球范围下相对连续、柔和渐变的云层比较困难。本文在体渲染的基础上,参考了开源社区中 shadertoy 和 three.js 关于体积云的一些例子,提出一种在 Cesium 上实现全球体积云效果的方案。

Cesium 上进行体渲染可以参考《Cesium中使用Sampler3D,3D纹理,实现体渲染》。体渲染有相对固定的实现流程,关键在于以三维纹理表达的体数据的构建。由于云没有统一的形状和密度,因此我们在构建体数据时,需要引入噪声来表达这种随机性。本文参考的 shadertoy 效果 用到了perlin 噪声和 worley 噪声的组合,并且在 perlin 噪声和 worley 噪声的基础上都加上了分形布朗运动(Fractal Brownian Motion),将不同振幅(amplitude)和频率(frequency)的多个噪声叠加起来,让噪声有更多细节,使得云层的形态更加自然。

体数据的计算我基本照搬了上面链接中 shadertoy 的代码。计算过程放在 CPU 或者 GPU 上都可以。我最开始是在 CPU 上做的,但是发现实在是太慢了,光计算体数据就要花两三分钟;后来转到 GPU 上,用渲染到纹理的方式,依次把三维纹理每一层(三维纹理可以看成是由一系列二维纹理叠放组成的)的数值绘制到二维纹理上;每绘制一层就把那一层的数据读取(gl.readPixels)到一个 Uint8Array 中,最后合并成一个大的 Uint8Array 用于构建存储体数据的三维纹理,基本流程如下面的代码片段所示。这种方式非常快,几乎感觉不到计算的耗时。这一步创建好的体数据在光线步进(RayMarching)的时候采样。我把计算好的体数据存在了一个 json 文件里,如果有同学想直接拿到一份可用的体积云数据,可以到这里下载。

  const slice = 128; // 体数据是一张 128 * 128 * 128的三维纹理const data = new Uint8Array(slice * slice * slice * 4);for (let i = 0; i < slice; ++i) {// 清空上一次绘制的纹理renderClearCommand.execute(viewer.scene.frameState.context, viewer.scene.view.passState);// 离屏渲染三维纹理的一张二维切片数据renderColorCommand.execute(viewer.scene.frameState.context, viewer.scene.view.passState);// 读取一层二维切片的像数值const pixels = viewer.scene.context.readPixels({ framebuffer: renderFbo,x: 0,y: 0,width: slice,height: slice});// 存储像数值到体数据的 Uint8Arraydata.set(pixels, i * slice * slice * 4);}

 用GPGPU的方式生成体数据

 

光线步进(RayMarching)一般不会从相机位置就开始迭代,为了提升性能,都是将射线和体渲染的范围几何体求一个近处的交点、一个远处的交点,缩短步进的距离。我们要渲染全球范围的体积云,云层的最小高度和最大高度分别确定了两个球体,射线需要和这两个球体求交。下面代码参考自《Unity URP RayMarching 体积云》,其中 raySphereDst 函数用于计算射线和球体的相交情况,rayCloudLayerDst 函数计算从相机发出的射线和云层最小高度和最大高度分别确定的两个球体的相交情况,从而得到体积云渲染光线步进的起点和终点。

	/*计算射线和球体的相交情况sphereCenter 球体中心sphereRadius 球体半径rayOrigin 步进起点rayDir 步进方向返回值:dstToSphere  射线起点到球体的距离dstInSphere  射线穿过球体的距离*/vec2 raySphereDst(vec3 sphereCenter, float sphereRadius, vec3 rayOrigin, vec3 rayDir){vec3 oc = rayOrigin - sphereCenter;float b = dot(rayDir, oc);float c = dot(oc, oc) - sphereRadius * sphereRadius;float t = b * b - c; // t > 0有两个交点, = 0 相切, < 0 不相交float delta = sqrt(max(t, 0.0));float dstToSphere = max(-b - delta, 0.0);float dstInSphere = max(-b + delta - dstToSphere, 0.0);return vec2(dstToSphere, dstInSphere);}/*计算相机发出的射线与云层范围的相交情况返回值:dstToCloudLayer  到云层的最近距离dstInCloudLayer  在云层中穿过的距离*/vec2 rayCloudLayerDst(vec3 rayOrigin, vec3 rayDir){vec3 sphereCenter = vec3(0.0);vec2 cloudDstMin = raySphereDst(sphereCenter, (minCloudHeight + earthRadius) / (maxCloudHeight + earthRadius), rayOrigin, rayDir);vec2 cloudDstMax = raySphereDst(sphereCenter, 1.0, rayOrigin, rayDir);float cameraHeight = czm_eyeHeight;// 射线到云层的最近距离float dstToCloudLayer = 0.0;// 射线穿过云层的距离float dstInCloudLayer = 0.0;// 在地表上if (cameraHeight <= minCloudHeight){vec3 startPos = rayOrigin + rayDir * cloudDstMin.y;dstToCloudLayer = cloudDstMin.y;dstInCloudLayer = cloudDstMax.y - cloudDstMin.y;return vec2(dstToCloudLayer, dstInCloudLayer);}// 在云层内if (cameraHeight > minCloudHeight && cameraHeight <= maxCloudHeight){dstToCloudLayer = 0.0;dstInCloudLayer = cloudDstMin.y > 0.0 ? cloudDstMin.x: cloudDstMax.y;return vec2(dstToCloudLayer, dstInCloudLayer);}// 在云层外dstToCloudLayer = cloudDstMax.x;dstInCloudLayer = cloudDstMin.y > 0.0 ? cloudDstMin.x - cloudDstMax.x: cloudDstMax.y;return vec2(dstToCloudLayer, dstInCloudLayer);}

 计算光线步进的起点和终点

上面的步骤分别是体数据的生成和光线步进起止点的计算,体积云的正式渲染可以参考 three.js 官方给出的体积云示例,这个示例也只适合小场景离散的云朵渲染,结合上面的步骤可以在 Cesium 上拓展为全球体积云效果。该示例的实现比较简单,主要工作量是在通过噪声生成体数据以及片元着色器中相关的着色代码。

本文介绍的全球体积云实现方案主要是把现有的一些开源方案做了组合,它不需要依赖外部的噪声纹理,也不用再从片元向光源步进去计算云层的漫反射光颜色,优势是实现步骤简单明了,在仿真要求不是特别高的场景是够用的。最终可以得到如下图1和2所示的体积云效果。

图1 全球视角下的体积云效果

图2 近地面体积云效果 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/869881.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c#调用c++ dll库报错System.BadImageFormatException

System.BadImageFormatException:“试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)” 1. dll需要选择release模式进行编译 2.选择相同位数&#xff0c;比如x64平台&#xff0c;c#也需要x64 3.不要设置c#不支持的函数供调用 比如&#xff1a; c可以输出到控制台…

Meta发布Llama 2驱动的AI代码生成器:Code Llama,开源来袭!

Meta 刚刚了号称是编程领域 “最先进的大语言模型”—— Code Llama &#xff0c;可根据 代码和自然语言提示 生成代码和有关代码的自然语言&#xff0c;支持多种主流编程语言&#xff0c; 包括 Python、C、Java、PHP、Typescript (Javascript)、C# 和 Bash 。 Code Llama 完全…

拆分盘究竟是什么?一篇文章带你了解!

拆分盘是一种特殊的理财产品或投资模式&#xff0c;它通常被描述为“只涨不跌”的投资方式&#xff0c;多指股票&#xff0c;但实质上与传统股市中的股票有本质区别。以下是对拆分盘的详细解析&#xff1a; 一、拆分盘的定义 拆分盘可以理解为一种只涨不跌的理财股票。其特点在…

OpenGL3.3_C++_Windows(29)

Demo exposure 0.1f exposure 5.0f HDR色调映射 问题&#xff1a;有多个亮光源使这些数值总和超过了1.0&#xff0c;颜色值会被约束在1.0&#xff0c;从而导致场景混成一片&#xff0c;难以分辨&#xff1a;色调映射&#xff1a;用更大范围的颜色值渲染从而获取大范围的黑暗…

怎么将3张照片合并成一张?这几种拼接方法很实用!

怎么将3张照片合并成一张&#xff1f;在我们丰富多彩的日常生活里&#xff0c;是否总爱捕捉那些稍纵即逝的美好瞬间&#xff0c;将它们定格为一张张珍贵的图片&#xff1f;然而&#xff0c;随着时间的推移&#xff0c;这些满载回忆的宝藏却可能逐渐演变成一项管理挑战&#xff…

鸿蒙语言基础类库:【@ohos.util (util工具函数)】

util工具函数 说明&#xff1a; 本模块首批接口从API version 7开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 该模块…

Linux系统之lscpu命令的基本使用

Linux系统之lscpu命令的基本使用 一、lscpu命令介绍二、lscpu命令的使用帮助2.1 命令格式2.2 命令选项2.3 使用帮助 三、lscpu命令的基本使用3.1 查看lscpu版本3.2 直接使用lspcu命令3.3 可解析的格式打印cpu信息3.4 可扩展格式打印cpu信息 四、lscpu命令使用注意事项 一、lscp…

【题目/算法训练】:单调队列单调栈

&#x1f680; 前言&#xff1a; 【算法】单调队列&&单调栈 可以在看完这篇文章后&#xff0c;再来写下面的题目 一、绝对差不超过限制的最长连续子数组 思路&#xff1a; 1&#xff09; 就相当于滑动窗口&#xff0c;维护滑动窗口内的两个值&#xff0c;一个是最大值…

Linux常用选项和指令

目录 Linux指令使用注意 用户创建与删除 ls指令 ls指令介绍 ls常见选项 ls选项组合使用 pwd指令 Linux文件系统结构 多叉树结构文件系统介绍 多叉树结构文件系统的特点 cd指令 绝对路径 相对路径 cd指令介绍 家户目录 最近访问的目录 touch指令 ​编辑mkdir指…

3D模型格式转换工具HOOPS Exchange如何访问产品制造信息(PMI)?

在当今的制造和设计领域&#xff0c;产品制造信息&#xff08;PMI&#xff09;在确保零件和产品满足精确规格方面发挥着至关重要的作用。PMI&#xff0c;特别是几何尺寸和公差&#xff08;GD&T&#xff09;&#xff0c;提供了制造过程中必须遵循的详细指导。 随着技术的进…

js ES6 part2

forEach遍历 forEach() 方法用于调用数组的每个元素&#xff0c;并将元素传递给回调函数 主要使用场景&#xff1a; 遍历数组的每个元素 语法 被遍历的数组.forEach(function(当前数组元素&#xff0c;当前元素索引号){ //函数体 }) 1. forEach 主要是遍历数组 2. 参数当前…

Milvus核心组件(1)- Architecture

目录 cluster 模式 数据请求处理流程 总流程 逻辑channel 到物理channel 数据维护流程 cluster 模式 上一篇其实已经说过 standalone 模式&#xff0c;其实集群模式大同小异&#xff0c;只是在不同机子间使用Kafka或者其他消息中间件保证数据及逻辑的一致性。 Log Broker…

Mac 上安转文字转 SQL 利器 WrenAI

WrenAI 是一个开源的 Text-SQL 的工具&#xff0c;通过导入数据库结构&#xff0c;通过提问的方式生成 SQL。本文将讲述如何在 MacOS 上安装 WrenAI。要运行WrenAI&#xff0c;首先需要安装 Docker 桌面版。 下载 WrenAI https://github.com/Canner/WrenAI/releases/tag/0.7.…

java数组之线性查找、二分法查找

一、线性查找 思想&#xff1a;如果想在一个数组中查找是否有某个元素&#xff0c;最容易想到的办法就是遍历数组&#xff0c;将数组中元素与想要查找的元素逐个对比&#xff0c;如果相等表示找到了&#xff0c;如果不等&#xff0c;则表示没找到。这就是线性查找的思想。 案例…

算法导论 总结索引 | 第四部分 第十七章:摊还分析

1、数据结构的一个操作序列中 所执行的 所有操作的平均时间&#xff0c;来评估该操作的代价。摊还分析 不同于平均情况分析&#xff0c;它并不涉及概率&#xff0c;它可以保证最坏情况下每个操作的平均性能 它是一种平均情况下的 性能分析方法&#xff0c;用于 评估一系列操作的…

开源流程表单设计器都有哪些值得一提的优势?

如果需要提质、增效、降本&#xff0c;不妨来了解下低代码技术平台、开源流程表单设计器的功能和优势特点。想要实现流程化办公&#xff0c;低代码技术平台是助力增效的理想工具。功能灵活、操作方便、好维修、可视化操作等优势都是其深受行业喜爱的优势特点。通过本文&#xf…

Errno2:No such file or directory,在当前文件确实没有该图片,怎么解决?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

常用网络概念

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ​​ 目录 了解组织 局域网技术 …

高深宽比刻蚀和纳米级图形化推进存储器的路线图

随着市场需求推动存储器技术向更高密度、更优性能、新材料、3D堆栈、高深宽比 &#xff08;HAR&#xff09; 刻蚀和极紫外 &#xff08;EUV&#xff09; 光刻发展&#xff0c;泛林集团正在探索未来三到五年生产可能面临的挑战&#xff0c;以经济的成本为晶圆厂提供解决方案。 …

【进阶篇-Day7:JAVA中Date、LocalDate等时间API的介绍】

目录 1、概述2、JDK8(-) 时间类2.1 Date类&#xff1a;&#xff08;1&#xff09;构造方法&#xff1a;&#xff08;2&#xff09;常用成员方法&#xff1a; 2.2 SimpleDateFormat类&#xff1a;2.3 总结&#xff1a;2.4 Calendar类介绍&#xff1a; 3、JDK8() 时间类3.1 日历类…