本文轉載請註明出處 —— polobymulberry-博客園html
mulberryAR是我業餘時間弄的一個AR引擎,目前主要支持單目視覺SLAM+3D渲染,而且支持iOS端,可是該引擎也能很方便地移植到Android端。slam模塊使用的是ORB-SLAM2,3d渲染模塊使用的是VVSION渲染引擎。該引擎目前實現的功能爲簡單的3D模型擺放,用戶能夠對3D模型進行平移、旋轉和縮放。git
先放兩張mulberryAR的效果圖。github
單目視覺SLAM模塊採用的是ORB-SLAM2。ORB-SLAM2是目前比較優秀的視覺SLAM系統,其輸入爲圖像視頻流,經過SLAM計算出每幀圖像對應的相機位姿以及一些特徵點對應的3D位置。不過mulberryAR目前只用到了每幀對應的相機位姿。函數
目前mulberryAR對ORB-SLAM2沒有作過多的修改,可是爲了集成進mulberryAR中,須要對ORB-SLAM2的接口作出一些修改以適應iOS系統的移動設備。這一部分主要參考兩份資料:性能
修改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中的處理方式,由於我也不是很清楚爲什麼要如此處理,尤爲是兩處取負號的部分,因此此處將代碼列出供你們參考。spa
// 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); // 將四元數轉化爲旋轉矩陣,即r一、r二、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);
3D渲染引擎模塊使用的是VVSION渲染引擎。選擇這款渲染引擎也是嘗試過不少其餘渲染方式才決定的,主要表明爲cocos2d-x、vvsion和原生opengl es。下面對着三種方式的優缺點進行對比。.net
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的紋理更新爲相機圖像便可。此處須要注意一下兩點:
修改3:爲了提升模型的真實感,增長了fake shadow的效果,就是在模型底部添加一塊圓形的陰影。就是在模型底部添加了一個fakeshadow.obj的模型,而後貼上透明的圓形陰影紋理。優勢是簡單,節省計算資源,而且還不須要考慮真實的光照方向。
視頻效果展現(騰訊視頻連接):
mulberryAR Demo:https://v.qq.com/x/page/c03635umclb.html
mulberryAR在iPhone5s上Release版本測試爲6FPS。可見其幀率還沒法使人滿意,主要是提取ORB特徵這一步耗時比較多,後期會再此基礎上作必定優化。下表中ExtractORB表示每幀ORB特徵提取的耗時,TrackMonocular爲每幀的整個SLAM系統的耗時。
另外,ORB-SLAM2的初始化很快,丟失後也能快速找回。總體來講,算是目前最好的單目視覺SLAM了。