Como os shaders multipass funcionam no OpenGL?

13

No Direct3D, os sombreadores multipass são simples de usar, porque você pode definir literalmente passes dentro de um programa. No OpenGL, parece um pouco mais complexo porque é possível fornecer a um programa de sombreador quantos shaders de vértice, geometria e fragmento desejar.

Um exemplo popular de um sombreador de várias passagens é um sombreador de toon. Uma passagem produz o efeito real de sombreamento e a outra cria o contorno. Se eu tiver dois shaders de vértice, "cel.vert" e "outline.vert", e dois shaders de fragmento, "cel.frag" e "outline.frag" (semelhante à maneira como você faz isso no HLSL), como posso combiná-los para criar o toon shader completo?

Não quero que você diga que um sombreador de geometria pode ser usado para isso, porque eu só quero conhecer a teoria por trás dos sombreadores GLSL multipass;)

jmegaffin
fonte
"No Direct3D, os shaders multipass são simples de usar, porque você pode literalmente definir passes dentro de um programa." Não, você não pode. Você pode definir passes no arquivo FX, mas isso não é o mesmo que um shader HLSL.
Nicol Bolas 21/03

Respostas:

11

Não há "teoria" por trás do multipass. Não há "shaders multipass". Multipass é muito simples: você desenha o objeto com um programa. Então você desenha o objeto com um programa diferente .

Você pode usar coisas do D3DX, como arquivos FX, para ocultar esses passes extras. Mas eles ainda funcionam dessa maneira. O OpenGL simplesmente não tem um esconderijo para isso.

Nicol Bolas
fonte
1

Renderize o objeto com o sombreador de célula e, em seguida, renderize-o novamente com o sombreador de contorno.

Adão
fonte
1
Então eu devo fazer dois programas shader diferentes? Por que é que eu posso dar a um programa de sombreador quantos shaders de vértice / geometria / fragmento que eu quiser? Deve haver uma maneira de fazer isso com um programa.
precisa saber é o seguinte
1
Você não está dando vários shaders de vértice (etc.). Existe apenas uma função principal, portanto você não tem vários pontos de entrada. É muito semelhante a um programa em C, pois você pode ter vários arquivos que são vinculados para criar um programa. Você pode compartilhar esses arquivos entre programas diferentes para não precisar duplicar o código, mas cada arquivo não é necessariamente um programa por si só.
quer
1
Esse é o caminho a seguir: Renderize uma vez com um par de shader (vértice + fragmento) e depois renderize novamente com outro par (ou use o mesmo vertex shader + outro fragmento shader). Às vezes, você renderiza em um buffer e usa (ainda) outro sombreador para 'misturá-lo' no resultado final.
Valmond 21/03
1

No OpenGL 4.0, existem sub-rotinas uniformes . Isso permite definir funções que podem ser trocadas em tempo de execução com muito pouca sobrecarga. Então você pode fazer 1 função para cada passe. Também 1 função para cada tipo de sombreador.

um tutorial aqui .

Para versões mais antigas do OpenGL, sua melhor aposta é ter um monte de shaders diferentes e trocar entre eles. Caso contrário, você pode adicionar valores multiplicados por um uniforme igual a 0,0 ou 1,0 para ativá-lo ou desativá-lo. Caso contrário, as instruções se condicionais puderem ser usadas, mas o OpenGL executará todos os resultados possíveis a cada execução / aprovação, para garantir que eles não sejam muito pesados.

David C. Bishop
fonte
0

Existem algumas pessoas que sabem muito sobre o GLSL, então espero que elas consigam esclarecer as coisas, mas com base no que eu vi, você deve, no seu shader de fragmentos, fazer algo assim (pseudocódigo, eu deixará o GLSL real para as pessoas que o conhecem melhor):

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 if s dessa maneira, você obtém algo como várias passagens. Isso faz sentido?

Edit: Além disso, espero que esta pergunta sobre o SO ajude a dissipar alguns mal-entendidos.

Comunidade
fonte
2
É importante observar que o GLSL executará todas as instruções nas ramificações if a cada passo, mesmo que não estejam habilitadas. Aparentemente, apenas calcular tudo e adicionar todas as cores, mas multiplicar os valores por 1,0 ou 0,0 para habilitá-las ou desabilitá-las, é mais rápido.
David C. Bishop
1
@ DavidC.Bishop: Isso não é verdade. Pelo menos, não é garantido que seja verdade. O compilador decidirá se uma ramificação real é mais rápida ou uma abordagem "calcule tudo e resolva depois" é mais rápida.
Nicol Bolas 21/03
1
Como um aparte, o comportamento referido por @ DavidC.Bishop dificilmente é específico para shaders e a GPU. As CPUs modernas que executam programas normais executam especulativamente pelo menos uma das ramificações se o valor testado ainda não estiver disponível. Se eles estiverem errados, eles retrocedem e refazem. Isso acaba gerando uma grande aceleração em média.
Ipeet 21/03