Привязать предварительно обработанную текстуру глубины к fbo или фрагментному шейдеру?

В рамках отложенного затенения я использую разные объекты кадрового буфера для выполнения различных проходов рендеринга. В первом проходе я записываю DEPTH_STENCIL_ATTACHMENT для всей сцены в текстуру, назовем ее DepthStencilTexture. Чтобы получить доступ к информации о глубине, хранящейся в DepthStencilTexture, из разных проходов рендеринга, для которых я использую разные объекты кадрового буфера, я знаю два способа:
1) я привязываю DepthStencilTexture к шейдеру и получаю доступ к нему во фрагментном шейдере, где я глубина вручную, вот так

uniform vec2 WinSize; //windows dimensions
vec2 uv=gl_FragCoord.st/WinSize;
float depth=texture(DepthStencilTexture ,uv).r;
if(gl_FragCoord.z>depth) discard;

Я также установил glDisable(GL_DEPTH_TEST) и glDepthMask(GL_FALSE)

2) Я привязываю DepthStencilTexture к объекту фреймбуфера как DEPTH_STENCIL_ATTACHMENT и устанавливаю glEnable(GL_DEPTH_TEST) и glDepthMask(GL_FALSE) (редактировать: в этом случае я не буду привязывать DepthStencilTexture к шейдеру, чтобы избежать циклической обратной связи, см. ответ Никола Боласа, и я, если я нужна глубина во фрагментном шейдере буду использовать gl_FragCorrd.z)

В определенных ситуациях, таких как рисование легких объемов, для которых мне нужен тест трафарета и запись в буфер трафарета, я выбираю решение 2). В других ситуациях, когда я полностью игнорирую трафарет и просто нуждаюсь в глубине, сохраненной в DepthStencilTexture, дает ли вариант 1) какие-либо преимущества по сравнению с более «естественным» вариантом 2)?

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

uniform mat4 invPV; //inverse PV matrix 
vec2 uv=gl_FragCoord.st/WinSize;
vec4 WorldPosition=invPV*vec4(uv, texture(DepthStencilTexture ,uv).r ,1.0f );
WorldPosition=WorldPosition/WorldPosition.w;

В случае 2) это будет так (редактировать: это неправильно, gl_FragCoord.z ​​- это текущая глубина фрагмента, а не фактическая глубина, хранящаяся в текстуре)

uniform mat4 invPV; //inverse PV matrix 
vec2 uv=gl_FragCoord.st/WinSize;
vec4 WorldPosition=invPV*vec4(uv, gl_FragCoord.z, 1.0f );
WorldPosition=WorldPosition/WorldPosition.w;

Я предполагаю, что gl_FragCoord.z в случае 2) будет таким же, как texture(DepthStencilTexture ,uv).r в случае 1), или, другими словами, глубина, хранящаяся в DepthStencilTexture. Это правда? Читается ли gl_FragCoord.z из текущего связанного DEPTH_STENCIL_ATTACHMENT также с glDisable(GL_DEPTH_TEST) и glDepthMask(GL_FALSE) ?


person darius    schedule 16.08.2013    source источник


Ответы (1)


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

Да, я понимаю, что вы используете маски записи, чтобы предотвратить запись глубины. Это не имеет значения; Спецификация OpenGL достаточно ясна. В соответствии с 9.3.1 OpenGL 4.4 петля обратной связи устанавливается, когда:

  • изображение из объекта текстуры T прикрепляется к привязанному в данный момент объекту кадрового буфера отрисовки в точке присоединения A

  • объект текстуры T в настоящее время привязан к блоку текстуры U, и

  • текущее программируемое состояние обработки вершин и/или фрагментов позволяет (см. ниже) производить выборку из объекта текстуры T, привязанного к блоку текстуры U

В вашем коде именно так. Таким образом, у вас технически неопределенное поведение.

Одна из причин, по которой это не определено, заключается в том, что простое изменение масок записи не требует таких действий, как очистка фреймбуфера и/или кеша текстур.

