离屏渲染到帧缓冲区
Offscreen rendering to Framebuffer
我有一个渲染网格、平面和立方体的简单场景。我还有两个不同的着色器。一种是以随机颜色渲染对象的平面着色器,另一种是渲染具有噪声效果的对象的噪声着色器。我希望能够:
- 通过噪声着色器渲染平面和立方体,使用平面着色器渲染网格。
- 用户单击场景后,我想仅使用平面着色器将整个场景渲染到(屏幕外)渲染缓冲区,并打印通过
glReadPixels
获取的单击颜色。
- 继续使用两个着色器渲染场景。
基本上我想实现颜色选择。它似乎不适用于立方体和平面。像素值始终是噪声着色器的灰度值。
这是我的绘图例程:
void Draw(const bool offscreen = false)
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 projection = glm::perspective(glm::radians(camera.zoom), (float)viewport_width / (float)viewport_height, 0.1f, 100.0f);
glm::mat4 view = camera.get_view_matrix();
if (offscreen)
{
flat_shader.use();
flat_shader.set_mat4("projection", projection);
flat_shader.set_mat4("view", view);
grid.Draw(&flat_shader);
box.Draw_offscreen(&flat_shader);
plane.Draw_offscreen(&flat_shader);
}
else
{
noise_shader.use();
noise_shader.set_mat4("projection", projection);
noise_shader.set_mat4("view", view);
noise_shader.set_float("iTime", delta_time);
plane.Draw(&noise_shader);
box.Draw(&noise_shader);
flat_shader.use();
flat_shader.set_mat4("projection", projection);
flat_shader.set_mat4("view", view);
grid.Draw(&flat_shader);
}
glfwSwapBuffers(window);
}
所以如果屏幕外是 false
场景看起来像这样(正常渲染):
这就是当 offscreen
为 true
时的场景:
这就是我创建 fbo 的方式:
void init_offscreen_buffer()
{
glGenFramebuffers(1, &fbo_off);
glGenRenderbuffers(1, &render_buf);
glBindRenderbuffer(GL_RENDERBUFFER, render_buf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, viewport_width, viewport_height);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_off);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_buf);
// I also checked for FRAMEBUFFER_COMPLETE
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
现在,当用户单击场景时,我 运行 pick_color_id
会在单击的像素上打印颜色。
void pick_color_id(double x, double y)
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_off);
Draw(true);
GLubyte pixel_color[4];
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(x, 800 - y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_color);
cout << "---------------------------------------------------------" << endl;
cout << "Mouse click position: " << x << "; " << y << endl;
cout << "Target pixel color: " << (unsigned int)pixel_color[0] << ";" << (unsigned int)pixel_color[1] << ";" << (unsigned int)pixel_color[2] << endl;
cout << "---------------------------------------------------------" << endl;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
正如我在绑定缓冲区后看到的那样,它应该是渲染目标并且它应该包含平面和立方体的平面颜色。实际上它总是像在噪声着色器中一样打印灰度颜色。
我认为我的 fbo 设置或使用有问题(可能两者都有)。我错过了什么?
glReadPixels
从帧缓冲区读取日期,因此帧缓冲区绑定的目标必须是 GL_READ_FRAMEBUFFER
而不是 GL_DRAW_FRAMEBUFFER
:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_off);
Draw(true);
GLubyte pixel_color[4];
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_off);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(x, 800 - y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_color);
分别
glBindFramebuffer(GL_FRAMEBUFFER, fbo_off);
Draw(true);
GLubyte pixel_color[4];
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(x, 800 - y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_color);
我建议启用 Debug Output 以查找 OpenGL 错误。例如:
#include <iostream>
void GLAPIENTRY DebugCallback(
unsigned int source,
unsigned int type,
unsigned int id,
unsigned int severity,
int length,
const char *message,
const void *userParam )
{
std::cout << message << std::endl;
}
void init_opengl_debug() {
glDebugMessageCallback(&DebugCallback, nullptr );
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
我有一个渲染网格、平面和立方体的简单场景。我还有两个不同的着色器。一种是以随机颜色渲染对象的平面着色器,另一种是渲染具有噪声效果的对象的噪声着色器。我希望能够:
- 通过噪声着色器渲染平面和立方体,使用平面着色器渲染网格。
- 用户单击场景后,我想仅使用平面着色器将整个场景渲染到(屏幕外)渲染缓冲区,并打印通过
glReadPixels
获取的单击颜色。 - 继续使用两个着色器渲染场景。
基本上我想实现颜色选择。它似乎不适用于立方体和平面。像素值始终是噪声着色器的灰度值。
这是我的绘图例程:
void Draw(const bool offscreen = false)
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 projection = glm::perspective(glm::radians(camera.zoom), (float)viewport_width / (float)viewport_height, 0.1f, 100.0f);
glm::mat4 view = camera.get_view_matrix();
if (offscreen)
{
flat_shader.use();
flat_shader.set_mat4("projection", projection);
flat_shader.set_mat4("view", view);
grid.Draw(&flat_shader);
box.Draw_offscreen(&flat_shader);
plane.Draw_offscreen(&flat_shader);
}
else
{
noise_shader.use();
noise_shader.set_mat4("projection", projection);
noise_shader.set_mat4("view", view);
noise_shader.set_float("iTime", delta_time);
plane.Draw(&noise_shader);
box.Draw(&noise_shader);
flat_shader.use();
flat_shader.set_mat4("projection", projection);
flat_shader.set_mat4("view", view);
grid.Draw(&flat_shader);
}
glfwSwapBuffers(window);
}
所以如果屏幕外是 false
场景看起来像这样(正常渲染):
这就是当 offscreen
为 true
时的场景:
这就是我创建 fbo 的方式:
void init_offscreen_buffer()
{
glGenFramebuffers(1, &fbo_off);
glGenRenderbuffers(1, &render_buf);
glBindRenderbuffer(GL_RENDERBUFFER, render_buf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, viewport_width, viewport_height);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_off);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_buf);
// I also checked for FRAMEBUFFER_COMPLETE
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
现在,当用户单击场景时,我 运行 pick_color_id
会在单击的像素上打印颜色。
void pick_color_id(double x, double y)
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_off);
Draw(true);
GLubyte pixel_color[4];
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(x, 800 - y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_color);
cout << "---------------------------------------------------------" << endl;
cout << "Mouse click position: " << x << "; " << y << endl;
cout << "Target pixel color: " << (unsigned int)pixel_color[0] << ";" << (unsigned int)pixel_color[1] << ";" << (unsigned int)pixel_color[2] << endl;
cout << "---------------------------------------------------------" << endl;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
正如我在绑定缓冲区后看到的那样,它应该是渲染目标并且它应该包含平面和立方体的平面颜色。实际上它总是像在噪声着色器中一样打印灰度颜色。
我认为我的 fbo 设置或使用有问题(可能两者都有)。我错过了什么?
glReadPixels
从帧缓冲区读取日期,因此帧缓冲区绑定的目标必须是 GL_READ_FRAMEBUFFER
而不是 GL_DRAW_FRAMEBUFFER
:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_off);
Draw(true);
GLubyte pixel_color[4];
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_off);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(x, 800 - y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_color);
分别
glBindFramebuffer(GL_FRAMEBUFFER, fbo_off);
Draw(true);
GLubyte pixel_color[4];
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(x, 800 - y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_color);
我建议启用 Debug Output 以查找 OpenGL 错误。例如:
#include <iostream>
void GLAPIENTRY DebugCallback(
unsigned int source,
unsigned int type,
unsigned int id,
unsigned int severity,
int length,
const char *message,
const void *userParam )
{
std::cout << message << std::endl;
}
void init_opengl_debug() {
glDebugMessageCallback(&DebugCallback, nullptr );
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}