CesiumJS 2022^ 源码解读[6] - 三维模型(ModelExperimental)新架构

三维模型架构(即 Scene/ModelExperimental 目录下的模块)有别于旧版模型 API(即 Scene/Model.js 模块为主的一系列处理 glTF 以及处理 3DTiles 点云文件的源码),它重新设计了 CesiumJS 中的场景模型加载、解析、渲染、调度架构,更合理,更强大。

这套新架构专门为 下一代 3DTiles(1.1版本,当前暂时作为 1.0 版本的扩展) 设计,接入了更强大的 glTF 2.0 生态,还向外暴露了 CustomShader API

ModelExperimental 的尾缀 Experimental 单词即“实验性的”,等待这套架构完善,就会去掉这个尾缀词(截至发文,CesiumJS 版本为 1.95)。

接下来,我想先从这套架构的缓存机制说起。

1. ModelExperimental 的缓存机制

1.1. 缓存池 ResourceCache

缓存机制由两个主管理类 ResourceCacheResourceCacheKey 负责,缓存的可不是 Resource 类实例,而是由 ResourceLoader 这个基类派生出来的 N 多个子类:

ResourceCache 类被设计成一个类似于“静态类”的存在,很多方法都是在这个类身上使用的,而不是 new 一个 ResourceCache 实例,用实例去调用。例如:

 ResourceCache.get("somecachekey...") // 使用键名获取缓存的资源
 ResourceCache.loadGltfJson({
   /* . */
 }) // 根据配置对象加载 glTF 的 json 

上面提到,ResourceCache 缓存的是各种 ResourceLoader,实际上为了统计这些 loader 被使用的次数,Cesium 团队还做了一个简单的装饰器模式封装,即使用 CacheEntry 这个在 ResourceCache.js 模块内的私有类:

 function CacheEntry(resourceLoader) {
   this.referenceCount = 1;
   this.resourceLoader = resourceLoader;
 } 

你可以在 ResourceLoader.js 源码中找到一个静态成员 cacheEntries

 function ResourceCache() {}
 ResourceCache.cacheEntries = {}; 

它只是一个简单的 JavaScript 对象,key 是字符串,也就是等会要讲的 ResourceCacheKey 部分,值即 CacheEntry 的实例。

ResourceCache.load 这个静态方法中可以看到是如何缓存的:

 ResourceCache.load = function (options) {
   // ...
   const cacheKey = resourceLoader.cacheKey;
   // ...

   if (defined(ResourceCache.cacheEntries[cacheKey])) {
     throw new DeveloperError(
       `Resource with this cacheKey is already in the cache: ${cacheKey}`
   );
 }

   ResourceCache.cacheEntries[cacheKey] = new CacheEntry(resourceLoader);
   resourceLoader.load();
 } 

设计上,缓存的 loader 只允许 load 一次,之后在取的时候都是使用 ResourceCache.get 方法获得。

1.2. 缓存对象的键设计 ResourceCacheKey

Cesium 团队在键的设计上充分利用了待缓存资源的自身信息,或唯一信息,或 JSON 字符串本身,但是我觉得这有些不妥,较长的字符串会带来较大的内存占用。

ResourceCacheKey 也是一个类似静态类的设计,它有很多个 getXXXKey 的静态方法:

 ResourceCacheKey.getSchemaCacheKey // return "external-schema:..."
 ResourceCacheKey.getExternalBufferCacheKey // return "external-buffer:..."
 ResourceCacheKey.getEmbeddedBufferCacheKey // return "embedded-buffer:..."
 ResourceCacheKey.getGltfCacheKey // return "gltf:..."
 ResourceCacheKey.getBufferViewCacheKey // return "buffer-view:..."
 ResourceCacheKey.getDracoCacheKey // return "draco:..."
 ResourceCacheKey.getVertexBufferCacheKey // return "vertex-buffer:..."
 ResourceCacheKey.getIndexBufferCacheKey // return "index-buffer:..."
 ResourceCacheKey.getImageCacheKey // return "image:..."
 ResourceCacheKey.getTextureCacheKey // return "texture:...-sampler-..." 

