Android增強現實(三)-3D模型展示器

1.Android增強現實(一)-AR的三種方式(展示篇)
2.Android增強現實(二)-支持拖拽控制進度和伸縮的VrGifView
3.Android增強現實(三)-3D模型展示器

前言

前段時間研究了一下增強現實在Android端的實現,目前大體分為兩種,全景立體圖(GIF和全景圖)和3D模型圖。這篇博客主要講一下關于3D模型的展示方式吧。

3D模型

使用方式

1.Add it in your root build.gradle at the end of repositories:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Step 2. Add the dependency

dependencies {
       compile 'com.github.sdfdzx:VRShow:v1.0.2'
}

XML and Java

<com.study.xuan.stlshow.widget.STLView
        android:id="@+id/stl"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

java

//讀取STL文件類
        STLViewBuilder
            .init(STLView stlView)
            .Reader(ISTLReader reader)
            .Byte(byte[] bytes)
            .File(File file)
            .Assets(Context context, String fileName)
            .InputStream(InputStream inputStream)
            .build();
//基礎使用方法
        STLViewBuilder.init(mStl).Assets(this, "bai.stl").build();
        mStl.setTouch(true);//是否可以觸摸
        mStl.setScale(true);//是否可以縮放
        mStl.setRotate(true);//是否可以拖拽
        mStl.setSensor(true);//是否支持陀螺儀
        //stl文件讀取過程中的回調
        mStl.setOnReadCallBack(new OnReadCallBack() {
            @Override
            public void onStart() {}
            @Override
            public void onReading(int cur, int total) {}
            @Override
            public void onFinish() {}
        });

技術分析

對于3D模型的渲染其實對于平常的應用平臺其實涉及的還是比較少的,在游戲平臺應用廣泛,我無意中在京東看到過這樣的功能


京東3D

起先我平常對于這種效果接觸的比較少,還不太清楚怎么實現,后來才了解到關于OpenGL的相關知識才了解到這種實現方式其實是利用OpenGL和GLSurfaceView進行實現。

大概了解了實現可行性,我們來看一下需求:

1.支持渲染3D模型
2.支持單指拖拽
3.支持雙指縮放
4.支持陀螺儀
5.支持讀取時的異步回調

對于這個的實現方式首先要了解這幾個知識點:

1.3D模型,STL文件格式
2.OpenGL相關知識
3.GLSurfaceView的使用

3D模型,STL文件格式

其實對于3D模型的渲染,這里其實要明白的就是我們要做的就是兩步:

1.3D模型數據文件->模型數據(異步讀取文件過程)
2.模型數據->模型展示(渲染展示過程)

這里只涉及STL文件格式的3D模型數據,不同的文件格式,讀取文件的格式也不一樣,我目前就實現了STL文件格式的,那么問題來了,何為STL文件,為什么要了解STL文件?
我們其實沒必要了解那么深入,這里引入百度百科的介紹其實已經夠我們進行了解;

STL是用三角網格來表現3D CAD模型的一種文件格式。

可能這樣我們理解還是比較困難,那么再加一張圖


3D模型

上圖可以看到是一個由STL文件描述的貓,就是由一個個小的三角形構成的,所以說STL描述的就是構成這個3D模型所用的所有的三角形的相關數據。
那么我們就需要了解一下STL文件是怎么描述三角形數據的。
STL文件分為兩種格式,一種是ASCII明碼格式,另一種是二進制格式。
ASCII明碼格式:(以下引自百度百科)

ASCII碼格式的STL文件逐行給出三角面片的幾何信息,每一行以1個或2個關鍵字開頭。
在STL文件中的三角面片的信息單元 facet 是一個帶矢量方向的三角面片,STL三維模型就是由一系列這樣的三角面片構成。
整個STL文件的首行給出了文件路徑及文件名。
在一個 STL文件中,每一個facet由7 行數據組成,
facet normal 是三角面片指向實體外部的法矢量坐標,
outer loop 說明隨后的3行數據分別是三角面片的3個頂點坐標,3頂點沿指向實體外部的法矢量方向逆時針排列。

