Come funzionano gli shader multipass in OpenGL?


13

In Direct3D, gli shader multipass sono semplici da usare perché puoi letteralmente definire i passaggi all'interno di un programma. In OpenGL, sembra un po 'più complesso perché è possibile fornire a un programma shader tutti gli shader di vertice, geometria e frammento desiderati.

Un esempio popolare di shader multipass è un toon shader. Un passaggio esegue l'effetto cel-shading effettivo e l'altro crea il contorno. Se ho due shader di vertice, "cel.vert" e "outline.vert", e due shader di frammento, "cel.frag" e "outline.frag" (simile al modo in cui lo si fa in HLSL), come posso combinarli per creare lo shader full toon?

Non voglio che tu dica che uno shader di geometria può essere usato per questo perché voglio solo conoscere la teoria dietro gli shader GLSL multipass;)


"In Direct3D, gli shader multipass sono semplici da usare perché puoi letteralmente definire i passaggi all'interno di un programma." No non puoi. Puoi definire i passaggi nel file FX, ma non è lo stesso di uno shader HLSL.
Nicol Bolas,

Risposte:


11

Non esiste una "teoria" dietro il multipass. Non ci sono "shader multipass". Multipass è molto semplice: si disegna l'oggetto con un programma. Quindi si disegna l'oggetto con un programma diverso .

Puoi usare oggetti D3DX come i file FX per nascondere questi passaggi extra. Ma funzionano ancora in quel modo. OpenGL semplicemente non ha un nascondiglio per questo.


1

Renderizza l'oggetto con lo shader di cella, quindi esegui nuovamente il rendering con lo shader di contorno.


1
Quindi dovrei fare due diversi programmi shader? Perché è allora che posso dare un programma shader quanti vertici / geometria / frammenti shader voglio? Ci deve essere un modo per farlo con un solo programma.
Jmegaffin,

1
Non gli stai dando più shader di vertici (ecc.). C'è solo una funzione principale, quindi non hai più punti di ingresso. È molto simile a un programma C in quanto è possibile avere più file collegati tra loro per creare un programma. È possibile condividere questi file tra programmi diversi, quindi non è necessario duplicare il codice, ma ogni file non è necessariamente un programma da solo.
Chewy Gumball,

1
Questa è la strada da percorrere, Rendering una volta con una coppia di shader (vertice + frammento), quindi esegui nuovamente il rendering con un'altra coppia (o usa lo stesso shader di vertice + un altro shader di frammento). A volte esegui il rendering in un buffer e usi (ancora) un altro shader per "fonderlo" nel risultato finale.
Valmond,

1

In OpenGL 4.0 ci sono subroutine uniformi . Ciò consente di definire funzioni che possono essere scambiate in fase di esecuzione con un sovraccarico minimo. Quindi puoi fare 1 funzione per ogni passaggio. Inoltre 1 funzione per ogni tipo di shader.

C'è un tutorial qui .

Per le versioni precedenti di OpenGL la soluzione migliore è quella di avere un sacco di shader diversi e scambiarli tra loro. Altrimenti puoi sommare i valori che moltiplichi per un'uniforme che è 0,0 o 1,0 per accenderlo o spegnerlo. Altrimenti, se le istruzioni possono essere utilizzate, ma OpenGL eseguirà tutti i possibili risultati ad ogni esecuzione / passaggio, quindi assicurati che non siano troppo pesanti.


0

Ci sono alcune persone che sanno molto della GLSL, quindi spero che arrivino a mettere le cose in chiaro, ma in base a quello che ho visto, dovresti essere in grado di fare qualcosa del genere nel tuo shader di frammenti (pseudocodice, I lascerò l'attuale GLSL alle persone che lo conoscono meglio):

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

Usando ifs in questo modo, ottieni qualcosa come passaggi multipli. Ha senso?

Modifica: Inoltre, si spera che questa domanda su SO aiuti a dissipare alcuni malintesi.


2
È importante notare che GLSL eseguirà tutte le istruzioni nei rami if ogni passaggio anche se non sono abilitate. Apparentemente basta calcolare tutto e sommare tutti i colori ma moltiplicare i valori per 1,0 o 0,0 per abilitarli o disabilitarli è più veloce.
David C. Bishop,

1
@ DavidC.Bishop: Questo non è vero. Almeno, non è garantito che sia vero. Il compilatore deciderà se un ramo effettivo è più veloce o più veloce è l'approccio "calcola tutto e risolverlo in un secondo momento".
Nicol Bolas,

1
A parte questo, il comportamento a cui fa riferimento @ DavidC.Bishop è poco specifico per gli shader e la GPU. Le moderne CPU che eseguono programmi normali eseguiranno speculativamente almeno uno dei rami se il valore testato non è ancora disponibile. Se si rivelano errati, tornano indietro e rifanno. Questo finisce per produrre un grande speedup in media.
ipeet,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.