OpenGL中VBO顶点位置变化为何会影响sampler2D采样结果?
OpenGL帧缓冲纹理渲染异常问题排查
我花了很长时间定位问题,尝试了所有能想到的方案仍未解决,该问题表现十分反常。我已将问题简化为接近原生的OpenGL代码,方便各位协助排查。
实现逻辑
首先在自定义“图层”(framebuffer+texture)上使用白色纹理绘制一个四边形,之后将该图层作为纹理,两次绘制使用同一个着色器,在窗口上绘制同一个四边形。
问题表现
- 当VBO存储的顶点前两个浮点数(位置属性)范围为-1到+1时,输出正常,窗口全白,对应顶点数据:
std::vector<float> vertices = { 1,1, 1,1, 1,-1, 1,0, -1,-1, 0,0, -1,1, 0,1 };

- 当VBO位置属性范围改为-1到0.5时,我预期屏幕75%区域为全白,但实际一半屏幕为白色、另一半亮度降低,对应顶点数据:
std::vector<float> vertices = { .5,.5, 1,1, .5,-1, 1,0, -1,-1, 0,0, -1,.5, 0,1 };

- 当VBO位置属性范围改为-1到0时,我预期屏幕1/4区域为白色,但实际输出全黑,对应顶点数据:
std::vector<float> vertices = { 0,0, 1,1, 0,-1, 1,0, -1,-1, 0,0, -1,0, 0,1 };

相关代码
帧缓冲、纹理、四边形VAO/VBO创建代码
// create framebuffer to draw in GLuint buffer,fb; glGenTextures(1, &buffer); glBindTexture(GL_TEXTURE_2D, buffer); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); glGenerateMipmap(GL_TEXTURE_2D); glGenFramebuffers(1, &fb); glBindFramebuffer(GL_FRAMEBUFFER, fb); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, buffer, 0); // create white texture GLuint white; unsigned char white_data[4] = {255,255,255,255}; glGenTextures(1, &white); glBindTexture(GL_TEXTURE_2D, white); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, &white_data[0]); glGenerateMipmap(GL_TEXTURE_2D); // create Quad GLuint vao, vertices_buffer,indices_buffer; std::vector<float> vertices = { 1,1, 1,1, 1,-1, 1,0, -1,-1, 0,0, -1,1, 0,1 }; std::vector<int> indices = {0,1,3,1,2,3}; glGenBuffers(1, &vertices_buffer); glGenBuffers(1, &indices_buffer); glGenVertexArrays(1, &vao); glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vertices_buffer); glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(float) , &vertices[0], GL_DYNAMIC_DRAW ); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(int) , &indices[0], GL_DYNAMIC_DRAW ); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (GLvoid *) (0 * sizeof(float))); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (GLvoid *) (2 * sizeof(float))); glEnableVertexAttribArray(1);
渲染循环代码
glBindFramebuffer(GL_FRAMEBUFFER, fb); glClearColor(0,0,0,0); glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, white); shader.use(); glBindVertexArray(vao); glDrawElements ( GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, buffer); shader.use(); glBindVertexArray(vao); glDrawElements ( GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
着色器代码
#version 330 core // 顶点着色器 layout (location = 0) in vec2 position; layout (location = 1) in vec2 texCoord; out vec2 texcoord; void main() { texcoord = texCoord; gl_Position = vec4(position,0,1); } // 片元着色器 in vec2 texcoord; out vec4 color; uniform sampler2D tex; void main() { color = texture(tex,texcoord); }
问题原因
问题出在帧缓冲关联纹理的mipmap处理逻辑上:
- 创建帧缓冲绑定的纹理
buffer后调用了glGenerateMipmap(GL_TEXTURE_2D),此时所有非0级的mip层级都是默认全黑的空值 - 往帧缓冲绘制内容只会更新纹理的0级mip,不会自动同步更新更高层级的mip
- OpenGL纹理默认的缩小过滤模式为
GL_NEAREST_MIPMAP_LINEAR,当绘制的四边形尺寸小于纹理原尺寸时,会自动采样更高层级的mip,也就是初始的全黑内容,就出现了亮度降低甚至全黑的现象。
修复方案
任选一种即可:
- 关闭mipmap使用:创建纹理时不调用
glGenerateMipmap,同时设置纹理过滤模式为非mip模式:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- 每次往帧缓冲绘制完内容后,手动重新生成mipmap:
// 往fb绘制完内容后添加以下代码 glBindTexture(GL_TEXTURE_2D, buffer); glGenerateMipmap(GL_TEXTURE_2D);
内容的提问来源于stack exchange,提问作者ebkgne
相关产品推荐
相关产品推荐

