Usando resolução total do buffer de profundidade para renderização 2D

9

Estou trabalhando em um renderizador de frente para trás para um mecanismo 2D usando uma projeção ortográfica. Eu quero usar o buffer de profundidade para evitar excesso. Eu tenho um buffer de profundidade de 16 bits, uma câmera em Z = 100 olhando para Z = 0, zNear é 1 e zFar é 1000. Cada sprite renderizado define suas coordenadas Z para valores cada vez mais distantes, permitindo que o teste de profundidade ignore a renderização qualquer coisa que está por baixo.

No entanto, eu sei que a maneira como as posições Z acabam com os valores do buffer Z não é linear. Eu quero fazer uso da resolução completa do buffer de profundidade de 16 bits, ou seja, permitindo 65536 valores exclusivos. Portanto, para cada sprite renderizado, quero incrementar a posição Z para a próxima posição para correlacionar com o próximo valor exclusivo do buffer de profundidade.

Em outras palavras, eu quero transformar um índice incremental (0, 1, 2, 3 ...) do sprite sendo atraído para a posição Z apropriada para cada sprite para ter um valor único de buffer de profundidade. Eu não tenho certeza da matemática por trás disso. Qual é o cálculo para fazer isso?

Observe que estou trabalhando no WebGL (basicamente OpenGL ES 2) e preciso oferecer suporte a uma ampla gama de hardware, portanto, embora extensões como gl_FragDepth possam facilitar isso, não posso usá-lo por motivos de compatibilidade.

AshleysBrain
fonte
Não consigo imaginar que o uso do buffer z ofereça muito ganho de desempenho (se houver) depois de adicionar toda a escrita, cálculos e comparações do buffer z contra copiar texturas de trás para frente, sem mencionar qualquer transparência / mistura alfa desgraças.
Matt Esch
@MattEsch: A idéia é que todos esses cálculos sejam feitos na GPU em velocidades incrivelmente altas, por isso faz sentido fazer isso.
Panda Pyjama
@MattEsch: O FWIW é voltado para GPUs integradas da Intel, que usam memória do sistema em vez de memória dedicada da GPU. Isso os torna muito lentos e passíveis de atingir os limites de taxa de preenchimento se você desenhar muitos sprites. A Intel recomendou essa abordagem para mim como uma maneira de contornar isso. Presumivelmente, a implementação dos testes de profundidade está bem otimizada e pode economizar muito na taxa de preenchimento. Ainda está para ser visto, ainda não o perfilei!
AshleysBrain
A memória de cópia em bloco do @PandaPajama é realmente muito rápida, portanto, se você estivesse apenas inserindo texturas em uma superfície, seria muito rápido. A primeira grande sobrecarga é obter dados na GPU em primeiro lugar, o que, como destaca Ashley, pode ser mais caro em GPUs integradas. Você descobre que muitos jogos 3D realizam uma quantidade não trivial de trabalho na CPU (como a animação óssea), porque o upload dos dados necessários para realizar esses cálculos matriciais é muito caro.
Matt Esch
@ MatEsch: Há muito que você pode fazer com apenas cegar. Rotações, redimensionamento e deformações vêm à mente, mas também como você tem sombreadores de pixel / vértice, o limite do que você pode fazer com o hardware é muito maior do que o que você pode fazer com apenas cegos.
Panda Pajama

Respostas:

5

De fato, os valores armazenados no buffer z não são lineares às coordenadas z reais de seus objetos, mas às suas recíprocas, a fim de dar mais resolução ao que está próximo ao olho do que ao que está mais próximo do plano traseiro.

O que você faz é que você mapear o zNearque 0e sua zFara 1. Para zNear=1e zFar=2, deve ficar assim

Zbuffer

A maneira de calcular isso é definida por:

z_buffer_value = k * (a + (b / z))

Onde

 k = (1 << N), maximum value the Z buffer can store
 N = number of bits of Z precision
 a = zFar / ( zFar - zNear )
 b = zFar * zNear / ( zNear - zFar )
 z = distance from the eye to the object

... e z_buffer_value é um número inteiro.

A equação acima é apresentada como cortesia desta página incrível , que explica os z-buffers de uma maneira realmente boa.

Portanto, para encontrar o necessário zpara um dado z_buffer_value, basta limpar o z:

z = (k * b) / (z_buffer_value - (k * a))
Panda Pajama
fonte
Obrigado pela resposta! Estou um pouco confuso sobre como você conseguiu sua fórmula final. Se eu pegar z_buffer_value = k * (a + (b / z))e simplesmente reorganizar para resolver z, então eu recebo: z = b / ((z_buffer_value / k) - a)- como você chegou à última fórmula diferente?
AshleysBrain #
@AshleysBrain: você pega o denominador (v / k) - a => (v - k * a) / ke reduz para (k * b) / (v - (k * a)). É o mesmo resultado.
Panda Pyjama
Ah entendo. Obrigado pela resposta, está funcionando bem!
AshleysBrain
0

Talvez você deva mudar sua abordagem para algo mais simples. O que eu faria; Mantenha sua profundidade Z, mas mantenha uma lista do que você renderiza. Encomende essa lista com base no valor z Profundidade e renderize os objetos na ordem da lista.

Espero que isso possa ajudar. As pessoas sempre me dizem para manter as coisas simples.

HgMerk
fonte
11
Desculpe, isso não ajuda muito. Eu já estou fazendo isso. A questão é sobre quais posições Z escolher.
AshleysBrain
0

Como você já tem uma lista ordenada de itens a serem renderizados (da frente para trás), realmente precisa incrementar o índice Z? Você não pode usar "menor ou igual" para a "função de verificação"? Dessa forma, ele realmente verifica se um pixel específico já foi desenhado ou não.

Elven
fonte
"menor ou igual" ainda fará com que absolutamente tudo seja excedido, pois tudo sempre teria um índice Z igual e, portanto, passaria no teste de profundidade.
AshleysBrain