Cesium-源码修改-gltf增加纹理贴图改变3dtiles外观
一、需求
Cesium支持加载gltf和3dtiles等三维数据模型,实现了很好的封装,往往只需要给一个uri就能加载模型文件,并实现贴图渲染等。但是好的封装带来的问题是如果开发者想要自定义贴图,那该怎么办?不得不从源码入手。
二、价值
这篇文章的价值不仅仅是gltf增加纹理贴图,因为刚才说到的3dtiles其实也是基于gltf来实现的模型,那么如果想给3dtiles增加自定义贴图,是否也意味着可以走gltf这条路,并且从gltf这层实现之后,是否意味着对b3dm/i3dm/cmpt等的统一。
三、源码解读
1.框架:
3dtiels中b3dm和i3dm是以gltf为基础进行加载和渲染的,因此Cesium在封装的B3dmLoader和I3dmLoader中都有调用_gltfLoader的地方,其中使用的是:gltfLoader.process
B3dmLoader.prototype.process = function(frameState) {...const ready = this._gltfLoader.process(frameState);...};
I3dmLoader.prototype.process = function(frameState) {...ready = gltfLoader.process(frameState);...};
在这个函数中抛开异常处理逻辑,关键函数在于:loadResources5(this, frameState):
async function loadResources5(loader, frameState) {//给出Json指引const gltf = loader.gltfJson;//具体的资源加载const promise = parse(loader, gltf, supportedImageFormats, frameState);...//注意这里由于模型资源加载完成后是不需要中间数据的,为了减少内存的消耗,Cesium这里对Json信息进行了清理if (defined_default(loader._gltfJsonLoader) && loader._releaseGltfJson) {ResourceCache_default.unload(loader._gltfJsonLoader);loader._gltfJsonLoader = void 0;}return promise;}
再来看parse:
function parse(loader, gltf, supportedImageFormats, frameState) {//...拓展项相关的数据处理//注意一下结构,实际就是按照Json的指引,将具体的数据请取出,熟悉gltf的Json项自然就明白了各项的含义const nodes = loadNodes(loader, gltf, supportedImageFormats, frameState);const skins = loadSkins(loader, gltf, nodes);const animations = loadAnimations(loader, gltf, nodes);const articulations = loadArticulations(gltf);const scene = loadScene(gltf, nodes);const components = new Components2();const asset = new Asset2();const copyright = gltf.asset.copyright;...//将取出的数据存储在components中components.asset = asset;components.scene = scene;components.nodes = nodes;...loader._components = components;...}
至此,数据的读取,处理就完成了,意味着渲染隐含与其中。下面将重点分析。
2.渲染逻辑
A.纹理资源的加载
如果了解gltf的管理方式:
不难看出node是总览全局的,那么进入 node处理部分:loadNodes::loadNode
function loadNode(loader, gltf, gltfNode, supportedImageFormats, frameState) {...//一个node对应一个meshId,用于获取对应的meshconst meshId = gltfNode.mesh;if (defined_default(meshId)) {const mesh = gltf.meshes[meshId];//mesh中又包括多个图元const primitives = mesh.primitives;const primitivesLength = primitives.length;for (let i = 0; i < primitivesLength; ++i) {node.primitives.push(//图元是最小的渲染可调度单位loadPrimitive(loader,gltf,primitives[i],defined_default(node.instances),supportedImageFormats,frameState));}...}return node;}
在最小的渲染单位primitive中:
function loadPrimitive(loader, gltf, gltfPrimitive, hasInstances, supportedImageFormats, frameState) {...//从图元取得MaterialIdconst materialId = gltfPrimitive.material;if (defined_default(materialId)) {//加载材质的入口,也意味着材质的管理(增删改)都可以从这里找到primitive.material = loadMaterial(loader,gltf,gltf.materials[materialId],supportedImageFormats,frameState);}...return primitive;}
加载材质部分主要包括:初始化一个空材质+往材质模板中填充数据
function loadMaterial(loader, gltf, gltfMaterial, supportedImageFormats, frameState) {//首先进来的第一件事先创建一个空材质用于填充数据const material = new Material3();...//直接计算的填充material.unlit = defined_default(extensions.KHR_materials_unlit);
...//针对特定类型材质特定参数计算,最后再填充specularGlossiness.glossinessFactor = pbrSpecularGlossiness.glossinessFactor;material.pbrSpecularGlossiness = pbrSpecularGlossiness;...//重头戏就是这里的加载纹理metallicRoughness.baseColorTexture = loadTexture(loader,gltf,pbrMetallicRoughness.baseColorTexture,supportedImageFormats,frameState);...return material;}
加载纹理的逻辑:
function loadTexture(loader, gltf, textureInfo, supportedImageFormats, frameState, samplerOverride) {//检查Image是否可用//纹理加载器const textureLoader = ResourceCache_default.getTextureLoader({gltf,textureInfo,gltfResource: loader._gltfResource,baseResource: loader._baseResource,supportedImageFormats,frameState,asynchronous: loader._asynchronous});//纹理解释器const textureReader = GltfLoaderUtil_default.createModelTextureReader({textureInfo});//将相关加载放入总加载器管理loader._textureLoaders.push(textureLoader);...loader._textureState = GltfLoaderState.FAILED;loader._textureErrors.push(error);loader._texturesPromises.push(promise);loader._textureCallbacks[index]...return textureReader;}
B.纹理资源应用
当纹理加载完成,就要考虑如何消费纹理,即编写shader和处理:
纹理的使用往往是在FragmentShader中,这块的编码在Cesium中为:
var MaterialStageFS_default = "// If the style color is white, it implies the feature has not been styled.\nbool isDefaultStyleColor(vec3 color)\n{\n return all(greaterThan(color, vec3(1.0 - czm_epsilon3)));\n}\n\nvec3 blend(vec3 sourceColor, vec3 styleColor, float styleColorBlend)\n{\n vec3 blendColor = mix(sourceColor, styleColor, styleColorBlend);\n vec3 color = isDefaultStyleColor(styleColor.rgb) ? sourceColor : blendColor;\n return color;\n}\n\nvec2 computeTextureTransform(vec2 texCoord, mat3 textureTransform)\n{\n return vec2(textureTransform * vec3(texCoord, 1.0));\n}\n\n#ifdef HAS_NORMALS\nvec3 computeNormal(ProcessedAttributes attributes)\n{\n // Geometry normal. This is already normalized \n vec3 ng = attributes.normalEC;\n\n vec3 normal = ng;\n #if defined(HAS_NORMAL_TEXTURE) && !defined(HAS_WIREFRAME)\n vec2 normalTexCoords = TEXCOORD_NORMAL;\n #ifdef HAS_NORMAL_TEXTURE_TRANSFORM\n normalTexCoords = computeTextureTransform(normalTexCoords, u_normalTextureTransform);\n #endif\n\n // If HAS_BITANGENTS is set, then HAS_TANGENTS is also set\n #ifdef HAS_BITANGENTS\n vec3 t = attributes.tangentEC;\n vec3 b = attributes.bitangentEC;\n mat3 tbn = mat3(t, b, ng);\n vec3 n = texture(u_normalTexture, normalTexCoords).rgb;\n normal = normalize(tbn * (2.0 * n - 1.0));\n #elif (__VERSION__ == 300 || defined(GL_OES_standard_derivatives))\n // If derivatives are available (not IE 10), compute tangents\n vec3 positionEC = attributes.positionEC;\n vec3 pos_dx = dFdx(positionEC);\n vec3 pos_dy = dFdy(positionEC);\n vec3 tex_dx = dFdx(vec3(normalTexCoords,0.0));\n vec3 tex_dy = dFdy(vec3(normalTexCoords,0.0));\n vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t);\n t = normalize(t - ng * dot(ng, t));\n vec3 b = normalize(cross(ng, t));\n mat3 tbn = mat3(t, b, ng);\n vec3 n = texture(u_normalTexture, normalTexCoords).rgb;\n normal = normalize(tbn * (2.0 * n - 1.0));\n #endif\n #endif\n\n #ifdef HAS_DOUBLE_SIDED_MATERIAL\n if (czm_backFacing()) {\n normal = -normal;\n }\n #endif\n\n return normal;\n}\n#endif\n\nvoid materialStage(inout czm_modelMaterial material, ProcessedAttributes attributes, SelectedFeature feature)\n{\n #ifdef HAS_NORMALS\n material.normalEC = computeNormal(attributes);\n #endif\n\n vec4 baseColorWithAlpha = vec4(1.0);\n // Regardless of whether we use PBR, set a base color\n #ifdef HAS_BASE_COLOR_TEXTURE\n vec2 baseColorTexCoords = TEXCOORD_BASE_COLOR;\n\n #ifdef HAS_BASE_COLOR_TEXTURE_TRANSFORM\n baseColorTexCoords = computeTextureTransform(baseColorTexCoords, u_baseColorTextureTransform);\n #endif\n\n baseColorWithAlpha = czm_srgbToLinear(texture(u_baseColorTexture, baseColorTexCoords));\n\n #ifdef HAS_BASE_COLOR_FACTOR\n baseColorWithAlpha *= u_baseColorFactor;\n #endif\n #elif defined(HAS_BASE_COLOR_FACTOR)\n baseColorWithAlpha = u_baseColorFactor;\n #endif\n\n #ifdef HAS_POINT_CLOUD_COLOR_STYLE\n baseColorWithAlpha = v_pointCloudColor;\n #elif defined(HAS_COLOR_0)\n vec4 color = attributes.color_0;\n // .pnts files store colors in the sRGB color space\n #ifdef HAS_SRGB_COLOR\n color = czm_srgbToLinear(color);\n #endif\n baseColorWithAlpha *= color;\n #endif\n\n material.diffuse = baseColorWithAlpha.rgb;\n material.alpha = baseColorWithAlpha.a;\n\n #ifdef USE_CPU_STYLING\n material.diffuse = blend(material.diffuse, feature.color.rgb, model_colorBlend);\n #endif\n\n #ifdef HAS_OCCLUSION_TEXTURE\n vec2 occlusionTexCoords = TEXCOORD_OCCLUSION;\n #ifdef HAS_OCCLUSION_TEXTURE_TRANSFORM\n occlusionTexCoords = computeTextureTransform(occlusionTexCoords, u_occlusionTextureTransform);\n #endif\n material.occlusion = texture(u_occlusionTexture, occlusionTexCoords).r;\n #endif\n\n #ifdef HAS_EMISSIVE_TEXTURE\n vec2 emissiveTexCoords = TEXCOORD_EMISSIVE;\n #ifdef HAS_EMISSIVE_TEXTURE_TRANSFORM\n emissiveTexCoords = computeTextureTransform(emissiveTexCoords, u_emissiveTextureTransform);\n #endif\n\n vec3 emissive = czm_srgbToLinear(texture(u_emissiveTexture, emissiveTexCoords).rgb);\n #ifdef HAS_EMISSIVE_FACTOR\n emissive *= u_emissiveFactor;\n #endif\n material.emissive = emissive;\n #elif defined(HAS_EMISSIVE_FACTOR)\n material.emissive = u_emissiveFactor;\n #endif\n\n #if defined(LIGHTING_PBR) && defined(USE_SPECULAR_GLOSSINESS)\n #ifdef HAS_SPECULAR_GLOSSINESS_TEXTURE\n vec2 specularGlossinessTexCoords = TEXCOORD_SPECULAR_GLOSSINESS;\n #ifdef HAS_SPECULAR_GLOSSINESS_TEXTURE_TRANSFORM\n specularGlossinessTexCoords = computeTextureTransform(specularGlossinessTexCoords, u_specularGlossinessTextureTransform);\n #endif\n\n vec4 specularGlossiness = czm_srgbToLinear(texture(u_specularGlossinessTexture, specularGlossinessTexCoords));\n vec3 specular = specularGlossiness.rgb;\n float glossiness = specularGlossiness.a;\n #ifdef HAS_SPECULAR_FACTOR\n specular *= u_specularFactor;\n #endif\n\n #ifdef HAS_GLOSSINESS_FACTOR\n glossiness *= u_glossinessFactor;\n #endif\n #else\n #ifdef HAS_SPECULAR_FACTOR\n vec3 specular = clamp(u_specularFactor, vec3(0.0), vec3(1.0));\n #else\n vec3 specular = vec3(1.0);\n #endif\n\n #ifdef HAS_GLOSSINESS_FACTOR\n float glossiness = clamp(u_glossinessFactor, 0.0, 1.0);\n #else\n float glossiness = 1.0;\n #endif\n #endif\n\n #ifdef HAS_DIFFUSE_TEXTURE\n vec2 diffuseTexCoords = TEXCOORD_DIFFUSE;\n #ifdef HAS_DIFFUSE_TEXTURE_TRANSFORM\n diffuseTexCoords = computeTextureTransform(diffuseTexCoords, u_diffuseTextureTransform);\n #endif\n\n vec4 diffuse = czm_srgbToLinear(texture(u_diffuseTexture, diffuseTexCoords));\n #ifdef HAS_DIFFUSE_FACTOR\n diffuse *= u_diffuseFactor;\n #endif\n #elif defined(HAS_DIFFUSE_FACTOR)\n vec4 diffuse = clamp(u_diffuseFactor, vec4(0.0), vec4(1.0));\n #else\n vec4 diffuse = vec4(1.0);\n #endif\n czm_pbrParameters parameters = czm_pbrSpecularGlossinessMaterial(\n diffuse.rgb,\n specular,\n glossiness\n );\n material.diffuse = parameters.diffuseColor;\n // the specular glossiness extension's alpha overrides anything set\n // by the base material.\n material.alpha = diffuse.a;\n material.specular = parameters.f0;\n material.roughness = parameters.roughness;\n #elif defined(LIGHTING_PBR)\n #ifdef HAS_METALLIC_ROUGHNESS_TEXTURE\n vec2 metallicRoughnessTexCoords = TEXCOORD_METALLIC_ROUGHNESS;\n #ifdef HAS_METALLIC_ROUGHNESS_TEXTURE_TRANSFORM\n metallicRoughnessTexCoords = computeTextureTransform(metallicRoughnessTexCoords, u_metallicRoughnessTextureTransform);\n #endif\n\n vec3 metallicRoughness = texture(u_metallicRoughnessTexture, metallicRoughnessTexCoords).rgb;\n float metalness = clamp(metallicRoughness.b, 0.0, 1.0);\n float roughness = clamp(metallicRoughness.g, 0.04, 1.0);\n #ifdef HAS_METALLIC_FACTOR\n metalness *= u_metallicFactor;\n #endif\n\n #ifdef HAS_ROUGHNESS_FACTOR\n roughness *= u_roughnessFactor;\n #endif\n #else\n #ifdef HAS_METALLIC_FACTOR\n float metalness = clamp(u_metallicFactor, 0.0, 1.0);\n #else\n float metalness = 1.0;\n #endif\n\n #ifdef HAS_ROUGHNESS_FACTOR\n float roughness = clamp(u_roughnessFactor, 0.04, 1.0);\n #else\n float roughness = 1.0;\n #endif\n #endif\n czm_pbrParameters parameters = czm_pbrMetallicRoughnessMaterial(\n material.diffuse,\n metalness,\n roughness\n );\n material.diffuse = parameters.diffuseColor;\n material.specular = parameters.f0;\n material.roughness = parameters.roughness;\n #endif\n}\n";
相当的长,但是这中间有上述分析过程中对材质单个参数(粗糙度,金属度)和纹理的处理,不妨一读。那么这段Shader如何使用的呢?
MaterialPipelineStage.process = function(renderResources, primitive, frameState) {...processMaterialUniforms(material,uniformMap2,shaderBuilder,defaultTexture,defaultNormalTexture,defaultEmissiveTexture,disableTextures);if (defined_default(material.specularGlossiness)) {processSpecularGlossinessUniforms(material,uniformMap2,shaderBuilder,defaultTexture,disableTextures);} else {processMetallicRoughnessUniforms(material,uniformMap2,shaderBuilder,defaultTexture,disableTextures); }...shaderBuilder.addFragmentLines(MaterialStageFS_default);...};
是的,直接在最下方添加到ShaderBuilder。
看上去我们这个过程从数据获取到消费似乎是完成了,但是细心的人应该发现了MaterialStageFS_default 还有一些宏或者不同的纹理它的采样器uv这样的数据其实也是要告知shader的,那么这种处理实际是在processGldLightMapUniforms::processTexture2这个函数中:
function processTexture2(shaderBuilder, uniformMap2, textureReader, uniformName, defineName, defaultTexture) {
//添加Uniform变量shaderBuilder.addUniform("sampler2D",//类型uniformName,//变量名ShaderDestination_default.FRAGMENT//添加到Fragment);uniformMap2[uniformName] = function() {return defaultValue_default(textureReader.texture, defaultTexture);};//shaderBuilder.addDefine用于在Shader中定义变量并给初值//(名称,默认值,添加位置)const textureDefine = `HAS_${defineName}_TEXTURE`;//宏shaderBuilder.addDefine(textureDefine, void 0, ShaderDestination_default.FRAGMENT);const texCoordIndex = textureReader.texCoord;const texCoordVarying = `v_texCoord_${texCoordIndex}`;const texCoordDefine = `TEXCOORD_${defineName}`;//uvshaderBuilder.addDefine(texCoordDefine,texCoordVarying,ShaderDestination_default.FRAGMENT);...}
至此,整个流程才算完成,理解了以上流程之后,要想加一张纹理那就比较容易了。
3.实操添加一张贴图
这里给出步骤思路,具体实现自己写一遍应该会好很多:
a.新增一张纹理贴图,意味着Material要加新成员,对应的是loadMaterial中的空材质构造函数:
const material = new Material3();
b. 对空material填充需要loadTexture,这里要注意纹理解释器的丰富,封装在了getAllTextureReaders中;
c.加载好纹理之后就是纹理处理,也就是shader部分,这里一共又可以分为两步:
processTexture2添加uniform数据资源,往shader压入变量及其值;
编写shader代码:MaterialStageFS_default。直接在这里改就可以利用上ShaderBuilder的添加一步到位。
Cesium-源码修改-gltf增加纹理贴图改变3dtiles外观相关推荐
- Ureport2源码修改:增加自定义功能
Ureport2源码修改:单元格超链接增加弹出窗口显示功能 需求来源 功能实现 ureport2-js项目: ureport2-console项目: ureport2-core项目: 最终效果 新人第 ...
- Android6.0 源码修改之Settings音量调节界面增加通话音量调节
Android6.0 源码修改之Settings音量调节界面增加通话音量调节 前言 今天客户提了个需求,因为我们的设备在正常情况下无法调节通话音量,只有在打电话过程中,按物理音量加减键才能出现调节通话 ...
- Deep Compression阅读理解及Caffe源码修改
Deep Compression阅读理解及Caffe源码修改 作者:may0324 更新: 没想到这篇文章写出后有这么多人关注和索要源码,有点受宠若惊.说来惭愧,这个工作当时做的很粗糙,源码修改的比 ...
- Android Dialer,Mms,Contacts源码修改笔记,移动端混合开发经验
②在AndroidManifest.xml中修改相应Activity的theme <activity android:name=".HomeActivity" android ...
- android打开volte代码,Android8.1 源码修改之插入SIM卡默认启用Volte功能
前言 公用电话产品,插入SIM卡后要求自动打开Volte功能,即插即用,用完拔卡就走 实现 第一步 开关对应的代码 通过打印日志和全局查找,源码位置 vendor/mediatek/proprieta ...
- Android TV 源码修改默认输入法
前一阵子,应泰国客户需求,需要在Android TV系统定制一个多语言输入法,至少支持中.英.泰三种语言.拿到这个任务,对于至今还是小白的我来说,当然先去google一下有没有大神专门做过符合要求的输 ...
- 《Python数据处理》7.2.4笔记:寻找唯一键的源码修改——由于zip方法
<Python数据处理>7.2.4笔记:寻找唯一键的源码修改--由于zip方法 一.原因:zip方法 二.修改后 一.原因:zip方法 参考<python中使用zip函数出现> ...
- BenchmarkSQL 5.0源码修改 -- 支持 人大金仓数据库 KingBase
benchmarksql 在5.0版本中没有对人大金仓数据库(KingBase)的TPC-C测试支持,本文通过修改部分源码,让benchmarksql 5.0支持KingBase. 个人理解: Ben ...
- 网站介绍三合一缩略图片kyuan源码 可自定义增加广告位
介绍: 网站介绍三合一缩略图片kyuan源码 可自定义增加广告位 上传主机根目录解压打开域名就行. 使用记事本进行修改资料即可 网盘下载地址: http://kekewl.org/E4hICmomNs ...
最新文章
- oracle if=,oracle中if/else的三种实现方式详解
- 图解VC#版DirectX开发教程二 - 摄像机
- 数据库笔记: SQL
- Python入门学习之异常处理机制
- 【sprinb-boot】Junit测试
- java rpm包安装_rpm包安装java jar开机自启
- 嵌入式、快速人脸算法库Vision.Face SDK开放下载!已经商用检验
- 可以用手机实现的移动端车牌识别功能
- 调整SAP系统中物料账和财务账的期间
- 对话框响应WM_KEYDOWN消息
- 为什么程序员有外包经历,面试就像有污点一样的存在?是这样吗?
- 一次解决DB2接口文件到Oracle无法导入问题的经历
- 微信公众号支付开发-php
- 前端开发工程师原生html进阶指南
- unity 可视化渲染管线_如何为高端可视化设置Unity的高清渲染管道
- Elasticsearch-分布式搜索引擎介绍
- 笔记本电脑没声音的解决方法教程
- TX2跑通yolov4
- 一体化枪机和快速球机
- Enigma算法图解