编辑
2025-05-10
cesium
0

目录

概述
核心文件路径
完整流程图
瓦片加载流程
瓦片集初始化
瓦片选择流程图
瓦片请求与加载
瓦片内容解析
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 许可协议。转载请注明出处!