AR 開發小記
背景
前段時間合伙人想做一個早教類的 AR 項目,并且扔給了我一個小冊子:
大概的功能是:
-
iPad 掃瞄識別右側的積木
-
屏幕上出現主角按照順序執行操作
-
最后顯示運行結果,闖關是否成功
折騰了一段時間,基本做完了上述功能,可以在這里看到演示的效果。
整個項目從第一次提交代碼,到最后出演示效果,花了兩天的時間,看了下 git commit 累計花了 25 小時(主要是炒股浪費了不少時間,也浪費了不少錢,此處略過不談)。
看上去最終的開發時間不多,不過前面還是做了不少功課,在此簡單的記錄一下。
文章中不會涉及任何工具的具體使用過程,所有的基礎操作在官方文檔里都有詳細的講解。
了解
正式開發之前,先做了一些準備工作,主要是搜索 AR 相關的功能,看看有哪些工具可供使用。一番調研對比和測試之后整理了以下待選方案:
OpenCV :計算機視覺庫,主要進行右側積木的識別。
Vuforia :可以方便的在軟件中實現 AR 功能,主要用于關卡識別和主角模型展示。
Python :主要是圍繞 OpenCV 的一系列科學運算工具,用于快速開發圖像識別功能的原型。
Unity3D :一款 3D 游戲引擎,可以很方便的進行 3D 場景搭建。
OpenGL :如果不用 Unity3D 就需要在 iOS 項目里基于 Vuforia 手寫 OpenGL 實現 AR 功能。
積木識別測試
在正式開發之前,先使用 OpenCV for Python 開發圖像識別原型,看看這個項目好不好搞。
在 Jupyter Notebook 里開發圖像識別項目真是一種非常流暢的體驗:
加載圖像之后,只需要在新的 Cell 里寫圖像識別相關的算法就可以了,圖像數據已經被加載到了內存里,不需要每次運行都執行全部腳本。然后 OpenCV 進行圖像處理, numpy 進行像素運算, matplotlib 展示圖像,一條龍服務,十分方便。
具體的圖像識別算法不再贅述,上一篇《 使用 OpenCV 識別 QRCode 》里基本都已包含。
iOS App
搞定了積木識別之后,接下來就是做 AR 功能,主要有兩個方案可供選擇: iOS App 或者 Unity3D 。
先用 iOS App 試一下效果。
Vuforia 官方的 Sample Code 里已經包含了一個可以完整運行的項目,可以下載體驗一下。然后用 pod 'OpenCV' 就能裝好 OpenCV for C++ ,基本的開發環境就齊全了。折騰了一段時間之后我決定放棄使用 iOS App 的方案,因為實在是太繁瑣了。
首先需要一個 ARSession 對象來管理 AR 的相關事務,里面包括了視頻的渲染(需要等比拉伸并裁切之后渲染在屏幕上)、資源的回收和處理(比如手機退到后臺)、線程切換(繪圖需要在主線程)等等;然后需要一個 ARViewController 對象來負責具體頁面的顯示,里面包括基礎資源的加載、識別模型的激活與切換、設備相關的事件監聽等等;然后需要一個 ARImageTargetGLView 來渲染 AR 場景,包括 buffer 的維護、model 的加載、shader 的渲染等等。
而最讓我崩潰的,是 Swift、Objective-C、C++ 的混寫,項目中大量這樣的代碼:
[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);
最后發現寫了上千行代碼,幾十個文件,才只是搭建好了一個基礎的 AR 開發環境,還不包括自定義的 3D 效果和自己的業務代碼,細思恐極,趕緊放棄。
Unity3D
接下來就是轉投 Unity3D 的懷抱。
遇到的第一個問題是如何集成 OpenCV ,通過買買買這個 OpenCV for Unity 插件解決了,它是基于 OpenCV for Java 的,所以接口和 Python C++ 相比略有些變化,不過基本是相同的。在整合 OpenCV 和 Vuforia 的時候看到了 OpenCV for Unity 開發團隊的 Voforia with OpenCV for Unity Sample 這個示例項目,再結合自帶的 Samples 文件基本就沒問題了。
遇到的第二個問題是 Unity3D 中如何處理項目文件的問題,在 iOS 項目里我的項目目錄是這樣的:
-
General
-
Macro
-
View
-
Extension
-
Section
-
Vendor
在 Unity3D 里,由于項目里不止是代碼文件,還包括 fbx 之類的模型文件、 mat 等材質文件、prefab 等預設文件、unity 等場景文件,如果都放在一起十分混亂,很難檢索。
參照 iOS 的項目目錄,現在 Unity3D 里的項目里是這樣安排的:
-
General:全局通用的文件
-
Scripts:通用的代碼文件
-
Class:自定義的通用類
-
Extension:基礎模塊的擴展,比如 List 的方差運算等等
-
Static:靜態工具類
-
Section:業務相關的文件,一個場景一個文件夾
-
Scene1:具體場景的文件夾,包括所有該場景下的資源
-
Scripts:該場景所需的代碼
-
Prefabs:該場景內的預設對象
-
Resources:該場景下的其他資源
-
Materials:該場景下的材質
基本上單個場景的目錄結構和 General 的目錄結構是一致的, General 像是所有場景的『基類』。
遇到的第三個問題是 OpenCV 繪圖如何處置的問題。我希望能夠將 OpenCV 的一些 Debug 信息繪制在屏幕上,比如找到的 contours 、比如計算出的方差/均值、比如畫面里的積木總數等等,可以很方便的了解圖像識別的情況,找到出現問題的原因。本來是通過注釋掉繪圖代碼的方式進行狀態切換,后來發現實在是太麻煩了,于是在所有的 OpenCV 繪圖方法外面套了一層,放在 CVUtil 里:
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);
}
}
}
}
然后這樣只要切換全局變量 Global.CurrentLogLevel 就能控制 Debug 內容的顯示和隱藏了。
整個 Unity3D 項目里,AR 相關的渲染完全不用操心,只需要把 Vuforia 里的 ImageTarget 這個 Prefab 脫拽到場景中就能實現基礎的 AR 功能。
小結
就簡單的寫這么多啦,沒什么干貨,只是簡單回顧一下自己的開發過程。
AR 開發的技術門檻并不是很高,目前現成的 SDK 很多,可以自行選擇。而如何通過 AR 做出有趣的產品,這才是核心所在。
玩得開心。
來自: http://www.cocoachina.com/vr/20160530/16500.html