使用帧缓冲区将绘图发送到纹理(纹理空白)
Issue drawing to textures with framebuffer (texture blank)
首先我要说我对这些主题还很陌生,最近几天我一直在为一个学校项目给自己上速成班。很抱歉这太长了,但由于我不确定问题出在哪里,我想我会尽量彻底。
我正在尝试使用帧缓冲区来绘制一些 RGBA 纹理,这些纹理稍后会用于其他绘制(我正在使用纹理中的值作为矢量来绘制粒子——纹理存储诸如它们的值位置和速度)。
然而,据我所知,绘制后的纹理是空白的。纹理中的采样值返回 (0, 0, 0, 1)。我后来的绘图过程似乎也证实了这一点,因为它 出现 所有粒子都在原点处重叠绘制(此观察基于我的混合函数和一些硬编码测试颜色值)。
我将 OpenGL 4 与 SDL2 和 glew 一起使用。
我会尝试 post 以有序的方式整理所有相关内容:
帧缓冲区 class 创建帧缓冲区并附加 (3) 个纹理。
它有:
GLuint buffer_id_
是framebuffer的id,初始化为0,unsigned int width, height
是纹理的宽度和高度,以像素为单位,numTextures
是多少个纹理为帧缓冲区创建(同样,在本例中为 3),并为与帧缓冲区关联的所有纹理创建 std::vector<GLuint> textures_
。
bool Framebuffer::init() {
assert( numTextures <= GL_MAX_COLOR_ATTACHMENTS );
// Get the buffer id.
glGenFramebuffers( 1, &buffer_id_ );
glBindFramebuffer( GL_FRAMEBUFFER, buffer_id_ );
// Generate the textures.
for (unsigned int i = 0; i < numTextures; ++i) {
GLuint tex;
glGenTextures( 1, &tex );
glBindTexture(GL_TEXTURE_2D, tex);
// Give empty image to OpenGL.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, 0);
// Use GL_NEAREST, since we don't want any kind of averaging across values:
// we just want one pixel to represent a particle's data.
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// This probably isn't necessary, but we don't want to have UV coords past the image edges.
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
textures_.push_back(tex);
}
glBindTexture(GL_TEXTURE_2D, 0);
// Bind our textures to the framebuffer.
GLenum drawBuffers[GL_MAX_COLOR_ATTACHMENTS];
for (unsigned int i = 0; i < numTextures; ++i) {
GLenum attach = GL_COLOR_ATTACHMENT0 + i;
glFramebufferTexture(GL_FRAMEBUFFER, attach, textures_[i], 0);
drawBuffers[i] = attach;
}
glDrawBuffers( numTextures, drawBuffers );
if ( glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE ) {
std::cerr << "Error: Failed to create framebuffer." << std::endl;
return false;
}
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
return true;
}
粒子系统制作了两个匹配的帧缓冲区,因此一个可以用作着色器程序的输入,另一个用作输出。
bool AmbientParticleSystem::initFramebuffers() {
framebuffers_[0] = new Framebuffer(particle_texture_width_, particle_texture_height_, NUM_TEXTURES_PER_FRAMEBUFFER);
framebuffers_[1] = new Framebuffer(particle_texture_width_, particle_texture_height_, NUM_TEXTURES_PER_FRAMEBUFFER);
return framebuffers_[0]->init() && framebuffers_[1]->init();
}
然后我尝试将初始状态绘制到第一个帧缓冲区:
void AmbientParticleSystem::initParticleDrawing() {
glBindFramebuffer( GL_FRAMEBUFFER, framebuffers_[0]->getBufferID() );
// Store the previous viewport.
GLint prevViewport[4];
glGetIntegerv( GL_VIEWPORT, prevViewport );
glViewport( 0, 0, particle_texture_width_, particle_texture_height_ );
glClear( GL_COLOR_BUFFER_BIT );
glDisable( GL_DEPTH_TEST );
glBlendFunc( GL_ONE, GL_ZERO);
init_shader_->use(); // This sets glUseProgram to the init shader.
GLint attrib = init_variable_ids_[constants::init::Variables::IN_VERTEX_POS]->id;
glEnableVertexAttribArray( attrib );
glBindBuffer( GL_ARRAY_BUFFER, full_size_quad_->getBufferID() );
glVertexAttribPointer( attrib, full_size_quad_->vertexSize,
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 ); // Array buffer offset
glDrawArrays( GL_TRIANGLES, 0, full_size_quad_->numVertices );
// Here I'm just printing some sample values to confirm it is blank/black.
glBindTexture( GL_TEXTURE_2D, framebuffers_[0]->getTexture(0) );
GLfloat *pixels = new GLfloat[particle_texture_width_ * particle_texture_height_ * 4];
glGetTexImage( GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, pixels);
std::cout << "some pixel entries:\n";
std::cout << "pixel0: " << pixels[0] << " " << pixels[1] << " " << pixels[2] << " " << pixels[3] << std::endl;
std::cout << "pixel10: " << pixels[40] << " " << pixels[41] << " " << pixels[42] << " " << pixels[43] << std::endl;
std::cout << "pixel100: " << pixels[400] << " " << pixels[401] << " " << pixels[402] << " " << pixels[403] << std::endl;
std::cout << "pixel10000: " << pixels[40000] << " " << pixels[40001] << " " << pixels[40002] << " " << pixels[40003] << std::endl;
// They all print 0, 0, 0, 1
delete pixels;
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glDisableVertexAttribArray( attrib );
init_shader_->stopUsing();
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
// Return the viewport to its previous state.
glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3] );
}
你可以看到这里也是我尝试获取一些像素值的地方,它们都是 return (0, 0, 0, 1)。
这里使用的full_size_quad_定义为:
// Create quad for textures to draw onto.
static const GLfloat quad_array[] = {
-1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
std::vector<GLfloat> quad(quad_array, quad_array + 18);
full_size_quad_ = new VertexBuffer(3, 6, quad);
VertexBuffer 是我自己的class,我认为不需要在这里显示。它只是 glGens 和 glBinds 顶点数组和缓冲区。
这里使用的着色器有这个顶点着色器:
#version 400 core
in vec3 inVertexPos;
void main() {
gl_Position = vec4(inVertexPos, 1.0);
}
片段着色器:
#version 400 core
layout(location = 0) out vec4 position;
layout(location = 1) out vec4 velocity;
layout(location = 2) out vec4 other;
uniform vec2 uResolution;
//
float rand(vec2 seed) {
return fract(sin(dot(seed.xy,vec2(12.9898,78.233))) * 43758.5453);
}
void main() {
vec2 uv = gl_FragCoord.xy/uResolution.xy;
vec3 pos = vec3(uv.x, uv.y, rand(uv));
vec3 vel = vec3(-2.0, 0.0, 0.0);
vec4 col = vec4(1.0, 0.3, 0.1, 0.5);
position = vec4(pos, 1.0);
velocity = vec4(vel, 1.0);
other = col;
}
uResolution 是以像素为单位的纹理的宽度和高度,设置为:
init_shader_->use();
glUniform2f( init_uniform_ids_[constants::init::Uniforms::U_RESOLUTION]->id, particle_texture_width_, particle_texture_height_ );
出于好奇,我尝试将 position = vec4(pos, 1.0);
更改为不同的值,但我的像素打印仍然给出 0 0 0 1。
我现在已经调试了大约 20-30 个小时,并在这里查找了十几个不同的教程和其他问题,但最近几个小时我觉得我没有任何进展。
这里有什么突出说明为什么纹理看起来是 blank/black,或者任何其他需要解决的问题?我正在使用这个项目来了解着色器,所以我对这一切都很陌生。任何帮助将不胜感激。
通过完全重写代码解决了这个问题。
我仍然不能 100% 确定问题出在哪里,但我想我可能有一些不匹配的 enable/disable 调用。
首先我要说我对这些主题还很陌生,最近几天我一直在为一个学校项目给自己上速成班。很抱歉这太长了,但由于我不确定问题出在哪里,我想我会尽量彻底。
我正在尝试使用帧缓冲区来绘制一些 RGBA 纹理,这些纹理稍后会用于其他绘制(我正在使用纹理中的值作为矢量来绘制粒子——纹理存储诸如它们的值位置和速度)。
然而,据我所知,绘制后的纹理是空白的。纹理中的采样值返回 (0, 0, 0, 1)。我后来的绘图过程似乎也证实了这一点,因为它 出现 所有粒子都在原点处重叠绘制(此观察基于我的混合函数和一些硬编码测试颜色值)。
我将 OpenGL 4 与 SDL2 和 glew 一起使用。
我会尝试 post 以有序的方式整理所有相关内容:
帧缓冲区 class 创建帧缓冲区并附加 (3) 个纹理。 它有:
GLuint buffer_id_
是framebuffer的id,初始化为0,unsigned int width, height
是纹理的宽度和高度,以像素为单位,numTextures
是多少个纹理为帧缓冲区创建(同样,在本例中为 3),并为与帧缓冲区关联的所有纹理创建 std::vector<GLuint> textures_
。
bool Framebuffer::init() {
assert( numTextures <= GL_MAX_COLOR_ATTACHMENTS );
// Get the buffer id.
glGenFramebuffers( 1, &buffer_id_ );
glBindFramebuffer( GL_FRAMEBUFFER, buffer_id_ );
// Generate the textures.
for (unsigned int i = 0; i < numTextures; ++i) {
GLuint tex;
glGenTextures( 1, &tex );
glBindTexture(GL_TEXTURE_2D, tex);
// Give empty image to OpenGL.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, 0);
// Use GL_NEAREST, since we don't want any kind of averaging across values:
// we just want one pixel to represent a particle's data.
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// This probably isn't necessary, but we don't want to have UV coords past the image edges.
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
textures_.push_back(tex);
}
glBindTexture(GL_TEXTURE_2D, 0);
// Bind our textures to the framebuffer.
GLenum drawBuffers[GL_MAX_COLOR_ATTACHMENTS];
for (unsigned int i = 0; i < numTextures; ++i) {
GLenum attach = GL_COLOR_ATTACHMENT0 + i;
glFramebufferTexture(GL_FRAMEBUFFER, attach, textures_[i], 0);
drawBuffers[i] = attach;
}
glDrawBuffers( numTextures, drawBuffers );
if ( glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE ) {
std::cerr << "Error: Failed to create framebuffer." << std::endl;
return false;
}
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
return true;
}
粒子系统制作了两个匹配的帧缓冲区,因此一个可以用作着色器程序的输入,另一个用作输出。
bool AmbientParticleSystem::initFramebuffers() {
framebuffers_[0] = new Framebuffer(particle_texture_width_, particle_texture_height_, NUM_TEXTURES_PER_FRAMEBUFFER);
framebuffers_[1] = new Framebuffer(particle_texture_width_, particle_texture_height_, NUM_TEXTURES_PER_FRAMEBUFFER);
return framebuffers_[0]->init() && framebuffers_[1]->init();
}
然后我尝试将初始状态绘制到第一个帧缓冲区:
void AmbientParticleSystem::initParticleDrawing() {
glBindFramebuffer( GL_FRAMEBUFFER, framebuffers_[0]->getBufferID() );
// Store the previous viewport.
GLint prevViewport[4];
glGetIntegerv( GL_VIEWPORT, prevViewport );
glViewport( 0, 0, particle_texture_width_, particle_texture_height_ );
glClear( GL_COLOR_BUFFER_BIT );
glDisable( GL_DEPTH_TEST );
glBlendFunc( GL_ONE, GL_ZERO);
init_shader_->use(); // This sets glUseProgram to the init shader.
GLint attrib = init_variable_ids_[constants::init::Variables::IN_VERTEX_POS]->id;
glEnableVertexAttribArray( attrib );
glBindBuffer( GL_ARRAY_BUFFER, full_size_quad_->getBufferID() );
glVertexAttribPointer( attrib, full_size_quad_->vertexSize,
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 ); // Array buffer offset
glDrawArrays( GL_TRIANGLES, 0, full_size_quad_->numVertices );
// Here I'm just printing some sample values to confirm it is blank/black.
glBindTexture( GL_TEXTURE_2D, framebuffers_[0]->getTexture(0) );
GLfloat *pixels = new GLfloat[particle_texture_width_ * particle_texture_height_ * 4];
glGetTexImage( GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, pixels);
std::cout << "some pixel entries:\n";
std::cout << "pixel0: " << pixels[0] << " " << pixels[1] << " " << pixels[2] << " " << pixels[3] << std::endl;
std::cout << "pixel10: " << pixels[40] << " " << pixels[41] << " " << pixels[42] << " " << pixels[43] << std::endl;
std::cout << "pixel100: " << pixels[400] << " " << pixels[401] << " " << pixels[402] << " " << pixels[403] << std::endl;
std::cout << "pixel10000: " << pixels[40000] << " " << pixels[40001] << " " << pixels[40002] << " " << pixels[40003] << std::endl;
// They all print 0, 0, 0, 1
delete pixels;
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glDisableVertexAttribArray( attrib );
init_shader_->stopUsing();
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
// Return the viewport to its previous state.
glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3] );
}
你可以看到这里也是我尝试获取一些像素值的地方,它们都是 return (0, 0, 0, 1)。
这里使用的full_size_quad_定义为:
// Create quad for textures to draw onto.
static const GLfloat quad_array[] = {
-1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
std::vector<GLfloat> quad(quad_array, quad_array + 18);
full_size_quad_ = new VertexBuffer(3, 6, quad);
VertexBuffer 是我自己的class,我认为不需要在这里显示。它只是 glGens 和 glBinds 顶点数组和缓冲区。
这里使用的着色器有这个顶点着色器:
#version 400 core
in vec3 inVertexPos;
void main() {
gl_Position = vec4(inVertexPos, 1.0);
}
片段着色器:
#version 400 core
layout(location = 0) out vec4 position;
layout(location = 1) out vec4 velocity;
layout(location = 2) out vec4 other;
uniform vec2 uResolution;
//
float rand(vec2 seed) {
return fract(sin(dot(seed.xy,vec2(12.9898,78.233))) * 43758.5453);
}
void main() {
vec2 uv = gl_FragCoord.xy/uResolution.xy;
vec3 pos = vec3(uv.x, uv.y, rand(uv));
vec3 vel = vec3(-2.0, 0.0, 0.0);
vec4 col = vec4(1.0, 0.3, 0.1, 0.5);
position = vec4(pos, 1.0);
velocity = vec4(vel, 1.0);
other = col;
}
uResolution 是以像素为单位的纹理的宽度和高度,设置为:
init_shader_->use();
glUniform2f( init_uniform_ids_[constants::init::Uniforms::U_RESOLUTION]->id, particle_texture_width_, particle_texture_height_ );
出于好奇,我尝试将 position = vec4(pos, 1.0);
更改为不同的值,但我的像素打印仍然给出 0 0 0 1。
我现在已经调试了大约 20-30 个小时,并在这里查找了十几个不同的教程和其他问题,但最近几个小时我觉得我没有任何进展。
这里有什么突出说明为什么纹理看起来是 blank/black,或者任何其他需要解决的问题?我正在使用这个项目来了解着色器,所以我对这一切都很陌生。任何帮助将不胜感激。
通过完全重写代码解决了这个问题。
我仍然不能 100% 确定问题出在哪里,但我想我可能有一些不匹配的 enable/disable 调用。