Utilisation de la pleine résolution du tampon de profondeur pour le rendu 2D

9

Je travaille sur un moteur de rendu d'avant en arrière pour un moteur 2D utilisant une projection orthographique. Je veux utiliser le tampon de profondeur pour éviter le surmenage. J'ai un tampon de profondeur de 16 bits, une caméra à Z = 100 en regardant Z = 0, zNear est 1 et zFar est 1000. Chaque sprite rendu définit ses coordonnées Z à des valeurs de plus en plus éloignées, permettant au test de profondeur d'ignorer le rendu tout ce qui est en dessous.

Cependant, je sais que la façon dont les positions Z se retrouvent avec des valeurs de tampon Z n'est pas linéaire. Je veux utiliser la pleine résolution du tampon de profondeur 16 bits, c'est-à-dire autoriser 65536 valeurs uniques. Donc, pour chaque image-objet rendue, je veux incrémenter la position Z à la position suivante pour la corréler à la prochaine valeur de tampon de profondeur unique.

En d'autres termes, je veux transformer un index d'incrémentation (0, 1, 2, 3 ...) du sprite en cours de dessin vers la position Z appropriée pour que chaque sprite ait une valeur de tampon de profondeur unique. Je ne suis pas sûr des mathématiques derrière cela. Quel est le calcul pour ce faire?

Remarque Je travaille dans WebGL (essentiellement OpenGL ES 2), et j'ai besoin de prendre en charge une large gamme de matériel, donc bien que des extensions comme gl_FragDepth puissent faciliter cela, je ne peux pas l'utiliser pour des raisons de compatibilité.

AshleysBrain
la source
Je ne peux pas imaginer que l'utilisation du tampon z vous offrira une grande partie des gains de performances (le cas échéant) après avoir ajouté toutes les écritures, calculs et comparaisons du tampon z par rapport à la copie de textures à l'avant, sans parler de la transparence / fusion alpha malheurs.
Matt Esch
@MattEsch: L'idée est que tous ces calculs sont effectués dans le GPU à des vitesses incroyablement élevées, il est donc logique de le faire.
Panda Pyjama
@MattEsch: FWIW, ceci est destiné aux GPU intégrés Intel, qui utilisent la mémoire système au lieu de la mémoire GPU dédiée. Cela les rend assez lents et susceptibles d'atteindre les limites de taux de remplissage en cas de dépassement de beaucoup de sprites. Intel m'a recommandé cette approche comme moyen de la contourner. On peut supposer que leur mise en œuvre des tests de profondeur est bien optimisée et peut économiser beaucoup de taux de remplissage. Il reste à voir cependant, je ne l'ai pas encore profilé!
AshleysBrain
La mémoire de copie de blocs @PandaPajama est en fait très rapide, donc si vous étiez juste en train de coller des textures sur une surface, ce serait vraiment très rapide. La première surcharge importante consiste à obtenir des données sur le GPU en premier lieu, ce qui, comme le souligne Ashley, peut être plus cher sur les GPU intégrés. Vous constatez que même un grand nombre de jeux 3D effectuent une quantité non négligeable de travail sur le processeur (comme l'animation OS), car le téléchargement des données nécessaires pour effectuer ces calculs matriciels en premier lieu est trop coûteux.
Matt Esch
@MattEsch: Il n'y a que beaucoup de choses que vous pouvez faire avec juste blitting. Les rotations, la mise à l'échelle et les déformations viennent à l'esprit, mais aussi parce que vous avez des shaders pixel / vertex, la limite de ce que vous pouvez faire avec du matériel est beaucoup plus élevée que ce que vous pouvez faire avec un simple blitting.
Panda Pyjama

Réponses:

5

En effet, les valeurs stockées dans le z-buffer ne sont pas linéaires aux coordonnées z réelles de vos objets, mais à leur réciproque, afin de donner plus de résolution à ce qui est près de l'œil qu'à ce qui est plus proche du plan arrière.

Ce que vous faites, c'est que vous mappez votre zNearvers 0et votre zFarvers 1. Pour zNear=1et zFar=2, cela devrait ressembler à ceci

Zbuffer

La façon de calculer cela est définie par:

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

 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

... et z_buffer_value est un entier.

L'équation ci-dessus vous est présentée avec l'aimable autorisation de cette page géniale , qui explique les z-buffers d'une très bonne manière.

Donc, afin de trouver le nécessaire zpour une donnée z_buffer_value, nous effaçons simplement z:

z = (k * b) / (z_buffer_value - (k * a))
Pyjama Panda
la source
Merci d'avoir répondu! Je suis cependant un peu confus sur la façon dont vous avez obtenu votre formule finale. Si je prends z_buffer_value = k * (a + (b / z))et simplement réorganiser pour résoudre z, alors je reçois: z = b / ((z_buffer_value / k) - a)- comment êtes-vous arrivé à la dernière formule différente?
AshleysBrain
@AshleysBrain: vous prenez le dénominateur (v / k) - a => (v - k * a) / ket vous vous effondrez (k * b) / (v - (k * a)). C'est le même résultat.
Panda Pyjama
Ah, je vois. Merci pour la réponse, ça marche bien!
AshleysBrain
0

Vous devriez peut-être changer votre approche pour quelque chose de plus simple. Ce que je ferais; Gardez votre profondeur Z, mais gardez une liste de ce que vous rendez. Triez cette liste en fonction de la valeur Profondeur z et affichez les objets dans l'ordre de la liste.

J'espère que cela peut vous aider. Les gens me disent toujours de garder les choses simples.

HgMerk
la source
1
Désolé, ce n'est pas très utile. Je fais déjà ça. La question est de savoir quelles positions Z choisir.
AshleysBrain
0

Puisque vous avez déjà une liste triée de choses à rendre (d'avant en arrière), avez-vous vraiment besoin d'incrémenter l'index Z? Ne pouvez-vous pas utiliser "inférieur ou égal" pour la "fonction de vérification"? De cette façon, il vérifierait si un pixel spécifique a déjà été dessiné ou non.

Elfique
la source
"inférieur ou égal" provoquera toujours un dépassement absolu de tout, car tout aura toujours un indice Z égal et passera donc le test de profondeur.
AshleysBrain