Как работают многопроходные шейдеры в OpenGL?

13

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

Популярный пример многопроходного шейдера - toon shader. Один проход создает реальный эффект затенения, а другой создает контур. Если у меня есть два вершинных шейдера, «cel.vert» и «outline.vert», и два фрагментных шейдера, «cel.frag» и «outline.frag» (аналогично тому, как вы это делаете в HLSL), как я могу это сделать? объединить их, чтобы создать полный шейдер Toon?

Я не хочу, чтобы вы говорили, что для этого можно использовать геометрический шейдер, потому что я просто хочу знать теорию, лежащую в основе многопроходных шейдеров GLSL;)

jmegaffin
источник
«В Direct3D многопроходные шейдеры просты в использовании, потому что вы можете буквально определять проходы в программе». Нет, ты не можешь. Вы можете определить проходы в файле FX, но это не то же самое, что шейдер HLSL.
Никол Болас

Ответы:

11

За многопроходностью нет «теории». Здесь нет «многопроходных шейдеров». Многопроходность очень проста: вы рисуете объект одной программой. Затем вы рисуете объект с другим программой.

Вы можете использовать D3DX, такие как файлы FX, чтобы скрыть эти дополнительные проходы. Но они все еще работают таким образом. У OpenGL просто нет тайника для этого.

Николь Болас
источник
1

Визуализируйте объект с помощью шейдерного элемента, а затем повторно отрендерите его с помощью контурного шейдера.

Адам
источник
1
Так я должен сделать две разные шейдерные программы? Почему же тогда я могу дать шейдерной программе столько вершинных / геометрических / фрагментных шейдеров, сколько захочу? Должен быть способ сделать это с помощью одной программы.
jmegaffin
1
Вы не даете ему несколько вершинных (и т. Д.) Шейдеров. Существует только одна основная функция, поэтому у вас нет нескольких точек входа. Это очень похоже на программу на C, в которой вы можете создать несколько файлов, связанных между собой. Вы можете обмениваться этими файлами между различными программами, поэтому вам не нужно дублировать код, но каждый файл не обязательно является программой сам по себе.
Chewy Gumball
1
Это путь, рендеринг один раз с одной парой шейдеров (вершина + фрагмент), затем рендеринг снова с другой парой (или используйте тот же вершинный шейдер + другой фрагментный шейдер). Иногда вы рендеритесь в буфер и используете (пока) другой шейдер, чтобы «смешать» его с конечным результатом.
Valmond
1

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

Здесь есть учебник .

Для более старых версий OpenGL лучше всего иметь несколько разных шейдеров и переключаться между ними. В противном случае вы можете сложить значения, которые вы умножаете на форму, равную 0,0 или 1,0, чтобы включить или выключить ее. В противном случае можно использовать условные операторы, но OpenGL будет выполнять все возможные результаты при каждом выполнении / проходе, поэтому убедитесь, что они не слишком тяжелые.

Дэвид С. Бишоп
источник
0

Есть некоторые люди, которые много знают о GLSL, так что я надеюсь, что они установят рекорд, но на основании того, что я видел, вы сможете в своем фрагментном шейдере сделать что-то подобное (псевдокод, я оставлю фактический GLSL до людей, которые знают это лучше):

out vec4 fragment_color
vec4 final_color
final_color = flat_color
If this fragment is in shadow
    final_color = shadow_color
If this fragment is an edge
    final_color = edge_color
If this fragment is a highlight
    final_color = highlight_color
fragment_color = final_color

Используя ifs таким способом, вы получаете что-то вроде нескольких проходов. Имеет ли это смысл?

Изменить: Кроме того, надеюсь, этот вопрос о SO помогает рассеять некоторые недоразумения.

Сообщество
источник
2
Важно отметить, что GLSL будет выполнять все операторы в ветвях if каждый проход, даже если они не включены. По-видимому, просто вычислить все и сложить все цвета вместе, но умножить значения на 1,0 или 0,0, чтобы включить или отключить их, быстрее.
Дэвид С. епископ
1
@ DavidC.Bishop: Это не правда. По крайней мере, не гарантируется быть правдой. Компилятор решит, является ли фактическая ветвь быстрее или подход «вычислить все и разобраться позже» будет быстрее.
Никол Болас
1
Кроме того, поведение, на которое ссылается @ DavidC.Bishop, вряд ли характерно для шейдеров и графического процессора. Современные процессоры, выполняющие обычные программы, умозрительно выполняют по крайней мере одну из ветвей, если проверенное значение еще не доступно. Если они оказываются неправы, они отступают и переделывают. В итоге это приводит к большому ускорению.
ipeet