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

    代码下载

  • 相关阅读:
    常见算法之10---从第一字符串中删除第二个字符串中所有的字符
    常见算法之9---折半查找(二分查找)
    常用算法之8---找到数组中只出现一次的数字
    常见算法之7---判断是否所有小字符串里的字母在大字符串里都有
    常见算法之6---判断集合S之中是否存在两个数之和为指定大小N
    常见算法之5---单例模式[java]
    常见算法之4---正整数二进制表示中的1的个数
    常见算法之3---数组中出现次数超过一半的数字
    UVa 495
    《编程之美》笔记(一)
  • 原文地址:https://www.cnblogs.com/qhyuan1992/p/6071963.html
Copyright © 2011-2022 走看看