O que é largura e como funciona?


18

A documentação do OpenGL indica essa largura returns the sum of the absolute value of derivatives in x and y.

O que isso significa em termos menos matemáticos, e existe uma maneira de visualizá-lo?

Com base no meu entendimento da função, fwidth(p)tem acesso ao valor de pem pixels vizinhos. Como isso funciona na GPU sem afetar drasticamente o desempenho e funciona de maneira confiável e uniforme em todos os pixels?

Respostas:


18

Derivados tela de espaço pixel fazer drasticamente o desempenho de impacto, mas eles afetar o desempenho se você usá-los ou não, então a partir de um certo ponto de vista eles estão livres!

Todas as GPUs da história recente agrupam um quadrilátero de quatro pixels e as colocam na mesma frente de onda / distorção, o que significa essencialmente que elas estão rodando próximas uma da outra na GPU, portanto, acessar valores a partir deles é muito barato. Como os warps / frentes de onda são executados em passo de bloqueio, os outros pixels também estarão exatamente no mesmo lugar no shader que você, portanto, o valor pdesses pixels estará apenas em um registro esperando por você. Esses outros três pixels sempre serão executados, mesmo que seus resultados sejam descartados. Portanto, um triângulo que cubra um único pixel sempre sombreia quatro pixels e joga fora os resultados de três deles, apenas para que esses recursos derivados funcionem!

Isso é considerado um custo aceitável (para o hardware atual), porque não são apenas funções como fwidthessas que usam esses derivados: todas as amostras de textura também o fazem, para escolher o mapa mip da sua textura. Considere: se você estiver muito próximo de uma superfície, a coordenada UV usada para amostrar a textura terá uma derivada muito pequena no espaço da tela, o que significa que você precisará usar um mapa mip maior e, se estiver mais longe, a coordenada UV terá uma derivada maior no espaço da tela, o que significa que você precisa usar um mapa mip menor.

Na medida em que isso significa em termos menos matemáticos: fwidthé equivalente a abs(dFdx(p)) + abs(dFdy(p)). dFdx(p)é simplesmente a diferença entre o valor de pno pixel x + 1 e o valor de pno pixel x, e da mesma forma para dFdy(p).


Portanto, se dFdx(p) = p(x1) - p(x), então x1pode ser um (x+1)ou (x-1), dependendo da posição do pixel xno quad. De qualquer maneira, x1deve estar no mesmo warp / wavefront que x. Estou correcto?
ApoorvaJ

3
@ApoorvaJ Essencialmente, o mesmo valor para dFdxé calculado para cada um dos 2 pixels vizinhos na grade 2x2. E esse valor é calculado usando a diferença entre os dois valores vizinhos, se isso é p(x+1)-p(x)ou p(x)-p(x-1)depende apenas da sua noção do que xé exatamente aqui. O resultado é o mesmo, no entanto. Então sim, você está correto.
Chris diz Reinstate Monica

@ChristianRau Isso responde à minha pergunta. Obrigado.
ApoorvaJ

11

Em termos inteiramente técnicos, fwidth(p)é definido como

fwidth(p) := abs(dFdx(p)) + abs(dFdy(p))

E dFdx(p)/ dFdy(p)são as derivadas parciais do valor pem relação às dimensões xe da ytela. Portanto, eles denotam como o valor de pse comporta ao passar um pixel para a direita ( x) ou um pixel para cima ( y).

Agora, como eles podem ser praticamente computados? Bem, se você conhece os valores dos pixels vizinhos p, pode apenas computar essas derivadas como diferenças finitas diretas como uma aproximação para suas derivadas matemáticas reais (que podem não ter uma solução analítica exata):

dFdx(p) := p(x+1) - p(x)

Mas é claro que agora você pode perguntar: como sabemos os valores de p(que depois poderiam ser qualquer valor arbitrariamente calculado dentro do programa shader) para os pixels vizinhos? Como calculamos esses valores sem incorrer em grandes despesas gerais, fazendo todo o cálculo do shader duas (ou três) vezes?

Bem, você sabe, esses valores vizinhos são calculados de qualquer maneira, já que para o pixel vizinho você também executa um shader de fragmento. Portanto, tudo o que você precisa é de acesso a essa chamada de shader de fragmento vizinho quando executado para o pixel vizinho. Mas é ainda mais fácil, porque esses valores vizinhos também são calculados ao mesmo tempo.

Os rasterizadores modernos chamam shaders de fragmentos em blocos maiores de mais de um pixel vizinho. No menor, esses seriam uma grade de pixels 2x2. E para cada um desses blocos de pixels, o sombreador de fragmento é chamado para cada pixel e essas invocações são executadas em uma etapa de bloqueio perfeitamente paralela, de modo que todos os cálculos sejam feitos exatamente na mesma ordem e no mesmo momento para cada um desses pixels no bloco (é também por isso que a ramificação no shader de fragmento, embora não seja mortal, deve ser evitada, se possível, pois cada invocação de um bloco deve explorar todos os ramos que são capturados por pelo menos uma das invocações, mesmo que apenas jogue fora os resultados posteriormente, como também foi abordado nas respostas a esta pergunta relacionada) Portanto, a qualquer momento, um shader de fragmento teoricamente tem acesso aos valores do shader de fragmento dos pixels vizinhos. E enquanto você não tem acesso direto a esses valores, você tem acesso a valores computados a partir deles, como as funções de derivativos dFdx, dFdy, fwidth, ...

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.