При этом вам может сойти с рук вариант 2, если вы используете NV_texture_barrier. Который, несмотря на название, довольно широко доступен на оборудовании AMD. Здесь главное выставить барьер после того, как вы сделаете всю свою глубинную запись, чтобы все последующие чтения гарантированно сработали. Барьер выполнит всю очистку кеша и все, что вам нужно.

В противном случае единственным выбором будет вариант 1: выполнение теста глубины вручную.

Я предполагаю, что gl_FragCoord.z ​​в случае 2) будет таким же, как текстура (DepthStencilTexture, uv).r в случае 1), или, другими словами, глубина, хранящаяся в DepthStencilTexture. Это правда?

Ни то, ни другое не верно. gl_FragCoord — координата обрабатываемого фрагмента. Это фрагмент, сгенерированный растеризатором на основе данных растеризируемого примитива. Это не имеет ничего общего с содержимым фреймбуфера.

person Nicol Bolas    schedule 16.08.2013
comment
Это многое объясняет, спасибо. Но в моем варианте 2) я не сэмплирую текстуру в шейдерах, а получаю доступ к информации о глубине с помощью gl_FragCoord.z. Таким образом, третья гипотеза для петли обратной связи не достигается. Я прав? (собираюсь отредактировать мой вопрос, чтобы сделать его более понятным). - person darius; 16.08.2013
comment
@darius: Итак, если вы не читаете глубину, как вы восстанавливаете позицию для отложенного рендеринга? Вы вообще позицию пишете? - person Nicol Bolas; 16.08.2013
comment
Нет. Я написал это в конце своего вопроса: с gl_FragCoord.z. Моя идея такова: у меня есть DepthStencilTexture, связанная с FBO как DEPTH_STENCIL_COMPONENT, поэтому gl_FragCoord.z ​​должен дать мне тот же результат, как если бы я делал выборку из текстуры, но без фактической выборки из нее. - person darius; 16.08.2013
comment
@darius: Это не так. gl_FragCoord - координата текущего фрагмента. Это не имеет абсолютно никакого отношения к фреймбуферу. - person Nicol Bolas; 16.08.2013
comment
Ах хорошо! Конечно! Это была моя большая ошибка здесь. Это действительно очистило мой разум. Спасибо. - person darius; 16.08.2013
comment
Кроме того, поскольку у меня есть ощущение, что способ, описанный в варианте 2, нарушит иерархическую Z-буферизацию и другие вещи, необходимые для раннего Z, тестирование глубины в вашем шейдере должно дать такую ​​​​же производительность, что и использование встроенного теста глубины. ... Он должен выполнять традиционное тестирование глубины после затенения на принятие / отклонение. Как вы должны знать, discard отбрасывает только результаты шейдера, большую часть времени GPU должен продолжать оценивать шейдер из-за способа планирования фрагментных шейдеров (поэтому вариант 1 не заменяет Early-Z). - person Andon M. Coleman; 17.08.2013
comment
@ AndonM.Coleman Да, я знаю об отбрасывании. Зачем мне нужна иерархическая Z-буферизация для ранних z? - person darius; 02.09.2013
comment
Ранняя Z часто реализуется с использованием иерархической Z, которая эффективно сжимает копию буфера глубины в мозаичные области. Примитивы могут быть обрезаны по этим плиткам в реализации Hi-Z до того, как произойдет затенение фрагментов, вместо того, чтобы ждать завершения фрагментного шейдера и выполнения теста глубины. Но если вы discard используете фрагментный шейдер или используете слишком много буферов глубины в своей сцене, Hi-Z может быть невозможен. Это зависит от аппаратного обеспечения, у более старых поколений были строгие правила относительно того, что может сломать Hi-Z и что позволит ему продолжать работать, тестирование — единственный способ узнать наверняка. - person Andon M. Coleman; 02.09.2013