Como os níveis do mipmap são computados no Metal?

14

Minha pergunta é específica em relação ao Metal, pois não sei se a resposta mudaria para outra API.

O que acredito entender até agora é o seguinte:

  • Uma textura de mapa mip pré-computou "níveis de detalhe", onde níveis mais baixos de detalhes são criados diminuindo a amostragem da textura original de alguma maneira significativa.

  • Os níveis do Mipmap são referidos no nível descendente de detalhes, em que level 0é a textura original e os níveis mais altos são duas reduções.

  • A maioria das GPUs implementa filtragem trilinear, que seleciona dois níveis de mipmap vizinhos para cada amostra, amostras de cada nível usando filtragem bilinear e, em seguida, combina linearmente essas amostras.

O que não entendo direito é como esses níveis de mipmap são selecionados. Na documentação da biblioteca padrão do Metal, vejo que amostras podem ser coletadas, com ou sem a especificação de uma instância de um lod_optionstipo. Eu diria que esse argumento altera a maneira como os níveis do mipmap são selecionados e, aparentemente, existem três tipos de lod_optionstexturas 2D:

  • bias(float value)
  • level(float lod)
  • gradient2d(float2 dPdx, float2 dPdy)

Infelizmente, a documentação não se preocupa em explicar o que essas opções fazem. Posso supor que isso bias()incline algum nível de detalhe escolhido automaticamente, mas o que significa esse viés value? Em que escala ele opera? Da mesma forma, como é a lodde level()traduzido em níveis mipmap discretos? E, operando sob a suposição de que gradient2d()usa o gradiente da coordenada da textura, como ele usa esse gradiente para selecionar o nível do mipmap?

Mais importante, se eu omitir o lod_options, como os níveis do mipmap são selecionados? Isso difere dependendo do tipo de função que está sendo executada?

E, se a operação padrão da função especificada no-lod-options sample()for fazer algo como gradient2D()(pelo menos em um shader de fragmento), utiliza derivadas simples do espaço da tela ou trabalha diretamente com rasterizador e coordenadas de textura interpoladas calcular um gradiente preciso?

E, finalmente, qual é a consistência desse comportamento de dispositivo para dispositivo? Um artigo antigo (antigo como o DirectX 9) que li referia-se à seleção complexa de mipmap específica de dispositivo, mas não sei se a seleção de mipmap é melhor definida em arquiteturas mais recentes.

lcmylin
fonte

Respostas:

17

Atualmente, a seleção de mip é muito bem padronizada entre os dispositivos - com exceção de alguns detalhes detalhados da filtragem anisotrópica, que ainda depende dos fabricantes individuais de GPU (e seus detalhes precisos geralmente não são documentados publicamente).

Um bom lugar para ler sobre a seleção mip em detalhes está na especificação OpenGL, seção 8.14, "Minificação de texturas" . Eu diria que funciona da mesma maneira no Metal. (A Apple poderia ter mudado alguma coisa, considerando que eles fazem tanto o hardware quanto a API ... mas duvido que tenham mudado.) Vou resumir aqui.

A seleção mip padrão (sem usar nenhum dos lod_optionsmodificadores) usa os gradientes de espaço da tela das coordenadas de textura para selecionar os níveis de mip. Essencialmente, ele tenta escolher os níveis mip que produzem o mais próximo possível de um mapeamento 1: 1 de texels para pixels. Por exemplo, se os gradientes tiverem um comprimento de 4 texels por pixel, ele escolherá o nível mip 2 (que é 1/4 do tamanho do nível 0 e, portanto, fornecerá 1 texel mipped por pixel).

Com a filtragem trilinear, como você geralmente não pousa em um mapeamento exato de 1: 1, ele seleciona os dois níveis mais próximos e interpola linearmente entre eles, para que você tenha uma transição suave entre os níveis de mip conforme a câmera ou os objetos em sua cena se movem por aí.

Para ser matematicamente preciso: você pega os gradientes de espaço da tela das coordenadas da textura, dimensiona-os pelo tamanho da textura (para obtê-los em unidades de "texels por pixel"), mede seus comprimentos, mede o maior dos dois (X e Y) gradientes e calcule sua base 2. do logaritmo. Essa quantidade é chamada na especificação OpenGL; também é conhecido como textura LOD (nível de detalhe). É simplesmente uma versão contínua do índice de nível mip inteiro. Portanto, os níveis de mip usados ​​são os dois números inteiros mais próximos de , e a parte fracionária de é usada para se misturar entre eles. Por exemplo, se , a GPU fará uma amostra dos níveis mip 2 e 3 da textura e depois se misturará com 80% nível 3 e 20% nível 2.λλλλ=2.8

Se a filtragem anisotrópica estiver ativada, em vez de simplesmente usar o mais longo dos dois gradientes, use a proporção deles para definir o número de amostras de aniso. Por exemplo, se o gradiente X for 4 vezes maior que o gradiente Y, você usaria 4 amostras de aniso, com suas posições espaçadas ao longo do gradiente X. Cada uma seria uma amostra trilinear, usando um correspondente a 1/4 do comprimento do gradiente X (ou seja, dois níveis de mip mais baixos, uma vez que ).λregistro2(1/4)=-2

Agora, quanto às opções do modificador:

  • biasaplica um deslocamento a antes de usá-lo para selecionar níveis de mip. Assim, por exemplo, um viés de +1 fará com que a filtragem trilinear use mips um nível mais alto do que normalmente. Um viés de -1 usará mips um nível mais baixo, e assim por diante.λ
  • levelsubstitui completamente o cálculo automático de partir de gradientes de espaço na tela e permite que você coloque seu próprio valor de (aka ) diretamente.λλlod
  • gradient2dpermite inserir seus próprios vetores de gradiente, que substituem os gradientes implícitos no espaço da tela das coordenadas da textura. O restante do processo de seleção e amostragem do mip continua como normal, mas com os gradientes alterados. Isso permite personalizar a filtragem anisotrópica.

Em outros tipos de sombreadores além dos fragmentos, não há noção de "gradientes de espaço na tela"; portanto, as duas últimas operações são normalmente as únicas permitidas - qualquer operação que tente usar gradientes implícitos causaria um erro de compilação. Não sei se é assim que o Metal faz, mas é o que eu esperaria de trabalhar com outras APIs.

Nathan Reed
fonte