如何处理OpenGL ES 1.1中的索引缓冲区

3
我想使用OPENGL ES 1.1在Android上呈现从.obj文件加载的模型。我有顶点、顶点法线和面。当我使用GL10.GL_POINTS渲染模型时,模型看起来很好: enter image description here 当我使用GL10.GL_TRIANGLES时,出现了一堆这样的形状: same bunny (facing other direction because I applied constant rotation) 这是我的绘图方法。我尝试过glDrawArrays和glDrawElements,但得到的结果相同。
public void draw(GL10 gl){
    gl.glEnable(GL10.GL_CULL_FACE);
    gl.glFrontFace(GL10.GL_CCW);
    gl.glCullFace(GL10.GL_BACK);

    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glVertexPointer(COORDS_PER_VERTEX, GL10.GL_FLOAT, BYTES_PER_VERTEX, m_vertexBuffer);

    gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
    gl.glNormalPointer(COORDS_PER_VERTEX, BYTES_PER_VERTEX, m_normalBuffer);

    gl.glColor4f(m_color[0], m_color[1], m_color[2], m_color[3]);
    gl.glScalef(0.1f, 0.1f, 0.1f);

    // gl.glDrawArrays(GL10.GL_TRIANGLES, 0, m_numNormals);
    gl.glDrawElements(GL10.GL_TRIANGLES, m_numIndices, GL10.GL_UNSIGNED_SHORT, m_indexBuffer);

    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
    gl.glDisable(GL10.GL_CULL_FACE);
}

以下是来自 .obj 文件的示例行:
v 1.012781 -1.591947 1.752353
...
vn 0.495193 0.342204 0.798517
...
# same indices within pairs
f 14978//14978 14977//14977 22659//22659
...
# different indices
f 34422//34418 34375//34371 34374//34370

关于面线的有趣之处在于,有时候v//vn对的索引相同,有时候不同。而且总共有4个法线比顶点少。我怀疑这就是导致问题的原因。但我应该如何处理呢?当我使用glDrawElements时,我应该传递哪些索引呢?

这是我初始化缓存区的方式:

public void setVertices(float[] coordinates, int size){
    final ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
    bb.order(ByteOrder.nativeOrder());
    m_vertexBuffer = bb.asFloatBuffer();
    m_vertexBuffer.put(coordinates, 0, size);
    m_vertexBuffer.position(0);
    m_numVertices = size / COORDS_PER_VERTEX;
}

public void setNormals(float[] coordinates, int size){
    final ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
    bb.order(ByteOrder.nativeOrder());
    m_normalBuffer = bb.asFloatBuffer();
    m_normalBuffer.put(coordinates, 0, size);
    m_normalBuffer.position(0);
    m_numNormals = size / COORDS_PER_VERTEX;
}

// array contains vertices and normals in the order read from the .obj file
// ie every even index is a vertex, every odd index is a normal
public void setIndices(int[] array, int size){
    m_numIndices = size / 2;
    final ByteBuffer bb = ByteBuffer.allocateDirect(m_numIndices * 4);
    bb.order(ByteOrder.nativeOrder());
    m_indexBuffer = bb.asIntBuffer();
    for(int i = 0; i < size; i+=2)
        m_indexBuffer.put(array[i]);
    m_indexBuffer.position(0);
}

您能给我一些提示如何初始化和使用索引缓冲吗?

编辑:这是模型:bunny.obj


每次我制作一个OBJ加载器时,我总是会忘记面的索引从1开始而不是0。最好的方法是获取一个立方体或四边形的OBJ文件来测试你的加载器。 - jozxyqk
1个回答

