Comment générer rapidement et en temps réel des champs de distance signés (2D)?


21

Dans une question précédente , il a été suggéré que les champs de distance signés puissent être précalculés, chargés lors de l'exécution puis utilisés à partir de là.

Pour des raisons que j'expliquerai à la fin de cette question (pour les personnes intéressées), j'ai besoin de créer les champs de distance en temps réel.

Il existe des articles sur différentes méthodes qui sont censées être viables dans des environnements en temps réel, comme les méthodes pour les transformations de distance de chanfrein et les transformations basées sur l'approximation du diagramme de Voronoï (comme suggéré dans cette présentation par le développeur dev Pixeljunk Shooter ), mais J'ai (et on peut donc supposer que beaucoup d'autres personnes) ont vraiment du mal à les utiliser, car ils sont généralement longs, largement gonflés de mathématiques et peu algorithmiques dans leur explication.

Quel algorithme proposeriez-vous pour créer les champs de distance en temps réel (favorablement sur le GPU) compte tenu notamment de la qualité résultante des champs de distance?

Étant donné que je recherche une explication / un didacticiel réel par opposition à un lien vers juste un autre document ou une diapositive, cette question recevra une prime une fois qu'elle sera éligible pour un :-).

Voici pourquoi je dois le faire en temps réel:

Si vous devez précalculer ces SDF pour de grands environnements 2D (pensez à une grande carte de type Terraria), cela signifierait que vous acceptez une surcharge assez importante dans l'espace de stockage (et le temps de génération de carte) en faveur de la mise en œuvre d'un plus algorithme compliqué suffisamment rapide pour la génération SDF en temps réel.

Par exemple, une carte relativement petite avec 1000 * 256 (largeur * hauteur) avec une taille de tuile de 10 * 10 pixels et donc des dimensions totales de 10000 * 2560 pixels vous coûterait déjà environ 2 mégaoctets de taille, si vous choisissez une taille relativement petite Résolution SDF de 128x128, en supposant que vous stockez uniquement les valeurs de distance de 0 à 255.

Évidemment, cela peut rapidement devenir trop et c'est un surcoût que je ne veux pas avoir.

Il y a autre chose:

Les SDF peuvent être utilisés pour de nombreuses choses (comme la détection de collision), et certaines applications utiles ne sont peut-être même pas encore découvertes. Je pense que beaucoup de gens vont chercher ces choses à l'avenir, et si nous obtenons une réponse complète ici, je pense que nous allons aider beaucoup de gens.


J'ai googlé "ce qui est un champ de distance signé" et le premier hit était une version GPU: http.developer.nvidia.com/GPUGems3/gpugems3_ch34.html C'est un peu vieux mais pourrait aider à poursuivre vos recherches.
Patrick Hughes

1
Peut-être que je manque quelque chose, mais je suis quelque peu confus par l'énoncé de la raison pour laquelle vous devez le faire en temps réel (notamment, pourquoi il est marqué d'un spoiler); tout d'abord, où obtenez-vous le chiffre de 2 Mo pour un SDF de 128x128? Deuxièmement, pourquoi considérez-vous que 2 Mo sont une utilisation de mémoire particulièrement lourde? Je suis d'accord que ce n'est pas négligeable, mais cela semble une petite fraction de votre utilisation globale de la mémoire de la carte. Et troisièmement, comment la génération du champ en temps réel permettra-t-elle d'économiser cette mémoire? Vous devez toujours stocker exactement les mêmes données, qu'elles soient générées à la volée ou précalculées, non?
Steven Stadnicki

Plus largement, il se pourrait facilement que les SDF ne soient pas la technique dont vous avez besoin. Plus d'informations sur votre situation spécifique - le nombre d'obstacles statiques, le nombre d'obstacles dynamiques, etc. - et précisément l'effet que vous espérez obtenir serait utile pour essayer de déterminer ce qui est le plus susceptible de vous être utile.
Steven Stadnicki

1
Si je générais le champ de distance en temps réel, je ne créerais ces 2 Mo qu'une seule fois par trame (la surcharge de mémoire totale serait toujours la mémoire nécessaire pour un champ de distance, car je n'ai besoin que de l'écran). Si j'ai une carte plus grande que mon exemple 1000x128 (je pense que les grandes cartes Terraria vont bien au-delà des 10000), j'ai besoin d'un de ces 2 Mo pour chaque sous-carte 1000x128 de cette carte. La raison pour laquelle j'ai besoin des SDF en premier lieu est décrite dans la première question que j'ai liée au début de cette question (c'est pour le moulage d'ombre 2D GPU).
TravisG

