You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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处理逻辑上:

  1. 创建帧缓冲绑定的纹理buffer后调用了glGenerateMipmap(GL_TEXTURE_2D),此时所有非0级的mip层级都是默认全黑的空值
  2. 往帧缓冲绘制内容只会更新纹理的0级mip,不会自动同步更新更高层级的mip
  3. OpenGL纹理默认的缩小过滤模式为GL_NEAREST_MIPMAP_LINEAR,当绘制的四边形尺寸小于纹理原尺寸时,会自动采样更高层级的mip,也就是初始的全黑内容,就出现了亮度降低甚至全黑的现象。

修复方案

任选一种即可:

  1. 关闭mipmap使用:创建纹理时不调用glGenerateMipmap,同时设置纹理过滤模式为非mip模式:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  1. 每次往帧缓冲绘制完内容后,手动重新生成mipmap:
// 往fb绘制完内容后添加以下代码
glBindTexture(GL_TEXTURE_2D, buffer);
glGenerateMipmap(GL_TEXTURE_2D);

内容的提问来源于stack exchange,提问作者ebkgne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 09:45:03