说明
原来写过《AR 中的 3D 素材那些事儿》对 3D 素材的加载问题做出过简单说明,主要策略:
- 利用后台线程,或SCNSceneSource类的加载方法,从磁盘加载模型到 CPU 和内存。
- 通过ARSCNView的
prepareObjects
方法,将模型数据传到 GPU 上供显示。
这两条基本策略,可以保证大部分情况下的 3D 模型加载卡顿问题得到解决。但是实际开发中的问题很多,并不能完全解决。
所以今天再来说一说这些特殊的问题。
特殊问题
这些特殊问题主要集中在SCNNode 贴图上。
也就是说在开发中,有时候我们并不是拿到一个 3D 模型(如.dae 文件)直接使用就可以了。有时我们需要自己用代码生成贴图,以达到特殊的效果。比如,一个 AR 触摸屏显示器,需要与用户进行交互。
以前写过《SCNNode到底应该怎么贴图?UIImage,UIImageView,CALayer用哪个?》和《再谈 SCNNode 贴图类型选择问题》,说明了面对复杂效果和动画时的贴图选择问题。根据总结,综合显示效果最好的是 CALayer 和 SpriteKit(SKScene),主要问题也就是它们的加载优化问题。
CALayer 问题
CALayer 问题主要集中在加载时,如果使用了后台线程创建 CALayer,那么显示时容易显示不出来或者延迟很久才显示,大约几秒到几分钟。
// 切换后台线程,加载模型到内存
dispatch_async(dispatch_get_global_queue(0, 0), ^{
SCNScene *scene = [SCNScene sceneNamed:@"mode.dae"];
SCNNode *modelNode = [scene.rootNode childNodeWithName:@"base" recursively:YES];
// 如果在这里创建 CALayer 对象,并给 modelNode 贴图,后面有可能显示不出来
dispatch_async(dispatch_get_main_queue(), ^{
// 这个方法必须在主线程调用。它的作用是:当 GPU 相对空闲时,用 CPU 的后台线程将模型和贴图等,从内存传输到 GPU 上,然后在主线程回调。这样减小了 GPU 带宽压力 和 CPU 主线程的压力
[scnView prepareObjects:@[modelNode] withCompletionHandler:^(BOOL success) {
if (success) {
[self addChildNode:modelNode];
}
}];
});
});
当模型数量很多时,这种显示不出来的问题会更加严重。解决方案就是 CALayer 贴图要在主线程创建并且给 modelNode 贴图也要在主线程完成,包括 CALayer 里面的 UIImage 等。
SpriteKit 问题
SpriteKit 问题有两个:
- 一个是显示性能低下,动画卡顿,内存占用太高,帧数过低甚至崩溃;
- 另一个是加载过程不支持
prepareObjects
,强行使用会在运行时崩溃; 解决方案暂未找到,所以只好在开发中尽可能少用 SpriteKit。这真是很让人奇怪的一件事:苹果官方在 WWDC 推荐过 ARKit 与 SpriteKit 联合使用,但在实际中 SpriteKit 表现出的问题这么多。
其他问题
其他问题也有,主要还是在prepareObjects
方法上面,它的具体作用我们也说过:这个方法必须在主线程调用。它的作用是:当 GPU 相对空闲时,用 CPU 的后台线程将模型和贴图等,从内存传输到 GPU 上,然后在主线程回调。这样减小了 GPU 带宽压力 和 CPU 主线程的压力。
它接收的参数是一个数组,里面可以放常见 SceneKit 中的对象。当你在 AR 场景中有很多模型时,你需要做的是:将这些模型,放到一个数组中,再统一调用prepareObjects
方法。
如果将项目中的 3D 模型分散开来,再多次调用prepareObjects
方法,那么也会造成卡顿。