Подтвердить что ты не робот

Как визуализировать на экране OpenGL?

Моя цель - визуализировать OpenGL-сцену без окна непосредственно в файл. Сцена может быть больше, чем у моего разрешения экрана.

Как я могу это сделать?

Я хочу иметь возможность выбирать размер области рендеринга для любого размера, например 10000x10000, если возможно?

4b9b3361

Ответ 1

Все начинается с glReadPixels, который вы будете использовать для переноса пикселей, хранящихся в конкретном буфере на графическом процессоре, в основную память (ОЗУ). Как вы заметили в документации, нет аргументов для выбора того, какой буфер. Как обычно в OpenGL, текущий буфер для чтения является состоянием, которое вы можете установить с помощью glReadBuffer.

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

//Before swapping
std::vector<std::uint8_t> data(width*height*4);
glReadBuffer(GL_BACK);
glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,&data[0]);

Это будет читать текущий буфер (обычно буфер, на который вы рисуете). Вы должны вызывать это перед заменой буферов. Обратите внимание, что вы также можете отлично читать задний буфер с помощью вышеуказанного метода, очистить его и нарисовать что-то совершенно другое, прежде чем менять его. Технически вы также можете прочитать передний буфер, но это часто обескураживается, поскольку теоретически реализациям разрешено делать некоторые оптимизации, которые могут сделать ваш передний буфер содержать мусор.

Есть несколько недостатков в этом. Во-первых, мы действительно не делаем рендеринг с экрана. Мы визуализируем экранные буферы и читаем их. Мы можем эмулировать визуализацию без экрана, никогда не переключаясь в задний буфер, но это не кажется правильным. Кроме того, передние и задние буферы оптимизированы для отображения пикселей, а не для их считывания. То, что Framebuffer Objects входит в игру.

По сути, FBO позволяет создавать фреймбуфер, отличный от стандартного (например, FRONT и BACK-буферы), которые позволяют рисовать в буфер памяти вместо буферов экрана. На практике вы можете либо нарисовать текстуру, либо renderbuffer. Первый из них является оптимальным, если вы хотите повторно использовать пиксели в OpenGL как текстуру (например, наивную "камеру безопасности" в игре), а второй - если вы просто хотите рендерить/прочитать. С этим вышеприведенный код станет чем-то вроде этого, опять-таки псевдокодом, поэтому не убивайте меня, если вы ошибаетесь или забыли некоторые утверждения.

//Somewhere at initialization
GLuint fbo, render_buf;
glGenFramebuffers(1,&fbo);
glGenRenderbuffers(1,&render_buf);
glBindRenderbuffer(render_buf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_BGRA8, width, height);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER​,fbo);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_buf);

//At deinit:
glDeleteFramebuffers(1,&fbo);
glDeleteRenderbuffers(1,&render_buf);

//Before drawing
glBindFramebuffer(GL_DRAW_FRAMEBUFFER​,fbo);
//after drawing
std::vector<std::uint8_t> data(width*height*4);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,&data[0]);
// Return to onscreen rendering:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER​,0);

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

Наконец, вы можете использовать пиксельные объекты буфера, чтобы апикалировать пиксели чтения. Проблема в том, что glReadPixels блокируется, пока данные пикселя не будут полностью перенесены, что может привести к остановке вашего CPU. С PBO реализация может немедленно вернуться, поскольку она все равно контролирует буфер. Только при отображении буфера, который будет заблокирован конвейер. Однако PBO может быть оптимизирован для буферизации данных только на ОЗУ, поэтому этот блок может занимать намного меньше времени. Код считываемых пикселей будет выглядеть примерно так:

//Init:
GLuint pbo;
glGenBuffers(1,&pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, width*height*4, NULL, GL_DYNAMIC_READ);

//Deinit:
glDeleteBuffers(1,&pbo);

//Reading:
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,0); // 0 instead of a pointer, it is now an offset in the buffer.
//DO SOME OTHER STUFF (otherwise this is a waste of your time)
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); //Might not be necessary...
pixel_data = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);

Часть в шапках имеет важное значение. Если вы просто выпустите glReadPixels в PBO, а затем glMapBuffer этого PBO, вы не получите ничего, кроме большого количества кода. Уверен, что glReadPixels может вернуться немедленно, но теперь glMapBuffer остановится, потому что он должен безопасно отображать данные из буфера чтения в PBO и в блок памяти в основной ОЗУ.

