zoukankan      html  css  js  c++  java
  • 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文件

    代码下载

  • 相关阅读:
    Poj 2017 Speed Limit(水题)
    Poj 1316 Self Numbers(水题)
    Poj 1017 Packets(贪心策略)
    Poj 1017 Packets(贪心策略)
    Poj 2662,2909 Goldbach's Conjecture (素数判定)
    Poj 2662,2909 Goldbach's Conjecture (素数判定)
    poj 2388 Who's in the Middle(快速排序求中位数)
    poj 2388 Who's in the Middle(快速排序求中位数)
    poj 2000 Gold Coins(水题)
    poj 2000 Gold Coins(水题)
  • 原文地址:https://www.cnblogs.com/qhyuan1992/p/6071963.html
Copyright © 2011-2022 走看看