使用现代OpenGL渲染纹理

时间:2016-07-28 05:54:39

标签: c++ opengl

所以我目前正在使用SDL2与OpenGL(glew)制作游戏,并使用SOIL加载图像,我正在使用现代opengl技术与顶点数组对象和着色器等等。我目前正试图只渲染一个纹理到窗口,但我似乎无法做到这一点。我查了很多教程和解决方案,但我似乎无法理解我应该如何正确地做到这一点。我不确定这是问题的着色器,还是我的代码本身。我将在下面发布所有必要的代码,并且需要更多代码我很乐意提供它。欢迎任何答案和解决方案。对于将来的上下文,为了方便起见,我有一个用于存储VAO数据的类。代码:

以下是加载纹理的代码:

 void PXSprite::loadSprite() {

     glGenTextures(1, &textureID);
     glBindTexture(GL_TEXTURE_2D, textureID);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glGenerateMipmap(GL_TEXTURE_2D);

     int imagew, imageh;

     //The path is a class variable, and I haven't received any errors from this function, so I can only assume it's getting the texture file correctly.
     unsigned char* image = SOIL_load_image(path.c_str(), &imagew, &imageh, 0, SOIL_LOAD_RGB);
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imagew, imageh, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
     SOIL_free_image_data(image);
     glBindTexture(GL_TEXTURE_2D, 0);

     //Don't worry about this code. It's just to keep the buffer object data.
     //It works properly when rendering polygons.
     spriteVAO.clear();
     spriteVAO.addColor(PXColor::WHITE());
     spriteVAO.addColor(PXColor::WHITE());
     spriteVAO.addColor(PXColor::WHITE());
     spriteVAO.addColor(PXColor::WHITE());

     spriteVAO.addTextureCoordinate(0, 0);
     spriteVAO.addTextureCoordinate(1, 0);
     spriteVAO.addTextureCoordinate(1, 1);
     spriteVAO.addTextureCoordinate(0, 1);

     glGenVertexArrays(1, &spriteVAO.vaoID);
     glGenBuffers(1, &spriteVAO.posVBOid);
     glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.posVBOid);
     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*12, nullptr, GL_STATIC_DRAW);
     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
     glGenBuffers(1, &spriteVAO.colVBOid);
     glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.colVBOid);
     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, &spriteVAO.colors[0], GL_STATIC_DRAW);
     glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
     glGenBuffers(1, &spriteVAO.texVBOid);
     glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.texVBOid);
     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 8, &spriteVAO.texCoords[0], GL_STATIC_DRAW);
     glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);

     glBindTexture(GL_TEXTURE_2D, 0);
}

这是我渲染纹理的代码:

void PXSprite::render(int x, int y) {
    spriteVAO.clear(PXVertexArrayObject::positionAttributeIndex);
    spriteVAO.addPosition(x, y);
    spriteVAO.addPosition(x+width, y);
    spriteVAO.addPosition(x, y+height);
    spriteVAO.addPosition(x+width, y+height);

    glBindTexture(GL_TEXTURE_2D, textureID);

    glBindVertexArray(spriteVAO.vaoID);
    glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.posVBOid);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat)*12, &spriteVAO.positions[0]);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    glBindTexture(GL_TEXTURE_2D, 0);
}

这是我的顶点着色器:

#version 330 core

in vec3 in_Position;
in vec4 in_Color;
in vec2 in_TexCoord;

out vec4 outPosition;
out vec4 outColor;
out vec2 outTexCoord;

void main() {
gl_Position = vec4(in_Position.x, in_Position.y*-1.0, in_Position.z, 1.0);

outTexCoord = in_TexCoord;
outColor = in_Color;

}

这是我的片段着色器:

#version 330 core

in vec2 outTexCoord;
in vec4 outColor;

out vec4 glFragColor;
out vec4 glTexColor;

uniform sampler2D pxsampler;

