Usando la resolución completa del buffer de profundidad para renderizado 2D

9

Estoy trabajando en un renderizador de adelante hacia atrás para un motor 2D usando una proyección ortográfica. Quiero usar el búfer de profundidad para evitar el sobregiro. Tengo un búfer de profundidad de 16 bits, una cámara en Z = 100 mirando Z = 0, zNear es 1 y zFar es 1000. Cada sprite renderizado establece sus coordenadas Z en valores cada vez más distantes, lo que permite que la prueba de profundidad omita el renderizado todo lo que hay debajo.

Sin embargo, soy consciente de que la forma en que las posiciones Z terminan con valores de buffer Z no es lineal. Quiero hacer uso de la resolución completa del búfer de profundidad de 16 bits, es decir, permitir 65536 valores únicos. Entonces, por cada sprite renderizado, quiero incrementar la posición Z a la siguiente posición para correlacionar con el siguiente valor de búfer de profundidad único.

En otras palabras, quiero convertir un índice incremental (0, 1, 2, 3 ...) del sprite que se dibuja en la posición Z adecuada para que cada sprite tenga un valor de búfer de profundidad único. No estoy seguro de las matemáticas detrás de esto. ¿Cuál es el cálculo para hacer esto?

Tenga en cuenta que estoy trabajando en WebGL (básicamente OpenGL ES 2), y necesito admitir una amplia gama de hardware, por lo que si bien las extensiones como gl_FragDepth pueden facilitarlo, no puedo usarlo por razones de compatibilidad.

AshleysBrain
fuente
No puedo imaginar que usar el búfer z le ofrezca una gran ganancia de rendimiento (si la hubiera) después de haber agregado toda la escritura, cálculos y comparaciones del búfer z frente a copiar texturas al frente, sin mencionar ninguna transparencia alfa / mezcla aflicciones
Matt Esch
@MattEsch: La idea es que todos esos cálculos se realicen en la GPU a velocidades extremadamente altas, por lo que tiene sentido hacerlo.
Panda Pyjama
@MattEsch: FWIW, esto está dirigido a las GPU integradas de Intel, que usan memoria del sistema en lugar de memoria de GPU dedicada. Esto los hace bastante lentos y propensos a alcanzar los límites de la tasa de llenado si se sobreexcitan muchos sprites. Intel me recomendó este enfoque como una forma de solucionarlo. Presumiblemente, su implementación de pruebas de profundidad está bien optimizada y puede ahorrar mucha tasa de llenado. Sin embargo, aún está por verse, ¡aún no lo he perfilado!
AshleysBrain
La memoria de copia de bloques de @PandaPajama es realmente muy rápida, por lo que si solo estuviera borrando texturas en una superficie, sería muy rápido. La primera sobrecarga importante es obtener datos en la GPU en primer lugar, lo que, como señala Ashley, puede ser más costoso en las GPU integradas. Descubres que incluso muchos juegos en 3D realizan una cantidad de trabajo no trivial en la CPU (como la animación de hueso) porque, en primer lugar, cargar los datos necesarios para hacer esos cálculos matriciales es demasiado costoso.
Matt Esch
@MattEsch: solo hay mucho que puedes hacer con solo bliting. Me vienen a la mente rotaciones, escalas y deformaciones, pero también dado que tiene sombreadores de píxeles / vértices, el límite de lo que puede hacer con el hardware es mucho más alto que lo que puede hacer con solo blit.
Panda Pyjama

Respuestas:

5

De hecho, los valores almacenados en el búfer z no son lineales a las coordenadas z reales de sus objetos, sino a su recíproco, para dar más resolución a lo que está cerca del ojo que a lo que está más cerca del plano posterior.

Lo que se hace es que asigne una letra zNeara 0, y su zFara 1. Para zNear=1y zFar=2, debería verse así

Zbuffer

La forma de calcular esto se define por:

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

Dónde

 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

... y z_buffer_value es un número entero.

La ecuación anterior se presenta por cortesía de esta increíble página , que explica los z-buffers de una manera realmente buena.

Entonces, para encontrar lo necesario zpara un determinado z_buffer_value, simplemente borramos z:

z = (k * b) / (z_buffer_value - (k * a))
Pijama Panda
fuente
¡Gracias por la respuesta! Sin embargo, estoy un poco confundido sobre cómo obtuviste tu fórmula final. Si tomo z_buffer_value = k * (a + (b / z))y simplemente reorganizo para resolver z, obtengo: z = b / ((z_buffer_value / k) - a)- ¿Cómo llegaste a la última fórmula diferente?
AshleysBrain
@AshleysBrain: tomas el denominador (v / k) - a => (v - k * a) / ky colapsas en (k * b) / (v - (k * a)). Es el mismo resultado.
Panda Pyjama
Ah, ya veo. Gracias por la respuesta, ¡está funcionando bien!
AshleysBrain
0

Tal vez deberías cambiar Tu enfoque hacia algo más simple. Que haría yo; Mantenga su profundidad Z, pero mantenga una lista de lo que renderiza. Ordene esa lista según el valor de Profundidad z y renderice los objetos en el orden de la lista.

Espero que esto pueda ayudar. La gente siempre me dice que mantenga las cosas simples.

HgMerk
fuente
1
Lo siento, eso no es de mucha ayuda. Ya estoy haciendo eso. La pregunta es sobre qué posiciones Z elegir.
AshleysBrain
0

Como ya tiene una lista ordenada de elementos para renderizar (de adelante hacia atrás), ¿realmente necesita incrementar el índice Z? ¿No puede usar "menos o igual" para la "función de verificación"? De esta manera, en realidad verificaría si un píxel específico ya estaba dibujado o no.

Elfos
fuente
"menor o igual" todavía causará que se sobregire absolutamente todo, ya que todo siempre tendrá un índice Z igual y, por lo tanto, pasará la prueba de profundidad.
AshleysBrain