android opengl 纹理映射如何受顶点顺序影响?

发布于 2024-10-14 15:18:32 字数 5989 浏览 4 评论 0原文

我试图了解在 android 平台上将纹理映射应用于 opengl 三角形的正确方法。 我发现了一个奇怪的行为:纹理映射似乎受到我指定顶点的顺序的影响。

这是具体的问题。在这张图片中,您可以看到纹理、三角形和所需的映射:(抱歉,我必须将 2 个图像合并为一个链接)

组合图像

实际纹理图片是2x2 png。

代码基本上分为两类:渲染器和模型。

  • 渲染器

这是opengl的初始化(这里有很多东西我还没有完全理解,我只是从一个例子中采用了这段代码:

[...]
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);
    }
}

这里是帧渲染函数(实际的绘制在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();
}
  • 模型

这是模型类:

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.position(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");
    }

}

最后是 AssetTextureLoader 类,它从项目中的“assets”文件夹加载纹理文件(清除了无聊的异常处理代码):

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();
    }
}

好的,如您所见。在 OneTriangle 类中,有一个带注释的“正确”初始化,它基本上按 ABC 顺序列出顶点,并按红-绿-黄顺序列出映射,

然后我尝试简单地更改顺序 。 “错误”部分中的顶点:BCA 和绿-黄-红,我希望得到完全相同的输出,因为 A 始终映射到红色,B 映射到绿色,C 映射到黄色。得到完全不同的输出。

在这里你可以看到 2 个输出:(参见之前图像的第二部分)

但是还有更多!如果我以这种方式再次更改模型初始化:

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

..我得到正确的映射!事实上,我可以为 idx 数组选择任意顺序,这并不重要:只要 vt 数组处于特定的 rgy 顺序,我就可以获得正确的图像。

为什么opengl对我这么敌视?

我对这篇很长的文章感到抱歉,但错误(如果有的话)可能隐藏在实现中的任何地方。

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.position(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..

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ゝ杯具 2024-10-21 15:18:32

顶点属性(位置、颜色、纹理坐标等)之间的映射始终为 1:1,因为属性出现在各自的数组中。这意味着 v 中的第 i 个位置始终对应于 vt 中的第 i 个纹理坐标。传递给 glDrawElements 的索引允许您指定由顶点属性数组的每个元素中的第 i 个元素定义的顶点,而不仅仅是位置。

请注意,此行为适用于所有平台上的 OpenGL 和 OpenGL ES,而不仅仅是 Android。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文