AR 开发小记

ysnan2010 5年前
   <p>背景</p>    <p>前段时间合伙人想做一个早教类的 AR 项目,并且扔给了我一个小册子:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/565ba50ac8626fde89514b40a2edbc22.jpg"></p>    <p>大概的功能是:</p>    <ul>     <li> <p>iPad 扫瞄识别右侧的积木</p> </li>     <li> <p>屏幕上出现主角按照顺序执行操作</p> </li>     <li> <p>最后显示运行结果,闯关是否成功</p> </li>    </ul>    <p>折腾了一段时间,基本做完了上述功能,可以在这里看到演示的效果。</p>    <p>整个项目从第一次提交代码,到最后出演示效果,花了两天的时间,看了下 git commit 累计花了 25 小时(主要是炒股浪费了不少时间,也浪费了不少钱,此处略过不谈)。</p>    <p>看上去最终的开发时间不多,不过前面还是做了不少功课,在此简单的记录一下。</p>    <p>文章中不会涉及任何工具的具体使用过程,所有的基础操作在官方文档里都有详细的讲解。</p>    <p>了解</p>    <p>正式开发之前,先做了一些准备工作,主要是搜索 AR 相关的功能,看看有哪些工具可供使用。一番调研对比和测试之后整理了以下待选方案:</p>    <p><a href="/misc/goto?guid=4958538807379284884" rel="nofollow,noindex">OpenCV</a> :计算机视觉库,主要进行右侧积木的识别。</p>    <p><a href="/misc/goto?guid=4959673939473967138" rel="nofollow,noindex">Vuforia</a> :可以方便的在软件中实现 AR 功能,主要用于关卡识别和主角模型展示。</p>    <p><a href="/misc/goto?guid=4958832508293054196" rel="nofollow,noindex">Python</a> :主要是围绕 OpenCV 的一系列科学运算工具,用于快速开发图像识别功能的原型。</p>    <p><a href="/misc/goto?guid=4958970707757290997" rel="nofollow,noindex">Unity3D</a> :一款 3D 游戏引擎,可以很方便的进行 3D 场景搭建。</p>    <p><a href="/misc/goto?guid=4959673939652656275" rel="nofollow,noindex">OpenGL</a> :如果不用 Unity3D 就需要在 iOS 项目里基于 Vuforia 手写 OpenGL 实现 AR 功能。</p>    <p>积木识别测试</p>    <p>在正式开发之前,先使用 OpenCV for Python 开发图像识别原型,看看这个项目好不好搞。</p>    <p>在 Jupyter Notebook 里开发图像识别项目真是一种非常流畅的体验:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/5520339d587ee82c41b86e9a6e6ad36b.jpg"></p>    <p>加载图像之后,只需要在新的 Cell 里写图像识别相关的算法就可以了,图像数据已经被加载到了内存里,不需要每次运行都执行全部脚本。然后 OpenCV 进行图像处理, numpy 进行像素运算, matplotlib 展示图像,一条龙服务,十分方便。</p>    <p>具体的图像识别算法不再赘述,上一篇《 <a href="http://www.open-open.com/lib/view/open1464566856199.html">使用 OpenCV 识别 QRCode</a> 》里基本都已包含。</p>    <p>iOS App</p>    <p>搞定了积木识别之后,接下来就是做 AR 功能,主要有两个方案可供选择: iOS App 或者 Unity3D 。</p>    <p>先用 iOS App 试一下效果。</p>    <p>Vuforia 官方的 <a href="/misc/goto?guid=4959673939749361542" rel="nofollow,noindex">Sample Code</a> 里已经包含了一个可以完整运行的项目,可以下载体验一下。然后用 pod 'OpenCV' 就能装好 OpenCV for C++ ,基本的开发环境就齐全了。折腾了一段时间之后我决定放弃使用 iOS App 的方案,因为实在是太繁琐了。</p>    <p>首先需要一个 ARSession 对象来管理 AR 的相关事务,里面包括了视频的渲染(需要等比拉伸并裁切之后渲染在屏幕上)、资源的回收和处理(比如手机退到后台)、线程切换(绘图需要在主线程)等等;然后需要一个 ARViewController 对象来负责具体页面的显示,里面包括基础资源的加载、识别模型的激活与切换、设备相关的事件监听等等;然后需要一个 ARImageTargetGLView 来渲染 AR 场景,包括 buffer 的维护、model 的加载、shader 的渲染等等。</p>    <p>而最让我崩溃的,是 Swift、Objective-C、C++ 的混写,项目中大量这样的代码:</p>    <pre>  <code class="language-java">[self setFramebuffer];  glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)buildingModel.vertices);  glEnableVertexAttribArray(textureCoordHandle);  if (offTargetTrackingEnabled) {  glBindTexture(GL_TEXTURE_2D, augmentationTexture[3].textureID);  } else {  glBindTexture(GL_TEXTURE_2D, augmentationTexture[targetIndex].textureID);  }  glUniformMatrix4fv(mvpMatrixHandle, 1, GL_FALSE, (const GLfloat*)&modelViewProjection.data[0]);  glUniform1i(texSampler2DHandle, 0);  glDisableVertexAttribArray(textureCoordHandle);</code></pre>    <p>最后发现写了上千行代码,几十个文件,才只是搭建好了一个基础的 AR 开发环境,还不包括自定义的 3D 效果和自己的业务代码,细思恐极,赶紧放弃。</p>    <p>Unity3D</p>    <p>接下来就是转投 Unity3D 的怀抱。</p>    <p>遇到的第一个问题是如何集成 OpenCV ,通过买买买这个 <a href="/misc/goto?guid=4959673939841618227" rel="nofollow,noindex">OpenCV for Unity</a> 插件解决了,它是基于 OpenCV for Java 的,所以接口和 Python C++ 相比略有些变化,不过基本是相同的。在整合 OpenCV 和 Vuforia 的时候看到了 OpenCV for Unity 开发团队的 <a href="/misc/goto?guid=4959673939932156240" rel="nofollow,noindex">Voforia with OpenCV for Unity Sample</a> 这个示例项目,再结合自带的 <a href="/misc/goto?guid=4959673940018063708" rel="nofollow,noindex">Samples</a> 文件基本就没问题了。</p>    <p>遇到的第二个问题是 Unity3D 中如何处理项目文件的问题,在 iOS 项目里我的项目目录是这样的:</p>    <ul>     <li> <p>General</p> </li>    </ul>    <ul>     <li> <p>Macro</p> </li>     <li> <p>View</p> </li>     <li> <p>Extension</p> </li>    </ul>    <ul>     <li> <p>Section</p> </li>     <li> <p>Vendor</p> </li>    </ul>    <p>在 Unity3D 里,由于项目里不止是代码文件,还包括 fbx 之类的模型文件、 mat 等材质文件、prefab 等预设文件、unity 等场景文件,如果都放在一起十分混乱,很难检索。</p>    <p>参照 iOS 的项目目录,现在 Unity3D 里的项目里是这样安排的:</p>    <ul>     <li> <p>General:全局通用的文件</p> </li>    </ul>    <ul>     <li> <p>Scripts:通用的代码文件</p> </li>    </ul>    <ol>     <li> <p>Class:自定义的通用类</p> </li>     <li> <p>Extension:基础模块的扩展,比如 List 的方差运算等等</p> </li>     <li> <p>Static:静态工具类</p> </li>    </ol>    <ul>     <li> <p>Section:业务相关的文件,一个场景一个文件夹</p> </li>    </ul>    <ul>     <li> <p>Scene1:具体场景的文件夹,包括所有该场景下的资源</p> </li>    </ul>    <ol>     <li> <p>Scripts:该场景所需的代码</p> </li>     <li> <p>Prefabs:该场景内的预设对象</p> </li>     <li> <p>Resources:该场景下的其他资源</p> </li>    </ol>    <ul>     <li> <p>Materials:该场景下的材质</p> </li>    </ul>    <p>基本上单个场景的目录结构和 General 的目录结构是一致的, General 像是所有场景的『基类』。</p>    <p>遇到的第三个问题是 OpenCV 绘图如何处置的问题。我希望能够将 OpenCV 的一些 Debug 信息绘制在屏幕上,比如找到的 contours 、比如计算出的方差/均值、比如画面里的积木总数等等,可以很方便的了解图像识别的情况,找到出现问题的原因。本来是通过注释掉绘图代码的方式进行状态切换,后来发现实在是太麻烦了,于是在所有的 OpenCV 绘图方法外面套了一层,放在 CVUtil 里:</p>    <pre>  <code class="language-java">public class CVUtil {  public class Draw {  public static void Text(Mat mat, string str, double x, double y,  double fontScale = 1, Scalar color = null,  LogLevel level = LogLevel.Debug) {  if (level >= Global.CurrentLogLevel) {  Imgproc.putText (mat, str, new Point (x,y), Core.FONT_HERSHEY_PLAIN, fontScale, color);  }  }  public static void Rectangle(Mat mat, OpenCVForUnity.Rect rect,  Scalar color = null, int thickness = 1,  LogLevel level = LogLevel.Debug) {  if (level >= Global.CurrentLogLevel) {  Imgproc.rectangle (mat, rect.tl (), rect.br (), color, thickness);  }  }  }  }</code></pre>    <p>然后这样只要切换全局变量 Global.CurrentLogLevel 就能控制 Debug 内容的显示和隐藏了。</p>    <p>整个 Unity3D 项目里,AR 相关的渲染完全不用操心,只需要把 Vuforia 里的 ImageTarget 这个 Prefab 脱拽到场景中就能实现基础的 AR 功能。</p>    <p>小结</p>    <p>就简单的写这么多啦,没什么干货,只是简单回顾一下自己的开发过程。</p>    <p>AR 开发的技术门槛并不是很高,目前现成的 SDK 很多,可以自行选择。而如何通过 AR 做出有趣的产品,这才是核心所在。</p>    <p>玩得开心。</p>    <p> </p>    <p>来自: <a href="/misc/goto?guid=4959673940101873053" rel="nofollow">http://www.cocoachina.com/vr/20160530/16500.html</a></p>    <p> </p>