编辑
2025-05-10
cesium
0
请注意,本文编写于 59 天前,最后修改于 50 天前,其中某些信息可能已经过时。

目录

概述
核心文件路径
完整流程图
瓦片加载流程
瓦片集初始化
瓦片选择流程图
瓦片请求与加载
瓦片内容解析
B3DM格式解析
B3DM结构图
glTF模型解析
创建WebGL缓冲区
顶点解析流程图
渲染命令创建与执行
渲染流程图
WebGL关键接口
缓冲区创建和数据上传
顶点数组对象(VAO)创建
着色器程序创建
Cesium 中性能优化策略
几何数据优化
批次处理优化
缓存优化
GPU实例化优化
视锥体裁剪优化

概述

Cesium的3D Tiles是一种用于流式传输和渲染大规模3D地理空间数据的开放规范。本文详细解析Cesium引擎如何加载、解析和渲染3D Tiles数据。

核心文件路径

js
packages/engine/Source/Scene/Cesium3DTileset.js - 瓦片集管理 packages/engine/Source/Scene/Cesium3DTile.js - 单个瓦片 packages/engine/Source/Scene/Cesium3DTileContent.js - 瓦片内容基类 packages/engine/Source/Scene/Batched3DModel3DTileContent.js - B3DM瓦片内容 packages/engine/Source/Scene/Instanced3DModel3DTileContent.js - I3DM瓦片内容 packages/engine/Source/Scene/PointCloud3DTileContent.js - PNTS瓦片内容 packages/engine/Source/Scene/Composite3DTileContent.js - 复合瓦片内容 packages/engine/Source/Scene/Model.js - 模型处理 packages/engine/Source/Scene/ModelDrawCommand.js - 模型绘制命令

完整流程图

image.png

瓦片加载流程

瓦片集初始化

Cesium3DTileset是整个瓦片集的容器,负责管理瓦片树结构和调度加载请求。

Cesium3DTileset.js

