GIS融合之路(二)CesiumJS和ThreeJS深度缓冲区整合

这篇具有很好参考价值的文章主要介绍了GIS融合之路(二)CesiumJS和ThreeJS深度缓冲区整合。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在这篇文章开始前再次重申一下,山海鲸并没有使用ThreeJS引擎。但由于ThreeJS引擎使用广泛,下文中直接用ThreeJS同CesiumJS的整合方案代替山海鲸中3D引擎和CesiumJS整合。

系列传送门:

山海鲸可视化:GIS融合之路(一)技术选型CesiumJS/loaders.gl/iTowns

文章开始之前大家可以看下这个视频当中山海鲸中CesiumJS与山海鲸深度整个的结果,图片中展示了Cesium的地形和山海鲸中的水面的整合,这个过程中就有一个完整的深度缓冲区的整合:

GIS融合之路(二)CesiumJS和ThreeJS深度缓冲区整合

具体内容可以移步完整的教程查看: GIS地形编辑-山海鲸可视化视频教程

上一篇文章里简单介绍了山海鲸中城市大师为了整合GIS系统所做的技术选型的探索,最终我们决定采用先后绘制的形式在单个Canvas上整合山海鲸的3D引擎和CesiumJS。那有同学要问了,如果一个先画,一个后画,后画的不就把先画的覆盖了吗?这里我们就要学到深度缓冲区的概念了。

深度缓冲度也称之为DepthBuffer,是GPU为了对光栅化渲染时物体的遮挡关系进行排序用到的概念。概念本身很简单,就是每绘制一个物体的同时,把这个物体在每一个像素点上的深度信息与这个像素点之前的深度信息进行对比,如果这个像素点的深度较小(注意这要看具体深度缓冲的DepthFunction,一般在WebGL上默认是最大的是Depth是1,因此越小越近)则继续渲染像素颜色,否则直接丢弃。

有了深度缓冲区,问题就变得简单了,让CesiumJS先画,山海鲸引擎后画。只要保证深度写入和深度测试都是默认打开的,那不就万事大吉了。Done,下班!

等等,好像有问题;等等,好像有不少问题!等等,好像完全不行....

GIS融合之路(二)CesiumJS和ThreeJS深度缓冲区整合

问题可是真不少,咱还得一关一关的过啊:

1.CesiumJS默认用的LogarithmicDepth,而普通的3D引擎默认用的是LinearDepth

按说这也不是什么大问题,CesiumJS支持修改Scene上的logarithmicDepthBuffer改成linearDepth,Threejs这类也基本都实现了LogarithmicDepth,因此不是大问题。不过由于CesiumJS一般都是大场景和超大场景,改成Linear的话一定会有严重的Z-Fighting,而ThreeJS这类主要是小场景,改成LogarithmicDepth,又会导致在近景部分depth精度不足。当然希望近景和远景同时完美,本身在技术上也是鱼和熊掌的问题,我们暂时不去深入解决这个问题。

2.CesiumJS默认的渲染方式是距离切段后逐段渲染

这条就非常坑了,CesiumJS默认会将整个相机裁切空间(近平面到远平面之间的空间)分成多段,然后逐段渲染。这样做的好处显而易见,可以进一步拓展Depth的利用率,非常适合CesiumJS这种知道所有模型的位置且不会有体积超出范围的大模型的情况。然后每次分段绘制结束之后,depth的信息就会被清除,导致最初规划的深度缓冲度整合的方案完全无法使用(除非关闭这个分段绘制机制),只能采用新的方案。

3.CesiumJS绘制过程无法嵌入

CesiumJS绘制过程机制及其复杂,想要找到一个合适的时机将ThreeJS这类引擎的绘制过程嵌入进去非常困难,而且也没有对应的接口,写起来对CesiumJS代码侵入性极强,后续CesiumJS升级时很难跟随升级,为将来的可维护性留下很深的隐患。

