开发者

How is android opengl texture-mapping affected by vertex order?

开发者 https://www.devze.com 2023-02-07 04:40 出处:网络
I\'m trying to understand the correct way to apply a texture mapping to an opengl triangle, on the android platform.

I'm trying to understand the correct way to apply a texture mapping to an opengl triangle, on the android platform. I found a weird behaviour: it seems that the texture mapping get affected by the order in which i specify the vertex.

Here is the concrete problem. In this picture you can see the texture, the triangle and the desired mapping: (i had to combine the 2 images into a single link, sorry)

combined images

The actual texture picture is a 2x2 png.

The code is subdivided basically in 2 classes: the renderer and the model.

  • The renderer

This is the opengl initialization (there are many things in here that i still didn't fully understand, i just adopted this code from an example:

[...]
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
    glDisable(GL_DITHER);
    gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbientBuffer);
    gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuseBuffer);
    gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPositionBuffer);
    gl.glEnable(GL10.GL_LIGHT0);

    gl.glShadeModel(GL10.GL_SMOOTH);
    gl.glClearColor(0.0f, 0.5f, 0.5f, 1f);

    // per rimuovere superfici nascoste
    gl.glClearDepthf(1.0f);
    gl.glEnable(GL10.GL_DEPTH_TEST);
    gl.glDepthFunc(GL10.GL_LEQUAL);

    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);

    // caricamento texture
    glEnable(GL_TEXTURE_2D);
    int[] textures = new int[1];
    gl.glGenTextures(1, textures, 0);

    textureId = textures[0];
    gl.glBindTexture(GL_TEXTURE_2D, textureId);

    gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    for (Drawable m : models) { // the models are kept in a Vector 
        if (m.getTextureLoader() != null)
            m.getTextureLoader().load(gl);
    }
}

And here the frame rendering function (the actual drawing gets done in the m.draw(gl) call):

public void onDrawFrame(GL10 gl)
{
    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
            GL_MODULATE);

    // Clear Screen And Depth Buffer
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    gl.glLoadIdentity();
    gl.glEnable(GL10.GL_LIGHTING);
    gl.glTranslatef(0.0f, -1.2f, -z); // just a little translation in order to see the complete drawing
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textureId);

    gl.glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE);
    gl.glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE);

    for (Drawable m : models) {
        m.draw(gl);
    }

    gl.glLoadIdentity();
}
  • The model

Here is the model class:

public class OneTriangle implements Drawable {

    Context     context;
    FloatBuffer textureMappingBuffer;
    FloatBuffer vertexBuffer;
    ShortBuffer facesIdxBuffer;

    public OneTriangle(Context context) {
        this.context = context;
        float[] v = {
                -2, 2, 0, //A
                2, 2, 0,//B
                -2, -2, 0 };//C
     //correct
    /*  
       short[] idx = {0,1,2};
        float[] vt ={
                0,0,//red
                1,0,//green
                0,1};//yellow
                */

    //wrong
       short[] idx = {1,2,0};
        float[] vt ={
                1,0,//green
                0,1,//yellow
                0,0//red
                };
        {
            ByteBuffer vBuf = ByteBuffer.allocateDirect(v.length * Float.SIZE / 8);
            vBuf.order(ByteOrder.nativeOrder());
            vertexBuffer = vBuf.asFloatBuffer();
            vertexBuffer.put(v);
            vertexBuffer.position(0);
        }

        {
            ByteBuffer fBuf = ByteBuffer.allocateDirect(idx.length * Short.SIZE / 8);
            fBuf.order(ByteOrder.nativeOrder());
            facesIdxBuffer = fBuf.asShortBuffer();
            facesIdxBuffer.put(idx);
            facesIdxBuffer.position(0);
        }
        {
            ByteBuffer vBuf = ByteBuffer.allocateDirect(vt.length * Float.SIZE / 8);
            vBuf.order(ByteOrder.nativeOrder());
            textureMappingBuffer = vBuf.asFloatBuffer();
            textureMappingBuffer.put(vt);
            textureMappingBuffer.p开发者_如何学编程osition(0);
        }
    }

    public void draw(GL10 gl)
    {
        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);

        glVertexPointer(3, GL_FLOAT, 0, vertexBuffer);
        glTexCoordPointer(2, GL_FLOAT, 0, textureMappingBuffer);
        glDrawElements(GL_TRIANGLES, facesIdxBuffer.capacity(), GL_UNSIGNED_SHORT, facesIdxBuffer);

        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    }

    public TextureLoader getTextureLoader()
    {
        return new AssetTextureLoader(context, "tetraedro.png");
    }

}

and finally here is the AssetTextureLoader class, which load the texture file from the "assets" folder in the project (cleaned of boring exception handling code):

public class AssetTextureLoader implements TextureLoader {
    private Context context;
    private String  filename;

    public AssetTextureLoader(Context context, String filename) {
        this.context = context;
        this.filename = filename;
    }

    public void load(GL10 gl)
    {
        InputStream is;
        is = context.getResources().getAssets().open(filename);
        Bitmap bitmap = BitmapFactory.decodeStream(is);
        is.close();
        Log.d("texture loader", "internal format:" + GLUtils.getInternalFormat(bitmap)+", type: "+GLUtils.getType(bitmap));
        GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
    }
}

Ok. As you can see in the OneTriangle class there is a commented "correct" initialization which basically lists the vertices in the A-B-C order and the mappings in the red-green-yellow order. That code produce the correct texture mapping.

Then i tried to simply change the order of the vertices in the "wrong" section: B-C-A and green-yellow-red. I would expect to get the exact same output, because A is always mapped to red, B is mapped to green and C is mapped to yellow. But instead i get a completely different output.

Here you can see the 2 outputs: (see the second part of the image before)

But there is more! If i change again the model initialization in this way:

     //should be wrong but is correct
       short[] idx = {1,0,2};
        float[] vt ={
                0,0,//red
                1,0,//green
                0,1};//yellow

.. i get the correct mapping! In fact, i can choose any arbitrary order for the idx array and it doesn't matter: as long as the vt array is in that specific r-g-y order i get the correct image.

Why is opengl so hostile to me?

I'm sorry for the very long post, but the mistake (if any) may hide anywhere in the implementation..


The mapping between vertex attributes (position, color, texture coordinates, etc.) is always 1:1 as the attributes appear in their respective arrays. This means that the ith position in v always corresponds to the ith texture coordinate in vt. The indices that are passed to glDrawElements are there to allow you to specify a vertex defined by the ith element from each of the vertex attribute arrays, not just the position.

Note that this behavior applies to OpenGL and OpenGL ES on all platforms, not just Android.

0

精彩评论

暂无评论...
验证码 换一张
取 消