1. 程式人生 > >OpenGL ES 載入3D模型

OpenGL ES 載入3D模型

前面繪製的矩形、立方體確實確實讓人看煩了,並且實際生活中的物體是非常複雜的,我們不可能像前面哪樣指定頂點來繪製,因此本篇部落格就說明通過OpenGL ES載入一個3D模型。這樣複雜物體的設計工作就可以交給專業的設計師來做了,進行3D建模的工具比如3dmax、maya等。

設計師通過這些軟體構建出來漂亮的3D模型,並且可以通過軟體匯出有關該模型的各種資料資訊檔案,比如頂點座標,法向量,紋理座標等資訊。模型檔案有很多型別,不同的型別的模型檔案其實就是按照不同的檔案格式來儲存有關3D模型的資訊的。

一個開源C++庫Assimp用來載入模型,Assimp可以匯入幾十種不同格式的模型檔案(同樣也可以匯出部分模型格式)可以通過Assimp獲取所有我們需要的模型資料資訊,目前還沒有java版的Assimp庫,不過也可以將這個庫

編譯到android上使用,不過對於簡單的型很,只需要瞭解obj檔案的格式也可以自己動手寫程式碼來載入。

#
# object Teapot01
#

v  15.7604 27.2033 -0.2686
v  14.5306 27.2033 5.9599
v  14.3264 28.0401 5.8730
...
# 529 vertices
vn -0.9667 -0.2558 -0.0000
vn -0.8930 -0.2563 -0.3699
vn -0.8934 0.2560 -0.3691
...
# 530 vertex normals

vt 0.9541 0.9784 0.0000
vt 0.8991 0.9784 0.0000
vt 0.8991 0.9729
0.0000 ... # 664 texture coords g Teapot01 f 1/1/1 2/2/2 3/3/3 f 3/3/3 4/4/4 1/1/1 f 4/4/4 3/3/3 5/5/5 ... # 992 faces

註釋行以符號“#”為開頭

v:幾何體頂點(Geometric vertices)
vt:貼圖座標點(Texture vertices)
vn:頂點法線(Vertex normals)
g:組名稱(Group name),類似於Assimp庫裡面的mesh的概念。
f :面(Face),對於OpenGL ES來說都是三角形。由空格分開的三組資料分別表示三角形的三個點,每組數中的三個值用/分開,表示定點的座標索引、紋理所因和法向量索引。計算行號時,各種字首是獨立計算的,索引為對應字首的型別資料開始的行數,從1開始。

手動解析的obj檔案的大致思路就是逐行讀取,將頂點、法向量和紋理分別儲存到三個陣列中,接下來的主要在解析g標籤下面,解析到每行以f開始時,解析每個頂點的座標、法向量和紋理座標的索引,在剛才的三個陣列中去索引即可,注意下標減一,因為索引的下標是從1開始的,大致程式碼如下。

/**
 * 載入obj檔案至陣列,包括頂點座標、頂點法向量和紋理座標
 * 返回的陣列在使用glVertexAttribPointer函式時注意利用stride引數
 * @param objFile
 * @return
 */
public static float[] loadFromFile(String objFile) {
    ArrayList<Float> vertexList = new ArrayList<Float>();
    ArrayList<Float> textureList = new ArrayList<Float>();
    ArrayList<Float> normalList = new ArrayList<Float>();
    ArrayList<Float> finalList = new ArrayList<Float>();
    try {
        FileReader fr = new FileReader(new File(objFile));
        BufferedReader br = new BufferedReader(fr);
        String line = null;
        while ((line = br.readLine()) != null) {
            String[] temp = line.split("[ ]+");
            if (temp[0].trim().equals("v")) {
                vertexList.add(Float.parseFloat(temp[1]));
                vertexList.add(Float.parseFloat(temp[2]));
                vertexList.add(Float.parseFloat(temp[3]));
              } else if (temp[0].trim().equals("vn")) {
                normalList.add(Float.parseFloat(temp[1]));
                normalList.add(Float.parseFloat(temp[2]));
                normalList.add(Float.parseFloat(temp[3]));
            } else if (temp[0].trim().equals("vt")) {
                textureList.add(Float.parseFloat(temp[1]));
                textureList.add(Float.parseFloat(temp[2]));
            } else if (temp[0].trim().equals("f")) {
                for (int i = 1; i < temp.length; i++) {
                    String[] temp2 = temp[i].split("/");
                    int indexVetex = Integer.parseInt(temp2[0]) - 1;
                    finalList.add(vertexList.get(3 * indexVetex));
                    finalList.add(vertexList.get(3 * indexVetex + 1));
                    finalList.add(vertexList.get(3 * indexVetex + 2));
                    int indexNormal = Integer.parseInt(temp2[1]) - 1;
                    finalList.add(vertexList.get(3 * indexNormal));
                    finalList.add(vertexList.get(3 * indexNormal + 1));
                    finalList.add(vertexList.get(3 * indexNormal + 2));
                    int indexTexture = Integer.parseInt(temp2[2]) - 1;
                    finalList.add(vertexList.get(3 * indexTexture));
                    finalList.add(vertexList.get(3 * indexTexture + 1));
                    finalList.add(vertexList.get(3 * indexTexture + 2));
                }
            }
        }
        float [] result = new float[finalList.size()];
        for (int i = 0; i < finalList.size(); i++) {
            result[i] = finalList.get(i);
        }
        return result;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

不過我還是在github上發現了一個java實現的解析obj檔案的庫obj2opengl,所以不要把時間花費在這些處理字串的細節上,直接用好了,關於它的詳細資訊可以參考README檔案。

使用這個庫的主要

RawOpenGLModel openGLModel = new Obj2OpenJL().convert("file");
OpenGLModelData openGLModelData = openGLModel.normalize().center().getDataForGLDrawElements();

openGLModelData.getVertices();
openGLModelData.getNormals();
openGLModelData.getTextureCoordinates();

效果圖

載入obj檔案

程式碼下載