Нужна рука для рендеринга, отображения и сохранения изображения

Ранее я спрашивал, как я мог бы отображать свой цветовой буфер, а также сохранять его на диск, и я получил ответ, что должен;

Рендеринг в FBO и использование glReadPixels() для извлечения изображений из него вместо переднего буфера.

Как создать снимок экрана, когда glReadPixels пуст?< /а>

Тем не менее, я немного читал о кадровых буферах и все еще полностью запутался, поэтому я подумал, что спрошу, как это сделать на SO. Мой код делает что-то вроде этого:

    /* Callback handler for window re-paint event */
    void display()
    {
        glClear(GL_COLOR_BUFFER_BIT);  //Clear the color buffer         
        glMatrixMode(GL_MODELVIEW);    //To operate on the model-view matrix

        // do some rendering

        glFlush(); // display
    }

когда я хочу сохранить изображение в любой момент, я запускаю это:

    std::unique_ptr<RGBA2D> GrabScreen()
    {
        //we get the width/height of the screen into this array
        GLint screen[4];

        //get the width/height of the window
        glGetIntegerv(GL_VIEWPORT, screen);

        GLint width = screen[2];
        GLint height = screen[3];

        std::unique_ptr<RGBA2D> pixels(new RGBA2D(height, width * 4));

        glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels->data());
        return std::move(pixels);
    }

Обратите внимание, что RGBA2D — это двумерный собственный векторный объект (не важно). Все это работает нормально, за исключением того, что изображение сохраняется только в том случае, если оно отображается. Я хочу иметь возможность запускать свою программу на Unix-машине без дисплея. Я хочу рендерить в FBO. Как мне это сделать?


person quant    schedule 02.05.2014    source источник
comment
Честно говоря, я не думаю, что ответ, который вы ранее дали, был полным. Вы действительно должны использовать Pixel Buffer Objects для асинхронного считывания визуализированных данных изображения из GL. В противном случае происходит ненужная остановка графического конвейера, так что glReadPixels (...) немедленно возвращает завершенное изображение. С помощью PBO вы можете сообщить GL, что вас интересуют данные пикселей, но вместо того, чтобы читать их немедленно, вы можете периодически проверять, доступны ли данные. Когда он доступен, вы можете прочитать его обратно без прилавка.   -  person Andon M. Coleman    schedule 02.05.2014
comment
Я понимаю, что если у вас возникли проблемы с FBO, эта дополнительная сложность ничем не поможет. Но это должно быть вашей конечной целью. Обзор всего процесса можно найти здесь.   -  person Andon M. Coleman    schedule 02.05.2014


Ответы (2)


Не держась за руки, но, надеюсь, я смогу указать вам правильное направление.

Вы будете использовать glGenFramebuffers и glBindFramebuffer для создания и привязки объекта фреймбуфера (FBO).

Затем у вас есть выбор, хотите ли вы выполнить рендеринг в текстуру или в буфер рендеринга. Для вашей цели подойдет любой из них. Рендербуфер проще, ИМХО. Используйте glGenRenderbuffers, glBindRenderbuffer и glRenderbufferStorage для настройки буфера рендеринга цвета.

Затем вы прикрепляете буфер рендеринга цвета к FBO, используя glFramebufferRenderbuffer.

Если вам нужен буфер глубины, повторите два предыдущих шага, чтобы создать и подключить еще один буфер рендеринга, используемый в качестве буфера глубины для рендеринга FBO.

Затем выполните визуализацию и захватите кадр с помощью glReadPixels.

Все эти вызовы задокументированы на справочных страницах на сайте www.opengl.org. Если вы ищете ключевые слова и некоторые имена функций, вы также сможете найти несколько полных примеров кода.

person Reto Koradi    schedule 02.05.2014

Недавно у меня была небольшая дискуссия в списке рассылки разработчиков Wayland, где я хотел продемонстрировать, как FBO не обновляются, когда X-сервер, управляющий GPU, не удерживает VT. Во всяком случае, в демонстрационных целях я взломал быструю и грязную программу из разных источников, которые у меня были, которая выполняет рендеринг в FBO в цикле и записывает созданное изображение в файл. Он не оптимизирован для производительности, но делает то, что вам нужно, поэтому я оставляю исходный код здесь (обратите внимание, что malloc для буфера считывания пропускает парный free, поэтому там есть нерастущая утечка памяти)

// framebuffer continuous dump demonstrator
//
// build:
// c++  -o test_fbo test_fbo.cpp -lm -lGL -lGLU -lglut -lGLEW


#include <GL/glew.h>
#include <GL/glut.h>

#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <math.h>
#include <stdio.h>

using namespace std;

namespace render
{
    int width, height;
    float aspect;

    void init();
    void reshape(int width, int height);
    void display();

    int const fbo_width = 512;
    int const fbo_height = 512;

    GLuint fb, color, depth;

    void *dumpbuf;
    int dumpbuf_fd;
};

void idle();

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );

    glutCreateWindow("FBO test");
    glutDisplayFunc(render::display);
    glutReshapeFunc(render::reshape);
    glutIdleFunc(idle);

    glewInit();

    render::init();
    glutMainLoop();

    return 0;
}

void idle()
{
    glutPostRedisplay();
}

void CHECK_FRAMEBUFFER_STATUS()
{                                                         
    GLenum status;
    status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); 
    switch(status) {
    case GL_FRAMEBUFFER_COMPLETE:
        break;

    case GL_FRAMEBUFFER_UNSUPPORTED:
    /* choose different formats */
        break;

    default:
        /* programming error; will fail on all hardware */
        throw "Framebuffer Error";
    }
}

