c++ - 使用现代 OpenGL 渲染纹理

标签 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");

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

最佳答案

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

VAO绑定(bind)

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

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),但不绑定(bind)它。 glVertexAttribPointer() 调用设置的状态存储在当前绑定(bind)的 VAO 中。这个调用实际上应该在核心配置文件上下文中给你一个 GL_INVALID_OPERATION 错误,因为你在制作它时需要有一个 VAO 绑定(bind)。

要解决此问题,请在创建 id 后绑定(bind) VAO:

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

glGenerateMipmap() 位置错误

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

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

glEnableVertexAttribArray() 位置错误

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

与其在绘制调用之前移动它们,不如将它们放置在属性设置代码中,沿着 glVertexAttribPointer() 调用。启用/禁用状态在 VAO 中进行跟踪,因此无需每次都进行这些调用。只需在设置期间设置所有这些状态一次,然后在绘制调用之前简单地绑定(bind) VAO 即可再次设置所有必要的状态。

不必要的 glVertexAttribPointer() 调用

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

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

同样,这个状态在 VAO 中被跟踪,所以一旦你解决了上面列出的问题,在设置期间调用一次就足够了。

关于c++ - 使用现代 OpenGL 渲染纹理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38628084/

相关文章:

c++ - glGetIntegerv 返回垃圾值

C++ gmock - 将空指针传递给 SaveArg 时会发生什么

c++ - 如何将 std::map 初始化为类成员

c++ - test.exe : 0xC0000005: Access violation reading location 0xfffffffc 中 0x00418c38 处的未处理异常

c++ - 用opengl画图SDL Renderer就没用了吗?

opengl - 如何渲染到 OpenGL 立方体贴图纹理的一个面

c++ - C++11 标准是否通过 "n2 is int&"保证 "auto n2 = const_cast<int&>(n);"?

c++ - 如何编写利用 ADL 的概念

java - 使用 libgdx 绘制带纹理的多边形

Linux 使用 OpenGL 3.2+ w/FBOs 进行离屏渲染