jsCesium3DTileset (请求瓦片)
↓
Cesium3DTile (加载瓦片)
↓
Model.fromGltfAsync (创建GLTF模型)
↓
GltfLoader (加载和解析GLTF数据)
|
├── loadGltfJson (解析JSON结构)
├── loadResources (加载外部资源)
├── parse (解析GLTF场景结构)
└── process (处理所有资源)
↓
ModelSceneGraph (构建场景图)
↓
buildDrawCommands (创建渲染命令)
|
├── ModelRenderResources (模型级渲染资源)
| ↓
├── NodeRenderResources (节点级渲染资源)
| ↓
└── PrimitiveRenderResources (图元级渲染资源)
↓
ModelDrawCommands (创建WebGL绘制命令)
↓
model.update (帧循环更新)
↓
submitDrawCommands (提交绘制命令)
详细流程解析
从Model.fromGltfAsync开始,它创建并初始化GltfLoader:
jsModel.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
jsfunction 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
jsModelSceneGraph.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);
}
}
在GltfLoader.js中
jsfunction 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;
}
jsfunction 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;
}
jsfunction 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;
}
jsModel.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数据的加载流程可以概括为以下几个阶段:
加载阶段:
场景图构建阶段:
渲染准备阶段:
渲染阶段:
本文作者:幽灵
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 幽灵AI 许可协议。转载请注明出处!