最近对AR产生了兴趣。先科普一下什么是AR吧。AR是Augmented Reality(增强现实)的简称,是基于摄像头对现实世界的实时图像采集、分析和理解,然后在此基础上融入虚拟物体(信息),以达到增强体验的目的——世界从此变得更加丰富多彩!典型的AR设备有Google Lens、微软的HoloLens等。因为智能手机都自带摄像头,所以手机上也比较方便做AR应用。
从应用开发的角度去看,平台支持SDK是少不了的:Google有ARCore,苹果有ARKit。我勉强算是一名Android程序员吧,顺手就看了一遍ARCore官方文档,对AR的技术概念有了大致的理解。要在Android设备上跑AR应用,除了系统要求7.0及以上之外,还需要一个叫“Google Play Services for AR”的关键组件——可以到Google Play应用商店在线安装,也可以从GitHub下载.apk后再离线安装。AR的核心功能都由这个Services来提供,而AR应用通过建立session来与它协作,它们之间的关系如下:
咱们程序员还是比较务实;有了一定的理论知识之后,就要动手写代码了。Google提供了很多示例,有效降低了学习门槛,但稍微有趣一点的就用到了Unity3D。为了做出上佳的AR体验,似乎还不得不学习Unity啊(编程语言是C#),尽管我们不必去开发游戏……使用Unity开发还有一个好处,就是可以一次开发、Android+iOS双端通用,提升开发效率!
从工具链的角度看,大致可以分为两类:
Google似乎偏爱Unity一些,介绍Unity的篇幅明显比Unreal多很多;这里有一篇极好的文档,手把手教我们用Unity来开发一个AR小游戏:通过手机摄像头识别平面,然后在平面上驾驶一辆玩具汽车。实操之前,最好对Unity编辑器有个基本了解,强烈推荐B站上有位叫“阿发你好”的UP主,他做了免费的视频教程,讲解很细致,每集视频只有几分钟,看完前面111集即可,非常适合零基础的初学者。
然后,跟着Google的文档做就是了。我记录一下这个过程中我踩过的几个小坑,确保大家能顺利完成这个AR小游戏。
1. 文档的第2章:文中提到的Unity编辑器版本是2020.3 LTS,而我使用了最新的2023.16f1。在新建项目时,已经没有Universal Render Pipeline这个模板,那就换成2D(URP)吧。项目面板的Assets > Settings目录下也没有ForwardRenderer了,对应修改Renderer2D即可。
2. 文档的第4章:正常的话,这一章的所有步骤完成之后,瞄准器就会跟着手机镜头移动;可惜事与愿违。需要修改ReticleBehaviour.cs文件,将hits.Length改成hits.Count。运行程序时,发现Unity的控制台输出这样的出错信息:
NullReferenceException: Object reference not set to an instance of an object
ReticleBehaviour.Update () (at Assets/Starter Package/ReticleBehaviour.cs:44)
原因是DrivingSurfaceManager.RaycastManager引用了一个空对象。需要去修改DrivingSurfaceManager.cs文件,在Start函数中增加下面这行代码:
RaycastManager = GetComponent<ARRaycastManager>();
改完这几处后,运行起来就符合预期了!同时也引出了一个问题,怎样调试Unity项目呢,比如在Visual Studio里单步调试C#代码?我参考了这篇文章,各种场景都谈到了。需要注意的一点是,确保Unity > Edit > Preferences… > External Tools设置中,将External Script Editor改成Microsoft Visual Studio 2019,而不是默认的Open by file extension。
3. 文档的第5章:做完之后其实环境光的渲染并没有生效,在Visual Studio里调试时,LightEstimation.cs文件的FrameReceived函数中设置断点后并不会进来。原因是,没有在Unity编辑器中给Directional Light的Light Estimation脚本组件的AR Camera Manager赋值。把它指向Scene的AR Camera即可。
4. 课后作业:可以在CarBehaviour.cs文件的OnTriggerEnter函数增加实现,当玩具汽车碰撞到礼盒时,播放一个音效,并产生一个粒子效果:)
总之,按照文档给出的步骤一步一步做下来,最终可以把这个AR应用跑起来,代码量也不是很大,比较容易消化。只是实际效果不甚理想,比如平面识别得不是很准确,得再深入研究一下怎么去优化。