本文轉載請注明出處 —— polobymulberry-博客園
0x00 - 前言
mulberryAR是我業余時間弄的一個AR引擎,目前主要支持單目視覺SLAM+3D渲染,並且支持iOS端,但是該引擎也能很方便地移植到Android端。slam模塊使用的是ORB-SLAM2,3d渲染模塊使用的是VVSION渲染引擎。該引擎目前實現的功能為簡單的3D模型擺放,用戶可以對3D模型進行平移、旋轉和縮放。
先放兩張mulberryAR的效果圖。
0x01 - 單目視覺SLAM模塊
單目視覺SLAM模塊采用的是ORB-SLAM2。ORB-SLAM2是目前比較優秀的視覺SLAM系統,其輸入為圖像視頻流,通過SLAM計算出每幀圖像對應的相機位姿以及一些特征點對應的3D位置。不過mulberryAR目前只用到了每幀對應的相機位姿。
目前mulberryAR對ORB-SLAM2沒有做過多的修改,但是為了集成進mulberryAR中,需要對ORB-SLAM2的接口做出一些修改以適應iOS系統的移動設備。這一部分主要參考兩份資料:
- ORB_SLAM_iOS ORB-SLAM在iOS上的移植,作者去除了ORB-SLAM對ROS的依賴,並使用了iOS的Metal和Scene Kit進行渲染。相比ORB-SLAM2,還需要依賴boost庫。
- ORB-SLAM2注釋版 作者對ORB-SLAM2進行了詳細地注釋,添加了BoW(Bag of Word)的二進制文件加載方式。
修改1:ORB-SLAM2里面使用了BoW(Bag of Word)進行特征匹配。其中的BoW是通過加載ORB-SLAM2原始文件中的ORBvoc.txt獲取的,不過移動端直接加載ORBVoc.txt文本文件來構建BoW非常耗時,在iPhone5s上要幾分鍾時間。使用ORB-SLAM2注釋版中Vocabulary/bin_vocabulary.cpp可以將ORBVoc.txt轉換為ORBVoc.bin。然后使用該版本DBoW2和g2o替換ORB-SLAM2中的DBoW2和g2o,ORB-SLAM2注釋版里面的/Thirdparty/DBoW2/DBoW2/TemplatedVocabulary.h添加了loadFromBinaryFile函數,可以直接加載ORBVoc.bin,在iPhone5s上加載的時間也降到小於3秒鍾。
修改2:ORB-SLAM2源碼中的示例獲取圖像視頻流的方式是通過解析預先處理好的視頻文件,而mulberryAR需要通過iPhone設備實時捕捉圖像視頻。這里需要使用iOS的視頻捕捉模塊。一開始捕捉方式參考了我之前的博客【AR實驗室】OpenGL ES繪制相機(OpenGL ES 1.0版本)中的0x02 - AVCaptureSession獲取拍攝內容小節。獲取到了圖像就可以調用ORB-SLAM2中的System::TrackMonocular函數求解位姿。注意TrackMonocular很耗時,所以我們構建一個DISPATCH_QUEUE_SERIAL類型的線程,並將TrackMonocular拋給它。另外在主線程dispatch_get_main_queue()中利用TrackMonocular得到的相機位姿進行繪制。
修改3:圖形學中繪制有一個很重要的矩陣:模型視圖矩陣ModelView,就是將3D模型從模型局部坐標系轉化到相機坐標系的一個轉化矩陣。注意TrackMonocular函數返回的Tcw需要一定的轉化才能作為模型視圖矩陣,這一步完全參考了ORB_SLAM_iOS中的處理方式,因為我也不是很清楚為何要如此處理,尤其是兩處取負號的部分,所以此處將代碼列出供大家參考。
// poseR = mCurrentFrame.mTcw.rowRange(0,3).colRange(0,3); // 當前幀變化矩陣的旋轉部分 cv::Mat R = _slam->getCurrentPose_R(); // poseT = mCurrentFrame.mTcw.rowRange(0,3).col(3); // 當前幀變化矩陣的平移部分 cv::Mat T = _slam->getCurrentPose_T(); // 將旋轉矩陣轉化為四元數,注意qy和qz的取了負號。 float qx,qy,qz,qw; qw = sqrt(1.0 + R.at<float>(0,0) + R.at<float>(1,1) + R.at<float>(2,2)) / 2.0; qx = (R.at<float>(2,1) - R.at<float>(1,2)) / (4*qw); qy = -(R.at<float>(0,2) - R.at<float>(2,0)) / (4*qw); qz = -(R.at<float>(1,0) - R.at<float>(0,1)) / (4*qw); // 將四元數轉化為旋轉矩陣,即r1、r2、r3。並且將平移矩陣填充到r4。 // 注意其中T.at<float>(1)和T.at<float>(2)取了負號。 vec4f r1(1 - 2*qy*qy - 2*qz*qz, 2*qx*qy + 2*qz*qw, 2*qx*qz - 2*qy*qw, 0); vec4f r2(2*qx*qy - 2*qz*qw, 1 - 2*qx*qx - 2*qz*qz, 2*qy*qz + 2*qx*qw, 0); vec4f r3(2*qx*qz + 2*qy*qw, 2*qy*qz - 2*qx*qw, 1 - 2*qx*qx - 2*qy*qy, 0); vec4f r4(T.at<float>(0), -T.at<float>(1), -T.at<float>(2), 1);
0x02 – 3D渲染引擎模塊
3D渲染引擎模塊使用的是VVSION渲染引擎。選擇這款渲染引擎也是嘗試過很多其他渲染方式才決定的,主要代表為cocos2d-x、vvsion和原生opengl es。下面對着三種方式的優缺點進行對比。
| cocos2d-x | vvsion | 原生opengl es | |
| 優點 | 1.支持的渲染組件很豐富,基本不需要后期添加新的功能 | 1.相對於cocos2d-x整體輕巧,易於集成和二次修改。 2.可以直接傳遞模型視圖矩陣,不要進行轉化。 |
1.完全可以根據自己的需求開發出相應的模塊,不會困於已有的功能模塊。 |
| 缺點 | 1.體積較大 2.我們此處獲取到的為原生的模型視圖矩陣,怎樣直接把模型視圖矩陣傳遞給cocos2d-x的繪制模塊就成為了一個難題。我嘗試了很多方式都沒有成功,可能因為本身對cocos2d-x不是特別熟悉,所以放棄。 |
1.沒有cocos2d-x的功能多 | 1.工作量巨大! |
vvsion本身支持一些簡單的渲染功能,比如模型的導入和渲染,使用的是opengl es 2.0。不過還存在幾個缺陷,mulberryAR對此進行了優化。
修改1:它本身提供的模型渲染過於簡單,只是簡單的貼圖,此處mulberryAR在原始shader中添加了diffuse功能,主要是將模型的法向傳入,做光照處理。
// vertex shader attribute vec4 position; attribute vec2 texCoord0; attribute vec4 normal; varying vec2 v_texCoord; varying vec4 v_normal; uniform mat4 matProjViewModel; // ModelView.inverse().transpose() uniform mat4 matNormal; void main() { v_texCoord = texCoord0; v_normal = matNormal * normal; gl_Position = matProjViewModel * position; } // fragment shader precision highp float; uniform sampler2D texture0; varying vec2 v_texCoord; varying vec4 v_normal; void main() { gl_FragColor = texture2D( texture0, v_texCoord); vec3 lightDir = vec3(0.0, 0.0, 1.0); // 假設光照方向 // 求解diffuse float dotRes = dot(normalize(v_normal.xyz), normalize(lightDir)); float diffuse = min(max(dotRes, 0.0), 1.0); gl_FragColor.rgb = vec3(diffuse * gl_FragColor.rgb); }
修改2:獲取到的相機圖像需要進行顯示,此處,mulberryAR使用了貼紋理的方式進行渲染。我們使用了一個camera.obj的平面模型作為相機圖像的展示平面,只需每次將camera.obj的紋理更新為相機圖像即可。此處需要注意一下兩點:
- camera.obj的顯示使用的是正投影,並且注意其深度值設置大一點,避免遮擋住了前面的模型。
- NPOT(No Power of Two)紋理的設置選項,其中Wrap方式要設置為GL_CLAMP_TO_EDGE,Mag/Min Filter方式設置為GL_LINEAR,並且不要產生MinMap。否則紋理會顯示為黑色。
修改3:為了提高模型的真實感,增加了fake shadow的效果,就是在模型底部添加一塊圓形的陰影。就是在模型底部添加了一個fakeshadow.obj的模型,然后貼上透明的圓形陰影紋理。優點是簡單,節省計算資源,並且還不需要考慮真實的光照方向。
0x03 - mulberryAR性能效果分析
視頻效果展示(騰訊視頻鏈接):
mulberryAR Demo:https://v.qq.com/x/page/c03635umclb.html
mulberryAR在iPhone5s上Release版本測試為6FPS。可見其幀率還無法令人滿意,主要是提取ORB特征這一步耗時比較多,后期會再此基礎上做一定優化。下表中ExtractORB表示每幀ORB特征提取的耗時,TrackMonocular為每幀的整個SLAM系統的耗時。
另外,ORB-SLAM2的初始化很快,丟失后也能快速找回。整體來說,算是目前最好的單目視覺SLAM了。



