Cesium中处理完成gltf的数据完整性检查,默认数据的完善。根据gltf1.0扩展KHR_materials_common(非pbr材质)进行glsl字符串的拼装、attributelocation、uniform等信息的收集处理,并将这些信息添加到gltf.extensions.KHR_techniques_webgl.扩展中,并建立材质中material.extensions.KHR_techniques_webgl.technique与对应gltf.extensions.KHR_techniques_webgl.techniques之间的映射关系。、
下面时material.extensions中的样例:
"materials":[
{
"extensions": {
"KHR_techniques_webgl":{
"technique": 0,
"values": "KHR_materials_common中的values相关的数据"
},
"KHR_materials_common": {
"technique":"BLINN",
"values":{
"ambient":[1.0,1.0,1.0,1.0],
"emission":[1.0,1.0,1.0,1.0],
"transparency":1,
"transparent": false,
"doubleSided": false,
"diffuse":[1.0,1.0,1.0,1.0],
"specular":[1.0,1.0,1.0,1.0],
"shininess": 1.0
}
},
}
}
]
下面时gltf.extensions.KHR_techniques_webgl.techniques中的样例:
extensions":{
"KHR_techniques_webgl":{
"programs": [
{
"fragmentShader": 0,
"vertexShader": 1
}
],
"shaders": [
{
"type": 35632,
"uri": "duck0FS.glsl"
},
{
"type": 35633,
"uri": "duck0VS.glsl"
},
{
"bufferView":{
}
},
"type": "WebGLConstants.VERTEX_SHADER",
"extras": {
"_pipeline": {
"source": "vertexShader",
"extension": ".glsl",
},
},
],
"techniques": [
{
"program": 0,
"attributes": {
"a_normal": {
"semantic": "NORMAL"
},
"a_position": {
"semantic": "POSITION"
},
"a_texcoord0": {
"semantic": "TEXCOORD_0"
},
"a_custom": {
"semantic": "_CUSTOM_ATTRIBUTE"
}
},
"uniforms": {
"u_ambient": {
"type": 35666
},
"u_diffuse": {
"type": 35678,
"value": {
"index": "0 【纹理】"
}
},
"u_shininess": {
"type": 5126
},
"u_light0Color": {
"type": 35665,
"value": [
1,
1,
1
]
},
"u_light0Transform": {
"semantic": "MODEL",
"node": 1,
"type": 35676
},
"u_modelViewMatrix": {
"semantic": "MODELVIEW",
"type": 35676
},
"u_normalMatrix": {
"semantic": "MODELVIEWINVERSETRANSPOSE",
"type": 35675
},
"u_projectionMatrix": {
"semantic": "PROJECTION",
"type": 35676
}
}
}
]
}
}
下面时gltf.extensions.KHR_materials_common中的样例:
extensions":{
"KHR_materials_common":{
"lights":[{
"type": "ambient",
"ambient":{
"color": [1.0, 1.0, 1.0],
},
"directional":{
"color": [1.0, 1.0, 1.0],
},
"point":{
"color": [1.0, 1.0, 1.0],
"constantAttenuation": 0,
"linearAttenuation": 0,
"quadraticAttenuation": 0
},
"spot":{
"color": [1.0, 1.0, 1.0],
"constantAttenuation": 0,
"fallOffAngle": 3.14159265,
"fallOffExponent": 0,
"linearAttenuation": 0,
"quadraticAttenuation": 0
}
}]
}
}
上述信息建立完成后,下一步就是将gltf的数据创建对应的gpu资源,也就是渲染资源,创建之前需要分析对应的结构,将结构中的某些数据先缓存到一个地方,这个地方的cesium处理显得有些错乱。
Model.prototype.update = function (frameState) {
。。。。。。
// 没有初始化
if (!loadResources.initialized) {
// 生成动态一张纹理brdf的纹理
frameState.brdfLutGenerator.update(frameState);
// 检查gltf的扩展cesium中是否都支持(不支持投递异常,日志中体现),虽然cesium支持但是浏览器不一定支持
ModelUtility.checkSupportedExtensions(
this.extensionsRequired,
supportsWebP
);
// 更新前向轴
ModelUtility.updateForwardAxis(this);
// glTF pipeline updates, not needed if loading from cache
// 是否定义了数据源版本, 将各个版本的信息统一处理成一种自定义的格式,后续统一处理
if (!defined(this.gltf.extras.sourceVersion)) {
var gltf = this.gltf;
// Add the original version so it remains cached
// 添加原始版本到缓存状态
gltf.extras.sourceVersion = ModelUtility.getAssetVersion(gltf);
// 定义了扩展KHR_techniques_webgl(内部包含自定义glsl等)
gltf.extras.sourceKHRTechniquesWebGL = defined(
ModelUtility.getUsedExtensions(gltf).KHR_techniques_webgl // 定义了KHR_techniques_webgl扩展
);
// gltf数据的原始版本,没有处理时的版本
this._sourceVersion = gltf.extras.sourceVersion;
// 定义了扩展KHR_techniques_webgl,即gltf原始数据中是否自定义了shader相关的相关信息,区别于cesium拼接过的Technique
this._sourceKHRTechniquesWebGL = gltf.extras.sourceKHRTechniquesWebGL;
// 应该是将gltf1.0版本的数据改成2.0的数据, 为了支持1.0中的technique在2.0中使用了KHR_techniques_webgl扩展
updateVersion(gltf);
// 为gltf中各个属性添加默认值(buffer、material等)
addDefaults(gltf);
// glsl中是否添加批次表片段
var options = {
addBatchIdToGeneratedShaders: this._addBatchIdToGeneratedShaders,
};
// 处理KHR_materials_common扩展,扩展的所有的材质相信息,扩展的gltf的默认参数设置、shader片段拼接
processModelMaterialsCommon(gltf, options);
// 处理pbr材质
processPbrMaterials(gltf, options);
}
// gltf版本
this._sourceVersion = this.gltf.extras.sourceVersion;
// 定义了扩展KHR_techniques_webgl,即gltf原始数据中是否自定义了shader相关的相关信息,区别于cesium拼接过的Technique
this._sourceKHRTechniquesWebGL = this.gltf.extras.sourceKHRTechniquesWebGL;
// Skip dequantizing in the shader if not encoded
// 如果未编码,则跳过着色器中的解码,解码过程可以在cpu中处理也可以在glsl中处理
this._dequantizeInShader =
this._dequantizeInShader && DracoLoader.hasExtension(this);
// We do this after to make sure that the ids don't change
// 之后我们会这样做,以确保ID不会更改
// 将buffer添加到gpu资源中, 存入ModelLoadResources,后续会使用这些数据创建顶点缓存
addBuffersToLoadResources(this);
// 解析骨骼动画关节
parseArticulations(this);
// 将gltf中的Techniques拷贝到model的成员变量中,存入model._sourcePrograms、model._sourceTechniques中
parseTechniques(this);
// 不是从缓存中加载(缓存中是已经解析过的数据,不用再处理了)
if (!this._loadRendererResourcesFromCache) {
// 解析bufferviewid,顶点、索引数据, 存入ModelLoadResources,后续会使用这些数据创建顶点数组对象
parseBufferViews(this);
// 着色器shaderid, 存入model._rendererResources中,glsl字符串可能来源于bufferView,字符串、外部链接等,后续
parseShaders(this);
// 着色程序programid, 存入ModelLoadResources中
parsePrograms(this);
// 纹理id, 存入ModelLoadResources
parseTextures(this, context, supportsWebP);
}
// 下面处理运行时需要的组织结构树,树节点,节点相关的材质
parseMaterials(this); // 解析材质(包含了真实的运行时数据存储)
parseMeshes(this); // 解析网格(包含了真实的运行时数据存储)
parseNodes(this); // 解析节点(包含了真实的运行时数据存储)
// Start draco decoding // 解析draco编码的二进制数据
DracoLoader.parse(this, context);
// 初始化完成
loadResources.initialized = true; // 资源部初始化完成
}
。。。。。。
}
1、保存gltf原始信息
// gltf原始数据的版本信息
this._sourceVersion = this.gltf.extras.sourceVersion;
// gltf原始数据中是否有KHR_techniques_webgl扩展信息
this._sourceKHRTechniquesWebGL = this.gltf.extras.sourceKHRTechniquesWebGL;
2、是否在shader中解码gltf中的二进制数据
this._dequantizeInShader = this._dequantizeInShader && DracoLoader.hasExtension(this);
3、将gltf的二进制数据保存到ModelLoadResources中,后续会创建顶点属性缓存对应webgl的GL_BUFFER
addBuffersToLoadResources(this);
4、关于骨骼动画相关信息处理
parseArticulations(this);
5、将gltf.extensions.KHR_techniques_webgl的techniques、program保存到Model的成员变量中
parseTechniques(this);
6、gltf数据多次使用
一个gltf数据被多次使用时,如果缓存到了context中,只解析一次并保存gpu资源,下次使用时不用再解析了,这些数据会通过this._loadRendererResourcesFromCache标记指定,
7、将bufferViews数据存储到ModelLoadResources中,对应webgl的ARRAY_BUFFER、ELEMENT_ARRAY_BUFFER
parseBufferViews(this);
8、将gltf.extensions.KHR_techniques_webgl.shaders数据存储到model._rendererResources.sourceShaders中,对应webgl的createShader函数
parseShaders(this);
9、将gltf.extensions.KHR_techniques_webgl.programs数据存储到model._rendererResources.programsToCreate中,对应webgl的createProgram
函数
parsePrograms(this);
10、将BufferView类型的图像数据保存到model._loadResources.texturesToCreateFromBufferView中,后续对应webgl的createTexture函数
parseTextures(this, context, supportsWebP);
11、将BufferView类型的图像数据保存到model._loadResources.texturesToCreateFromBufferView中,后续对应webgl的createTexture函数
12、parseMaterials(model)
对于parseMaterials(model)中主要时将材质封装再了ModelMaterial,并将其存储再了 model._runtime.materialsByName = runtimeMaterialsByName;
model._runtime.materialsById = runtimeMaterialsById;中,model._runtime主要是封装了渲染时用到的一些信息,包括node、mesh、material等。再就是对model._uniformMaps的封装,这里面是模型自身的一些uniform信息的管理,用于向着色器中传递uniform信息,只是对于模型自身特定属性如环境光反射、漫反射、纹理等,而对于投影矩阵、视图矩阵等这些是所有渲染都需要的uniform数据会存储再context的context._us中。
function parseMaterials(model) {
var gltf = model.gltf;
var techniques = model._sourceTechniques;
var runtimeMaterialsByName = {};
var runtimeMaterialsById = {};
var uniformMaps = model._uniformMaps; // 模型的uniform映射
// 遍历材质
ForEach.material(gltf, function (material, materialId) {
// Allocated now so ModelMaterial can keep a reference to it.
// uniform数据
uniformMaps[materialId] = {
uniformMap: undefined, // uniform映射
values: undefined, // uniform数据
jointMatrixUniformName: undefined, // 骨骼动画相关的uniform
morphWeightsUniformName: undefined, // 变形动画相关的uniform
};
// 封装材质信息
var modelMaterial = new ModelMaterial(model, material, materialId);
// 遍历technique
if (
defined(material.extensions) &&
defined(material.extensions.KHR_techniques_webgl)
) {
// 材质所使用的techniqueId
var techniqueId = material.extensions.KHR_techniques_webgl.technique;
// 材质使用的glsl的索引
modelMaterial._technique = techniqueId;
// 程序索引
modelMaterial._program = techniques[techniqueId].program;
// 遍历材质的灯光数据等uniform数据
ForEach.materialValue(material, function (value, uniformName) {
if (!defined(modelMaterial._values)) {
modelMaterial._values = {};
}
// 存储uniform数据(如灯光等数据)
modelMaterial._values[uniformName] = clone(value);
});
}
// 通过材质名、材质id存储对应的材质
runtimeMaterialsByName[material.name] = modelMaterial;
runtimeMaterialsById[materialId] = modelMaterial;
});
// 存储运行时的材质信息(包含了真实的数据如颜色值)
model._runtime.materialsByName = runtimeMaterialsByName;
model._runtime.materialsById = runtimeMaterialsById;
}
13、parseMeshes(this);
将gltf中.meshs数据封装在ModelMesh中,并将其存储到model._runtime.meshesByName = runtimeMeshesByName;中,后续遍历gltf中场景节点,会遍历到这些信息。
// 解析网格
function parseMeshes(model) {
var runtimeMeshesByName = {};
// 所有的材质字典
var runtimeMaterialsById = model._runtime.materialsById;
ForEach.mesh(model.gltf, function (mesh, meshId) {
// 封装模型的网格信息
runtimeMeshesByName[mesh.name] = new ModelMesh(
mesh,
runtimeMaterialsById,
meshId
);
if (
defined(model.extensionsUsed.WEB3D_quantized_attributes) || // 使用了压缩属性
model._dequantizeInShader
) {
// Cache primitives according to their program
ForEach.meshPrimitive(mesh, function (primitive, primitiveId) {
// 查询mesh对应的材质中的program
var programId = getProgramForPrimitive(model, primitive);
// 程序对应的图元
var programPrimitives = model._programPrimitives[programId];
if (!defined(programPrimitives)) {
programPrimitives = {};
// 程序对应的网格的Primitive
model._programPrimitives[programId] = programPrimitives;
}
// 对应的图元
programPrimitives[meshId + ".primitive." + primitiveId] = primitive;
});
}
});
// 运行时用到的mesh
model._runtime.meshesByName = runtimeMeshesByName;
}
14、parseNodes(this);
这个函数主要是对gltf中node封装在ModelNode中,并存储在了下面三个成员中
model._runtime.nodes = runtimeNodes;
model._runtime.nodesByName = runtimeNodesByName;
model._runtime.skinnedNodes = skinnedNodes;
// 解析node组织结构
function parseNodes(model) {
var runtimeNodes = {};
var runtimeNodesByName = {};
var skinnedNodes = [];
var skinnedNodesIds = model._loadResources.skinnedNodesIds;
var articulationsByName = model._runtime.articulationsByName;
// 遍历node
ForEach.node(model.gltf, function (node, id) {
var runtimeNode = {
// Animation targets
// 动画相关
matrix: undefined,
// 平移旋转缩放
translation: undefined,
rotation: undefined,
scale: undefined,
// Per-node show inherited from parent
// 是否显示取决于父节点
computedShow: true,
// Computed transforms 计算转换
//根矩阵
transformToRoot: new Matrix4(),
// 计算矩阵
computedMatrix: new Matrix4(),
// 动画脏了
dirtyNumber: 0, // The frame this node was made dirty by an animation; for graph traversal
// Rendering 渲染
commands: [], // empty for transform, light, and camera nodes
// Skinned node 骨骼动画
inverseBindMatrices: undefined, // undefined when node is not skinned
bindShapeMatrix: undefined, // undefined when node is not skinned or identity
joints: [], // empty when node is not skinned
computedJointMatrices: [], // empty when node is not skinned
// Joint node 关节节点
jointName: node.jointName, // undefined when node is not a joint
// 权重
weights: [],
// Graph pointers 图形指针
children: [], // empty for leaf nodes 空叶子节点
parents: [], // empty for root nodes 根节点
// Publicly-accessible ModelNode instance to modify animation targets
publicNode: undefined,
};
// 运行时用到的节点
runtimeNode.publicNode = new ModelNode(
model,
node,
runtimeNode,
id,
ModelUtility.getTransform(node)
);
// 运行时用到的节点结构
runtimeNodes[id] = runtimeNode;
runtimeNodesByName[node.name] = runtimeNode;
if (defined(node.skin)) {
skinnedNodesIds.push(id);
skinnedNodes.push(runtimeNode);
}
if (
defined(node.extensions) &&
defined(node.extensions.AGI_articulations)
) {
var articulationName = node.extensions.AGI_articulations.articulationName;
if (defined(articulationName)) {
var transform = Matrix4.clone(
runtimeNode.publicNode.originalMatrix,
scratchArticulationStageInitialTransform
);
var articulation = articulationsByName[articulationName];
articulation.nodes.push(runtimeNode.publicNode);
var numStages = articulation.stages.length;
for (var s = 0; s < numStages; ++s) {
var stage = articulation.stages[s];
transform = applyArticulationStageMatrix(stage, transform);
}
runtimeNode.publicNode.matrix = transform;
}
}
});
// 运行时用到的节点
model._runtime.nodes = runtimeNodes;
model._runtime.nodesByName = runtimeNodesByName;
model._runtime.skinnedNodes = skinnedNodes;
}
15、DracoLoader.parse(this, context);
这个函数主要是处理使用Draco算法压缩过的数据,后续会通过wasm的方式进行解压索
// 解析数据
DracoLoader.parse = function (model, context) {
// 是否存在扩展
if (!DracoLoader.hasExtension(model)) {
return;
}
var loadResources = model._loadResources;
var cacheKey = model.cacheKey;
// 缓存
if (defined(cacheKey)) {
if (!defined(DracoLoader._decodedModelResourceCache)) {
if (!defined(context.cache.modelDecodingCache)) {
context.cache.modelDecodingCache = {};
}
DracoLoader._decodedModelResourceCache = context.cache.modelDecodingCache;
}
// Decoded data for model will be loaded from cache
var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
if (defined(cachedData)) {
cachedData.count++;
// 临时缓存解码后的数据
loadResources.pendingDecodingCache = true;
return;
}
}
// 着色器中解析
var dequantizeInShader = model._dequantizeInShader;
var gltf = model.gltf;
ForEach.mesh(gltf, function (mesh, meshId) {
ForEach.meshPrimitive(mesh, function (primitive, primitiveId) {
// 没有扩展
if (!defined(primitive.extensions)) {
return;
}
// 不是压缩过的数据就不用处理了
var compressionData = primitive.extensions.KHR_draco_mesh_compression;
if (!defined(compressionData)) {
return;
}
var bufferView = gltf.bufferViews[compressionData.bufferView];
// 获取数据
var typedArray = arraySlice(
gltf.buffers[bufferView.buffer].extras._pipeline.source,
bufferView.byteOffset,
bufferView.byteOffset + bufferView.byteLength
);
// 存储数据
loadResources.primitivesToDecode.enqueue({
mesh: meshId,
primitive: primitiveId,
array: typedArray, // 数据
bufferView: bufferView,
compressedAttributes: compressionData.attributes, // 压缩过的属性
dequantizeInShader: dequantizeInShader, // 是否在着色器中解码
});
});
});
};
最后loadResources.initialized = true;标记代表着数据初始化完成,后续是用这些数据创建gpu资源的过程了。
上述过程中有很多的数据会再Model中缓存,缓存的原因时这些数据会被改变,例如model._rendererResources.sourceShaders、model._sourceTechniques、model._uniformMaps