尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

〖共创稿事节〗HarmonyOS 7 新特性实战(06):TiledGSNode 分块请求、相机驱动与离页清理

发布时间:2026/9/27 22:48:09

资讯中心
01
ARTICLE

〖共创稿事节〗HarmonyOS 7 新特性实战(06):TiledGSNode 分块请求、相机驱动与离页清理

〖共创稿事节〗HarmonyOS 7 新特性实战(06):TiledGSNode 分块请求、相机驱动与离页清理
一个小展品可以整体加载整座展厅却未必适合一次读入。用户站在入口时只需要入口附近可见的内容把远处房间也全部准备好会延长首屏并抬高内存占用。分块场景要解决的是可见范围、数据就绪与资源预算之间的协调。API 26 的 TiledGSNode 面向分块高斯场景与加载单个 GSNode 的入口有所区别。应用需要先取得符合分块描述格式的素材及清单再围绕真实相机组织调度机械切分普通 GLB 无法生成可用的分块场景。清单是资源关系不能只存一组文件名分块素材需要表达空间层级和块之间的关系。SDK 的TiledGSImportSettings通过uri指向描述层级的 JSON 清单。清单内部字段应遵循配套素材工具和官方格式应用不能自行发明一个数组再期待加载器识别。资源目录应作为一个版本整体管理清单、块文件和校验信息必须来自同一份导出。只替换清单却保留旧块可能出现加载错误或空间位置不一致。模型版本号适合放在应用资源索引中更新成功后一次切换目录指针。谁负责选择块谁负责画面TiledGSNode 提供setCamera用于指定驱动分块选择的相机。该相机可以与最终渲染相机不同这让预取与显示有了可分离的空间也增加了配置错误的可能。import{Scene,Camera}fromkit.ArkGraphics3D;import{spatialRender}fromkit.SpatialReconKit;asyncfunctionattachTiles(scene:Scene,camera:Camera,manifestUri:string):PromisespatialRender.TiledGSNode{consttiledawaitspatialRender.GSPlugin.loadTiledGSNode(scene,{uri:manifestUri},scene.root);tiled.setCamera(camera);returntiled;}这段示例表达 SDK 的加载与相机绑定关系调用前仍需建立渲染上下文并加载 GS 插件。第一版建议选块与显示使用同一相机先排除两套坐标或朝向不一致的问题再评估预测相机是否值得加入。分块请求必须回应真实可读文件API 26 的 TiledGSNode 提供 setTileRequestCallback回调参数是 GSTile 数组每个对象的 uri 对应一个 .sog 文件。引擎决定需要哪些分块应用负责让这些文件可读然后逐个调用 notifyTileReady。只完成 loadTiledGSNode 和 setCamera并不能代替数据就绪通知。本地 Demo 采用完整素材目录不自动联网下载。先打开清单再创建场景、相机与分块节点绑定同一相机用于显示和选块。收到请求后检查路径在清单目录内、扩展名为 .sog、文件能以只读方式打开且大小处于 1 字节至 128 MiB 的实验上限。关闭描述符后才通知引擎不能把文件存在直接等同于内容能成功渲染。node.setCamera(camera);node.setTileRequestCallback((tiles:spatialRender.GSTile[]){if(generation!this.generation){return;}tiles.forEach((tile:spatialRender.GSTile){this.provideTile(tile,node,generation,onStatus);});});provideTile 使用 pending 集合合并正在检查的相同 URI完成或失败后移除让引擎后续请求可以重新检查。文件缺失时显示失败原因不调用 notifyTileReady。目录来自可信素材导出实验拒绝父目录跳转、转义路径与远程 URI不把这个简单路径检查当成任意不可信压缩包的安全解包器。页面退出后文件检查仍可能完成每次加载先销毁上一场景并递增 generation。代次必须在第一次文件异步操作前取得若用户在清单打开期间退出后续流程直接结束不能重新创建场景。场景创建完成但已过期时销毁临时 Scene不交给页面。constinputawaitfileIo.open(local,fileIo.OpenMode.READ_ONLY);try{conststatawaitfileIo.stat(input.fd);if(stat.size0||stat.size128*1024*1024){thrownewError(分块大小须在 1 字节至 128 MiB 内);}}finally{awaitfileIo.close(input);}if(generation!this.generation){return;}node.notifyTileReady(tile);dispose 先清除当前对象引用再用 setTileRequestCallback(null) 注销回调最后在 finally 中销毁 Scene。每一代请求保存自己使用的 pending 集合旧请求的 finally 不会删除新场景的同名请求。重复调用 dispose 不会再次销毁同一个 Scene。预算应该拆成三笔账磁盘缓存、CPU 侧解码数据和 GPU 资源并不是同一个指标。磁盘剩余空间充足也可能因为 GPU 资源过多出现画面异常。记录峰值时应说明测量来源不能把进程内存曲线直接命名为显存。首次实验可以固定入口到展台再返回的路线记录首个可见画面耗时、移动中的空洞、帧时间以及进程内存变化。比较不同预算前确保素材版本、相机路线、冷暖缓存条件一致否则更快可能只是上一次已经下载过。缺块时保留方向感当前实现遇到文件缺失时提示“分块未就绪”不销毁已经加载的场景。恢复文件后可重新加载清单没有自动下载或定时重试。若后续增加远程资源层再区分网络失败、校验失败和不支持的素材版本并为自动重试设置有限次数与退避。在 HBN-AL80 真机的 API 26 构建中使用默认的应用沙箱路径执行“加载本地清单”页面回读No such file or directory场景保持在“等待分块场景”没有创建可见的 TiledGSNode。这个结果验证了清单缺失时的失败边界。当前工程没有可供系统加载的兼容分块清单和.sog目录因此没有成功渲染结果。运行画面如下目录切换也需要代次检查。场景 A 的下载回调不能把数据写入场景 B 的资源目录相对路径还要防止越出预期根目录。下载文件先写临时位置校验通过再转正以免加载器读到一半的数据。从一条固定路线开始验收Demo 提供本地清单路径、加载按钮、相机方位和距离滑块以及复位按钮。先把合法清单与全部 .sog 文件放入应用 files/tiles 目录保留素材导出结构再加载该目录的 manifest.json。当前上限仅约束单个分块文件大小不是引擎总内存预算。成功条件包括正常浏览、缺块提示、连续快速转向后恢复可见内容以及退出后不继续增长的资源占用。分块加载最终服务于内容浏览因此结果记录必须同时有路线、画面与资源曲线。只有内存下降、却出现明显空洞的配置不能直接判定优于整体加载。分块方案先设一个整体加载对照组素材条件首选路径需要付出的成本单件小模型单个 GSNode整体驻留但链路简单有合法分块清单的大场景TiledGSNode 与同一相机素材工具、目录完整性及画面连续性检查只有普通 GLB走对应网格加载器无法据此测试高斯分块先让清单在本地完整目录里加载再加入应用负责的资源下载最后评估预取。若第一阶段就缺块优先核查清单版本和文件组织避免把素材错误归因于网络调度。对照报告分别列画面空洞和加载时间不把较低内存单独当作胜出标准。参考TiledGSNode 与相机选择接口、API 26 版本说明。缺块与过期回调的控制器处理相机移动会产生新的瓦片请求。控制器在读取本地瓦片前检查路径读取完成后核对所属场景旧场景请求不再通知新节点。相同瓦片的重复请求合并到既有工作中。import{Scene,Camera}fromkit.ArkGraphics3D;import{spatialRender}fromkit.SpatialReconKit;import{fileIo}fromkit.CoreFileKit;exportclassTiledSceneController{privatescene:Scene|undefinedundefined;privatecamera:Camera|undefinedundefined;privatenode:spatialRender.TiledGSNode|undefinedundefined;privategeneration:number0;privatepending:SetstringnewSetstring();privatebasePath:string;asyncload(manifestPath:string,onStatus:(message:string)void):PromiseScene|undefined{this.dispose();constgenerationthis.generation;if(!manifestPath.startsWith(/data/)||!manifestPath.endsWith(.json)||manifestPath.includes(..)||manifestPath.includes(%)||manifestPath.includes(\\)){thrownewError(请输入应用沙箱内完整的 JSON 清单路径);}constreadableawaitfileIo.open(manifestPath,fileIo.OpenMode.READ_ONLY);awaitfileIo.close(readable);if(generation!this.generation){returnundefined;}this.basePathmanifestPath.substring(0,manifestPath.lastIndexOf(/)1);constcontextScene.getDefaultRenderContext();if(!context){thrownewError(当前设备无法创建 3D 渲染上下文);}context.loadPlugin(spatialRender.GSPlugin.PLUGIN_ID);constsceneawaitScene.load();try{constcameraawaitscene.getResourceFactory().createCamera({name:TiledCamera});camera.position{x:0,y:0,z:4};camera.nearPlane0.01;camera.farPlane1000;camera.fovMath.PI/3;camera.enabledtrue;constnodeawaitspatialRender.GSPlugin.loadTiledGSNode(scene,{uri:file://${manifestPath}},scene.root);if(generation!this.generation){scene.destroy();returnundefined;}node.setCamera(camera);this.scenescene;this.cameracamera;this.nodenode;node.setTileRequestCallback((tiles:spatialRender.GSTile[]){if(generation!this.generation){return;}tiles.forEach((tile:spatialRender.GSTile){this.provideTile(tile,node,generation,onStatus);});});returnscene;}catch(error){if(this.scenescene){this.dispose();}else{scene.destroy();}throwerrorasError;}}privateasyncprovideTile(tile:spatialRender.GSTile,node:spatialRender.TiledGSNode,generation:number,onStatus:(message:string)void):Promisevoid{if(this.pending.has(tile.uri)){return;}constpendingthis.pending;pending.add(tile.uri);try{constlocaltile.uri.startsWith(file://)?tile.uri.substring(7):tile.uri;if(!local.startsWith(this.basePath)||local.includes(..)||local.includes(%)||local.includes(\\)||!local.endsWith(.sog)){thrownewError(分块必须是清单目录内的本地 .sog 文件);}constinputawaitfileIo.open(local,fileIo.OpenMode.READ_ONLY);try{conststatawaitfileIo.stat(input.fd);if(stat.size0||stat.size128*1024*1024){thrownewError(分块大小须在 1 字节至 128 MiB 内);}}finally{awaitfileIo.close(input);}if(generation!this.generation){return;}node.notifyTileReady(tile);onStatus(已通知一个分块可读画面由渲染器加载);}catch(error){if(generationthis.generation){onStatus(分块未就绪${(errorasError).message});}}finally{pending.delete(tile.uri);}}setView(yaw:number,distance:number):void{if(!this.camera||!Number.isFinite(yaw)||!Number.isFinite(distance)){return;}constradiusMath.max(0.2,Math.min(30,distance));this.camera.position{x:radius*Math.sin(yaw),y:0,z:radius*Math.cos(yaw)};this.camera.rotation{x:0,y:Math.sin(yaw/2),z:0,w:Math.cos(yaw/2)};}dispose():void{this.generation;constnodethis.node;constscenethis.scene;this.nodeundefined;this.sceneundefined;this.cameraundefined;this.pendingnewSetstring();try{if(node){node.setTileRequestCallback(null);}}finally{if(scene){scene.destroy();}}}}操作或边界应检查的结果请求重复复用正在进行的读取瓦片不存在报告缺块不伪造 ready场景已销毁丢弃迟到结果
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。