Cesium 源码解析 Model(五)

        关于model.js中用于解码几何数据的压缩部分,cesium的87版兼容了两种解码数据的过程,一种是再vs中解码压缩过的顶点数据,还有一种是使用draco解码几何数据。

        第一种在vs种:

        这种方式使用的是gltf的WEB3D_quantized_attributes扩展,即在gltf的json文件定义了extensionsUsed.WEB3D_quantized_attributes,在cesium中大概的几个过程是parseMeshes、createUniformsForQuantizedAttributes、modifyShaderForQuantizedAttributes、createResources、createProgram,这几个函数。因为需要在vs中解析顶点数据,所以需要在gltf中设置关于解码的glsl片段,或者cesium直接就在源码中添加了解析源码。

        首先parseMeshes函数中:

// 解析网格
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);
        //  网格Primitive对应的材质中的program
        var programPrimitives = model._programPrimitives[programId];
        if (!defined(programPrimitives)) {
          programPrimitives = {};
          // 网格Primitive对应的材质中的program
          model._programPrimitives[programId] = programPrimitives;
        }
        // 网格Primitive对应的材质中的program
        programPrimitives[meshId + ".primitive." + primitiveId] = primitive;
      });
    }
  });

  // 运行时用到的mesh
  model._runtime.meshesByName = runtimeMeshesByName;
}

上述过程是建立了mesh对应的着色程序索引programId。

        在createResources函数中:

// 创建渲染所需要的所有的gpu资源
function createResources(model, frameState) {
  // 上下文
  var context = frameState.context;
  // 3D模式
  var scene3DOnly = frameState.scene3DOnly;
  // 顶点着色器中解析
  var quantizedVertexShaders = model._quantizedVertexShaders;
  // 所有的techniques的引用
  var techniques = model._sourceTechniques;
  // gltf中着色程序索引
  var programs = model._sourcePrograms;
  // 渲染资源
  var resources = model._rendererResources;
  // glsl代码
  var shaders = resources.sourceShaders;
  // 如果是缓存过的资源(一般不缓存)
  if (model._loadRendererResourcesFromCache) {
    // glsl代码
    shaders = resources.sourceShaders =
      model._cachedRendererResources.sourceShaders;
  }

  // Techniques遍历
  for (var techniqueId in techniques) {
    if (techniques.hasOwnProperty(techniqueId)) {
      // 使用的程序索引
      var programId = techniques[techniqueId].program;
      // 程序
      var program = programs[programId];
      // 程序中使用的顶点shader
      var shader = shaders[program.vertexShader];

      // 程序中包含webgl扩展
      ModelUtility.checkSupportedGlExtensions(program.glExtensions, context);

      // 数据需要在shader中解析
      if (
        model.extensionsUsed.WEB3D_quantized_attributes ||
        model._dequantizeInShader
      ) {
        // 修改着色器
        var quantizedVS = quantizedVertexShaders[programId];
        if (!defined(quantizedVS)) {
          // 修改着色器
          quantizedVS = modifyShaderForQuantizedAttributes(
            shader,
            programId,
            model
          );
          quantizedVertexShaders[programId] = quantizedVS;
        }
        shader = quantizedVS;
      }

      // 钩子拦截并修改着色器字符串
      shader = modifyShader(shader, programId, model._vertexShaderLoaded);
    }
  }
。。。。。。
}

这个函数中根据model.extensionsUsed.WEB3D_quantized_attributes这个条件,如果使用了扩展则在cesium中拼接用于解码的glsl片段