js
Cesium3DTileset.prototype.update = function(frameState) { // ... this._requestedTiles.length = 0; // 获取可见瓦片 this._selectedTiles.length = 0; this._selectedTilesToStyle.length = 0; if (defined(this._loadTimestamp)) { processTiles(this, frameState); } // ... }; function processTiles(tileset, frameState) { // ... // 视锥体裁剪和瓦片选择 traverseAndSelect(tileset, frameState); // 更新瓦片几何误差、优先级 updateTiles(tileset, frameState); // 发送网络请求,加载未加载的瓦片 requestTiles(tileset, frameState); // ... }

瓦片选择流程图

image.png

瓦片请求与加载

Cesium3DTileset.js

js
function requestTiles(tileset, frameState) { // 对请求队列排序 tileset._requestedTiles.sort(sortRequestByPriority); for (let i = 0; i < tileset._requestedTiles.length; ++i) { const tile = tileset._requestedTiles[i]; // 触发瓦片内容加载 tileset._tileResource.retryAttempts = tileset._retryAttempts; tile.requestContent(tile, tileset, tileset._tileResource); } } Cesium3DTile.prototype.requestContent = function() { // ... // 创建请求,加载瓦片二进制数据 const request = new Request({ throttle: false, throttleByServer: true, type: RequestType.TILES3D, priorityFunction: createPriorityFunction(this), serverKey: serverKey }); // 发起请求 const promise = requestResource.fetchArrayBuffer(request); // 处理请求结果 const contentPromise = promise.then((arrayBuffer) => { // 创建瓦片内容对象 const content = createContent(this, arrayBuffer, contentFactory); this._content = content; this.contentState = Cesium3DTileContentState.PROCESSING; // 解析瓦片内容 return content.process(this._tileset.mainThreadOrWorkerWorkerScheduler); }); // ... };

瓦片内容解析

B3DM格式解析

Batched 3D Model (B3DM) 是最常用的3D Tiles格式,包含多个批处理的3D模型。

Batched3DModel3DTileContent.js

js
function Batched3DModel3DTileContent( tileset, tile, resource, arrayBuffer, byteOffset ) { // ... // 解析二进制头部 const headerView = new DataView(arrayBuffer); const byteLength = headerView.getUint32(byteOffset, true); byteOffset += 4; // 解析glTF标志和版本 const gltfFormat = headerView.getUint32(byteOffset, true); // ... // 提取特性表 const featureTableJsonByteLength = headerView.getUint32(byteOffset, true); byteOffset += 4; // ... // 解析特性表数据 const featureTableBinaryByteLength = headerView.getUint32(byteOffset, true); byteOffset += 4; const batchTableJsonByteLength = headerView.getUint32(byteOffset, true); byteOffset += 4; // ... // 提取glTF数据 const gltfByteLength = byteLength - byteOffset; let gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength); // ... // 创建glTF模型 const model = new Model({ gltf: gltfView, // ...其他参数 }); // 存储解析结果 this._model = model; this._batchTable = batchTable; // ... }

B3DM结构图

image.png

glTF模型解析

Model类负责解析glTF数据,提取顶点属性并创建WebGL资源。 Model.js

js
Model.prototype.initialize = function(arrayBuffer) { // ... // 解析glTF JSON let gltf; if (defined(arrayBuffer)) { gltf = parseGLTF(arrayBuffer); } // ... // 处理顶点缓冲区 ForEach.mesh(gltf, (mesh) => { ForEach.meshPrimitive(mesh, (primitive) => { // 获取顶点索引 const indices = accessorId(primitive.indices); // 获取顶点位置 const positionId = primitive.attributes.POSITION; // 获取法线数据 const normalId = primitive.attributes.NORMAL; // 获取纹理坐标 const texcoordId = primitive.attributes.TEXCOORD_0; // ... }); }); // ... };

创建WebGL缓冲区

Model.js

js
function createBuffers(model, frameState) { const loadResources = model._loadResources; const bufferLoads = loadResources.bufferLoads; for (const bufferLoad of bufferLoads) { // 创建WebGL缓冲区 const buffer = Buffer.createVertexBuffer({ context: frameState.context, typedArray: bufferLoad.typedArray, usage: BufferUsage.STATIC_DRAW }); // 保存缓冲区引用 bufferLoad.vertexBuffer = buffer; } }

顶点数组对象(VAO)创建

js
function createVertexArrays(model, frameState) { // ... for (const primitive of primitivesByteLength) { // 获取顶点属性描述 const attributes = primitive.attributes; const vaAttributes = []; // 处理每个顶点属性(位置、法线、纹理坐标等) for (const attribute of attributes) { const accessor = accessors[attribute.accessor]; const bufferLoad = bufferLoads[accessor.bufferView]; // 创建顶点属性描述 vaAttributes.push({ index: attribute.index, vertexBuffer: bufferLoad.vertexBuffer, componentsPerAttribute: accessor.componentsPerAttribute, componentDatatype: accessor.componentDatatype, normalize: attribute.normalize, offsetInBytes: accessor.byteOffset + attribute.byteOffset, strideInBytes: accessor.byteStride }); } // 创建WebGL顶点数组对象 const va = new VertexArray({ context: frameState.context, attributes: vaAttributes, indexBuffer: defined(primitive.indices) ? indexBufferLoads[primitive.indices].indexBuffer : undefined }); // 保存VAO引用 primitive.va = va; } }

顶点解析流程图

image.png

渲染命令创建与执行

创建绘制命令 ModelDrawCommand.js

js
ModelDrawCommand.prototype.buildCommand = function( model, context, frameState, modelMatrix, runtimeNode, lightColor ) { // ... // 创建绘制命令 const command = new DrawCommand({ boundingVolume: new BoundingSphere(), // 模型的包围盒 modelMatrix: modelMatrix, // 模型变换矩阵 primitiveType: primitive.primitiveType, // 图元类型(如三角形) vertexArray: primitive.va, // 顶点数组对象 count: primitive.count, // 索引或顶点数量 shaderProgram: shaderProgram, // 着色器程序 uniformMap: uniformMap, // uniform变量映射 renderState: renderState, // 渲染状态 pass: pass // 渲染通道 }); return command; };

绘制命令执行 DrawCommand.js

js
DrawCommand.prototype.execute = function(context, passState) { // ... // 绑定着色器程序 shaderProgram._bind(); // 设置Uniform变量 let uniform; let uniformId; for (uniformId in uniforms) { if (uniforms.hasOwnProperty(uniformId)) { uniform = uniforms[uniformId]; uniform._set(); } } // 绑定顶点数组 vertexArray._bind(); // 应用渲染状态 context._applyRenderState(renderState); // 绘制图元 if (defined(this.count)) { if (defined(instanceCount)) { // 实例化绘制 context._gl.drawElementsInstanced( this.primitiveType, this.count, indexDatatype, offset, instanceCount ); } else { // 普通索引绘制 context._gl.drawElements( this.primitiveType, this.count, indexDatatype, offset ); } } else { // 数组绘制 context._gl.drawArrays(this.primitiveType, offset, vertexCount); } // 解除绑定 vertexArray._unBind(); shaderProgram._unBind(); };

渲染流程图

image.png

WebGL关键接口

缓冲区创建和数据上传

Buffer.js

js
function Buffer(options) { // ... this._buffer = context._gl.createBuffer(); // 绑定缓冲区 const target = this._target; const gl = context._gl; gl.bindBuffer(target, this._buffer); // 上传数据到GPU gl.bufferData(target, typedArray, usage); // 跟踪显存使用 this._sizeInBytes = sizeInBytes; // 解除绑定 gl.bindBuffer(target, null); }

顶点数组对象(VAO)创建

VertexArray.js

js
function VertexArray(options) { // ... const va = gl.createVertexArray(); gl.bindVertexArray(va); // 绑定顶点属性 for (const attribute of attributes) { // 绑定顶点缓冲区 gl.bindBuffer(gl.ARRAY_BUFFER, attribute.vertexBuffer._buffer); // 启用顶点属性 gl.enableVertexAttribArray(attribute.index); // 定义顶点属性格式 gl.vertexAttribPointer( attribute.index, // 属性位置 attribute.componentsPerAttribute, // 每个顶点的组件数量(如位置=3,法线=3) attribute.componentDatatype, // 数据类型(如FLOAT) attribute.normalize, // 是否归一化 attribute.strideInBytes, // 步长 attribute.offsetInBytes // 偏移量 ); // 设置顶点属性除数(用于实例化渲染) if (defined(attribute.instanceDivisor)) { gl.vertexAttribDivisor(attribute.index, attribute.instanceDivisor); } } // 绑定索引缓冲区(如果有) if (defined(indexBuffer)) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer._buffer); } // 解除VAO绑定 gl.bindVertexArray(null); this._va = va; }

着色器程序创建

ShaderProgram.js

js
function ShaderProgram(options) { // ... // 创建顶点着色器 const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexShaderSource); gl.compileShader(vertexShader); // 创建片元着色器 const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentShaderSource); gl.compileShader(fragmentShader); // 创建着色器程序 const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); // 绑定属性位置 for (const attributeLocation in attributeLocations) { gl.bindAttribLocation( program, attributeLocations[attributeLocation], attributeLocation ); } // 链接程序 gl.linkProgram(program); // 检查编译和链接状态 // ... // 获取Uniform位置 const uniformLocations = {}; const numberOfUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < numberOfUniforms; ++i) { const activeUniform = gl.getActiveUniform(program, i); uniformLocations[activeUniform.name] = gl.getUniformLocation( program, activeUniform.name ); } this._program = program; this._uniformLocations = uniformLocations; }

Cesium 中性能优化策略

image.png

几何数据优化

法线压缩

js
AttributeCompression.octEncodeInRange = function(normal, rangeMax) { // 法线压缩到8位或16位 const x = normal.x; const y = normal.y; const z = normal.z; const octX = (x >= 0.0 ? 1.0 : -1.0) * (1.0 - Math.abs(y / (Math.abs(x) + Math.abs(y) + Math.abs(z)))); const octY = (y >= 0.0 ? 1.0 : -1.0) * (1.0 - Math.abs(z / (Math.abs(x) + Math.abs(y) + Math.abs(z)))); // 从[-1,1]映射到[0,rangeMax] return new Cartesian2( AttributeCompression.scaleToSnorm(octX, rangeMax), AttributeCompression.scaleToSnorm(octY, rangeMax) ); };

批次处理优化

js
function createFeatures(content) { // ... // 分批处理,减少绘制调用 // batchTable中包含多个实体的元数据 const featuresLength = content._featuresLength; for (let i = 0; i < featuresLength; ++i) { // 创建每个批次的特性对象 content._features.push(new Cesium3DTileFeature(content, i)); } }

缓存优化

js
Cesium3DTilesetCache.prototype.add = function(tile) { // ... // LRU缓存策略 if (this._list.length > this._maximumMemoryUsageInBytes) { // 淘汰最久未使用的瓦片 this.unloadTile(this._last); } // 将瓦片添加到缓存头部 tile._cacheNode = this._list.add(tile); };

GPU实例化优化

js
function createFeatures(content) { // ... // 实例化渲染优化,一次绘制调用渲染多个实例 const instancesLength = content._instancesLength; // 创建实例化属性缓冲区 const instancedAttributes = []; instancedAttributes.push({ index: attributeLocation, vertexBuffer: createInstancedBuffer(/* 实例位置数据 */), componentsPerAttribute: 3, instanceDivisor: 1 // 每个实例一条数据 }); // 添加到顶点数组对象 for (const attribute of instancedAttributes) { vertexArray.addAttribute(attribute); } }

视锥体裁剪优化

js
function updateVisibility(tileset, frameState) { const cullingVolume = frameState.cullingVolume; const cameraPosition = frameState.camera.positionWC; // 遍历瓦片树 const stack = tileset._stack; stack.push(tileset._root); while (stack.length > 0) { const tile = stack.pop(); // 计算与视锥体的关系 const boundingVolume = tile.boundingVolume; const visibility = boundingVolume.computeVisibility(cullingVolume); // 完全不可见的瓦片裁剪掉 if (visibility === CullingVolume.MASK_OUTSIDE) { continue; } // 距离计算 const distanceToCamera = tile._distanceToCamera = tile.distanceToTile(frameState); // 处理可见瓦片 // ... } }
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:幽灵

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 幽灵AI 许可协议。转载请注明出处!