纹渊 HarmonyOS 7 工程实战(04):3D 材质贴图:纹样如何进入模型节点
一、问题与目标
3D 预览真正有价值的地方,不是把模型摆出来,而是让用户生成的纹样进入模型材质。否则 3D 容器只是一个静态展示区,和创作流程没有形成闭环。
纹样进入 3D 模型通常会经过几步:生成或绘制得到图片,图片进入可访问的沙箱路径,模型加载完成后找到目标材质节点,再把贴图绑定到节点上。任意一步失败,页面都要能说明原因,而不是只留下一个灰色预览区。
二、实现思路
实现上要先明确材质槽位。不同模型的可贴图区不一样,杯体可能对应 bodyMaterial,头盔可能对应 shellMaterial,动物模型可能只有部分节点适合替换。把槽位写进模型目录,比在页面里按模型名称判断更稳。
异步顺序也要收敛。图片结果和模型场景谁先回来都可能发生,因此状态层需要同时记录 textureReady 和 modelReady。只有两者都满足时才执行绑定;否则页面显示等待、降级或重试入口。
| 贴图链路 | 处理方式 |
|---|---|
| 纹样结果 | 来自 AI 生图或 Canvas 绘制,先统一成图片载荷 |
| 沙箱路径 | 把临时结果转成运行时可访问的资源 |
| 材质槽位 | 根据模型目录找到目标节点,不在页面里硬编码 |
| 预览刷新 | 绑定成功后刷新 3D 视图,失败时回到二维预览 |
三、关键实现
贴图绑定不适合直接散落在点击事件里。下面片段保留模型加载入口,实际项目中可以在服务层继续补上材质槽位查找和错误返回。
async function loadModel(path: ResourceStr): Promise<Scene> { const scene = await Scene.load(path) const factory = scene.getResourceFactory() if (scene.root !== undefined) { scene.root.position = { x: 0, y: 0, z: 0 } } if (factory !== null) { const camera = await factory.createCamera({ name: 'main-camera' }) scene.root?.addChild(camera) } return scene }重点在于让页面只等待一个明确结果:贴图是否已经进入目标材质。模型加载、节点查找、图片解码这些细节都留在服务层,页面拿到成功或失败状态后再刷新预览。
如果模型已经加载但图片还没准备好,3D 区域可以显示默认材质;如果图片已经生成但模型加载失败,页面可以保留平面预览和导出能力。这样用户不会因为 3D 单点失败而失去整个创作结果。
四、失败场景与取舍
材质贴图最容易出现“看起来成功、实际没生效”的问题。比如节点名称变化后仍然返回成功,或者贴图尺寸不合适导致模型表面模糊。服务层应该返回绑定结果和目标槽位,必要时记录失败原因。
另一个取舍是不要把 3D 预览当成唯一结果。对创作类应用来说,AI 图和 Canvas 图本身也是成果。3D 预览用于增强确认感,但失败时必须能降级,不应该阻断保存、导出和分享。
五、验证步骤
- 打开创作入口,确认标题、主要卡片、底部操作区和预览区域显示正常。
- 走一遍主流程,观察加载态、成功态和下一步按钮是否同步变化。
- 人为制造空输入、重复点击或不可用条件,确认页面给出可理解的提示。
- 返回上一页再进入,确认选中态、预览结果和关键状态没有漂移。
- 在较窄窗口和较宽窗口各看一遍,确认文字、卡片和按钮没有重叠。
六、总结
3D 材质贴图的关键是把图片结果、模型资源和材质槽位串成可恢复的异步链路。节点绑定成功就刷新预览,失败则保留二维结果和明确提示。
