OpenGL 纹理未正确映射

Posted

技术标签:

【中文标题】OpenGL 纹理未正确映射【英文标题】:OpenGL texture not mapping correctly 【发布时间】:2014-12-19 15:06:18 【问题描述】:

我正在修复以下项目:objLoader

效果很好,加载到我找到的一个 OBJ 中。

但是,该项目缺少处理将纹理映射到对象上的代码,所以我一直在添加它。

我现在可以加载我的 OBJ(香蕉),实际上我可以看到上面的纹理!

但是,纹理没有正确映射。它似乎是平铺和扭曲的(见下文)

下面是我的代码:

TDModel.java

public void draw(GL10 gl) 
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); // bind texture

    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

    for(int i=0; i<parts.size(); i++)
        TDModelPart t=parts.get(i);
        Material m=t.getMaterial();
        if(m!=null)
            FloatBuffer a=m.getAmbientColorBuffer();
            FloatBuffer d=m.getDiffuseColorBuffer();
            FloatBuffer s=m.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.glEnableClientState(GL10.GL_NORMAL_ARRAY);
        gl.glNormalPointer(GL10.GL_FLOAT, 0, t.getNormalBuffer());
        gl.glDrawElements(GL10.GL_TRIANGLES,t.getFacesCount(),GL10.GL_UNSIGNED_SHORT,t.getFaceBuffer());
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    



public void loadGLTexture(GL10 gl, Context context) 
    // loading texture
    InputStream is = null;
    Bitmap bitmap = null;
    try 
        is = context.getAssets().open("banana.jpg");

        bitmap = BitmapFactory.decodeStream(is);
     catch (IOException e) 
        e.printStackTrace();
    

    gl.glGenTextures(1, textures, 0);
    // ...and bind it to our array
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

    // create nearest filtered texture
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
    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);

    // Use android GLUtils to specify a two-dimensional texture image from our bitmap
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

    // Clean up
    bitmap.recycle();

MyRenderer.java

public void onSurfaceCreated(GL10 gl, EGLConfig config) 

    model.loadGLTexture(gl, getContext()); // load texture

    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.glEnable(GL10.GL_TEXTURE_2D); // Enable texture
    gl.glShadeModel(GL10.GL_SMOOTH);            
    gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
    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); 

任何想法为什么它不能正确包装?

这是纹理

【问题讨论】:

我怀疑网格文件中的纹理坐标有些不正确。你能发布你的 obj 文件的一部分吗? dropbox.com/s/25nyr0td61zwi5y/banana.zip?dl=0 包含 obj、mtl 和 jpg 的 zip 您的纹理坐标可能是 YZ 或 ZX 而不是 XY,请尝试调整它们。 您能进一步解释一下“swizzling”吗?我对 OpenGL 很陌生,我只是一起抨击这个:) 没关系,真正的问题在回答。 【参考方案1】:

真正的问题在于 OBJ 文件中的索引。

每个属性:pos、normal、texcoord 使用单独的索引。

因此,您的面孔会指定应在其 3 个顶点中的每个顶点上分别为每个属性使用哪个索引。

您需要重新排列法线和纹理坐标,以便它们的索引与顶点位置索引匹配。

最简单的解决方案是分配新数组(不使用顶点数 = 3 * 面数进行索引)并手动填充它,从索引数据中查找属性。

然后你可以使用 DrawArrays(而不是 DrawElements)来绘制它

int faces, vertices, normals, texcoords;
int indexes[faces][3][3]; // currently your indices [face][vertex][attrib_index]
float vertex[vertices][3]; // your vertex positions
float normal[normals][3]; // your normals
float texcoord[texcoords][3]; // your texcoords

您需要转换为:

int vertices = 3*faces;
float vertex2[vertices][3]; // your vertex positions
float normal2[vertices][3]; // your normals
float texcoord2[vertices][2]; // your texcoords

以下方式:

int v=0;
for (int f=0; f<faces; f++)


    for (int fv=0; fv<3; fv++,v++)
    
        vertex2[v][0] = vertex[ indexes[f][fv][0] ][0];
        vertex2[v][1] = vertex[ indexes[f][fv][0] ][1];
        vertex2[v][2] = vertex[ indexes[f][fv][0] ][2];

        normal2[v][0] = normal[ indexes[f][fv][1] ][0];
        normal2[v][1] = normal[ indexes[f][fv][1] ][1];
        normal2[v][2] = normal[ indexes[f][fv][1] ][2];

        texcoord2[v][0] = texcoord[ indexes[f][fv][2] ][0];
        texcoord2[v][1] = texcoord[ indexes[f][fv][2] ][1];
    

【讨论】:

那么我该怎么做呢?我是否需要使用特定设置重新导出 OBJ?或者我可以用代码实现这个吗?我不想手动编辑 OBJ 文件... 我将在哪里介绍此代码?并在 DrawArray 中使用它? 您需要在读取 OBJ 文件之后和绘制调用之前进行此转换。在您的 Draw() 函数中,您应该将这些新的(转换后的)数组指定为 glXXXPointer() 调用。然后只需调用 glDrawArrays(GL_TRIANGLES,0,3*faces); - 代替 glDrawElements 我去看看。快速提问 - 有没有更好的方法来读取 obj 文件?这个还不错,但它是一个测试。我的实际 OBJ 会很大,大约需要通读 400,000 行。 您也可以盲目尝试(因为我看不到您的 OBJ 文件解析器):将 tex 坐标的数量从 2 更改为 3:gl.glTexCoordPointer(3, GL10.GL_FLOAT, 0, textureBuffer );

以上是关于OpenGL 纹理未正确映射的主要内容,如果未能解决你的问题,请参考以下文章

Opengl 纹理映射和图像分辨率

OpenGL - 纹理映射不完整

纹理未正确显示 - 可能坐标是错误的 OpenGL、C++

OpenGL的纹理映射问题

OpenGL 纹理映射禁用着色

OpenGL - 使用 glDrawElements 错误地映射纹理