编辑
2025-05-15
cesium
0

目录

数据流程图
GLTF数据加载与解析
场景图构建
渲染资源和流水线创建
图元渲染资源详解
绘制命令创建和提交
GLTF解析中的特殊处理
文件结构和调用关系
总结

数据流程图

js
Cesium3DTileset (请求瓦片) ↓ Cesium3DTile (加载瓦片) ↓ Model.fromGltfAsync (创建GLTF模型) ↓ GltfLoader (加载和解析GLTF数据) | ├── loadGltfJson (解析JSON结构) ├── loadResources (加载外部资源) ├── parse (解析GLTF场景结构) └── process (处理所有资源) ↓ ModelSceneGraph (构建场景图) ↓ buildDrawCommands (创建渲染命令) | ├── ModelRenderResources (模型级渲染资源) | ↓ ├── NodeRenderResources (节点级渲染资源) | ↓ └── PrimitiveRenderResources (图元级渲染资源) ↓ ModelDrawCommands (创建WebGL绘制命令) ↓ model.update (帧循环更新) ↓ submitDrawCommands (提交绘制命令)

详细流程解析

GLTF数据加载与解析

从Model.fromGltfAsync开始,它创建并初始化GltfLoader:

js
Model.fromGltfAsync = async function (options) { // 设置加载选项 const loaderOptions = { releaseGltfJson: options.releaseGltfJson, asynchronous: options.asynchronous, incrementallyLoadTextures: options.incrementallyLoadTextures, upAxis: options.upAxis, forwardAxis: options.forwardAxis, // ...其他选项 }; // 创建加载器 const loader = new GltfLoader(loaderOptions); try { // 开始加载过程 await loader.load(); } catch (error) { // 错误处理 } // 创建模型实例 const model = new Model(modelOptions); return model; };

然后,GltfLoader负责实际的GLTF加载过程:

GltfLoader.js

js
// 构造函数初始化 function GltfLoader(options) { this.gltfResource = options.gltfResource; // ...初始化其他属性 } // 加载方法 GltfLoader.prototype.load = async function () { await loadGltfJson(this); return this; }; // 处理方法 - 每帧调用 GltfLoader.prototype.process = function (frameState) { if (this._state === ResourceLoaderState.READY) { return true; } if (this._state === ResourceLoaderState.LOADING) { processLoaders(this, frameState); } // ...其他状态处理 }; // 解析GLTF JSON结构 async function loadGltfJson(loader) { // 从URL或ArrayBuffer加载JSON const gltf = await (loader的资源获取过程); // 验证GLTF版本 const version = gltf.asset.version; if (version !== "2.0") { throw new RuntimeError(`Unsupported glTF version: ${version}`); } // 处理扩展 // 处理JSON结构 // ... } // 解析GLTF的场景、节点、网格等组件 function parse(loader, frameState) { const gltf = loader.gltf; // 创建ModelComponents结构 const components = new ModelComponents.Components(); loader._components = components; // 加载节点 loadNodes(loader, frameState); // 加载场景 const sceneJson = gltf.scenes[sceneIndex]; loader._components.scene = loadScene(gltf, nodes); // 加载动画 if (defined(gltf.animations)) { loader._components.animations = loadAnimations(loader, nodes); } // 加载蒙皮 // 加载材质 // ... }

场景图构建

当GltfLoader完成数据加载后,Model创建ModelSceneGraph来组织渲染结构: Model.js中update方法的片段

js
Model.prototype.update = function(frameState) { // 处理加载 finishedProcessing = processLoader(this, frameState); if (!this._resourcesLoaded && finishedProcessing) { this._resourcesLoaded = true; const components = this._loader.components; // 创建场景图 const sceneGraph = new ModelSceneGraph({ model: this, modelComponents: components, }); this._sceneGraph = sceneGraph; } // ... 更多更新逻辑 };

ModelSceneGraph创建了节点层次结构: ModelSceneGraph.js

js
function ModelSceneGraph(options) { const components = options.modelComponents; // 初始化属性 this._model = options.model; this._components = components; this._runtimeNodes = []; this._rootNodes = []; // ... // 初始化场景图 initialize(this); } function initialize(sceneGraph) { // 计算模型矩阵 computeModelMatrix(sceneGraph, modelMatrix); // 创建运行时节点 const nodes = components.nodes; sceneGraph._runtimeNodes = new Array(nodesLength); // 处理根节点 const rootNodes = scene.nodes; for (let i = 0; i < rootNodesLength; i++) { const rootNode = scene.nodes[i]; const rootNodeIndex = traverseAndCreateSceneGraph( sceneGraph, rootNode, Matrix4.IDENTITY ); sceneGraph._rootNodes.push(rootNodeIndex); } // 处理蒙皮 // ... }

渲染资源和流水线创建

模型更新时会构建绘制命令:

ModelSceneGraph.js

js
ModelSceneGraph.prototype.buildDrawCommands = function (frameState) { const model = this._model; // 创建模型级渲染资源 const modelRenderResources = new ModelRenderResources(model); // 配置渲染流水线 this.configurePipeline(frameState); const modelPipelineStages = this.modelPipelineStages; // 应用模型级流水线阶段 for (let i = 0; i < modelPipelineStages.length; i++) { const modelPipelineStage = modelPipelineStages[i]; modelPipelineStage.process(modelRenderResources, model, frameState); } // 对每个节点创建渲染资源 for (let i = 0; i < this._runtimeNodes.length; i++) { const runtimeNode = this._runtimeNodes[i]; if (!defined(runtimeNode)) { continue; } // 创建节点级渲染资源 const nodeRenderResources = new NodeRenderResources( modelRenderResources, runtimeNode, ); // 应用节点级流水线阶段 for (let j = 0; j < nodePipelineStages.length; j++) { // ...处理节点级流水线 } // 对每个图元创建渲染资源 for (let j = 0; j < runtimeNode.runtimePrimitives.length; j++) { const runtimePrimitive = runtimeNode.runtimePrimitives[j]; // 这是关键部分 - 创建图元级渲染资源 const primitiveRenderResources = new PrimitiveRenderResources( nodeRenderResources, runtimePrimitive, ); // 应用图元级流水线阶段 for (let k = 0; k < primitivePipelineStages.length; k++) { const primitivePipelineStage = primitivePipelineStages[k]; primitivePipelineStage.process( primitiveRenderResources, runtimePrimitive.primitive, frameState, ); } // 创建绘制命令 const drawCommand = ModelDrawCommands.buildModelDrawCommand( primitiveRenderResources, frameState, ); runtimePrimitive.drawCommand = drawCommand; } } // 计算边界球 this._boundingSphere = BoundingSphere.fromCornerPoints( modelPositionMin, modelPositionMax, new BoundingSphere(), ); };

特别关注这部分代码中的渲染资源构建层次:

ModelRenderResources - 模型级别的渲染资源

NodeRenderResources - 节点级别的渲染资源

PrimitiveRenderResources - 图元级别的渲染资源

图元渲染资源详解

PrimitiveRenderResources 是负责直接构建WebGL绘制命令的关键类: PrimitiveRenderResources.js

js
function PrimitiveRenderResources(nodeRenderResources, runtimePrimitive) { // 继承自节点资源的属性 this.model = nodeRenderResources.model; this.runtimeNode = nodeRenderResources.runtimeNode; this.attributes = nodeRenderResources.attributes.slice(); this.attributeIndex = nodeRenderResources.attributeIndex; // ...其他继承属性 // 图元特有的属性 this.runtimePrimitive = runtimePrimitive; const primitive = runtimePrimitive.primitive; // 计算绘制数量 this.count = defined(primitive.indices) ? primitive.indices.count : ModelUtility.getAttributeBySemantic(primitive, "POSITION").count; // 获取索引 this.indices = primitive.indices; // 图元类型 this.primitiveType = primitive.primitiveType; // 计算位置范围 const positionMinMax = ModelUtility.getPositionMinMax( primitive, this.runtimeNode.instancingTranslationMin, this.runtimeNode.instancingTranslationMax, ); this.positionMin = Cartesian3.clone(positionMinMax.min, new Cartesian3()); this.positionMax = Cartesian3.clone(positionMinMax.max, new Cartesian3()); // 计算边界球 this.boundingSphere = BoundingSphere.fromCornerPoints( this.positionMin, this.positionMax, new BoundingSphere(), ); // 其他渲染选项 this.lightingOptions = new ModelLightingOptions(); }

这个资源包含了所有创建WebGL绘制命令所需的信息,包括顶点属性、索引、图元类型、边界体积等。

绘制命令创建和提交

最终,ModelDrawCommands根据渲染资源创建WebGL绘制命令: 在ModelDrawCommands中

js
buildModelDrawCommand = function (primitiveRenderResources, frameState) { const shaderBuilder = primitiveRenderResources.shaderBuilder; const uniformMap = primitiveRenderResources.uniformMap; const renderStateOptions = primitiveRenderResources.renderStateOptions; // 创建着色器 const shaderProgram = shaderBuilder.buildShaderProgram(frameState.context); // 创建RenderState const renderState = RenderState.fromCache(renderStateOptions); // 创建DrawCommand const drawCommand = new DrawCommand({ modelMatrix: primitiveRenderResources.runtimeNode.computedTransform, boundingVolume: primitiveRenderResources.boundingSphere, primitiveType: primitiveRenderResources.primitiveType, vertexArray: vertexArray, shaderProgram: shaderProgram, uniformMap: uniformMap, renderState: renderState, pass: primitiveRenderResources.alphaOptions.pass, // ...其他选项 }); return drawCommand; }

最后,在模型的update循环中,绘制命令被提交给Cesium的渲染引擎: 在Model.js中

js
function submitDrawCommands(model, frameState) { // 检查是否可见 const showModel = model._show && model._computedScale !== 0 && displayConditionPassed && (!invisible || silhouette); if (showModel && !model._ignoreCommands && submitCommandsForPass) { // 添加信用显示 addCreditsToCreditDisplay(model, frameState); // 推送绘制命令 model._sceneGraph.pushDrawCommands(frameState); } }

GLTF解析中的特殊处理

  1. Buffer和BufferView处理 在GltfLoader.js中
js
function getBufferViewLoader(loader, bufferViewId) { const bufferViewLoader = new GltfBufferViewLoader({ resourceCache: loader._resourceCache, gltf: loader.gltf, bufferViewId: bufferViewId, gltfResource: loader._gltfResource, baseResource: loader._baseResource, }); loader._bufferViewLoaders.push(bufferViewLoader); return bufferViewLoader; }
  1. 材质和纹理处理
js
function loadMaterial(loader, gltfMaterial, frameState) { // 创建材质 const material = new ModelComponents.Material(); // 加载基本属性 const pbr = gltfMaterial.pbrMetallicRoughness; if (defined(pbr)) { if (defined(pbr.baseColorTexture)) { material.baseColorTexture = loadTexture( loader, pbr.baseColorTexture, frameState ); } if (defined(pbr.metallicRoughnessTexture)) { material.metallicRoughnessTexture = loadTexture( loader, pbr.metallicRoughnessTexture, frameState ); } // ...其他PBR属性 } // 加载法线贴图 if (defined(gltfMaterial.normalTexture)) { material.normalTexture = loadTexture( loader, gltfMaterial.normalTexture, frameState ); material.normalScale = defaultValue( gltfMaterial.normalTexture.scale, 1.0 ); } // ...其他材质属性 return material; }
  1. 动画和蒙皮处理
js
function loadAnimation(loader, animationJson, nodes) { // 创建动画 const animation = new ModelComponents.Animation(); animation.name = animationJson.name; // 加载采样器 const samplerLength = animationJson.samplers.length; for (let i = 0; i < samplerLength; i++) { const samplerJson = animationJson.samplers[i]; animation.samplers.push(loadAnimationSampler(loader, samplerJson)); } // 加载通道 const channelLength = animationJson.channels.length; for (let i = 0; i < channelLength; i++) { const channelJson = animationJson.channels[i]; animation.channels.push( loadAnimationChannel(channelJson, animation.samplers, nodes) ); } return animation; }

文件结构和调用关系

js
Model.js ├── GltfLoader.js │ ├── GltfJsonLoader.js │ ├── GltfVertexBufferLoader.js │ ├── GltfIndexBufferLoader.js │ └── GltfTextureLoader.js │ ├── ModelSceneGraph.js │ ├── ModelRuntimeNode.js │ └── ModelRuntimePrimitive.js │ ├── ModelRenderResources.js │ ├── NodeRenderResources.js │ └── PrimitiveRenderResources.js │ └── ModelDrawCommands.js

总结

Cesium中GLTF数据的加载流程可以概括为以下几个阶段:

加载阶段:

  • 加载GLTF JSON结构
  • 加载外部资源(缓冲区、纹理等)
  • 解析场景结构(节点、网格、材质等)

场景图构建阶段:

  • 创建运行时节点层次
  • 应用变换和蒙皮
  • 计算边界球

渲染准备阶段:

  • 创建模型级渲染资源
  • 创建节点级渲染资源
  • 创建图元级渲染资源
  • 应用各种渲染流水线处理

渲染阶段:

  • 创建WebGL绘制命令
  • 提交命令到渲染引擎
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:幽灵

本文链接:

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