Operações por amostra
Os testes e combinações fixos entre a saída do shader de fragmento e o pixel que de fato muda.
Como funciona
Quando o shader de fragmentoO programa da GPU que roda uma vez para cada pixel coberto por um triângulo e decide a cor dele. Shader de fragmento O programa da GPU que roda uma vez para cada pixel coberto por um triângulo e decide a cor dele.
Nenhuma delas é escrita em GLSL. O código host as liga e configura (gl.enable, gl.depthFunc, gl.blendFunc e
companhia) antes da chamada de desenho, e elas valem para tudo o que essa chamada produzir.
A ordem
A especificação do OpenGL ES 3.0 as lista nesta ordem (seção 4.1):
- Posse do pixel: o pixel pertence a este contexto (uma janela coberta, por exemplo, pode não pertencer).
- Teste de tesoura (scissor): descarta fragmentos fora de um retângulo, definido com
gl.scissor(). Um recorte barato para painéis de interface e tela dividida. - Operações de multisample: com MSAA, decidem quais amostras do pixel o fragmento cobre. Com
gl.SAMPLE_ALPHA_TO_COVERAGEligado, o alfa da saída também remove amostras, transformando alfa em cobertura com borda suave. - Teste de stencil: compara o valor do pixel no buffer de stencil, um inteiro pequeno por amostra, com uma referência, e o atualiza. É uma máscara que a própria cena desenha: portais, espelhos, contornos desenhados só do lado de fora de um objeto.
- Teste de profundidade: compara a profundidade do fragmento com a guardada e fica só com a mais próxima (veja o
buffer de profundidadeUma imagem do tamanho do framebuffer que guarda, para cada pixel, a distância da superfície mais próxima desenhada até agora. O teste de profundidade a usa para esconder o que fica atrás.Renderização
Buffer de profundidade
Uma imagem do tamanho do framebuffer que guarda, para cada pixel, a distância da superfície mais próxima desenhada até agora. O teste de profundidade a usa para esconder o que fica atrás.
Fontes). - Blending: combina a cor do fragmento com a cor já guardada, em vez de substituí-la (veja
blendingCombinar a cor que o shader de fragmento devolve com a cor que já está no framebuffer, em vez de substituí-la. Transparência, brilho aditivo e multiplicação vêm daí.Renderização
Blending
Combinar a cor que o shader de fragmento devolve com a cor que já está no framebuffer, em vez de substituí-la. Transparência, brilho aditivo e multiplicação vêm daí.
result = src × srcFactor + dst × dstFactorFontes). - Conversão para sRGB e dithering: codificam a cor para um alvo sRGB, se houver, e opcionalmente aplicam dithering para a profundidade de bits do alvo.
Aí o resultado é escrito, passando pelas máscaras de escrita: gl.colorMask() pode bloquear canais isolados, e
gl.depthMask(false) mantém o teste de profundidade funcionando sem mexer na profundidade guardada. Um fragmento que
falha em qualquer teste, ou que o shader descartou, não escreve nada.
Configurando
Um quadro típico liga e desliga essas etapas entre chamadas de desenho. Primeiro a geometria opaca, com profundidade testada e escrita; depois a transparente, com profundidade testada mas não escrita, misturada por cima:
// passada opaca
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LESS);
gl.depthMask(true);
gl.disable(gl.BLEND);
drawOpaque();
// passada transparente: escondida por paredes na frente, mas não esconde o que está atrás dela
gl.depthMask(false);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
drawTransparent();
Por amostra, e cedo
Com MSAA, cada pixel guarda várias amostras, e o shader de fragmento continua rodando uma vez por pixel para cada triângulo. Os testes e a escrita acontecem por amostra, usando a cobertura que o rasterizador encontrou, então a aresta de um triângulo corta um pixel em parte; no fim as amostras viram uma média (o resolve) numa cor só. É assim que o MSAA suaviza as bordas sem sombrear cada amostra.
A especificação descreve os testes de profundidade e de stencil rodando depois do shader, mas rodar um shader cujo
resultado será jogado fora é perda de tempo, então as GPUs testam antes sempre que o resultado não pode mudar. Duas
coisas no shader impedem isso: escrever gl_FragDepth, porque a profundidade só é conhecida quando o shader termina,
e discard em muitas GPUs, porque o fragmento pode sumir depois do teste. Deixe os dois fora de shaders que cobrem
boa parte da tela, a não ser que precise deles.
No Godot e neste site
O Godot configura essas etapas pelo material e pelo render_mode do shader, em vez de código host: blend_mix,
blend_add, blend_sub, blend_mul e blend_premul_alpha para o blending; depth_draw_opaque,
depth_draw_always e depth_draw_never para a escrita de profundidade; depth_test_disabled para pular o teste;
alpha_to_coverage para a cobertura no MSAA. Escrever ALPHA leva o material para o pipeline transparente,
desenhado depois do opaco.
Neste site, o blend e o alphaClip de uma passada no render.json escolhem uma combinação: "opaque" é testado e
escrito, "alpha" e "additive" são testados e misturados mas não mexem na profundidade, e alphaClip descarta os
fragmentos abaixo de um limiar.