Как вершинные и фрагментные шейдеры взаимодействуют в OpenGL?

Я действительно не понимаю, как работает фрагментный шейдер.

я знаю это

  • вершинный шейдер запускается один раз для каждой вершины
  • фрагментный шейдер запускается один раз на фрагмент

Поскольку фрагментный шейдер работает не для каждой вершины, а для фрагмента, как он может отправлять данные в фрагментный шейдер? Количество вершин и количество фрагментов не равны.

Как он может решить, какой фрагмент какой вершине принадлежит?


person Simon Müller    schedule 04.02.2015    source источник


Ответы (2)


Чтобы понять это, вам нужно рассмотреть весь конвейер рендеринга. Выходные данные вершинного шейдера (помимо специального выхода gl_Position) передаются как «связанные данные» вершины на следующие этапы конвейера.

Хотя вершинный шейдер работает с одной вершиной за раз, совершенно не заботясь о примитивах, на следующих этапах конвейера все же учитывается тип примитива (и информация о связности вершин). Это то, что обычно называют «примитивной сборкой». Теперь у нас все еще есть отдельные вершины со связанными данными, созданными VS, но мы также знаем, какие вершины сгруппированы вместе, чтобы определить базовый примитив, такой как точка (1 вершина), линия (2 вершины) или треугольник (3 вершины).

Во время растеризации фрагменты генерируются для каждого местоположения пикселя в выходном растре пикселей, которое принадлежит примитиву. При этом связанные данные вершин, определяющих примитив, могут быть интерполированы по всему примитиву. В строке это довольно просто: выполняется линейная интерполяция. Назовем конечные точки A и B каждым связанным выходным вектором v, так что у нас есть v_A и v_B. Через строку мы получаем интерполированное значение для v как v (x) = (1-x) * v_A + x * v_B в каждой конечной точке, где x находится в диапазоне от 0 (в точке A) до 1 (в точке Б). Для треугольника используется барицентрическая интерполяция между данными всех трех вершин. Таким образом, хотя между вершинами и фрагментами не существует сопоставления 1: 1, выходные данные VS по-прежнему определяют значения соответствующих входных данных FS, но не напрямую, а косвенно путем интерполяции по используемому типу примитива.

поток концептуального шейдера данных в GL

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

Вы можете отключить коррекцию перспективы, используя квалификатор noperspective для переменных _3 _ / _ 4_ в GLSL. Затем используется линейная / барицентрическая интерполяция, как я ее описал.

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

person derhass    schedule 04.02.2015
comment
Так что я думаю, что мы можем применить эту формулу также к uv-координатам для текстурирования. - person Simon Müller; 05.02.2015
comment
Хорошо. Основной принцип применяется ко всем выходным переменным, и uv-координаты являются лишь одним из примеров. Я расширил свой ответ, коснувшись коррекции перспективы для интерполяции. - person derhass; 05.02.2015

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

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

person Chris Dodd    schedule 04.02.2015