void main() {
    vec4 texColor = texture(pxsampler, outTexCoord);
    //This outputs the color of polygons if I don't multiply outColor by texColor, but once I add texColor, no colors show up at all.
    glFragColor = texColor*outColor;

}

最后,这里有一些代码可以参考我在加载着色器时在正确的时间使用的VAO属性指针:

glBindAttribLocation(ProgramID, 0, "in_Position");
glBindAttribLocation(ProgramID, 1, "in_Color");
glBindAttribLocation(ProgramID, 2, "in_TexCoord");

欢迎任何帮助或建议。如果需要任何额外的代码,我会添加它。如果这个问题看起来多余,我很抱歉,但我找不到任何帮助我的方法。如果有人能够向我解释渲染纹理的代码究竟是如何工作的,那就太棒了,因为从我读过的教程中他们通常不解释什么是什么,所以我知道怎么再做,所以我'我不清楚我在做什么。再次,任何帮助表示赞赏。谢谢!

2 个答案:

答案 0 :(得分:1)

最好简化问题以追踪问题。例如,您可以只分配width*heigth*3缓冲区并使用127填充以获取灰色图像(或粉红色以使其更明显),而不是加载实际图像。尝试使用uv坐标对片段进行着色,而不是使用采样器来查看这些值是否设置正确。

我认为你应该在绘制调用之前启用顶点属性数组:

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

此外,在将纹理绑定到纹理单元之前,您应该指定要使用的单位而不是依赖于默认值:

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

并确保将您的采样器绑定到该纹理单元:

glUniform1i(glGetUniformLocation(ProgramID, "pxsampler"), 0);

对于采样器制服,我们直接设置索引而不是GL_TEXTURE*值。因此,GL_TEXTURE0在设置制服时需要索引0,而不是 GL_TEXTURE0

答案 1 :(得分:1)

我在这段代码中发现了一些问题。部分已在其他答案/评论中提及,部分未提及。

VAO绑定

主要问题是您在设置顶点属性时未绑定VAO。请参阅此代码序列:

glGenVertexArrays(1, &spriteVAO.vaoID);
glGenBuffers(1, &spriteVAO.posVBOid);
glBindBuffer(GL_ARRAY_BUFFER, spriteVAO.posVBOid);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*12, nullptr, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

在这里,您创建了一个VAO(或更准确地说,是VAO的id),但不要绑定它。 glVertexAttribPointer()调用建立的状态存储在当前绑定的VAO中。此调用实际上应该在核心配置文件上下文中给出GL_INVALID_OPERATION错误,因为在创建时需要绑定VAO。

要解决此问题,请在创建ID后绑定VAO:

glGenVertexArrays(1, &spriteVAO.vaoID);
glBindVertexArray(spriteVAO.vaoID);
...

glGenerateMipmap()在错误的地方

正如评论中所指出的,此次通话属于 {/ 1}} glTexImage2D()之后。它根据当前纹理内容生成mipmap,这意味着您需要首先指定纹理数据。

此错误不会对您当前的代码造成直接伤害,因为您实际上并未使用mipmap。但是如果你设置GL_TEXTURE_MIN_FILTER值来使用mipmapping,这就很重要了。

glEnableVertexAttribArray()在错误的地方

这需要在<{strong> glDrawArray()调用之前发生,因为显然必须为绘图启用属性。

不是在绘制调用之前移动它们,而是将它们放在属性设置代码中,沿着glVertexAttribPointer()调用更好。在VAO中跟踪启用/禁用状态,因此不需要每次都进行这些调用。只需在设置过程中设置一次所有状态,然后在绘制调用之前简单地绑定VAO将再次设置所有必要状态。

不必要的glVertexAttribPointer()来电

无害,但render()方法中的此调用是多余的:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

同样,在VAO中跟踪此状态,因此一旦您解决了上面列出的问题,在安装过程中进行一次调用就足够了。