2
也许这能帮到你,我用它来导入一个.obj文件,并且它可以使用纹理等所有东西。你需要挑选出与你的项目相关的部分,因为它与其他类紧密相连,例如GLGame实例和GLGraphics用于文件输入和获取GL10实例。祝你好运,希望这有所帮助。
对象加载器
public class ObjLoader {
    public static Vertices3 load(GLGame game, String file) {
        InputStream in = null;
        try {
            in = game.getFileIO().readAsset(file);
            List<String> lines = readLines(in);

            float[] vertices = new float[lines.size() * 3];
            float[] normals = new float[lines.size() * 3];
            float[] uv = new float[lines.size() * 2];

            int numVertices = 0;
            int numNormals = 0;
            int numUV = 0;
            int numFaces = 0;

            int[] facesVerts = new int[lines.size() * 3];
            int[] facesNormals = new int[lines.size() * 3];
            int[] facesUV = new int[lines.size() * 3];
            int vertexIndex = 0;
            int normalIndex = 0;
            int uvIndex = 0;
            int faceIndex = 0;

            for (int i = 0; i < lines.size(); i++) {
                String line = lines.get(i);
                if (line.startsWith("v ")) {
                    String[] tokens = line.split("[ ]+");
                    vertices[vertexIndex] = Float.parseFloat(tokens[1]);
                    vertices[vertexIndex + 1] = Float.parseFloat(tokens[2]);
                    vertices[vertexIndex + 2] = Float.parseFloat(tokens[3]);
                    vertexIndex += 3;
                    numVertices++;
                    continue;
                }

                if (line.startsWith("vn ")) {
                    String[] tokens = line.split("[ ]+");
                    normals[normalIndex] = Float.parseFloat(tokens[1]);
                    normals[normalIndex + 1] = Float.parseFloat(tokens[2]);
                    normals[normalIndex + 2] = Float.parseFloat(tokens[3]);
                    normalIndex += 3;
                    numNormals++;
                    continue;
                }

                if (line.startsWith("vt")) {
                    String[] tokens = line.split("[ ]+");
                    uv[uvIndex] = Float.parseFloat(tokens[1]);
                    uv[uvIndex + 1] = Float.parseFloat(tokens[2]);
                    uvIndex += 2;
                    numUV++;
                    continue;
                }

                if (line.startsWith("f ")) {
                    String[] tokens = line.split("[ ]+");

                    String[] parts = tokens[1].split("/");
                    facesVerts[faceIndex] = getIndex(parts[0], numVertices);
                    if (parts.length > 2)
                        facesNormals[faceIndex] = getIndex(parts[2], numNormals);
                    if (parts.length > 1)
                        facesUV[faceIndex] = getIndex(parts[1], numUV);
                    faceIndex++;

                    parts = tokens[2].split("/");
                    facesVerts[faceIndex] = getIndex(parts[0], numVertices);
                    if (parts.length > 2)
                        facesNormals[faceIndex] = getIndex(parts[2], numNormals);
                    if (parts.length > 1)
                        facesUV[faceIndex] = getIndex(parts[1], numUV);
                    faceIndex++;

                    parts = tokens[3].split("/");
                    facesVerts[faceIndex] = getIndex(parts[0], numVertices);
                    if (parts.length > 2)
                        facesNormals[faceIndex] = getIndex(parts[2], numNormals);
                    if (parts.length > 1)
                        facesUV[faceIndex] = getIndex(parts[1], numUV);
                    faceIndex++;
                    numFaces++;
                    continue;
                }
            }

            float[] verts = new float[(numFaces * 3)
                    * (3 + (numNormals > 0 ? 3 : 0) + (numUV > 0 ? 2 : 0))];

            for (int i = 0, vi = 0; i < numFaces * 3; i++) {
                int vertexIdx = facesVerts[i] * 3;
                verts[vi++] = vertices[vertexIdx];
                verts[vi++] = vertices[vertexIdx + 1];
                verts[vi++] = vertices[vertexIdx + 2];

                if (numUV > 0) {
                    int uvIdx = facesUV[i] * 2;
                    verts[vi++] = uv[uvIdx];
                    verts[vi++] = 1 - uv[uvIdx + 1];
                }

                if (numNormals > 0) {
                    int normalIdx = facesNormals[i] * 3;
                    verts[vi++] = normals[normalIdx];
                    verts[vi++] = normals[normalIdx + 1];
                    verts[vi++] = normals[normalIdx + 2];
                }
            }

            Vertices3 model = new Vertices3(game.getGLGraphics(), numFaces * 3,
                    0, false, numUV > 0, numNormals > 0);
            model.setVertices(verts, 0, verts.length);
            return model;
        } catch (Exception ex) {
            throw new RuntimeException("couldn't load '" + file + "'", ex);
        } finally {
            if (in != null)
                try {
                    in.close();
                } catch (Exception ex) {

                }
        }
    }

    static int getIndex(String index, int size) {
        int idx = Integer.parseInt(index);
        if (idx < 0)
            return size + idx;
        else
            return idx - 1;
    }

    static List<String> readLines(InputStream in) throws IOException {
        List<String> lines = new ArrayList<String>();

        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        String line = null;
        while ((line = reader.readLine()) != null)
            lines.add(line);
        return lines;
    }
}

顶点3

public class Vertices3 {
    final GLGraphics glGraphics;
    final boolean hasColor;
    final boolean hasTexCoords;
    final boolean hasNormals;
    final int vertexSize;
    final IntBuffer vertices;
    final int[] tmpBuffer;
    final ShortBuffer indices;

