Я бы очень хотел задать краткий вопрос, который дает четкий ответ, но я боюсь, что есть слишком много мелких вещей, которые я не до конца понимаю относительно инициализации FBO, которые мне нужно прояснить. Я пишу отложенный шейдер, ориентированный как на OpenGL 4.3, так и на OpenGL ES 3.0, причем первый ведет себя точно так, как я ожидал, но последний вызывает у меня проблемы, которые я не могу определить источник.
Сначала я опишу свое понимание / путаницу в отношении настройки FBO MRT для GL 4.2 и ES 3.0 и надеюсь, что кто-то проявит любезность, чтобы исправить любые заблуждения.
Спецификации OpenGL ES 3.0 говорят, что он поддерживает «четыре или более целей рендеринга», но не упоминает (что я мог найти) спецификаций этих целей рендеринга. Что можно предположить о размерах этих целей рендеринга? Могу ли я просто предположить, что он может иметь внутренний формат RGBA32F
(четыре 32-битных поплавковых канала)? Мне кажется, что это принципиальное предположение / знание для шейдеров, пишущих в RT. Является ли распространенная процедура: попытка создать FBO с определенными спецификациями, после чего следует проверка на полноту FBO? Если не удалось: уменьшить требования и использовать альтернативные шейдеры, которые компенсируют уменьшенную битовую глубину?
Точные квалификаторы, как говорят, «Переносимость кода помощи с OpenGL ES, и не имеет никакого эффекта с обычным OpenGL», но мне сложно понять что именно эти highp
, mediump
, lowp
, используются для и как они играют вместе с битовой глубиной целей рендеринга. Во-первых, я предполагаю, что битовая глубина целей рендеринга определяется и настраивается в FBO, и что прецизионный прецизионный параметр автоматически соответствует этому, что заставляет меня думать, что high
, medium
а также low
имеет какое-то отношение к 32
, 16
, 8
биты глубины. Я посмотрел через OpenGL ES 3.0 specs
и не все так ясно об этом.
Текстурное вложение для FBO настраивается с помощью glTexStorage2D
(с target=GL_TEXTURE_2D
, levels=1
), который я полагаю, более правильно использовать здесь, чем glTexImage2D
, как только internalformat
должно иметь значение.
Настроенная текстура из (3.) затем присоединяется к FBO COLOR_ATTACHMENT
с помощью glFramebufferTexture2D
,
packHalf2x16
/unpackHalf2x16
):Допустим, я настроил FBO с двумя цветными вложениями, первый (RT1
) с внутренним форматом GL_RGBA32UI
, второй (RT2
) с GL_RGBA32F
, Объекты отображаются в два прохода. Сначала к RT FBO, а затем к двум полноэкранным четырем, которые обрабатываются кадровым буфером по умолчанию.
Для упрощения я сосредоточусь только на передаче цветовых данных RGB между двумя этапами. Я попытался сделать это тремя разными способами:
[Работает для GL & ES] С помощью RT2
регулярно сохраняя данные о цвете как float, считывая их как текстуру float и выводя их в стандартный кадровый буфер.
[Работает для GL & ES] С помощью RT1
, сохраняя данные цвета, преобразованные в uint
(в [0,..,255]
для каждого канала), читая его как uint
текстура, преобразовав его в плавающее [0,1]
и вывод его в стандартный кадровый буфер.
[Работает только для GL] С помощью RT1
, упаковывая данные о цвете в полтора канала, используя packHalf2x16
, Читая это как uint
текстуры, и преобразовать его обратно в плавающее с помощью unpackHalf2x16
,
Не уверен, насколько важны / важны детали кода (я быстро выполню любые запросы). я использую highp
для обоих float
а также int
, Цели рендеринга первого прохода определяются как:
layout (location = 0) out uvec4 fs_rt1;
layout (location = 1) out vec4 fs_rt2;
И во втором проходе, доступны как текстуры:
uniform highp usampler2D RT1;
uniform highp sampler2D RT2;
...
// in main():
uvec4 rt1 = texelFetch(RT1, ivec2(gl_FragCoord.xy), 0);
vec4 rt2 = texelFetch(RT2, ivec2(gl_FragCoord.xy), 0);
метод 1.
:
// in first pass:
fs_rt2.rgb = decal.rgb;
// in second pass:
color = vec4(rt2.rgb, 1.0);
метод 2.
:
// in first pass:
fs_rt1.rgb = uvec3(decal.xyz * 256.0f);
// in second pass:
color = vec4(vec3(rt1.xyz)/256.0f, 1);
метод 3.
:
// in first pass:
fs_rt1.x = packHalf2x16(decal.xy);
fs_rt1.y = packHalf2x16(vec2(decal.z, 0.0f));
// in second pass:
vec2 tmp = unpackHalf2x16(rt1.y);
color = vec4(vec3(unpackHalf2x16(rt1.x), tmp.x), 1);
В методах 1
, 2
, а также 3
, вывод рабочего стола GL выглядит следующим образом:
На Nexus 5, методы 1
а также 2
Вывод OpenGL ES 3.0 выглядит следующим образом:
метод 3
на нексус 5 однако выглядит так:
Я не могу понять, почему третий метод не работает на OpenGL ES 3.0. Любая помощь или предложения будут с благодарностью. Я не против чтения документации, так что, если вы хотите указать мне правильное направление, это тоже поможет.
Для первых нескольких вопросов:
Вы можете запросить GL_MAX_COLOR_ATTACHMENTS
чтобы получить количество цветных вложений, которые вы можете прикрепить к FBO. Это гарантированно будет> 4 для ES 3.0. Это независимо формата цветового вложения (будь то рендер-буфер или текстура). Однако есть ограничения на то, какие форматы вы можете визуализировать. Посмотрите на стол для glTexStorage2D и конкретно в графе «Цвета рендеринга». Это позволяет узнать, какие форматы вы можете прикрепить к FBO. Код делает Необходимо проверить на полноту FBO, но не из-за нескольких цветовых вложений. glCheckFramebufferStatus проверяет, что все вложения имеют одинаковое количество образцов и другие специфичные для поставщика вещи, такие как вложения глубины / трафарета.
В GLES есть прецизионные квалификаторы для оптимизации. Некоторые арифметические операции быстрее или эффективнее выполнять с низкой точностью, когда вы знаете, что работаете с числами в определенном диапазоне, см. раздел 4.5.1 спецификаций GLSL ES Обратите внимание, что это минимальные значения точности и что некоторые поставщики дают вам высокие значения, даже когда вы запрашиваете низкие значения. Эти прецизионные квалификаторы действительны только на языке затенения GLSL. Это не влияет на формат целей рендеринга. Некоторые оптимизации, которые вы, возможно, захотите сделать, например, для должны выполнять операции со значениями цвета в lowp: значения от 0.0f до 1.0f. Это позволяет графическому процессору использовать меньше энергии, потому что он использует ALU, который предназначен, например, для операций с низкой точностью. PowerVR. Я бы сказал, что использование чего-либо, кроме highp, должно быть зарезервировано для случаев, когда ваше приложение работает медленно или в качестве этапа оптимизации. Вам не нужно беспокоиться об этом с самого начала.
Существует разница между использованием glTexStorage2D и glTexImage2D. Когда вы используете glTexStorage2D, это дает водителю подсказку, что этот формат текстуры не изменится. Это становится неизменным. Это означает, что драйвер может выполнять оптимизацию при использовании этой текстуры. Всегда предпочитайте glTexStorage2D. 🙂
Да, это. Опять проверь GL_MAX_COLOR_ATTACHMENTS
чтобы увидеть, сколько вы можете прикрепить к FBO. Ты можешь использовать GL_COLOR_ATTACHMENT0
в GL_COLOR_ATTACHMENT0 + maxColourAttachments
,
Что касается вашей проблемы, я не вижу никаких ошибок в вашем коде, однако я боюсь, что может быть ошибка в драйвере используемого вами графического процессора. Я столкнулся с подобной проблемой, прежде чем при использовании uint
переменные на определенных графических процессорах. Я бы посоветовал вам попробовать приложение на телефоне с графическим процессором другого производителя, чтобы убедиться, что вы получаете ту же ошибку.
Вы также можете попытаться избежать этой проблемы, используя другой способ упаковки см. этот ответ. Это не использовать uint
s для хранения информации, кроме цели рендеринга с плавающей точкой, поэтому может обойти ошибку.
Надеюсь, это ответит на ваш вопрос. 🙂