OBJ加载器,纹理绑定和纹理缓冲区

时间:2014-05-22 14:44:15

标签: android opengl-es 3d

我目前正在Android上开发.obj文件加载器。我已经完成了基础知识,并使用OpenGl正确绘制了3d网格。不幸的是我在绑定纹理时遇到了问题。让我解释一下更多细节:

.obj文件具有以下结构:

v -0.751804 0.447968 -1.430558
v -0.751802 2.392585 -1.428428
... etc list with all the vertices ...

vt 0.033607 0.718905
vt 0.033607 0.718615
... etc list with all the texture coordinates ...

f 237/1 236/2 253/3 252/4
f 236/2 235/5 254/6 253/3
... etc list with all the faces ...

f 线条用于存储相应顶点和纹理坐标的索引,例如

f vertex_index/texture_coord_index

所以我的程序

  1. 解析顶点并将其存储在Vector<Float>
  2. 解析纹理坐标并将其存储在Vector<Float>
  3. 最后解析面并将每个顶点索引存储在Vector<Short>中,并将每个纹理坐标索引存储在Vector<Short>
  4. 在完成所有这些代码后,我创建了适当的缓冲区:

    public void buildVertexBuffer(){
        ByteBuffer vBuf = ByteBuffer.allocateDirect(vertices.size() * 4);
        vBuf.order(ByteOrder.nativeOrder());
        vertexBuffer = vBuf.asFloatBuffer();
        vertexBuffer.put(toFloatArray(vertices));
        vertexBuffer.position(0);
    }
    

    其中vertices是存储浮点顶点的向量

    public void buildFaceBuffer(){
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(faces.size() * 2);
        byteBuffer.order(ByteOrder.nativeOrder());
        faceBuffer = byteBuffer.asShortBuffer();
        faceBuffer.put(toShortArray(faces));
        faceBuffer.position(0);
    }
    

    其中faces是存储索引和

    的向量
    public void buildTextureBuffer(Vector<Float> textures){
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(texturePointers.size() * 4 * 2);
        byteBuffer.order(ByteOrder.nativeOrder());
        textureBuffer = byteBuffer.asFloatBuffer();
    
        for(int i=0; i<texturePointers.size(); i++){
            float u = textures.get(texturePointers.get(i) * 2);
            float v = textures.get(texturePointers.get(i) * 2 + 1);
    
            textureBuffer.put(u);
            textureBuffer.put(v);
        }
        textureBuffer.position(0);  
    }
    

    其中textures是浮动纹理坐标,texturePointers指向纹理&#39;值。

    绑定发生在这里:

    public int[] loadTexture(GL10 gl, Context context){
        if(textureFile == null)
            return null;
    
        int resId = getResourceId(textureFile, R.drawable.class);
    
        if(resId == -1){
            Log.d("Bako", "Texture not found...");
            return null;
        }
    
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId);
    
        int[] textures = new int[1];
        gl.glGenTextures(1, textures, 0);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
    
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
    
        /*gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);*/
    
        /*int size = bitmap.getRowBytes() * bitmap.getHeight();
        ByteBuffer buffer = ByteBuffer.allocateDirect(size);
        bitmap.copyPixelsToBuffer(buffer);
        gl.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(), 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_INT, buffer);*/
    
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
    
        bitmap.recycle();
    
        return textures;
    }
    

    最后,我网格的draw()方法看起来像这样

    public void draw(GL10 gl){
        if(bindedTextures != null){
            gl.glBindTexture(GL10.GL_TEXTURE_2D, bindedTextures[0]);
            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
            gl.glFrontFace(GL10.GL_CW);
        }
    
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
    
        for(int i=0; i<parts.size(); i++){
            ModelPart modelPart = parts.get(i);
            Material material = modelPart.getMaterial();
    
            if(material != null){
                FloatBuffer a = material.getAmbientColorBuffer();
                FloatBuffer d = material.getDiffuseColorBuffer();
                FloatBuffer s = material.getSpecularColorBuffer();
                gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, a);
                gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, s);
                gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, d);
            }
    
            gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, modelPart.getTextureBuffer()); // returns the texture buffer created with the buildTextureBuffer() method
            gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
            gl.glNormalPointer(GL10.GL_FLOAT, 0, modelPart.getNormalBuffer());
            gl.glDrawElements(GL10.GL_TRIANGLES, modelPart.getFacesSize(), GL10.GL_UNSIGNED_SHORT, modelPart.getFaceBuffer());
            //gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
            //gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
            gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
            gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        }
    }
    

    当我运行这个应用程序时,3d模型被绘制成一个魅力,但纹理以某种方式拉伸。包含纹理的图像具有红色背景,中间具有适当的图像,并且该红色背景被绘制到整个3d模型上。

    我的第一个问题是textureBuffer是否正确构建。我是否必须更改buildTextureBuffer()中的代码?

    第二个; draw()方法是正确的吗?我的问题是否必须与faces缓冲区一起使用?

1 个答案:

答案 0 :(得分:1)

因此,在OpenGL中,顶点是描述模型上特定点的任何信息组合。您正在使用旧的固定管道,因此顶点是一个位置,一些纹理坐标,法线和颜色中的一个或多个。

在OBJ中,vt只是一个位置。映射到顶点的OpenGL概念的东西是每个独特的组合 - 在您的情况下 - 在f之后给出的位置+纹理坐标对。

你需要一个映射,如果f表示56/92那么你可以查找56/92然后发现你认为它是,例如,顶点23并且已经将合适的数组传递给OpenGL,这样第23个插槽中的位置值是OBJ作为v给出的第56个内容,以及作为vt给出的第92个内容。

换句话说,OBJ文件具有额外的间接级别,而OpenGL则没有。

在我看来,你似乎并没有解决这个问题。一种常见的方法是使用v / vt对中的HashMap输出索引,在解析f时按需构建输出数组。