namespace render
{
    float const light_dir[]={1,1,1,0};
    float const light_color[]={1,0.95,0.9,1};

    void init()
    {
        glGenFramebuffers(1, &fb);
        glGenTextures(1, &color);
        glGenRenderbuffers(1, &depth);

        glBindFramebuffer(GL_FRAMEBUFFER, fb);

        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(   GL_TEXTURE_2D, 
                0, 
                GL_RGB8, 
                fbo_width, fbo_height,
                0, 
                GL_RGBA, 
                GL_UNSIGNED_BYTE, 
                NULL);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);

        glBindRenderbuffer(GL_RENDERBUFFER, depth);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, fbo_width, fbo_height);
        glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);

        GLint red_bits, green_bits, blue_bits, alpha_bits;

        glGetIntegerv(GL_RED_BITS,   &red_bits);
        glGetIntegerv(GL_GREEN_BITS, &green_bits);
        glGetIntegerv(GL_BLUE_BITS,  &blue_bits);
        glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);

        fprintf(stderr, "FBO format R%dG%dB%dA%d\n",
            (int)red_bits,
            (int)green_bits,
            (int)blue_bits,
            (int)alpha_bits );

        CHECK_FRAMEBUFFER_STATUS();

        dumpbuf_fd = open("/tmp/fbodump.rgb", O_CREAT|O_SYNC|O_RDWR, S_IRUSR|S_IWUSR);
        assert(-1 != dumpbuf_fd);
        dumpbuf = malloc(fbo_width*fbo_height*3);
        assert(dumpbuf);
    }

    void reshape(int width, int height)
    {
        render::width=width;
        render::height=height;
        aspect=float(width)/float(height);
        glutPostRedisplay();
    }

    void prepare()
    {
        static float a=0, b=0, c=0;

        glBindTexture(GL_TEXTURE_2D, 0);
        glEnable(GL_TEXTURE_2D);
        glBindFramebuffer(GL_FRAMEBUFFER, fb);

        glViewport(0,0,fbo_width, fbo_height);

        glClearColor(1,1,1,0);
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45, 1, 1, 10);

        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHTING);

        glEnable(GL_DEPTH_TEST);
        glDisable(GL_CULL_FACE);

        glLightfv(GL_LIGHT0, GL_POSITION, light_dir);
        glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);

        glTranslatef(0,0,-5);

        glRotatef(a, 1, 0, 0);
        glRotatef(b, 0, 1, 0);
        glRotatef(c, 0, 0, 1);

        glutSolidTeapot(0.75);

        a=fmod(a+0.1, 360.);
        b=fmod(b+0.5, 360.);
        c=fmod(c+0.25, 360.);

        glReadBuffer(GL_COLOR_ATTACHMENT0);
        glReadPixels(0,0,fbo_width,fbo_height,GL_RGB,GL_UNSIGNED_BYTE,dumpbuf);
        lseek(dumpbuf_fd, SEEK_SET, 0);
        write(dumpbuf_fd, dumpbuf, fbo_width*fbo_height*3);

    }

    void intermediary()
    {
    }

    void final()
    {
        static float a=0, b=0, c=0;

        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        glViewport(0,0, width, height);

        glClearColor(1.,1.,1.,0.);
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45, aspect, 1, 10);

        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glTranslatef(0,0,-5);

        glRotatef(b, 0, 1, 0);

        b=fmod(b+0.5, 360.);

        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, color);

        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glDisable(GL_LIGHTING);

        float cube[][5]=
        {
            {-1, -1, -1,  0,  0},
            { 1, -1, -1,  1,  0},
            { 1,  1, -1,  1,  1},
            {-1,  1, -1,  0,  1},

            {-1, -1,  1, -1,  0},
            { 1, -1,  1,  0,  0},
            { 1,  1,  1,  0,  1},
            {-1,  1,  1, -1,  1},
        };
        unsigned int faces[]=
        {
            0, 1, 2, 3,
            1, 5, 6, 2,
            5, 4, 7, 6,
            4, 0, 3, 7,
            3, 2, 6, 7,
            4, 5, 1, 0
        };

        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);

        glVertexPointer(3, GL_FLOAT, 5*sizeof(float), &cube[0][0]);
        glTexCoordPointer(2, GL_FLOAT, 5*sizeof(float), &cube[0][3]);

        glCullFace(GL_BACK);
        glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, faces);

        glCullFace(GL_FRONT);
        glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, faces);

        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    }

    void display()
    {
        prepare();
        intermediary();
        final();

        glutSwapBuffers();
    }
}
person datenwolf    schedule 02.05.2014
comment
В прикрепленном изображении текстуры отсутствует альфа-канал (GL_RGB8) по сравнению с форматом передачи пикселей (GL_RGBA). Это не очень важно, поскольку фактической передачи пикселей нет, но это заставляет меня задаться вопросом, предназначали ли вы буфер кадра для хранения альфы или нет. - person Andon M. Coleman; 02.05.2014
comment
@ AndonM.Coleman: Вероятно, там намного больше червей. Я только что открыл эту штуку в Vim и мог копировать и вставлять ее (посмотрите в списке рассылки Wayland Devel недавнее обсуждение Deep Color, для которого я взломал это из фрагментов из различных программ). - person datenwolf; 02.05.2014