1
@heishe essayez-vous de générer 2 Mo de données une fois par image? Sérieusement?
kaoD

Réponses:


9

Catalin Zima explique comment réaliser des ombres 2D dynamiques dans son article - et il utilise un champ de distance signé (d'après ce que je peux dire, c'est juste un nom de fantaisie pour un tampon d'ombre dans ce contexte). Sa méthode a besoin d'un GPU, et sa mise en œuvre telle quelle n'est pas la meilleure (la sienne est tombée en dessous de 60 Hz à environ 20 lumières sur ma machine, la mienne a environ 500 lumières); ce qui est normal car il a privilégié la clarté du code à la vitesse.

la mise en oeuvre

Exactement tel qu'il l'a mis en œuvre:

  1. Rendez toutes les roulettes d'ombre dans une texture.
  2. Calculez la distance au centre de la lumière pour chaque pixel et affectez cette valeur au RVB des pixels opaques.
  3. Déformez l'image afin qu'elle représente la façon dont une caméra 3D aurait vu ces pixels.
  4. Réduisez l'image en une image de taille 2xN en utilisant le redimensionnement inhabituel décrit dans son article (un redimensionnement simple ne fonctionnera pas).
  5. L'image 2xN est maintenant votre champ de distance signé pour les quatre quadrants de la lumière (rappelez-vous qu'un quadrant est essentiellement un tronc de caméra unique à 90 degrés).
  6. Rendez le lightmap.
  7. Flou le lightmap (basé sur la distance de la lumière) pour obtenir des ombres douces.

Mon implémentation finale était (chaque étape étant un shader unique):

  1. Faites (1).
  2. Faites (2) et (3) .
  3. Faites (4). Son implémentation est vraiment lente: si vous pouvez essayer d'utiliser GPGPU pour cela. Je ne pouvais pas utiliser GPGPU (XNA) donc ce que j'ai fait était:
    • Mettre en place un maillage où les N / 2 premières colonnes étaient représentées par N / 2 quads avec la même position EXACT (couvrant la première colonne du tampon final) mais des coordonnées de texture différentes (même chose pour les deuxièmes N / 2 colonnes)
    • Désactivez les tests de profondeur sur le GPU.
    • Utilisez la fonction de mélange de pixels MIN.
  4. Faites (6) et (7).

C'est assez ingénieux: il s'agit essentiellement d'une traduction directe de la façon dont les ombres sont traitées en 3D en 2D.

Pièges

Le principal écueil est que certains objets ne doivent pas être masqués: dans mon exemple, j'écrivais un clone Liero (vers en temps réel) et je ne voulais donc pas, par exemple, que les vers des joueurs soient masqués (au moins celui sur l'écran de chaque joueur). Tout ce que j'ai fait pour ces objets «spéciaux» a été de les redessiner comme dernière étape. L'ironie était que la plupart des objets n'étaient pas ombragés (vers, premier plan du paysage), il y a donc un problème de dépassement ici.


Votre ajustement à la méthode de redimensionnement a-t-il été la seule chose à accélérer pour gérer 500 lumières au-dessus de 60 images par seconde?
TravisG

OK, j'accepte la réponse car elle résout mon problème d'origine, mais elle ne répond pas vraiment à ce pour quoi j'ai donné la prime. J'attendrai et peut-être que quelqu'un viendra longtemps pour expliquer l'une des nombreuses méthodes O (N) pour la génération de champ de distance signée.
TravisG

@heishe concernant votre première question: pas sûr. J'ai fait toutes les optimisations en un seul passage - je pense que je me souviens de l'avoir éteint et d'avoir vu le taux de rafraîchissement baisser sensiblement. Au total, 6 appels par tirage par lumière tueront votre fréquence d'images. Comme je l'ai dit, il semble que d'après ce que je peux dire, vous avez 4 champs de distance signés à l'étape (5) - mais quelqu'un qui en sait plus à leur sujet devra le confirmer.
Jonathan Dickinson

Eh bien, c'est un cas très spécial d'un champ de distance signé. Dans un champ de distance signé normal, chaque pixel contient la distance de l'obstacle le plus proche. Dans cet algorithme, le champ de distance ne contient qu'un seul obstacle, et l'obstacle n'est également que de 1 pixel dans l'image entière (la source de lumière), c'est pourquoi ce champ de distance peut être généré en O (N).
TravisG

1
@heishe voici mon shader: gist.github.com/2384073 . DistortPS est 2 + 3.
Jonathan Dickinson
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.