综合这三个问题,最终决定不再让CesiumJS直接绘制到Canvas上,而且采用CesiumJS提供的PostProcessStage接口将整个绘制的ColorBuffer和DepthBuffer都存入FrameBuffer当中,在ThreeJS中再将这两个FrameBuffer转换为WebGLRenderTarget。通过这两种方式就可以拿到CesiumJS的绘制结果。

将CesiumJS的绘制结果转换为两个Texture之后,就要在ThreeJS端绘制进去。这个过程类似PostProcess的过程,但是要先做。这里参考CesiumJS中的ViewportQuad接口,在ThreeJS中创建一个PlaneGeometry,设置一个ShaderMaterial,在VetexShader中,将四个点对齐到整个视口的四个角上,实现代码非常简单。

void main() {
  gl_Position = vec4( position.x, position.y, 1., 1. );
}

在FragmentShader当中读入ColorBuffer和DepthBuffer

uniform sampler2d czmColorSampler;
uniform sampler2d czmDepthSampler;

ColorBuffer直接写入,非常简单,depth如何写入呢。

首先我们要明确我们从CesiumJS拿到的是什么Depth,查看Cesium源码可以发现,我们拿到的depth是LogarithmicDepth被映射到0~1之间之后被pack在rgba四个通道上之后的结果。因此我们首先要对CesiumJS的depth进行unpack,并且根据相机的near和far将depth恢复到相机空间的z距离。拿到这个距离之后为了方便存储,山海鲸目前的做法是将这个z距离再在自己引擎的相机中的near和far做一次映射,算出线性的0~1的depth,这样就可以和自己引擎中拿到的depth一致了,当然为了方便存储,也pack到rgba中区。最终得到的结果存入czmDepthSampler,具体结果如下图所示:

GIS融合之路(二)CesiumJS和ThreeJS深度缓冲区整合

czmColorSampler

GIS融合之路(二)CesiumJS和ThreeJS深度缓冲区整合

czmDepthSampler

GIS融合之路(二)CesiumJS和ThreeJS深度缓冲区整合

最终合成图

这个结果理论上就可以直接和ThreeJS里的相机空间的depth进行对比了,但是注意我们这里并不打算认为对比,而是希望用GPU自己的深度缓冲区测试,这个怎么做呢。这里就要用到Shader的深度写入功能。一般来说GPU在拿到vetexShader中的gl_Position之后会自己把得到的坐标转换到NDC空间中,并进一步将depth映射到0~1之间,再存入depth buffer。我们需要再FragmentShader中接管这个功能,WebGL也提供的接口:gl_FragDepth。(但是要特别注意的是,一旦开启Shader的缓冲区写入,GPU的early-Z优化就会自动关闭,所有的像素点着色都会进行,因此不是我们这种迫不得已的情况,还是尽量不要用的。)有的这些只是,我们只需要最后将线性空间的depth模拟GPU的计算过程,转换为ndc空间的depth写入就可以了。

我们通过以上方式正式将Cesium的渲染过程并入了山海鲸引擎的渲染过程当中,当然这中间还要处理很多gl state状态的问题,不过不管怎样,最难的一步已经越过去了,剩下的就是对目前的机制进行完善,防止状态冲突问题即可。但是深度整合成功是不是Cesium就整合完成了呢,正式成为了山海鲸可视化的一部分?答案显然是否定的,现在的Cesium除了遮挡正常了以外,相机还没有同步,一旦移动,就会发现完全对不上位置。另外除了相机起码还有3个比较大问题:1.阴影的整合 2.光照的整合 3.G-buffer管线数据的同步。别急,我们一步一步来,后面的文章逐个给大家展开这些问题的处理。文章来源地址https://www.toymoban.com/news/detail-487298.html

