Comment les shaders multipass fonctionnent-ils dans OpenGL?

13

Dans Direct3D, les shaders multipass sont simples à utiliser car vous pouvez littéralement définir des passes dans un programme. Dans OpenGL, cela semble un peu plus complexe car il est possible de donner à un programme de shaders autant de vertex, de géométries et de shaders de fragments que vous le souhaitez.

Un exemple populaire de shader multipasse est un shader toon. Un passage fait l'effet cel-shading réel et l'autre crée le contour. Si j'ai deux vertex shaders, "cel.vert" et "outline.vert", et deux fragments shaders, "cel.frag" et "outline.frag" (similaire à la façon dont vous le faites dans HLSL), comment puis-je les combiner pour créer le shader toon complet?

Je ne veux pas que vous disiez qu'un shader de géométrie peut être utilisé pour cela parce que je veux juste connaître la théorie derrière les shaders GLSL multipass;)

jmegaffin
la source
"Dans Direct3D, les shaders multipass sont simples à utiliser car vous pouvez littéralement définir des passes dans un programme." Non tu ne peux pas. Vous pouvez définir des passes dans le fichier FX, mais ce n'est pas la même chose qu'un shader HLSL.
Nicol Bolas

Réponses:

11

Il n'y a pas de "théorie" derrière le multipass. Il n'y a pas de "shaders multipass". Le multipass est très simple: vous dessinez l'objet avec un seul programme. Ensuite, vous dessinez l'objet avec un programme différent .

Vous pouvez utiliser des éléments D3DX comme des fichiers FX pour masquer ces passes supplémentaires. Mais ils fonctionnent toujours de cette façon. OpenGL n'a tout simplement pas de cachette pour cela.

Nicol Bolas
la source
1

Rendre l'objet avec le shader de cellule, puis le restituer avec le shader de contour.

Adam
la source
1
Je devrais donc faire deux programmes de shader différents? Pourquoi alors puis-je donner à un programme de shaders autant de shaders de vertex / géométrie / fragments que je veux? Il doit y avoir un moyen de le faire avec un seul programme.
jmegaffin
1
Vous ne lui donnez pas plusieurs vertex (etc.) Shaders. Il n'y a qu'une seule fonction principale, vous n'avez donc pas plusieurs points d'entrée. Il est très similaire à un programme C en ce sens que vous pouvez avoir plusieurs fichiers liés entre eux pour créer un programme. Vous pouvez partager ces fichiers entre différents programmes, vous n'avez donc pas besoin de dupliquer le code, mais chaque fichier n'est pas nécessairement un programme en soi.
Chewy Gumball
1
C'est la voie à suivre, effectuer le rendu une fois avec une paire de shaders (sommet + fragment), puis effectuer un nouveau rendu avec une autre paire (ou utiliser le même vertex shader + un autre fragment shader). Parfois, vous effectuez un rendu dans un tampon et utilisez (encore) un autre shader pour le «fondre» dans le résultat final.
Valmond
1

Dans OpenGL 4.0, il existe des sous-programmes uniformes . Ceux-ci vous permettent de définir des fonctions qui peuvent être échangées au moment de l'exécution avec très peu de surcharge. Vous pouvez donc créer 1 fonction pour chaque passe. Aussi 1 fonction pour chaque type de shader.

Il y a un tutoriel ici .

Pour les anciennes versions d'OpenGL, le mieux est d'avoir un tas de shaders différents et de les échanger. Sinon, vous pouvez additionner les valeurs que vous multipliez par un uniforme de 0,0 ou 1,0 pour l'activer ou le désactiver. Sinon, conditionnel si des instructions peuvent être utilisées, mais OpenGL exécutera tous les résultats possibles à chaque exécution / passage, alors assurez-vous qu'ils ne sont pas trop lourds.

David C. Bishop
la source
0

Il y a des gens qui connaissent beaucoup le GLSL, donc j'espère qu'ils viendront remettre les pendules à l'heure, mais d'après ce que j'ai vu, vous devriez pouvoir, dans votre fragment shader, faire quelque chose comme ça (pseudocode, je Je laisserai le GLSL réel aux personnes qui le connaissent mieux):

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

En utilisant ifs de cette manière, vous obtenez quelque chose comme plusieurs passes. Cela a-t-il du sens?

Edit: En outre, j'espère que cette question sur SO aide à dissiper certains malentendus.

Communauté
la source
2
Il est important de noter que GLSL exécutera toutes les instructions dans les branches if à chaque passage, même si elles ne sont pas activées. Apparemment, tout calculer et ajouter toutes les couleurs ensemble, mais multiplier les valeurs par 1,0 ou 0,0 pour les activer ou les désactiver est plus rapide.
David C. Bishop
1
@ DavidC.Bishop: Ce n'est pas vrai. Du moins, ce n'est pas garanti d'être vrai. Le compilateur décidera si une branche réelle est plus rapide ou une approche "tout calculer et trier plus tard" est plus rapide.
Nicol Bolas
1
Soit dit en passant, le comportement évoqué par @ DavidC.Bishop n'est guère spécifique aux shaders et au GPU. Les processeurs modernes exécutant des programmes normaux exécuteront de manière spéculative au moins une des branches si la valeur testée n'est pas encore disponible. S'ils se trompent, ils reculent et recommencent. Cela finit par donner une grande accélération en moyenne.
ipeet