使用现代OpenGL渲染纹理
Rendering Textures with Modern 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绑定
主要问题是,在设置顶点属性时,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()
位置错误
正如注释中所指出的,此调用属于glTexImage2D()
调用之后的。它根据当前纹理内容生成mipmaps,这意味着您需要首先指定纹理数据。
这个错误不会对您当前的代码造成直接的危害,因为您实际上并没有使用mipmaps。但是,如果您将GL_TEXTURE_MIN_FILTER
值设置为使用mipmapping,这将很重要。
glEnableVertexAttribArray()
位置错误
这需要在glDrawArray()
调用之前发生,因为显然必须为绘图启用属性。
与其只在draw调用之前移动它们,不如将它们与glVertexAttribPointer()
调用一起放置在属性设置代码中。启用/禁用状态在VAO中进行跟踪,因此无需每次都进行这些调用。只需在设置过程中设置一次所有这些状态,然后在draw调用之前简单地绑定VAO,即可再次设置所有必要的状态。
不必要的glVertexAttribPointer()
调用
无害,但render()
方法中的此调用是多余的:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
同样,此状态在VAO中被跟踪,因此一旦您解决了上面列出的问题,在设置期间进行一次此调用就足够了。
最好简化问题以跟踪问题。例如,您可以不加载实际的图像,而是分配一个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
。
- OpenGL大的3D纹理(>2GB)非常慢
- OpenGL将纹理四边形渲染为(0,0)
- OpenGL 4.3 错误地将第 4 个纹理坐标映射到与第 3 个纹理坐标相同的位置
- Qt OpenGL 渲染到纹理性能问题
- 使用 C++在 OpenGL 中对 3D 多边形进行纹理处理
- 无法获取要在 OpenGL ES2 中显示的 RGB 纹理
- 在 OpenGL 中加载纹理C++
- 添加新纹理OpenGL时,模型消失了
- 初始化多个纹理 OpenGL、SDL C++ 时出现问题
- 将多个纹理 OpenGL 绑定到不同的四边形
- 如何加载纹理Opengl
- 读写一个纹理(OpenGL)
- 渲染时加载纹理 (OpenGL)
- 如何绑定纹理openGL的一部分
- 将两个图像堆栈的非电源加载到 3D 纹理 OpenGL C++
- 我的精灵在纹理opengl之间切换
- 使用深度纹理OpenGL/GLSL的深度遮挡(3.3/330)
- 加载多纹理opengl 2.0
- 将位置写入纹理OpenGL/GLSL
- 为纹理OpenGL C++加载多个.bmp文件时出现问题