明碼://字符段意義
solidfilenamestl//文件路徑及文件名
facetnormalxyz//三角面片法向量的3個分量值
outerloop
vertexxyz//三角面片第一個頂點坐標
vertexxyz//三角面片第二個頂點坐標
vertexxyz//三角面片第三個頂點坐標
endloop
endfacet//完成一個三角面片定義
 
......//其他facet
 
endsolidfilenamestl//整個STL文件定義結束

看到上面的介紹,其實不難發現,其實對于ASCII碼格式的STL文件我們需要怎么讀取哪?其實很簡單,有固定的字段表示文件的開始和結束,有固定的字段表示一個三角的開始和結束,固定每個三角形由7行數據構成,固定每一行表示的含義,這所有的都是固定的,一個for循環,按照文件的格式讀取即可。
二進制格式:(以下引自百度百科)

二進制STL文件用固定的字節數來給出三角面片的幾何信息。
文件起始的80個字節是文件頭,用于存貯文件名;
緊接著用 4 個字節的整數來描述模型的三角面片個數,
后面逐個給出每個三角面片的幾何信息。每個三角面片占用固定的50個字節,依次是:
3個4字節浮點數(角面片的法矢量)
3個4字節浮點數(1個頂點的坐標)
3個4字節浮點數(2個頂點的坐標)
3個4字節浮點數(3個頂點的坐標)個
三角面片的最后2個字節用來描述三角面片的屬性信息。
一個完整二進制STL文件的大小為三角形面片數乘以 50再加上84個字節。

UINT8//Header//文件頭
UINT32//Numberoftriangles//三角面片數量
//foreachtriangle(每個三角面片中)
REAL32[3]//Normalvector//法線矢量
REAL32[3]//Vertex1//頂點1坐標
REAL32[3]//Vertex2//頂點2坐標
REAL32[3]//Vertex3//頂點3坐標
UINT16//Attributebytecountend//文件屬性統計

其實讀取方法和上面的相似,只不過上面的是操作文件的行,這里就是操作字節數了,可以看到每個三角面占用的字節數固定,固定的字節數內數據依次占用固定的字節數,所以還是一個for循環,按照字節的格式讀取即可。

OpenGL相關知識

OpenGL的相關知識怎么說哪,很多渲染過程中的相關api我也沒搞懂,這里只說幾個我們實現過程中需要了解的吧(具體網上資料很多,這方面我反正是個小白,就不充胖子了)。
1.glTranslatef(x,y,z)
2.glRotatef(angle,x,y,z)
3.glScalef(x,y,z)
看到字面意思就很好理解吧,平移,旋轉,縮放,有api就好說了,剩下的就是我們將我們觸摸得到的量轉化成這里面的數值就行。

GLSurfaceView的使用

GLSurfaceView是Android一個專門處理3D模型的的View,他的基本用法和平常的View沒什么差異,唯一需要注意的就是需要調用setRenderer()傳入一個Renderer對象。理解起來也比較容易,GLSurfaceView其實就是一個View,也就是一個展示的視圖,而控制展示的也就是Renderer對象了。Renderer其實是一個接口,對應有三個方法需要我們實現,onSurfaceCreated對應視圖創建時調用,onSurfaceChanged對應視圖改變時調用,onDrawFrame對應視圖繪制時調用。

public interface Renderer {
        void onSurfaceCreated(GL10 gl, EGLConfig config);
        void onSurfaceChanged(GL10 gl, int width, int height);
        void onDrawFrame(GL10 gl);
    }

對應配合上面OpenGL的相關知識,其實大概的實現過程已經有個雛形了。

關鍵代碼

1.讀取STL文件(這里以ASCII格式為例)
這里我是定義了一個讀取的接口ISTLReader


public interface ISTLReader {
    public STLModel parserBinStl(byte[] bytes);