这些方法均返回一个字符串,有兴趣的读者可以自己跟进源码了解它是如何从资源本身的信息“计算”出 key 的。

我认为这里存在优化的可能性,三维场景的资源会非常多,对内存容量是一个不小的要求,减小 key 的内存大小或许能提升内存消耗表现,这需要优秀的软件设计,等待官方团队优化或者大手子提交 PR。(2022年6月)

2. 三维模型的加载与解析

ModelExperimental API 的主要入口就是 ModelExperimental 类,它有几个静态方法可供加载不同的模型资源(glTF/b3dm/i3dm/pnts…):

 ModelExperimental.fromGltf = function (options) { /* ... */ }
 ModelExperimental.fromB3dm = function (options) { /* ... */ }
 ModelExperimental.fromPnts = function (options) { /* ... */ }
 ModelExperimental.fromI3dm = function (options) { /* ... */ }
 ModelExperimental.fromGeoJson = function (options) { /* ... */ } 

均返回一个 ModelExperimental 实例。从这几个方法可以看出,还是兼容了 3DTiles 1.0 中的三个主要瓦片格式的。其中,fromGeoJson 方法是一个尚未完全实现的规范,允许使用 geojson 作为瓦片的内容,有兴趣可以看 CesiumGS/3d-tiles 仓库中的一个提案。

以 glTF 为例,先看示例代码:

 import {
   ModelExperimental,
   Transforms,
   Cartesian3
 } from 'cesium'

 const origin = Cartesian3.fromDegrees(113.5, 22.4)
 const modelPrimitive = ModelExperimental.fromGltf({
   gltf: "path/to/glb_or_gltf_file",
   modelMatrix: Transforms.eastNorthUpToFixedFrame(origin)
 })
 viewer.scene.primitives.add(modelPrimitive) 

以 glTF 模型(文件格式 glb 或 gltf)为例,从流程上来看,创建 ModelExperimental 实例的过程是这样的:

 ModelExperimental.fromGltf
 new GltfLoader()
 new ModelExperimental()
   fn initialize
     GltfLoader.prototype.load
       ~promise.then → new ModelExperimentalSceneGraph 

大部分的初始化工作是由 GltfLoader 去完成的,现在就进入 GltfLoader 中看看吧。

2.1. GltfLoader 的初步加载

GltfLoader 的 load 方法本身是同步的,但是它里面的过程却是一些异步的写法,使用了 ES6 的 Promise。

GltfLoader 把加载结果允诺给 Promise 成员变量 promise,可以看到在 ModelExperimental.js 模块内的 initialize 函数中,then 链接收初步加载完毕的各种 glTF 组件:

 // ModelExperimental.js

 function initialize(model) {
   const loader = model._loader;
   const resource = model._resource;

   loader.load();

   loader.promise
   .then(function (loader) {
       const components = load
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ChatGLM-6B源码是基于GLM的2D位置编码实现的。该位置编码的详细原理可以在原文《GLM: General Language Model Pretraining with Autoregressive Blank Infilling》中找到。在GitHub上,有一个微调ChatGLM-6B项目的代码库,作者是mymusise。该项目使用Stanford Alpaca的52K数据集,并通过LoRA(低秩适应)的方式进行微调。在评测时,使用中文Rouge分数和BLEU-4指标,并将生成的结果保存在"./output/adgen-chatglm-6b-pt-8-1e-2/generated_predictions.txt"文件中。 以上是关于ChatGLM-6B源码的一些解读。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ChatGLM-6B模型结构组件源码阅读](https://blog.csdn.net/yjh_SE007/article/details/130728164)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [ChatGLM-6B的基座/部署/微调/实现:从GLM到6B的LoRA/P-Tuning微调、及6B源码解读](https://blog.csdn.net/v_JULY_v/article/details/129880836)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值