到了这里,关于GIS融合之路(二)CesiumJS和ThreeJS深度缓冲区整合的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Redis 缓冲区

    缓冲区的应用场景 : 客户端与服务器端的通信时,暂存客户端发送的命令数据,或暂存服务器端返给客户端的数据结果 主从节点间进行数据同步时,暂存主节点接收的写命令和数据 缓冲区 : 避免客户端和服务器端的请求发送和处理速度不匹配 服务器给每个连接的客户端都准

    2024年02月07日
    浏览(69)
  • 理解缓冲区

    对于这样的代码,首先可以肯定的是 printf 语句先于 sleep 执行,既然如此那么就应该是先打印语句然后进行休眠,下面看看结果: 但这里却是先休眠以后再打印语句,这是因为存在一个叫缓冲区的东西,当我们要向外设写入数据(让显示器显示就是向显示器写入数据)时会将

    2023年04月25日
    浏览(72)
  • 【Linux】理解缓冲区

    我们发现 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和fork有关! C接口的函数被打印了两次系统接口前后只是打印了一次:和fork函数有关,fork会创建子进程。在创建子进程的时候,数据会被处理成两份,父子进程发生写时拷

    2024年01月23日
    浏览(54)
  • 【Linux】文件缓冲区

    提到文件缓冲区这个概念我们好像并不陌生,但是我们对于这个概念好像又是模糊的存在脑海中,之间我们在介绍c语言文件操作已经简单的提过这个概念,今天我们不妨深入理解什么是文件缓冲区 通过自己实现库中的一些文件操作函数更加深入的理解文件缓冲区 自定义实现

    2024年02月10日
    浏览(58)
  • SEED-缓冲区溢出攻击

    实验环境:SEED-Ubuntu20.04虚拟机 a) 缓冲区溢出原理 **缓冲区溢出攻击原理:**利用溢出的数据改变源程序的控制流,如覆盖返回地址 b) 分析生成badfile文件的exploit.py程序 Shellcode部分 字节数组末尾处填入shellcode c) 编译目标服务器上具有缓冲区溢出漏洞的stack.c程序,并将其缓冲

    2024年02月07日
    浏览(45)
  • C/C++缓冲区

    什么是缓冲区? 程序和磁盘文件之间不能直接交换数据,必须通过内存中一个被称为文件缓冲区的区域来中转。ANSIC标准规定,系统会自动为每个正在使用的文件在内存中开辟一个缓冲区,缓冲区的大小随机器而异。 缓冲区有什么作用? 假设我们在家中休息看电视吃零食,

    2024年02月15日
    浏览(51)
  • 【Linux】深入理解缓冲区

    目录 什么是缓冲区 为什么要有缓冲区 缓冲区刷新策略 缓冲区在哪里  手动设计一个用户层缓冲区 缓冲区本质上一块内存区域,用来保存临时数据。 缓冲区在各种计算任务中都广泛应用,包括输入/输出操作、网络通信、图像处理、音频处理等。 这块内存区域是由 谁提供的

    2024年02月15日
    浏览(64)
  • 【linux】重定向+缓冲区

    自我名言 : 只有努力,才能追逐梦想,只有努力,才不会欺骗自己。 喜欢的点赞,收藏,关注一下把! close(1),为什么没有打印新建文件fd呢? printf(“%dn”,fd); printf会把内容打印到stdout文件中。 但是close(1)关闭标准输出stdout—显示器,int fd=open();新打开的文件fd是1。 st

    2024年02月08日
    浏览(56)
  • 网络安全——缓冲区溢出攻击

    1、缓冲区溢出概述 什么是缓冲区?它是指程序运行期间,在内存中分配的一个连续的区域,用于保存包括字符数组在内的各种数据类型。所谓溢出,其实就是所填充的数据超出了原有的缓冲区边界,并非法占据了另一段内存区域。 两者结合进来,所谓缓冲区溢出,就是由于

    2024年02月13日
    浏览(48)
  • TCP缓冲区参数调优

    1、系统原值查询 2、设置值 3、参数解析 tcp_wmem(3个INTEGER变量): min, default, max **min:**为TCP socket预留用于发送缓冲的内存最小值。每个tcp socket都可以在建议以后都可以使用它。默认值为4096(4K)。 **default:**为TCP socket预留用于发送缓冲的内存数量,默认情况下该值会影响其它

    2024年02月11日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包