【问题标题】:Wrong texture rendering in OpenGL 3.0 ESOpenGL 3.0 ES 中的错误纹理渲染
【发布时间】:2018-01-13 02:38:39
【问题描述】:

我正在尝试在 Android 中使用 C++ 渲染一个带有纹理的简单正方形,但在使用纹理时遇到了一些问题。

这是准备 OpenGL ES 的代码

// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
        // positions          // colors           // texture coords
        0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // top right
        0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // bottom right
        -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // bottom left
        -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // top left
};
unsigned int indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
};

glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);

glBindVertexArray(VAO);

glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// texture coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);

这里是顶点着色器:

attribute vec4 vertexPosition;
attribute vec2 vertexTexCoord;

varying vec2 texCoord;

void main()
{
   gl_Position = vec4(vertexPosition.x, vertexPosition.y, vertexPosition.z, 1.0);
   texCoord = vertexTexCoord;
}

片段着色器:

precision mediump float;
uniform sampler2D texSampler2D;
varying vec2 texCoord;
void main()
{
    gl_FragColor = texture2D(texSampler2D, texCoord);
}

但是当我在 Android 上运行这段代码时,纹理没有正确放置。

原来的纹理是这样的:

我做错了什么?

【问题讨论】:

  • 两件事是显而易见的。它不是一个正方形,而且 uv 坐标看起来很乱。您确定顶点声明不会将颜色值与纹理坐标混淆吗?否则显示带有 uv 贴图颜色的四边形可能会有所帮助。
  • 如果你的顶点布局是vec3、vec3、vec2,为什么在顶点着色器中你有vec4、vec2和vec2?
  • 你确定纹理坐标是属性2吗?

标签: android c++ opengl-es


【解决方案1】:

在您的着色器代码中,您没有任何颜色属性。你只有顶点位置和纹理坐标:

attribute vec4 vertexPosition;
attribute vec2 vertexTexCoord;

但是在 c++ 代码中,您定义并启用索引为 0、1 和 2 的通用顶点属性数组。尤其是纹理坐标与属性索引 2 的关联可能不起作用。

使用glGetAttribLocation获取某个属性的属性索引(glLinkProgram之后):

GLuint prog .....;   

GLint vert_loc = glGetAttribLocation(prog , "vertexPosition");
GLint texc_loc = glGetAttribLocation(prog , "texCoord");

然后像这样改变你的代码:

glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);

glBindVertexArray(VAO);

glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

// position attribute
glVertexAttribPointer(vert_loc, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(vert_loc);

// texture coord attribute
glVertexAttribPointer(texc_loc, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(texc_loc);


如果您使用具有顶点坐标、颜色属性和纹理坐标的顶点着色器,您的代码可能会起作用:

attribute vec3 aPos;
attribute vec3 aColor;
attribute vec2 aTexCoord;

varying vec3 ourColor;
varying vec2 texCoord;

void main()
{
   gl_Position = vec4(aPos.xyz, 1.0);
   ourColor = aColor;
   texCoord = aTexCoord;
}

但是,不能保证aPos、aColor 和aTexCoord 的属性索引是连续的 0、1、2。这取决于硬件和 OpenGL 驱动程序。因此,您应该使用glGetAttribLocation 来获取属性索引。
此外,您必须使用着色器代码中的所有属性。未使用的属性变为无效。这意味着它不会被添加到资源中,也不会获得属性索引。


见OpenGL ES 2 Specifications - 2.10.4 Shader Variables - p. 32:

如果编译器和链接器确定在执行着色器时可以访问该属性,则认为通用属性变量是活动的。在顶点着色器中声明但从未使用过的属性变量不被认为是活动的。在编译器和链接器无法做出决定性决定的情况下,一个属性将被认为是活动的。

.....

要确定程序使用的活动顶点属性集,并确定它们的类型,请使用以下命令:

void GetActiveAttrib( uint program, uint index, sizei bufSize, sizei *length, int *size, enum *type, char *name );

.....

程序对象链接成功后,可以查询属性变量名与索引的绑定。命令

int GetAttribLocation( uint program, const char *name );

【讨论】:

    【解决方案2】:

    问题所在的着色器。我已将着色器更改如下:

    attribute vec3 aPos;
    attribute vec3 aColor;
    attribute vec2 aTexCoord;
    
    varying vec3 ourColor;
    varying vec2 texCoord;
    
    void main()
    {
       gl_Position = vec4(aPos.xyz, 1.0);
       ourColor = aColor;
       texCoord = aTexCoord;
    }
    

    和片段:

    precision mediump float;
    
    varying vec3 ourColor;
    varying vec2 texCoord;
    
    uniform sampler2D texSampler2D;
    
    void main()
    {
        gl_FragColor = texture2D(texSampler2D, texCoord);
    }
    

    但有两件事我不明白:

    1. 如果我从矢量着色器中删除行 ourColor = aColor; 我也会遇到同样的问题,为什么?
    2. 纹理现在颠倒了,为什么?

    【讨论】:

    • 纹理是颠倒的,因为我在使用stb_image.h加载图像之前忘记调用stbi_set_flip_vertically_on_load(true);
    • 如果您从着色器中删除ourColor = aColor;,则aColor 将永远不会被使用并且不会处于活动状态。这意味着它没有链接,没有添加到资源中并且没有属性索引。此外,不能保证属性索引是连续的 0、1、2。这取决于硬件和 OpenGL 驱动程序。因此,您应该使用glGetAttribLocation 并阅读我的答案。
    • 如果纹理被翻转,你可以交换纹理属性的 Y 坐标 0.0 1.0 或者你可以在顶点着色器中纠正这个问题:texCoord = vec2(vertexTexCoord.x, 1.0-vertexTexCoord.y);
    • 顺便说一句,您不应该在答案中添加更多问题。回答自己的问题是完全可以的。但答案不应包含更多问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多