Внеэкранный рендеринг в Framebuffer - PullRequest
3 голосов
/ 05 апреля 2020

У меня есть простая сцена, которая отображает сетку, плоскость и куб. У меня также есть два разных шейдера. Один - плоский шейдер, который отображает объекты случайным цветом, другой - шумовой шейдер, который отображает объекты с эффектом шума. Я хочу иметь возможность:

  1. Рендеринг плоскости и куба с помощью шумового шейдера и сетки с плоским шейдером.
  2. Как только пользователь щелкнет сцену, я хочу рендеринг всей сцены в (закадровый) рендер-буфер, используя только плоский шейдер, и распечатывать выбранный цвет, полученный с помощью glReadPixels.
  3. . Продолжить рендеринг сцены, используя оба шейдера.

В основном я хочу реализовать подбор цвета. Похоже, это не работает для куба и самолета. Пиксельные значения всегда являются полутоновыми значениями шумового шейдера.

Вот как выглядит моя процедура рисования:

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, сцена выглядит следующим образом (нормально рендеринг):

enter image description here

Вот как выглядит сцена, когда offscreen равен true:

enter image description here

Вот как я создаю 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 (может быть, и то, и другое). Чего мне не хватает?

1 Ответ

3 голосов
/ 05 апреля 2020

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);

Я рекомендую включить Отладочный вывод , чтобы найти ошибки 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);
}
...