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文件

代码下载

posted @ 2016-11-07 23:23  浩荡乾坤  阅读(1854)  评论(0编辑  收藏  举报