Самый эффективный способ получить данные текстуры пикселей?
Я знаю, что Directx для Dx9, по крайней мере, имеет объект текстуры, где вы можете получить только небольшую часть текстуры в доступную для процессора память. Я считаю, что это была функция под названием "LockRect". OpenGL имеет glGetTexImage()
но он захватывает все изображение, и если формат не совпадает с форматом текстуры, то ему придется преобразовать всю текстуру в новый формат пикселей поверх передачи всей текстуры. Эта функция также отсутствует в OpenGL ES. Framebuffers - это еще один вариант, но где я мог бы потенциально связать кадровый буфер, где привязка цвета связана с текстурой. Тогда есть glReadPixels
который читает из кадрового буфера, поэтому он должен читать из текстуры. glReadPixels
имеет ограниченные параметры формата пикселей, поэтому преобразование должно произойти, но я могу прочитать нужные мне пиксели (это всего 1 пиксель). Я не использовал этот метод, но кажется, что это возможно. Если кто-то может подтвердить метод framebuffer, то это рабочая альтернатива. Тогда этот метод также будет работать для OpenGL ES 2+.
Есть ли другие методы? Насколько эффективен метод фреймбуфера (если он работает), нужно ли в конечном итоге преобразовывать всю текстуру в нужный формат, прежде чем он читает пиксели, или это полностью определяется реализацией?
Редактировать: @Nicol_Bolas Пожалуйста, прекратите удаление OpenGL из тегов и добавление OpenGL-ES, OpenGL-ES не применимо, OpenGL есть. Это специально для OpenGL, но я бы хотел, чтобы он был совместим с Open ES 2+, если это возможно, хотя это не обязательно. Если доступно только решение OpenGL, то я рассмотрю его, если оно того стоит. Спасибо.
1 ответ
Обратите внимание, что у меня нет особого опыта работы с ES, в частности, поэтому могут быть более эффективные способы сделать это именно в этом контексте. Общая суть применима либо в обычном OpenGL, либо в ES.
Прежде всего, наиболее важным фактором производительности должно быть, когда вы делаете чтение. Если вы запрашиваете данные с видеокарты во время рендеринга, ваша программа (сторона ЦП) должна будет остановиться до тех пор, пока видеокарта не вернет данные, что замедлит рендеринг из-за вашей неспособности выдавать дополнительные команды рендеринга. Как правило, вы всегда должны загружать, отображать, загружать - не смешивайте ни один из этих процессов, это очень сильно повлияет на скорость, и насколько это будет зависеть от драйверов / оборудования / ОС.
Я предлагаю использовать glReadPixels( )
в конце вашего цикла рендеринга. Я подозреваю, что ограничения на форматы для этой функции связаны с ограничениями на форматы кадрового буфера; кроме того, вы действительно должны использовать 8-битные без знака или с плавающей запятой, оба из которых поддерживаются. Если у вас есть какой-то случай, когда вы не можете использовать какой-либо из этих поддерживаемых форматов, вы должны объяснить, что это такое, так как может быть способ решить это конкретно.
Если вам нужно содержимое кадрового буфера в определенной точке рендеринга (а не в конце), создайте второй текстурный + кадровый буфер (опять же с тем же форматом), чтобы он был эффективным "буферным буфером", а затем скопируйте из целевого кадрового буфера в него текстура. Это происходит на видеокарте, поэтому это не накладывает задержки на чтение шины. Вот что я написал, что делает эту операцию:
glActiveTexture( GL_TEXTURE0 + unit );
glBindTexture( GL_TEXTURE_2D, backbufferTextureHandle );
glBindFramebuffer( GL_READ_FRAMEBUFFER, framebufferHandle );
glCopyTexSubImage2D(
GL_TEXTURE_2D,
0, // level
0, 0, // offset
0, 0, // x, y
screenX, screenY );
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, framebufferHandle );
Затем, когда вы хотите получить данные, свяжите буфер с GL_READ_FRAMEBUFFER и используйте glReadPixels( )
в теме.
Наконец, вы должны иметь в виду, что загрузка данных все равно остановит процессор. Если вы загружаете до отображения кадрового буфера, вы откладываете отображение изображения до тех пор, пока не сможете снова выполнить команды, что может привести к видимой задержке. Поэтому я по-прежнему предлагаю использовать кадровый буфер не по умолчанию, даже если вы заботитесь только о конечном состоянии буфера, и заканчиваете цикл рендеринга с эффектом:
(1.) Перейдите к кадровому буферу по умолчанию:
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 ); // Default framebuffer
glBindFramebuffer( GL_READ_FRAMEBUFFER, framebufferHandle );
glBlitFramebuffer(
0, 0, screenX, screenY,
0, 0, screenX, screenY,
GL_COLOR_BUFFER_BIT,
GL_NEAREST );
(2.) Вызовите, какой бы ни была ваша команда буферов обмена в данной ситуации.
(3.) Ваш вызов загрузки из кадрового буфера (будь то glReadPixels( )
или что-то другое).
Что касается влияния на скорость операций blit/texcopy, то оно весьма неплохо для большинства современных аппаратных средств, и я не обнаружил, что оно оказало заметное влияние даже на 10+ кадров в кадре, но если вы имеете дело с устаревшим оборудованием, это может быть стоит подумать