// 修改着色器的glsl代码用于在顶点着色器中解码压缩过的顶点数据
function modifyShaderForQuantizedAttributes(shader, programName, model) {
  var primitive;

  // program对应的mesh的primitive
  var primitives = model._programPrimitives[programName];

  // If no primitives were cached for this program, there's no need to modify the shader
  // 如果没有为此程序缓存任何基元,则无需修改着色器
  if (!defined(primitives)) {
    return shader;
  }

  var primitiveId;
  for (primitiveId in primitives) {
    if (primitives.hasOwnProperty(primitiveId)) {
      primitive = primitives[primitiveId];
      // 得到mesh对应材质中的program程序
      if (getProgramForPrimitive(model, primitive) === programName) {
        break;
      }
    }
  }

  // This is not needed after the program is processed, free the memory
  // 释放内存(修改完glsl片段,这个对象就不在需要了)
  model._programPrimitives[programName] = undefined;

  var result;
  // 使用了扩展
  if (model.extensionsUsed.WEB3D_quantized_attributes) {
    // 修改glsl用来支持解码
    result = ModelUtility.modifyShaderForQuantizedAttributes(
      model.gltf,
      primitive,
      shader
    );
    // 保存用于解码的uniform
    model._quantizedUniforms[programName] = result.uniforms;
  } else {
    // draco解码后的数据
    var decodedData = model._decodedData[primitiveId];
    if (defined(decodedData)) {
      // 修改着色器用来支持draco解码
      result = ModelUtility.modifyShaderForDracoQuantizedAttributes(
        model.gltf,
        primitive,
        shader,
        decodedData.attributes  // 解码后的属性??
      );
    } else {
      // 返回着色器
      return shader;
    }
  }

  return result.shader;
}

上述函数可以看到,解码的数据来自两个过程,一个是gltf数据中包含了解码的数据,另一个来源是draco解码过的数据(下个过程分析)。

对于第一个数据来源,

// 修改glsl用来支持解码顶点数据
ModelUtility.modifyShaderForQuantizedAttributes = function (
  gltf,
  primitive,
  shader
) {
  var quantizedUniforms = {};
  // 属性
  var attributes = primitive.attributes;
  // 遍历属性
  for (var attributeSemantic in attributes) {
    if (attributes.hasOwnProperty(attributeSemantic)) {
      // 得到属性语义对应的glsl中的属性名
      var attributeVarName = getAttributeVariableName(
        gltf,
        primitive,
        attributeSemantic
      );
      // 获取语义属性对应的访问器的id
      var accessorId = primitive.attributes[attributeSemantic];

      // 如果有下划线前缀,去掉下划线
      if (attributeSemantic.charAt(0) === "_") {
        attributeSemantic = attributeSemantic.substring(1);
      }

      // 根据属性创建glsl中用到的uniform
      var decodeUniformVarName =
        "gltf_u_dec_" + attributeSemantic.toLowerCase();

      // 创建解码用到的gltf_u_dec_position_scale、gltf_u_dec_position_translate
      var decodeUniformVarNameScale = decodeUniformVarName + "_scale";
      var decodeUniformVarNameTranslate = decodeUniformVarName + "_translate";
      if (
        !defined(quantizedUniforms[decodeUniformVarName]) &&
        !defined(quantizedUniforms[decodeUniformVarNameScale])
      ) {
        // 获取访问器中关于解码的扩展属性
        var quantizedAttributes = getQuantizedAttributes(gltf, accessorId);
        if (defined(quantizedAttributes)) {
          // 解码矩阵
          var decodeMatrix = quantizedAttributes.decodeMatrix;
          // 新的入口
          var newMain = "gltf_decoded_" + attributeSemantic;
          // 使用gltf_a_dec_前缀替换所有属性中有的a_前缀
          var decodedAttributeVarName = attributeVarName.replace(
            "a_",
            "gltf_a_dec_"
          );
          // 开方
          var size = Math.floor(Math.sqrt(decodeMatrix.length));

          // replace usages of the original attribute with the decoded version, but not the declaration
          // 将glsl中除了第一个以外的a_相关的属性都替换成gltf_a_dec_开头的属性
          shader = replaceAllButFirstInString(
            shader,
            attributeVarName,
            decodedAttributeVarName
          );
          // declare decoded attribute
          // 声明解码使用的属性
          var variableType;
          if (size > 2) {
            variableType = "vec" + (size - 1);
          } else {
            variableType = "float";
          }

          // 声明属性vec4 gltf_a_dec_position
          shader =
            variableType + " " + decodedAttributeVarName + ";\n" + shader;
          // splice decode function into the shader - attributes are pre-multiplied with the decode matrix
          // uniform in the shader (32-bit floating point)
          // 顶点都提前乘以矩阵decode matrix

          var decode = "";
          if (size === 5) {
            // separate scale and translate since glsl doesn't have mat5
            // 分辨使用缩放和平移,因为没有mat5类型的矩阵
            // 拼接缩放矩阵
            shader =
              "uniform mat4 " + decodeUniformVarNameScale + ";\n" + shader;
            // 拼接平移矩阵
            shader =
              "uniform vec4 " + decodeUniformVarNameTranslate + ";\n" + shader;
            // 解码计算(gltf_a_dec_POSOTION = gltf_u_dec_position_scale * a_position + gltf_u_dec_position_translate)
            decode =
              "\n" +
              "void main() {\n" +
              "    " +
              decodedAttributeVarName +
              " = " +
              decodeUniformVarNameScale +
              " * " +
              attributeVarName +
              " + " +
              decodeUniformVarNameTranslate +
              ";\n" +
              "    " +
              newMain +
              "();\n" +
              "}\n";

            // 传入glsl是需要的用于解码的uniform
            quantizedUniforms[decodeUniformVarNameScale] = { mat: 4 };
            quantizedUniforms[decodeUniformVarNameTranslate] = { vec: 4 };
          } else {

            shader =
              "uniform mat" +
              size +
              " " +
              decodeUniformVarName +
              ";\n" +
              shader;

            decode =
              "\n" +
              "void main() {\n" +
              "    " +
              decodedAttributeVarName +
              " = " +
              variableType +
              "(" +
              decodeUniformVarName +
              " * vec" +
              size +
              "(" +
              attributeVarName +
              ",1.0));\n" +
              "    " +
              newMain +
              "();\n" +
              "}\n";

            // 传入glsl是需要的用于解码的uniform
            quantizedUniforms[decodeUniformVarName] = { mat: size };
          }
          // glsl入口main函数替换
          shader = ShaderSource.replaceMain(shader, newMain);
          shader += decode;
        }
      }
    }
  }
  // 返回新shader和用于解码的uniform
  return {
    shader: shader,
    uniforms: quantizedUniforms,
  };
};

