使用 VBO/IBO 的 OpenGL 纹理三角形

Posted

技术标签:

【中文标题】使用 VBO/IBO 的 OpenGL 纹理三角形【英文标题】:Textured triangles with OpenGL using VBO/IBO 【发布时间】:2016-02-07 19:51:26 【问题描述】:

我正在尝试使用包含屏幕坐标和纹理坐标的 VBO 以及带有索引的 IBO 绘制两个带纹理的三角形来制作正方形。但是我遇到了一个问题。在不使用着色器的情况下,我得到了两个三角形的正确大小和位置,但无论我如何尝试都没有纹理。使用着色器我得到一个纹理,但三角形的大小错误,位置错误,并且没有以任何方式使用纹理坐标。

创建纹理

        glGenTextures(1, &texture);

        glBindTexture(GL_TEXTURE_2D, texture);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

        glTexImage2D(GL_TEXTURE_2D, 0, nOfColors, surface->w, surface->h, 0,
                        texture_format, GL_UNSIGNED_BYTE, surface->pixels);

        glBindTexture(GL_TEXTURE_2D, NULL);

填写 VBO 和 IBO (TileData 是一个结构体,有两个大小为 2 的数组)

    indices.push_back(0);
    indices.push_back(1);
    indices.push_back(2);
    indices.push_back(0);
    indices.push_back(2);
    indices.push_back(3);

    TileData tile;

    tile.texCoord[0] = 0 / imageSizeX;
    tile.texCoord[1] = 0 / imageSizeY;
    tile.worldCoord[0] = 0;
    tile.worldCoord[1] = 0;
    tiles.push_back(tile);

    tile.texCoord[0] = (0 + texWidth) / imageSizeX;
    tile.texCoord[1] = 0 / imageSizeY;
    tile.worldCoord[0] = 0 + texWidth;
    tile.worldCoord[1] = 0;
    tiles.push_back(tile);

    tile.texCoord[0] = (0 + texWidth) / imageSizeX;
    tile.texCoord[1] = (0 + texHeight) / imageSizeY;
    tile.worldCoord[0] = 0 + texWidth;
    tile.worldCoord[1] = 0 + texHeight;
    tiles.push_back(tile);

    tile.texCoord[0] = 0 / imageSizeX;
    tile.texCoord[1] = (0 + texHeight) / imageSizeY;
    tile.worldCoord[0] = 0;
    tile.worldCoord[1] = 0 + texHeight;
    tiles.push_back(tile);

这给了我世界坐标和纹理坐标的代码:

0, 0          0, 0
16, 0         0.25, 0
16, 16        0.25, 1
0, 16         0, 1

生成我的缓冲区

        glGenBuffers(1, &vboID);
        glBindBuffer(GL_ARRAY_BUFFER, vboID);
        glBufferData(GL_ARRAY_BUFFER, tiles.size() * sizeof(TileData), &tiles[0], GL_STATIC_DRAW);

        glGenBuffers(1, &iboID);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID); 
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

使用着色器绘制函数

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(camera.x, camera.x + 320, camera.y + 240, camera.y, -1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(shaderHandler.programID);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    GLint texLocation = glGetUniformLocation(shaderHandler.programID, "tex");

    glUniform1i(texLocation, 0);

    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vboID);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(CGame::TileData), (GLvoid *)0);

    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, vboID);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(CGame::TileData), (GLvoid*)offsetof(CGame::TileData, CGame::TileData::worldCoord));

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID); 
    glDrawElements(GL_TRIANGLES, indices->size(), GL_UNSIGNED_INT, (GLvoid*)0);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glBindTexture(GL_TEXTURE_2D, 0);

    SDL_GL_SwapWindow(window);

结果 With shader

不带着色器的绘图功能 (如果我取消链接着色器程序,上面的代码也可以得到与此相同的结果)

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glClientActiveTexture(GL_TEXTURE0);

    glBindBuffer(GL_ARRAY_BUFFER, vboID);
    glVertexPointer(2, GL_FLOAT, sizeof(CGame::TileData), (GLvoid*)0);
    glTexCoordPointer(2, GL_FLOAT, sizeof(CGame::TileData), (GLvoid*)offsetof(CGame::TileData, CGame::TileData::worldCoord));

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID); 
    glDrawElements(GL_TRIANGLES, indices->size(), GL_UNSIGNED_INT, (GLvoid*)0);

    glDisableClientState(GL_TEXTURE_COORD_ARRAY); 
    glDisableClientState(GL_VERTEX_ARRAY);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindTexture(GL_TEXTURE_2D, 0);

    SDL_GL_SwapWindow(window);

结果 Without shader

顶点着色器

#version 330
layout(location = 0) in vec2 vert;
layout(location = 1) in vec2 vertTexCoord;

out vec2 fragTexCoord;

void main() 

    fragTexCoord = vertTexCoord; 
    gl_Position = vec4(vert, 0, 1);

片段着色器

#version 330

uniform sampler2D tex; 
in vec2 fragTexCoord; 
out vec4 finalColor; 

void main() 

    finalColor = texture2D(tex, fragTexCoord);

此时我不知道如何继续。我在这里或任何地方都找不到任何对我有帮助的东西。我尝试了很多东西,我可能使用了一些已弃用的东西或做了一些不受支持的东西,但我不知道是什么。

【问题讨论】:

【参考方案1】:

我可以看到几个问题:

    1234563

    您的顶点着色器不会根据您的变换状态变换顶点。不推荐使用所有与矩阵相关的 GL 函数,如 glOrthoglTranslateglMatrixMode。您必须使用自己的矩阵函数(或 glm 之类的库)并通过制服(或其他方式)将矩阵提供给着色器。在兼容性配置文件中,您还可以使用 GLSL 内置制服,它允许您访问使用那些已弃用的函数设置的旧内置矩阵,但我强烈建议不要依赖已弃用的功能。无论哪种情况,您的着色器都必须实际应用您的顶点和模型视图转换,这通常归结为矩阵向量产品,如projection * modelView * vec4(pos,1)

    至少可以说,您的属性指针很奇怪。顶点位置使用偏移量 0,texture 坐标使用偏移量offsetof(...::worldCoord)。现在,根据定义成员的顺序,两者都可能指向内存中完全相同的值,这将解释您提到的“并且不以任何方式使用纹理坐标”部分。

【讨论】:

非常感谢!我使用 glEnable 只是在粘贴代码时错过了它。我之前确实尝试过制作模型视图/投影矩阵,但我似乎做错了,或者在测试之前忘记删除 glMatrix 函数,我不记得了。是的,它们很奇怪,出于某种原因,我认为它们是一种跨步论证之类的东西, ::worldCoord 会让它跳过那部分。但是我修复了它,所以现在它使用了纹理的正确部分!只剩下投影了。

以上是关于使用 VBO/IBO 的 OpenGL 纹理三角形的主要内容,如果未能解决你的问题,请参考以下文章

两个不同的对象 OpenGL。 VAO VBO IBO 网格变形问题

如何优化 VBO/IBO 以最大化 GPU 缓存使用率

OpenGL加载模型

OpenGL 使用单个 VBO 渲染多个对象,并使用另一个 VBO 更新对象的矩阵

为啥使用 VBO 和/或 IBO 而不是简单的顶点数据?

OpenGL不使用纹理渲染