    public Vertices3(GLGraphics glGraphics, int maxVertices, int maxIndices,
            boolean hasColor, boolean hasTexCoords, boolean hasNormals) {
        this.glGraphics = glGraphics;
        this.hasColor = hasColor;
        this.hasTexCoords = hasTexCoords;
        this.hasNormals = hasNormals;
        this.vertexSize = (3 + (hasColor ? 4 : 0) + (hasTexCoords ? 2 : 0) + (hasNormals ? 3
                : 0)) * 4;
        this.tmpBuffer = new int[maxVertices * vertexSize / 4];

        ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize);
        buffer.order(ByteOrder.nativeOrder());
        vertices = buffer.asIntBuffer();

        if (maxIndices > 0) {
            buffer = ByteBuffer.allocateDirect(maxIndices * Short.SIZE / 8);
            buffer.order(ByteOrder.nativeOrder());
            indices = buffer.asShortBuffer();
        } else {
            indices = null;
        }
    }

    public void setVertices(float[] vertices, int offset, int length) {
        this.vertices.clear();
        int len = offset + length;
        for (int i = offset, j = 0; i < len; i++, j++)
            tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]);
        this.vertices.put(tmpBuffer, 0, length);
        this.vertices.flip();
    }

    public void setIndices(short[] indices, int offset, int length) {
        this.indices.clear();
        this.indices.put(indices, offset, length);
        this.indices.flip();
    }

    public void bind() {
        GL10 gl = glGraphics.getGL();

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        vertices.position(0);
        gl.glVertexPointer(3, GL10.GL_FLOAT, vertexSize, vertices);

        if (hasColor) {
            gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
            vertices.position(3);
            gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices);
        }

        if (hasTexCoords) {
            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
            vertices.position(hasColor ? 7 : 3);
            gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices);
        }

        if (hasNormals) {
            gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
            int offset = 3;
            if (hasColor)
                offset += 4;
            if (hasTexCoords)
                offset += 2;
            vertices.position(offset);
            gl.glNormalPointer(GL10.GL_FLOAT, vertexSize, vertices);
        }
    }

    public void draw(int primitiveType, int offset, int numVertices) {
        GL10 gl = glGraphics.getGL();

        if (indices != null) {
            indices.position(offset);
            gl.glDrawElements(primitiveType, numVertices,
                    GL10.GL_UNSIGNED_SHORT, indices);
        } else {
            gl.glDrawArrays(primitiveType, offset, numVertices);
        }
    }

    public void unbind() {
        GL10 gl = glGraphics.getGL();
        if (hasTexCoords)
            gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

        if (hasColor)
            gl.glDisableClientState(GL10.GL_COLOR_ARRAY);

        if (hasNormals)
            gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
    }

    public int getNumIndices() {
        return indices.limit();
    }

    public int getNumVertices() {
        return vertices.limit() / (vertexSize / 4);
    }
}

谢谢您发布您的代码!它为我澄清了许多事情。现在我明白NormalPointer和VertexPointer都使用相同的缓冲区,但具有不同的索引。然后使用Stride按正确的偏移量推进每个指针。但仍有一个问题:正如我所写的,我的模型比位置少一些法线。(但是,该模型仍然是正确的,因为我将其与使用Desktop Java和lwjgl渲染模型的完整教程一起复制了过来。它在那里被正确加载。)也许您的代码也可以处理这种情况。我会尽快检查并回报。 - hooch
慢慢地,所有的部分都开始拼接在一起了 :) 我看到你在load方法中构建了一个大的顶点数组'verts',其中按索引顺序放置了obj文件中的所有数据。从那时起,您再也没有使用过这些索引。我还缺少最后一块信息:在调用Vertices3.draw()时,您传递了哪些偏移量和numVertices值? - hooch
还有一个与我之前评论相关的问题。如果我理解正确,您在Vertex3类中提供了setIndices方法,但从未调用它(至少不在您提供的load方法中)。因此,在每次调用Vertex3.draw()时indices==null,因此您总是调用glDrawArrays()。您是否像这样调用它:myVertex3.draw(GL10.GL_TRIANGLES, 0, numVertices)? - hooch
我不认为我曾经使用过索引方法,但如果你愿意,它们可以用来搞事情。 - JRowan
很不幸,我有个坏消息。使用你的代码,我得到了相同混乱的结果。也许这个模型需要一些特殊处理,我不知道...它不能完全错误,因为我在另一个程序中看到它工作过(我从这里拿的 https://github.com/OskarVeerhoek/YouTube-tutorials )。我放了一个链接到这个模型,也许你想试试。无论它是否有效,我仍然从你那里学到了很多。谢谢! - hooch
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接