上面的代码中首先找到mesh中某个primitive的所有语义属性,根据语义属性找对应于glsl代码片段中属性,这个对照关系如下:

"meshes": [
        {
            "primitives": [
                {
                    "attributes": {
                        "POSITION": 0,
                        "TEXCOORD_0": 1
                    },
                    "indices": 2,
                    "material": 0,
                    "mode": 4,
					"extensions":"CESIUM_primitive_outline"
                }
            ]
        }
],

"attributes": {
    "a_normal":  {
		"semantic": "NORMAL"
    },
	"a_position": {
		"semantic": "POSITION"
	},
	"a_texcoord0": {
		"semantic": "TEXCOORD_0"
	},
		"a_custom": {
	"semantic": "_CUSTOM_ATTRIBUTE"
	}
}

例如:在glsl中的"a_position"属性,表达的语义是"POSITION",而这个语义对应的实际数据是primitive中{"POSITION": 0},对应的位置0。

        因为要解码数据,所以原来glsl中所有使用"a_position"的字段,除"attibute  vec4 a_position"

之外都要重命名为"gltf_a_dec_position",主要是因为其他使用position的时候都是解码后的gltf_a_dec_position,然后解码的过程中需要解码缩放矩阵和偏移向量,即gltf_u_dec_position_scale和gltf_u_dec_position_translate,所以glsl代码中需要加入解码计算(gltf_a_dec_position = gltf_u_dec_position_scale * a_position + gltf_u_dec_position_translate)

并需要收集uniform映射quantizedUniforms,最后将glsl的入口函数进行替换ShaderSource.replaceMain(shader, newMain);

        在createUniformsForQuantizedAttributes函数中:

function createUniformsForQuantizedAttributes(model, primitive) {
  var programId = getProgramForPrimitive(model, primitive);
  var quantizedUniforms = model._quantizedUniforms[programId];
  return ModelUtility.createUniformsForQuantizedAttributes(
    model.gltf,
    primitive,
    quantizedUniforms
  );
}

