Montagem de primitivas e rasterização
Como a GPU transforma triângulos no espaço de recorte em fragmentos e mistura as saídas dos vértices para cada um.
Como funciona
Entre o shader de vérticeO programa da GPU que roda uma vez para cada vértice de uma malha. Ele posiciona o vértice na tela escrevendo gl_Position e repassa valores para o shader de fragmento. Shader de vértice O programa da GPU que roda uma vez para cada vértice de uma malha. Ele posiciona o vértice na tela escrevendo gl_Position e repassa valores para o shader de fragmento. Shader de fragmento O programa da GPU que roda uma vez para cada pixel coberto por um triângulo e decide a cor dele.normalize() e por que as bordas saem serrilhadas.
Dos vértices a um triângulo na tela
- A montagem de primitivas agrupa os vértices em triângulos, do jeito que a chamada de desenho pediu: com
gl.TRIANGLES, cada três vértices formam um; comgl.TRIANGLE_STRIP, cada vértice novo forma um triângulo com os dois anteriores. - O recorte (clipping) mantém só o que está dentro do volume visível, onde −w ≤ x, y, z ≤ w. Um triângulo que cruza a borda é cortado em triângulos menores que não cruzam; um que está todo fora é descartado.
- A divisão de perspectiva transforma o espaço de recorte em coordenadas normalizadas de dispositivo (NDC):
xyz / w. As três terminam entre −1 e 1 e, como uma projeção em perspectiva guarda a distância emw, o que está longe encolhe em direção ao centro. - A transformação de viewport leva as NDC para pixels, conforme
gl.viewport(x, y, width, height): −1..1 vira 0..largura e 0..altura, e z vira uma profundidade entre 0 e 1. - O descarte de faces (face culling), quando ligado, elimina triângulos pelo lado para o qual estão virados. A
ordem dos três vértices na tela, anti-horária por padrão, diz qual lado é a frente;
gl.enable(gl.CULL_FACE)comgl.cullFace(gl.BACK)pula as faces de trás de malhas fechadas, que não apareceriam de qualquer jeito.
Quais pixels um triângulo cobre
Em seguida o rasterizador testa os centros dos pixels contra as três arestas do triângulo. Todo centro que cai dentro vira um fragmento. Um centro que cai exatamente sobre uma aresta compartilhada por dois triângulos tem que ir para exatamente um deles: a especificação do OpenGL ES exige que nenhum pixel seja perdido nem desenhado duas vezes ao longo de uma aresta compartilhada, então as malhas não têm frestas e malhas transparentes não mostram costuras dobradas.
Uma amostra por pixel significa que o pixel está dentro ou fora, e é daí que vêm as bordas serrilhadas, em escadinha. O multisampling (MSAA) testa vários pontos de amostra por pixel; como essas amostras são guardadas e combinadas é assunto das operações por amostra, que vêm depois.
Interpolação
Cada fragmento recebe o seu próprio valor de cada varyingUm valor que o shader de vértice escreve para cada vértice (um out) e o shader de fragmento lê (um in). O rasterizador o mistura ao longo do triângulo, então cada pixel recebe a sua própria mistura. Varying Um valor que o shader de vértice escreve para cada vértice (um out) e o shader de fragmento lê (um in). O rasterizador o mistura ao longo do triângulo, então cada pixel recebe a sua própria mistura.w enquanto
mistura, então uma textura num chão que se estende ao longe mantém as linhas retas em vez de entortá-las.
A interpolação é linear, e isso tem consequências:
- Vetores unitários encolhem. A mistura de duas normaisUm vetor unitário perpendicular a uma superfície, apontando para onde ela está virada. Iluminação e efeitos Fresnel o comparam com outras direções.Fundamento
Normal
Um vetor unitário perpendicular a uma superfície, apontando para onde ela está virada. Iluminação e efeitos Fresnel o comparam com outras direções.
Fontesde comprimento 1 é mais curta que 1, então o código de iluminação chamanormalize()nas normais interpoladas antes de usá-las. - Curvas viram retas. Um valor calculado por vértice só está certo nos vértices; entre eles é uma mistura plana. É por isso que um brilho calculado por vértice fica facetado.
- Alguns valores não deveriam ser misturados. Um id ou o índice de uma célula não faz sentido pela metade. O
qualificador
flatdesliga a interpolação de um varying: todo fragmento recebe o valor de um único vértice, o último de cada triângulo no OpenGL ES.
// no shader de vértice
flat out int vMaterialId; // sem mistura: o triângulo inteiro recebe o valor de um vértice
out vec3 vNormal; // misturado, com correção de perspectiva (o padrão, "smooth")
// no shader de fragmento
flat in int vMaterialId;
in vec3 vNormal;
// ...
vec3 n = normalize(vNormal); // mais curto que 1 entre os vértices, então normalize primeiro
Varyings inteiros precisam ser flat; o GLSL ES 3.00 não os interpola.
No Godot e neste site
A rasterização também não é programável no Godot. Um shader controla as partes que pode: o descarte de faces com
render_mode cull_back (o padrão), cull_front ou cull_disabled para superfícies de dois lados, e o que chega ao
fragment() por declarações varying.
Neste site, o cull de uma passada no render.json é "back", "front" ou "none". O contorno por casca invertida
usa "front": ele desenha uma cópia levemente inflada da malha com as faces da frente descartadas, então só as faces
de trás aparecem em volta da silhueta.