Также обратите внимание, что везде я использую GL_BGRA, потому что многие графические карты используют это как оптимальный формат рендеринга (или версию GL_BGR без альфы). Он должен быть самым быстрым форматом для передачи пикселей таким образом. Я попытаюсь найти статью nvidia, которую я прочитал об этом несколько монах назад.

При использовании OpenGL ES 2.0 GL_DRAW_FRAMEBUFFER может быть недоступен, вы должны просто использовать GL_FRAMEBUFFER в этом случае.

Ответ 2

Я предполагаю, что создание фиктивного окна (вы его не визуализируете, оно просто потому, что API требует, чтобы вы его создали), в котором вы создаете свой основной контекст, является приемлемой стратегией реализации.

Вот ваши варианты:

Пиксельные буферы

Буфер пикселя или pbuffer (который не является пиксельным буферным объектом), является прежде всего контекстом OpenGL. В принципе, вы создаете окно как обычно, а затем выбираете формат пикселей из wglChoosePixelFormatARB (отсюда должны быть получены форматы pbuffer). Затем вы вызываете wglCreatePbufferARB, предоставляя ему ваше окно HDC и формат буфера пикселей, который вы хотите использовать. О, и ширина/высота; вы можете запросить максимальную ширину/высоту реализации.

Фреймбуфер по умолчанию для pbuffer не отображается на экране, а максимальная ширина/высота - это то, что аппаратные средства позволяют вам использовать. Поэтому вы можете отобразить его и использовать glReadPixels для чтения с него.

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

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

Это минусы. Pbuffers не работают с основными контекстами OpenGL. Они могут работать для совместимости, но нет возможности предоставить wglCreatePbufferARB информацию о версиях и профилях OpenGL.

Объекты Framebuffer

Framebuffer Objects являются более "правильными" внеэкранными rendertargets, чем pbuffers. FBO находятся в контексте, а pbuffers - о создании новых контекстов.

FBOs - это просто контейнер для изображений, которые вы визуализируете. Максимальные размеры, которые допускает реализация, могут быть запрошены; вы можете предположить, что это будет GL_MAX_VIEWPORT_DIMS (убедитесь, что FBO привязан, прежде чем проверять это, поскольку он изменяется в зависимости от того, привязан ли FBO).

Поскольку вы не отбираете текстуры из них (вы просто читаете значения назад), вы должны использовать renderbuffers вместо текстур. Их максимальный размер может быть больше, чем у текстур.

Поверхность - это простота использования. Вместо того, чтобы иметь дело с форматами пикселей и т.д., Вы просто выбираете подходящий формат изображения для своего вызова glRenderbufferStorage.

Единственный реальный недостаток - более узкая полоса аппаратного обеспечения, которая их поддерживает. В общем, все, что AMD или NVIDIA делает, что они по-прежнему поддерживают (сейчас GeForce 6xxx или лучше [обратите внимание на количество x] и любую карту Radeon HD) будет иметь доступ к ARB_framebuffer_object или OpenGL 3.0+ (где это основная функция). У старых драйверов может быть только поддержка EXT_framebuffer_object (что имеет несколько отличий). Аппаратное обеспечение Intel является potluck; даже если они утверждают, что поддержка 3.x или 4.x может по-прежнему не работать из-за ошибок драйвера.

Ответ 3

Если вам нужно отобразить то, что превышает максимальный размер FBO вашей реализации GL libtr, работает очень хорошо:

Библиотека TR (Tile Rendering) является библиотекой утилиты OpenGL для выполнения черепичный рендеринг. Плиточный рендеринг - это способ создания больших изображения в кусках (плитки).

TR - эффективная память; могут генерироваться произвольно большие файлы изображений без выделения полноразмерного буфера изображения в основной памяти.

Ответ 4

Самый простой способ - использовать что-то, называемое Frame Buffer Objects (FOB). Вам все равно придется создавать окно для создания контекста opengl (но это окно может быть скрыто).

Ответ 5

Самый простой способ выполнить свою задачу - использовать FBO для выключения экрана. И вам не нужно визуализировать текстуру, а затем получить teximage. Просто визуализируйте буфер и используйте функцию glReadPixels. Эта ссылка будет полезна. См. Примеры объектов Framebuffer