“ 在WebGL环境下,将不同的3D引擎(如Cesium、Three.js、Babylon.js等)融合在同一个场景中的方法有多种,每种方法都有其特定的优缺点,适用于不同的场景和需求,这里主要提供一些思路和可行性,具体需要开发者完善。本文仅为个人观点和实践,感兴趣可以自行尝试研究,当然好的方案也不只这几种。”
01
—
WebGL基础概述
WebGL 的渲染管线是一套由硬件加速的逐步流水线,每一阶段的任务与输入/输出十分明确,整体流程如下:
顶点着色器阶段
接收顶点数据(例如顶点位置、法线、纹理坐标等)。
通过顶点着色器计算顶点的变换,完成模型矩阵、视图矩阵、投影矩阵的乘积计算,输出裁剪坐标(Clip Space)。
输出:裁剪坐标(带有齐次坐标,
gl_Position
)。
裁剪与视锥体剔除
- 依据裁剪坐标裁剪出视锥体外的顶点,避免多余计算。
光栅化阶段
将裁剪空间坐标转化为屏幕上的像素(片段)。
根据顶点插值计算片段的属性值(如颜色、纹理坐标等)。
片段着色器阶段
使用片段着色器对光栅化生成的片段逐一着色,结合纹理、光照、材质计算最终颜色值。
片段着色器还会输出深度值(Z 值)。
深度与模板测试、混合
深度测试决定是否绘制当前片段(依据深度缓冲)。
模板测试、混合等会进一步处理透明度与叠加关系。
帧缓冲写入
- 最终颜色值、深度值等被写入帧缓冲(FBO),完成渲染。
WebGL 使用齐次坐标系统(4D),通过矩阵变换实现坐标系的转换,核心流程如下:
模型矩阵(Model Matrix)
定义模型从局部坐标系 到世界坐标系 的变换(旋转、平移、缩放)。视图矩阵(View Matrix)
定义从世界坐标系 到相机视角坐标系 的变换。视图矩阵通常是相机变换的逆矩阵。投影矩阵(Projection Matrix)
定义从相机视角坐标系 到裁剪坐标系 的变换,包括正交投影或透视投影。视口变换(Viewport Transform)
将裁剪空间坐标(NDC,[-1, 1])映射到屏幕坐标系。
02
—
WebGL功能对比
以下是Cesium、Three.js 和Babylon.js 在坐标系、深度缓冲、渲染分类、透明度处理、场景规模 和后处理 等方面的基本对比表格:
特性 | Cesium | Three.js | Babylon.js |
---|---|---|---|
坐标系 | |||
球心笛卡尔坐标系(ECEF),即地球中心坐标系 |
|
右手坐标系(X:向右,Y:向上,Z:向外)
|
右手坐标系(X:向右,Y:向上,Z:向前)
| | 深度缓冲 |
对数深度缓冲(Log Depth Buffer)
|
线性深度缓冲(Linear Depth Buffer)
|
线性深度缓冲(Linear Depth Buffer)
| | 渲染分类 |
11个Pass(包括不透明、透明、环境、地形3dtiles等)
|
不透明和半透明物体分类渲染,手动深度排序
|
不透明和半透明物体分类渲染,手动深度排序
| | 透明度处理 |
使用OIT(Order Independent Transparency)
|
使用深度排序算法,支持多种混合模式(Alpha Blending)
|
使用深度排序算法,支持多种混合模式(Alpha Blending)
| | 场景规模 |
支持全球级大场景,具有高效的空间剔除(Quadtree/Octree)
|
支持中小型场景,较适合桌面和Web应用
|
支持中小型场景,较适合桌面和Web应用
| | 后处理 |
高级后处理效果(FXAA、HDR、Bloom等)
|
支持多种后处理效果(如FXAA、Bloom、SSR)
|
支持多种后处理效果(如FXAA、Bloom、Motion Blur等)
|
1.坐标系
Cesium
使用球心笛卡尔坐标系(ECEF),即地球中心坐标系。在这种坐标系下,所有的坐标都是相对于地球中心的,这使得它能够在全球范围内处理 3D 数据,适用于大规模地理空间应用。
Three.js
和Babylon.js 使用的是右手坐标系,这是图形学中最常见的坐标系。在这种坐标系中,X 轴指向右,Y 轴指向上,Z 轴指向观察者(Three.js)或离开观察者(Babylon.js)。
结合点:在Cesium中结合需要统一坐标系和顶点数据,在转换到上面的webgl坐标系的视图坐标系中需要保持位置正确,同时也需要保证大场景精度需要加入RTC和RTE机制。
2.深度缓冲
Cesium
使用对数深度缓冲(Log Depth Buffer),可以解决大场景深度精度不足的问题。在渲染大范围的场景时,传统的线性深度缓冲会导致远距离的深度信息丢失,使用对数深度缓冲可以有效提高远距的深度分辨率,避免深度冲突。
Three.js
和Babylon.js 默认使用线性深度缓冲,其中深度值在
[0, 1]
范围内,随着物体离相机的距离增加,深度值的分辨率逐渐降低。它们也可以通过启用对数深度缓冲(Log Depth)来优化远距离深度分辨率,特别是在渲染大场景时。结合点:Cesium支持Lod Depth和多视锥两种绘制,默认使用Lod Depth。第一种渲染场景深度结合时需要统一重写深度支持Cesium的LogDepth,第二种直接换到多视锥模式 ,Cesium多视锥按视距分割成多个工作视锥统计渲染场景,虽然能不用重写深度解决大场景深度问题但也会增加额外DC开销。
3.渲染分类
Cesium
采用了精细化的11 个渲染 Pass,分别处理环境光、天空盒、地形、3d瓦片、透明物体、不透明物体等不同类别的渲染。每个 Pass 负责不同的渲染目标,例如深度缓冲、颜色缓冲、阴影、光照等。
Three.js
和Babylon.js 通常将渲染分为不透明物体和半透明物体两个主要分类。在透明物体的渲染上,透明物体需要进行深度排序,以保证它们正确地显示在场景中。
结合点:结合Cesium可以通过自定义Pass流程加入融合引擎的渲染类别,一般分为不透明和半透明物体渲染,这里渲染半透明物体可以直接在Cesium的PASS 7(不透明物体)完成之后渲染自定义的Pass流程。
4.半透明处理
Cesium
使用OIT(Order Independent Transparency) 技术来处理半透明物体的渲染。OIT 是一种不依赖渲染顺序的透明度处理方法,可以有效地解决多重透明物体渲染中的顺序问题,从而得到正确的视觉效果。
Three.js
和Babylon.js 采用深度排序算法,先渲染不透明物体,再渲染透明物体。在渲染透明物体时,通常需要禁用深度写入,仅进行深度测试,并按从远到近的顺序对透明物体进行排序。
结合点:渲染半透明一直都是各引擎的难点,很多半透明渲染算法:α混合,深度排序,深度剥离,OIT,Abuffer..等等但都各有优缺点,所以选择适合引擎的半透明算法才是最优解,这里主要是Cesium结合,所渲染半透明可以选择Pass 8沿用Cesium的OIT机制。
5.场景规模
Cesium
设计上支持全球级大场景,尤其适合地理信息系统(GIS)和大规模 3D 地图应用。它使用空间分层剔除 技术(如 Quadtree、Octree)来高效管理和渲染大范围的场景,确保即使在全球范围内也能流畅地渲染。
Three.js
和Babylon.js 适用于中小型的场景,尤其适合在 Web、桌面应用中使用。它们并没有像 Cesium 那样设计专门的全局场景管理,通常用于游戏、虚拟现实和其他小范围的 3D 可视化。
6.后处理
Cesium
提供了丰富的后处理效果,包括FXAA、HDR(高动态范围)、Bloom、SSAO(屏幕空间环境光遮蔽)等,适合用于处理大规模场景中的视觉效果。
Three.js
和Babylon.js 也支持类似的后处理效果,能够进行FXAA、Bloom、Motion Blur、SSR(屏幕空间反射)等效果。它们的后处理管线非常灵活,支持通过自定义材质和效果来实现更多特效。
03
—
浅谈Cesium融合
在WebGL环境下,将不同的3D引擎(如Cesium、Three.js、Babylon.js等)融合在同一个场景中的方法有多种,每种方法都有其特定的优缺点,适用于不同的场景和需求,这里主要提供一些思路和可行性,具体需要开发者完善。
1.从WebGL层面融合
这种方案的核心思想是在Cesium的渲染流程完成后,在它渲染完不透明物体和半透明物体的前后,插入其他引擎(如Three.js)渲染的物体。具体流程如下:
- 渲染顺序:Cesium渲染主场景的几何和不透明物体。在完成这些渲染之后,不透明物体已经被渲染到帧缓冲区中,接下来进行半透明物体的渲染。在此时,可以插入Three.js的渲染,先渲染Three.js的固态(不透明)物体,然后再使用Cesium的OIT(Order-Independent Transparency)技术渲染半透明物体。
- 半透明物体的渲染:Cesium的OIT技术通过多次渲染并排序半透明物体来处理遮挡问题,这时Three.js渲染的物体已经插入到了合适的位置,能够正确参与透明物体的计算。
优点是:
- 较少修改引擎内部结构:基本上是在现有引擎之间插入渲染步骤,兼容性较好。
- 灵活性:可以根据不同的需求灵活插入不同的渲染过程。
缺点是:
- 渲染性能:可能会因为多次渲染、合成过程导致性能开销较大,特别是在渲染复杂场景时。
- 深度遮挡问题:可能需要小心处理深度信息,以确保不同引擎的物体正确遮挡
Cesium+3DGS渲染
Cesium+OpenVDB体素渲染
Cesium+VFX粒子系统
Cesium+Twgl.js渲染Demo
开源地址:
https://gitee.com/m3d/cesium.js--twgl.js
2.从API层面融合
从API层面的融合是通过将Three.js的Mesh对象转换成Cesium的DrawCommand来实现。DrawCommand是Cesium的渲染接口,允许将渲染指令直接传递到WebGL上下文进行渲染。
具体步骤如下:
将Three.js的Mesh转换为DrawCommand:首先需要将Three.js中的几何体(Geometry)和材质(Material),纹理(Texture)转换为Cesium支持的格式,例如将Three.js的BufferGeometry转换为Cesium的Geometry,材质则需要映射到Cesium的相应材质类型(如Cesium.Material或Cesium.ShaderMaterial)。
创建DrawCommand:Cesium的DrawCommand代表了一个渲染单元,包括了需要绘制的几何数据、材质、着色器等。通过将Three.js转换为Cesium支持的格式后,可以将其作为一个DrawCommand加入到Cesium的渲染队列中。
集成渲染管线:所有的渲染过程,包括Three.js的物体、Cesium的物体,都通过Cesium的DrawCommand渲染管线进行调度。这样做的好处是能充分利用Cesium的渲染优势(如高效的地理空间处理和深度优化)同时不失去Three.js的灵活性。
优点是:
- 高效统一渲染管线:通过DrawCommand的标准化接口,能在Cesium的渲染系统中统一管理所有物体的渲染。
- 可扩展性强:不需要修改现有引擎的核心逻辑,可以方便地增加更多的渲染物体(例如其他引擎的物体)。
缺点是:
- 复杂的转换过程:需要将Three.js的对象映射到Cesium支持的格式,特别是处理复杂的几何和材质时可能比较麻烦,而且每个引擎都有优化机制,处理不好适得其反。
- 潜在的性能瓶颈:尽管通过DrawCommand可以获得一定的性能优化,但大量的Mesh转换和材质转换操作可能会带来性能损失。
Cesium+Three.js GPUWater渲染
Cesium+PBR灯光渲染
Cesium+PBR+后处理渲染
3.深度共享融合
利用Cesium默认提供colorTexture和depthTexture来进行深度重建和着色,如能提供GBuffer信息融合效果会更完善,这里以GBuffer提供深度信息融合为例,由于Cesium默认不支持GBuffer这里以引擎A和引擎B举例:
通过两个引擎之间共享GBuffer(几何缓冲区)信息来解决深度冲突的问题。具体来说,GBuffer是渲染过程中的一种中间缓冲区,存储了物体的颜色、法线、深度等信息。在这两种引擎之间共享GBuffer,可以避免在不同引擎之间的深度冲突。
步骤如下:
渲染引擎A的GBuffer:首先,在引擎A(例如Cesium)中完成场景的渲染,并将几何信息(颜色、法线、深度等)输出到GBuffer。
共享深度信息:引擎B(如Three.js)在渲染之前,通过从引擎A的GBuffer中读取深度信息,重建A引擎的深度值,并对齐自己的渲染深度缓冲区。这意味着在引擎B中绘制物体时,可以根据A引擎的深度信息来正确遮挡物体。
分别渲染:两套引擎可以各自渲染自己的物体,但每个引擎渲染时都会使用共享的深度信息,避免了深度冲突。
优点是:
- 解决深度遮蔽问题:通过共享深度信息,能够有效避免两个引擎之间的深度冲突。
- 灵活性高:每个引擎依然可以独立工作,适用于需要将不同引擎结合的复杂场景。
缺点是:
- 内存开销大:为了共享深度信息,必须保留两套缓冲区和两套场景资源,而且每个引擎的机制不同,优化手段也不同,这会导致内存消耗倍增,尤其是在大型场景下。
- 渲染性能问题:每次渲染之前都需要读取其他引擎的GBuffer或缓冲区信息,计算深度写入深度等,可能会带来一定的性能开销。
Cesium+M3D for WebGPU渲染
这些方法各有特点,具体使用哪种方法要根据应用场景来决定:
- WebGL层面融合:适合快速实现,不需要太多修改引擎本身,适用于深度渲染的场景。
- API层面融合: 可以更深入地集成两个引擎,性能优化空间较大,但需要处理引擎间的接口转换。
- 深度共享:方法适用于对深度遮挡要求较高的场景,但需要权衡内存开销和性能问题。
上文仅供参考,如果其他意见请保留。
本文转自 https://mp.weixin.qq.com/s/TPUeRFh2zNw5SGvg97XUqQ,如有侵权,请联系删除。