¿Cómo funcionan los sombreadores multipass en OpenGL?

13

En Direct3D, los sombreadores multipass son fáciles de usar porque literalmente puede definir pases dentro de un programa. En OpenGL, parece un poco más complejo porque es posible darle a un programa de sombreado tantos sombreadores de vértices, geometrías y fragmentos como desee.

Un ejemplo popular de un sombreador multipass es un sombreador de toon. Un pase realiza el efecto de sombreado de celdas real y el otro crea el contorno. Si tengo dos sombreadores de vértice, "cel.vert" y "outline.vert", y dos sombreadores de fragmentos, "cel.frag" y "outline.frag" (de forma similar a como lo hace en HLSL), ¿cómo puedo combinarlos para crear el sombreador de toon completo?

No quiero que digas que se puede usar un sombreador de geometría para esto porque solo quiero conocer la teoría detrás de los sombreadores GLSL multipass;)

jmegaffin
fuente
"En Direct3D, los sombreadores multipass son fáciles de usar porque literalmente puedes definir pases dentro de un programa". No, no puedes. Puede definir pases en el archivo FX, pero eso no es lo mismo que un sombreador HLSL.
Nicol Bolas

Respuestas:

11

No hay "teoría" detrás de multipass. No hay "sombreadores multipass". Multipass es muy simple: dibuja el objeto con un programa. Luego dibujas el objeto con un programa diferente .

Puede usar cosas D3DX como archivos FX para ocultar estos pases adicionales. Pero todavía funcionan de esa manera. OpenGL simplemente no tiene un escondite para ello.

Nicol Bolas
fuente
1

Renderice el objeto con el sombreador de celdas y luego vuelva a renderizarlo con el sombreador de contorno.

Adán
fuente
1
¿Entonces debería hacer dos programas de sombreado diferentes? ¿Por qué entonces puedo darle a un programa de sombreado tantos sombreadores de vértices / geometrías / fragmentos como quiera? Debe haber una manera de hacerlo con un programa.
jmegaffin
1
No le está dando múltiples sombreadores de vértices (etc.). Solo hay una función principal, por lo que no tiene múltiples puntos de entrada. Es muy similar a un programa en C en el sentido de que puede tener varios archivos que están vinculados para crear un programa. Puede compartir estos archivos entre diferentes programas para que no necesite duplicar el código, pero cada archivo no es necesariamente un programa en sí mismo.
Chewy Gumball
1
Ese es el camino a seguir, renderice una vez con un par de sombreadores (vértice + fragmento) y luego vuelva a renderizar con otro par (o use el mismo sombreador de vértices + otro sombreador de fragmentos). A veces se procesa en un búfer y se usa (aún) otro sombreador para 'mezclarlo' en el resultado final.
Valmond
1

En OpenGL 4.0 hay subrutinas uniformes . Estos le permiten definir funciones que se pueden intercambiar en tiempo de ejecución con muy poca sobrecarga. Entonces puedes hacer 1 función para cada pase. También 1 función para cada tipo de sombreador.

Hay un tutorial aquí .

Para versiones anteriores de OpenGL, su mejor opción es tener un montón de sombreadores diferentes e intercambiar entre ellos. De lo contrario, puede sumar valores que multiplique por un uniforme que sea 0.0 o 1.0 para activarlo o desactivarlo. De lo contrario, se pueden usar condicionales si se pueden usar declaraciones, pero OpenGL ejecutará todos los resultados posibles en cada ejecución / pase, así que asegúrese de que no sean demasiado pesados.

David C. Bishop
fuente
0

Hay algunas personas que saben mucho acerca de GLSL, así que espero que aclaren las cosas, pero en base a lo que he visto, en tu sombreador de fragmentos, deberías poder hacer algo como esto (pseudocódigo, I Dejaré el GLSL real a las personas que lo conocen mejor):

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

Al usar ifs de esta manera, obtienes algo así como múltiples pases. ¿Tiene sentido?

Editar: Además, espero que esta pregunta sobre SO ayude a disipar algunos malentendidos.

Comunidad
fuente
2
Es importante tener en cuenta que GLSL ejecutará todas las declaraciones en las ramas if cada paso, incluso si no están habilitadas. Aparentemente, calcular todo y sumar todos los colores, pero multiplicar los valores por 1.0 o 0.0 para habilitarlos o deshabilitarlos, es más rápido.
David C. Bishop
1
@ DavidC.Bishop: Eso no es cierto. Al menos, no se garantiza que sea cierto. El compilador decidirá si una rama real es más rápida o si un enfoque de "calcular todo y resolverlo más tarde" es más rápido.
Nicol Bolas
1
Como comentario aparte, el comportamiento al que se refiere @ DavidC.Bishop no es específico para los sombreadores y la GPU. Las CPU modernas que ejecutan programas normales ejecutarán especulativamente al menos una de las ramas si el valor probado aún no está disponible. Si resultan estar equivocados, retroceden y rehacen. Esto termina produciendo una gran aceleración en promedio.
Ipeet