如何在浏览器中快速看穿 3D 体积内部:CesiumJS 体素渲染完整指南
【免费下载链接】cesiumAn open-source JavaScript library for world-class 3D globes and maps :earth_americas:项目地址: https://gitcode.com/GitHub_Trending/ce/cesium
想在浏览器里直接「看穿」一整块地质体,观察它内部的密度与温度分布吗?CesiumJS 的体素渲染(三维体积数据可视化)就能做到:它把体积数据的内部结构变成可以旋转、缩放、点击拾取的三维画面,而不再只是表面的一层皮。
一、概念速览:像素的「三维亲戚」
一句话定义:体素(Voxel)是「体积像素」,即三维空间中的最小数据单元。
打个比方:屏幕上的像素是二维平面里的一个小方格,体素则是把平面卷成空间后的「小方糖」。一张 1920×1080 的图由两百多万个像素组成,而一个 128×128×64 的体积数据则由约一百万个体素组成——每个体素都能携带颜色、密度、温度等属性,而不只是「有没有表面」这一个信息。
相比传统的表面网格(只描述外壳),体素渲染有 3 条核心优势:
- 直达内部:渲染的是整个体积,断层、矿体、云层内部一目了然;
- 属性丰富:每个体素可挂多个通道(如密度 + 温度),支持透明度、密度渐变等表现;
- 规模友好:数据天然分块(tile),可以像加载地图瓦片一样按视角流式加载,不必一次性塞进内存。
理解了「体素 = 小方糖」,接下来就看 CesiumJS 是怎么把这些小方糖画出来的。
二、原理拆解:光线追踪与步进采样
体素渲染的核心是光线追踪(Ray Tracing):从相机出发向画面发射大量射线,每条射线穿过体积时逐步采样、累积颜色。CesiumJS 在 GPU 上实现的正是这套「发射 → 步进 → 累积」流程,简化成伪代码只有 10 行出头:
function traceRay(origin, direction): # 每条射线的计算 color = (0, 0, 0, 0) # 累积颜色从零开始 while step < MAX_STEPS: p = origin + direction * step * SIZE # 1) 沿射线步进一小段 if not insideVolume(p): break # 2) 走出体积则提前结束 d = sample(p) # 3) 采样该点的体素属性 color = blend(color, d) # 4) 把采样值混入累积颜色 step += 1 return color三个关键词值得记住:发射(每个像素一条射线)、步进(步长决定质量与开销的平衡)、累积(越「厚」的路径越不透明,这正是体积感来源)。CesiumJS 把这套过程写进着色器,你在自定义 shader 里能读到的fsInput.voxel.travelDistance就是射线在体积内走过的路径长度——下面快速上手时会直接用它。
原理明白了,那就动手搭一个能跑的体素场景。
三、快速上手:3 步接入体素数据
第 1 步:克隆仓库
git clone https://gitcode.com/GitHub_Trending/ce/cesium第 2 步:建场景,挂上体素 Provider
CesiumJS 用VoxelProvider抽象「数据从哪来」,最常用的是从 3D Tiles 地址加载的Cesium3DTilesVoxelProvider(源码见 VoxelPrimitive.js、Cesium3DTilesVoxelProvider.js):
const viewer = new Cesium.Viewer('cesiumContainer'); // 1. 从 3D Tiles tileset.json 加载体素数据 const provider = await Cesium.Cesium3DTilesVoxelProvider.fromUrl( 'https://your-server/voxel/tileset.json' ); // 2. 创建体素图元并加入场景 viewer.scene.primitives.add(new Cesium.VoxelPrimitive({ provider })); // 3. 相机飞向体素包围球 viewer.camera.flyToBoundingSphere( provider.boundingSphere, { duration: 1.0 } );第 3 步:配材质,决定「怎么看穿」
体素的外观由自定义 shader 控制。下面这段 shader 用路径长度模拟了光散射:穿过越厚,越不透明(写法参考官方示例 packages/sandcastle/gallery/voxels/main.js):
const primitive = new Cesium.VoxelPrimitive({ provider, customShader: new Cesium.CustomShader({ fragmentShaderText: ` void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) { material.diffuse = fsInput.metadata.color.rgb; // 取体素元数据颜色 float thickness = fsInput.voxel.travelDistance / 1e6; // 归一化路径长度 material.alpha = 1.0 - pow(1.0 - fsInput.metadata.color.a, thickness); }`, }), });仓库里还备好了可直接指向的示例数据(如 Apps/SampleData/Cesium3DTiles/Voxel/ 下的 box / cylinder / ellipsoid 三套 tileset),把上面 URL 换成它们即可跑通。
能跑通之后下一个问题就来了:真实数据动辄上亿体素,怎么组织、怎么分级?
四、进阶机制:数据格式与 LOD 分级
体素数据格式:3D Tiles 扩展
体素数据不发明新格式,而是复用 3D Tiles,通过3DTILES_content_voxels扩展声明「这是体素」。看一眼真实的 tileset.json:
{ "extensionsUsed": ["3DTILES_content_voxels"], "root": { "content": { "uri": "tiles/{level}/{x}/{y}/{z}.gltf", "extensions": { "3DTILES_content_voxels": { "dimensions": [2, 4, 3], "class": "voxel" } } } } }其中dimensions声明每个瓦片里体素的三维数量,属性(颜色、密度等)通过 3D Tiles 的 metadata schema 定义,逐体素数据存在 glTF 内容里(加载逻辑见 VoxelContent.js)。
体素形状:方糖不一定是立方体
CesiumJS 用 VoxelShapeType 把「体素网格」映射到真实空间,目前支持 3 种形状:
| 形状 | 枚举值 | 典型用途 |
|---|---|---|
| 立方体 | BOX | 规则网格数据,效率最高 |
| 椭球体 | ELLIPSOID | 贴合球面(地球)场景 |
| 圆柱体 | CYLINDER | 井筒、钻孔等柱状数据 |
形状由 provider 声明,射线追踪时会用它在空间上裁剪采样范围,形状外的空气体素直接被跳过。
LOD 细节层次:远处用粗瓦片
与 3D Tiles 一样,体素数据集可以按八叉树(子树)分多个 level:根瓦片分辨率最低,逐级细化。相机距离决定加载哪一级,这正是「距离—策略—性能」的权衡表:
| 相机距离 | 渲染策略 | 性能影响 |
|---|---|---|
| 近 | 加载高 level 细分瓦片,细步长采样 | 质量最佳,GPU 开销最大 |
| 中 | 加载中间 level,常规步长 | 质量与开销均衡 |
| 远 | 仅加载根级粗瓦片,射线提前终止 | 最快,内存占用最低 |
provider 上可以用availableLevels声明总级数,用paddingBefore/paddingAfter给瓦片边缘垫 1 个体素的缓冲,减少相邻瓦片采样接缝。机制讲完,看看它能落到哪些真实业务里。
五、场景落地:体素渲染能干什么
- 地质勘探:把地下密度/电阻率体数据叠加到三维地球上,岩层、矿体形态随视角旋转实时可见;
- 气象动态:云量、温度、降水强度作为时间序列体素通道,逐帧更新即可实现「会动的天气球」;
- 医学与科研:CT/MRI 切片本质是二维体素数据,体素渲染让你直接「切开」任意角度观察内部结构。
能落地是好事,但大体积数据一上,帧率先掉为敬——下面是三条可执行的优化建议。
六、性能与避坑:3 条建议 + 1 个高频坑
- 控制单瓦片尺寸:把数据切成 8³~32³ 左右的小瓦片流式加载,而不是一整块 256³ 的「巨无霸」,这样相机视角外的数据根本不会被请求;
- 善用 LOD 与提前终止:明确声明
availableLevels,远视角自动退回粗瓦片;射线一旦走出形状范围立即结束(上文的insideVolume判断),空区域零开销; - 选对数据精度与缓存:颜色/分类类通道用
Uint8,连续物理量才上Float32;同时让瓦片走磁盘缓存,视角来回飞不重复下载(CesiumJS 的瓦片缓存机制参考 PerformanceTestingGuide)。
高频踩坑点:把元数据的第 4 通道直接当 alpha 用。官方示例里这个通道存的是「不透明度权重」,shader 中要通过1.0 - pow(1.0 - a, thickness)换算成最终 alpha(见 voxels/main.js 的customShaderColor)。直接拿原始值当透明度,画面会呈现深浅颠倒、内部发死白——如果你的体素「看起来不对」,先检查这一行。
优化到位之后,不妨把目光放远一点,看看这条路通向哪里。
七、展望与收尾
WebGPU 的逐步普及会让体素渲染迎来质变:更大规模的 compute 调度意味着更细的采样步长和更复杂的散射模型,实时体积光照、动态体素更新、地球级大数据集都将从「实验室效果」变成「默认能力」。对今天上手的你来说,现在积累的「provider + shader + LOD」思路,到时几乎可以无缝迁移。
最后用 3 个要点收个尾:
✅概念:体素是「三维小方糖」,CesiumJS 用 GPU 光线追踪(发射→步进→累积)把体积内部直接画进浏览器; ✅接入:git clone后,Cesium3DTilesVoxelProvider.fromUrl+VoxelPrimitive三步出图,材质交给自定义 shader,支持 BOX / ELLIPSOID / CYLINDER 三种形状; ✅调优:小瓦片流式加载 + LOD 分级 + 精度选择是性能三板斧,第 4 通道的 alpha 语义是最常见的坑。
【免费下载链接】cesiumAn open-source JavaScript library for world-class 3D globes and maps :earth_americas:项目地址: https://gitcode.com/GitHub_Trending/ce/cesium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考