之前在glsl中拼接过程中涉及到解码的矩阵和偏移的uniform变量,这些变量存储在哪里呢,下面的代码中揭示

ModelUtility.createUniformsForQuantizedAttributes = function (
  gltf,
  primitive,
  quantizedUniforms
) {
  var accessors = gltf.accessors;
  var setUniforms = {};
  var uniformMap = {};

  var attributes = primitive.attributes;
  for (var attribute in attributes) {
    if (attributes.hasOwnProperty(attribute)) {
      var accessorId = attributes[attribute];
      var a = accessors[accessorId];
      var extensions = a.extensions;

      if (attribute.charAt(0) === "_") {
        attribute = attribute.substring(1);
      }

      if (defined(extensions)) {
        var quantizedAttributes = extensions.WEB3D_quantized_attributes;
        if (defined(quantizedAttributes)) {
          var decodeMatrix = quantizedAttributes.decodeMatrix;
          var uniformVariable = "gltf_u_dec_" + attribute.toLowerCase();

          switch (a.type) {
            case AttributeType.SCALAR:
              uniformMap[uniformVariable] = getMat2UniformFunction(
                decodeMatrix
              ).func;
              setUniforms[uniformVariable] = true;
              break;
            case AttributeType.VEC2:
              uniformMap[uniformVariable] = getMat3UniformFunction(
                decodeMatrix
              ).func;
              setUniforms[uniformVariable] = true;
              break;
            case AttributeType.VEC3:
              uniformMap[uniformVariable] = getMat4UniformFunction(
                decodeMatrix
              ).func;
              setUniforms[uniformVariable] = true;
              break;
            case AttributeType.VEC4:
              // VEC4 attributes are split into scale and translate because there is no mat5 in GLSL
              var uniformVariableScale = uniformVariable + "_scale";
              var uniformVariableTranslate = uniformVariable + "_translate";
              uniformMap[uniformVariableScale] = getMat4UniformFunction(
                scaleFromMatrix5Array(decodeMatrix)
              ).func;
              uniformMap[uniformVariableTranslate] = getVec4UniformFunction(
                translateFromMatrix5Array(decodeMatrix)
              ).func;
              setUniforms[uniformVariableScale] = true;
              setUniforms[uniformVariableTranslate] = true;
              break;
          }
        }
      }
    }
  }

  // If there are any unset quantized uniforms in this program, they should be set to the identity
  for (var quantizedUniform in quantizedUniforms) {
    if (quantizedUniforms.hasOwnProperty(quantizedUniform)) {
      if (!setUniforms[quantizedUniform]) {
        var properties = quantizedUniforms[quantizedUniform];
        if (defined(properties.mat)) {
          if (properties.mat === 2) {
            uniformMap[quantizedUniform] = getMat2UniformFunction(
              Matrix2.IDENTITY
            ).func;
          } else if (properties.mat === 3) {
            uniformMap[quantizedUniform] = getMat3UniformFunction(
              Matrix3.IDENTITY
            ).func;
          } else if (properties.mat === 4) {
            uniformMap[quantizedUniform] = getMat4UniformFunction(
              Matrix4.IDENTITY
            ).func;
          }
        }
        if (defined(properties.vec)) {
          if (properties.vec === 4) {
            uniformMap[quantizedUniform] = getVec4UniformFunction([
              0,
              0,
              0,
              0,
            ]).func;
          }
        }
      }
    }
  }
  return uniformMap;
};

可以看到是从quantizedAttributes.decodeMatrix;中得到了,位置如下:

var quantizedAttributes = extensions.WEB3D_quantized_attributes;
if (defined(quantizedAttributes)) {
  var decodeMatrix = quantizedAttributes.decodeMatrix;
 }

        第二种使用draco压缩过的几何数据,这部分分两个阶段,第一个阶段是使用wasm+worker的过程,这个过程解析完成后还会在vs中在进行数据处理才能变成普通的顶点属性数据。下一篇文章讲解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值