    public STLModel parserAsciiStl(byte[] bytes);

    public void setCallBack(OnReadListener listener);
}

可以通過STLViewBuilder.Reader(ISTLReader reader)方法自己實現。
我默認實現的STLReader這里只放上對于ASCII格式文件讀取的偽代碼吧。

public STLModel parserAsciiStl(byte[] bytes) {
        ...
        String stlText = new String(bytes);
        String[] stlLines = stlText.split("\n");
        vertext_size = (stlLines.length - 2) / 7;
        ...
        for (int i = 0; i < stlLines.length; i++) {
            String string = stlLines[i].trim();
            if (string.startsWith("facet normal ")) {
                string = string.replaceFirst("facet normal ", "");
                String[] normalValue = string.split(" ");
                for (int n = 0; n < 3; n++) {
                    ...
                }
            }
            if (string.startsWith("vertex ")) {
                string = string.replaceFirst("vertex ", "");
                String[] vertexValue = string.split(" ");
                ...
            }

            ...
        }
      ...
    }

這里可以看到我是將byte[]轉為了String,接著就通過固定的格式來進行讀取,偽代碼在上面,便于理解讀取過程,可以看到,基本的就是通過對行數,startsWith,split等對字符串處理的函數進行讀取的,讀取規則其實可以仿照上面對于STL文件格式的介紹。
2.自定義Renderer渲染
自定義Renderer其實主要是對于OPenGL函數的調用,由于我對于這塊也不是特別了解,我是在別人的基礎上進行了一定的修改,里面參數的修改影響的就是渲染效果。而對于我們要實現的關于旋轉縮放的函數其實就比較基礎了,這里我還加入了關于縮放范圍的控制。

        gl.glRotatef(angleX, 0, 1, 0);
        gl.glRotatef(angleY, 1, 0, 0);
        gl.glPopMatrix();

        scale_rember = scale_now * scale;
        if (scaleRange) {
            if (scale_rember > SCALE_MAX) {
                scale_rember = SCALE_MAX;
            }
            if (scale_rember < SCALE_MIN) {
                scale_rember = SCALE_MIN;
            }
        }
        gl.glScalef(scale_rember, scale_rember, scale_rember);

3.手勢監聽
其實對于縮放和旋轉的處理和前一篇Android增強現實(二)-支持拖拽控制進度和伸縮的VrGifView的處理大同小異,具體大家可以看前一篇博客,而對于陀螺儀的處理其實也比較簡單,只不過用的比較少所以比較陌生,步驟也不比較固定。

private void initSensor() {
        sensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
        gyroscopeSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
        sensorEventListener = new SensorEventListener() {
            @Override
            public void onSensorChanged(SensorEvent sensorEvent) {
                if (sensorEvent.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
                    if (timestamp != 0) {
                        final float dT = (sensorEvent.timestamp - timestamp) * NS2S;
                        stlRenderer.angleX += sensorEvent.values[0] * dT * 180.0f % 360.0f;
                        stlRenderer.angleY += sensorEvent.values[1] * dT * 180.0f % 360.0f;
                        stlRenderer.requestRedraw();
                        requestRender();
                    }
                    timestamp = sensorEvent.timestamp;
                }
            }

            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {

            }
        };
        sensorManager.registerListener(sensorEventListener, gyroscopeSensor, SensorManager
                .SENSOR_DELAY_GAME);
    }

總結

其實相較于前一篇的對于GIF圖的處理,這里技術上的考慮不是特別多,主要是對于3D文件STL格式的學習,OpenGL基礎知識的學習,還有陀螺儀傳感器使用的學習。

這里對于STL文件的讀取還有Renderer中OpenGL的使用參考學習了以下資料,大家感興趣的可以去查看學習:

1.一個不錯的STL解析器,支持貼紋理,坐標系等
2.Android OpenGL入門系列,一個不錯的系列入門文章
3.Android OpenGL入門系列

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373