Étude de cas - Création d’une galaxie dans la réalité mixte

Avant Microsoft HoloLens expédiés, nous avons demandé à notre communauté de développeurs quel type d’application ils aimeraient voir une build d’équipe interne expérimentée pour le nouvel appareil. Plus de 5 000 idées ont été partagées, et après un sondage Twitter de 24 heures, le gagnant a été une idée appelée Galaxy Explorer.

Andy Zibits, responsable artistique du projet, et Karim Luccin, ingénieur graphique de l’équipe, parlent de l’effort collaboratif entre l’art et l’ingénierie qui a conduit à la création d’une représentation précise et interactive de la galaxie de la Voie lactée dans Galaxy Explorer.

La technologie

Notre équipe , composée de deux concepteurs, trois développeurs, quatre artistes, un producteur et un testeur, a eu six semaines pour créer une application entièrement fonctionnelle qui permettrait aux gens d’en savoir plus sur l’immensité et la beauté de notre Voie lactée Galaxy.

Nous voulions tirer pleinement parti de la capacité d’HoloLens à restituer des objets 3D directement dans votre espace de vie, alors nous avons décidé de créer une galaxie réaliste où les gens pourraient zoomer de près et voir des étoiles individuelles, chacune sur leurs propres trajectoires.

Au cours de la première semaine de développement, nous avons trouvé quelques objectifs pour notre représentation de la Voie lactée Galaxie: Elle devait avoir de la profondeur, du mouvement et de la sensation volutrique , pleine d’étoiles qui aideraient à créer la forme de la galaxie.

Le problème de la création d’une galaxie animée avec des milliards d’étoiles était que le nombre d’éléments uniques qui doivent être mis à jour serait trop grand par image pour qu’HoloLens puisse s’animer à l’aide du processeur. Notre solution impliquait un mélange complexe d’art et de science.

Dans les coulisses

Pour permettre aux gens d’explorer des étoiles individuelles, notre première étape a été de déterminer le nombre de particules que nous pouvions restituer à la fois.

Rendu des particules

Les processeurs actuels sont parfaits pour le traitement des tâches série et jusqu’à quelques tâches parallèles à la fois (en fonction du nombre de cœurs qu’ils ont), mais les GPU sont beaucoup plus efficaces pour traiter des milliers d’opérations en parallèle. Toutefois, comme ils ne partagent généralement pas la même mémoire que le processeur, l’échange de données entre gpu de processeur<>peut rapidement devenir un goulot d’étranglement. Notre solution était de faire une galaxie sur le GPU, et il devait vivre complètement sur le GPU.

Nous avons commencé des tests de contrainte avec des milliers de particules ponctuelles dans différents modèles. Cela nous a permis d’obtenir la galaxie sur HoloLens pour voir ce qui a fonctionné et ce qui n’a pas fonctionné.

Création de la position des étoiles

L’un des membres de notre équipe avait déjà écrit le code C# qui générerait des étoiles à sa position initiale. Les étoiles se trouvent sur une ellipse et leur position peut être décrite par (curveOffset, ellipseSize, élévation) où curveOffset est l’angle du star le long de l’ellipse, ellipseSize est la dimension de l’ellipse le long de X et Z, et élévation de l’élévation appropriée du star dans la galaxie. Ainsi, nous pouvons créer une mémoire tampon (ComputeBuffer d’Unity) qui serait initialisée avec chaque attribut star et l’envoyer sur le GPU où il vivrait pour le reste de l’expérience. Pour dessiner cette mémoire tampon, nous utilisons drawProcedural d’Unity qui permet d’exécuter un nuanceur (code sur un GPU) sur un ensemble arbitraire de points sans avoir de maillage réel qui représente la galaxie :

CPU:

GraphicsDrawProcedural(MeshTopology.Points, starCount, 1);

GPU:

v2g vert (uint index : SV_VertexID)
{

 // _Stars is the buffer we created that contains the initial state of the system
 StarDescriptor star = _Stars[index];
 …

}

Nous avons commencé avec des motifs circulaires bruts avec des milliers de particules. Cela nous a donné la preuve dont nous avions besoin que nous pouvions gérer de nombreuses particules et les exécuter à des vitesses performantes, mais nous n’étions pas satisfaits de la forme globale de la galaxie. Pour améliorer la forme, nous avons essayé différents modèles et systèmes de particules avec rotation. Ceux-ci étaient initialement prometteurs parce que le nombre de particules et les performances sont restés constants, mais la forme s’est brisée près du centre et les étoiles émettant vers l’extérieur, ce qui n’était pas réaliste. Nous avions besoin d’une émission qui nous permettrait de manipuler le temps et de faire en sorte que les particules se déplacent de manière réaliste, en boucle toujours plus près du centre de la galaxie.

Nous avons essayé différents modèles et systèmes de particules qui ont fait pivoter, comme ceux-ci.

Nous avons essayé différents modèles et systèmes de particules qui ont fait pivoter, comme ceux-ci.

Notre équipe a effectué des recherches sur le fonctionnement des galaxies et nous avons créé un système de particules personnalisé spécifiquement pour la galaxie afin de pouvoir déplacer les particules sur des ellipses en fonction de la « théorie des ondes de densité », qui théorise que les bras d’une galaxie sont des zones de densité plus élevée mais en flux constant, comme un embouteillage. Il semble stable et solide, mais les étoiles se déplacent en fait dans et hors des bras à mesure qu’elles se déplacent le long de leurs ellipses respectives. Dans notre système, les particules n’existent jamais sur le processeur: nous générons les cartes et les orientons toutes sur le GPU, de sorte que l’ensemble du système est simplement état initial + temps. Il a progressé comme suit :

Progression du système de particules avec rendu GPU

Progression du système de particules avec rendu GPU

Une fois que suffisamment d’ellipses sont ajoutées et sont définies pour pivoter, les galaxies ont commencé à former des « bras » où convergent le mouvement des étoiles. L’espacement des étoiles le long de chaque chemin elliptique a reçu un certain caractère aléatoire, et chaque star a reçu un peu d’aléatoire positionnel ajouté. Cela a créé une répartition beaucoup plus naturelle du mouvement star et de la forme du bras. Enfin, nous avons ajouté la possibilité de piloter la couleur en fonction de la distance par rapport au centre.

Création du mouvement des étoiles

Pour animer le mouvement général star, nous avions besoin d’ajouter un angle constant pour chaque image et de faire en sorte que les étoiles se déplacent le long de leurs points de suspension à une vitesse radiale constante. Il s’agit de la raison principale de l’utilisation de curveOffset. Ce n’est pas techniquement correct, car les étoiles se déplaceront plus rapidement le long des côtés longs des points de suspension, mais le mouvement général était bon.

Les étoiles se déplacent plus vite sur le long arc, plus lentement sur les bords.

Les étoiles se déplacent plus vite sur le long arc, plus lentement sur les bords.

Avec cela, chaque star est entièrement décrit par (curveOffset, ellipseSize, élévation, Âge) où Age est une accumulation du temps total qui s’est écoulé depuis le chargement de la scène.

float3 ComputeStarPosition(StarDescriptor star)
{

  float curveOffset = star.curveOffset + Age;
  
  // this will be coded as a “sincos” on the hardware which will compute both sides
  float x = cos(curveOffset) * star.xRadii;
  float z = sin(curveOffset) * star.zRadii;
   
  return float3(x, star.elevation, z);
  
}

Cela nous a permis de générer des dizaines de milliers d’étoiles une fois au début de l’application, puis nous avons animé un ensemble d’étoiles unique le long des courbes établies. Étant donné que tout se trouve sur le GPU, le système peut animer toutes les étoiles en parallèle sans coût pour le processeur.

Voici à quoi il ressemble lorsque vous dessinez des quads blancs.

Voici à quoi il ressemble lorsque vous dessinez des quads blancs.

Pour que chaque quad face à l’appareil photo, nous avons utilisé un nuanceur de géométrie pour transformer chaque position star en rectangle 2D sur l’écran qui contiendra notre texture star.

Diamants au lieu de quads.

Diamants au lieu de quads.

Comme nous voulions limiter autant que possible le surdraw (nombre de fois qu’un pixel sera traité), nous avons fait pivoter nos quads afin qu’ils aient moins de chevauchement.

Ajout de clouds

Il existe de nombreuses façons d’obtenir une sensation volumétrique avec des particules, de la marche des rayons à l’intérieur d’un volume au dessin du plus grand nombre de particules possible pour simuler un nuage. La marche des rayons en temps réel allait être trop coûteuse et difficile à créer, donc nous avons d’abord essayé de créer un système d’imposteur à l’aide d’une méthode de rendu des forêts dans les jeux, avec beaucoup d’images 2D d’arbres face à l’appareil photo. Lorsque nous le faisons dans un jeu, nous pouvons avoir des textures d’arborescences rendues à partir d’une caméra qui tourne autour, enregistrer toutes ces images et, au moment de l’exécution pour chaque panneau d’affichage carte, sélectionner l’image qui correspond au sens de la vue. Cela ne fonctionne pas aussi bien lorsque les images sont des hologrammes. La différence entre l’œil gauche et l’œil droit fait en sorte que nous avons besoin d’une résolution beaucoup plus élevée, ou bien il semble simplement plat, alias, ou répétitif.

Lors de notre deuxième tentative, nous avons essayé d’avoir autant de particules que possible. Les meilleurs visuels ont été obtenus lorsque nous avons dessiné de façon additive des particules et les a floutées avant de les ajouter à la scène. Les problèmes typiques de cette approche étaient liés au nombre de particules que nous pouvions dessiner en une seule fois et à la surface d’écran qu’elles couvraient tout en conservant 60 ips. Le flou de l’image obtenue pour obtenir ce sentiment cloud était généralement une opération très coûteuse.

Sans texture, c’est ce à quoi ressembleraient les nuages avec une opacité de 2 %.

Sans texture, c’est ce à quoi ressembleraient les nuages avec une opacité de 2 %.

Le fait d’être additif et d’en avoir beaucoup signifie que nous aurions plusieurs quads les uns sur les autres, en ombrant à plusieurs reprises le même pixel. Au centre de la galaxie, le même pixel a des centaines de quads les uns sur les autres et cela a eu un coût énorme quand il est fait plein écran.

Faire des clouds plein écran et essayer de les flouter aurait été une mauvaise idée, donc à la place, nous avons décidé de laisser le matériel faire le travail pour nous.

Un peu de contexte d’abord

Lorsque vous utilisez des textures dans un jeu, la taille de texture correspond rarement à la zone dans laquelle nous voulons l’utiliser, mais nous pouvons utiliser différents types de filtrage de texture pour obtenir le graphique carte d’interpoler la couleur souhaitée à partir des pixels de la texture (filtrage de texture). Le filtrage qui nous intéresse est le filtrage bilinéaire qui calcule la valeur d’un pixel à l’aide des 4 voisins les plus proches.

Original avant filtrage

Résultat après le filtrage

À l’aide de cette propriété, nous voyons que chaque fois que nous essayons de dessiner une texture dans une zone deux fois plus grande, le résultat est flou.

Au lieu d’effectuer un rendu en plein écran et de perdre ces précieuses millisecondes que nous pourrions dépenser pour autre chose, nous restituons une version minuscule de l’écran. Ensuite, en copiant cette texture et en l’étirant d’un facteur de 2 plusieurs fois, nous revenons au plein écran tout en brouillant le contenu dans le processus.

X3 upscale back to full resolution.

X3 upscale back to full resolution.

Cela nous a permis d’obtenir la partie cloud avec seulement une fraction du coût d’origine. Au lieu d’ajouter des nuages sur la résolution complète, nous peinons seulement 1/64e des pixels et nous allons simplement étirer la texture en pleine résolution.

À gauche, avec une mise à l’échelle de 1/8e à la résolution complète ; et à droite, avec 3 haut de gamme utilisant la puissance de 2.

À gauche, avec une mise à l’échelle de 1/8e à la résolution complète ; et à droite, avec 3 haut de gamme utilisant la puissance de 2.

Notez que le fait d’essayer de passer du 1/64e de la taille à la taille complète en une seule fois serait complètement différent, car le graphique carte utiliserait toujours 4 pixels dans notre configuration pour nuancer une plus grande zone et les artefacts commencent à apparaître.

Ensuite, si nous ajoutons des étoiles pleine résolution avec des cartes plus petites, nous obtenons la galaxie complète :

Résultat proche du rendu des galaxies à l’aide d’étoiles pleine résolution

Une fois que nous étions sur la bonne voie avec la forme, nous avons ajouté une couche de nuages, échangé les points temporaires avec ceux que nous avons peints dans Photoshop, et ajouté une couleur supplémentaire. Le résultat a été une Galaxie de la Voie lactée que nos équipes d’art et d’ingénierie ont tous deux bien à propos et elle a atteint nos objectifs d’avoir la profondeur, le volume et le mouvement, tout cela sans taxer le processeur.

Notre dernière galaxie de la Voie lactée en 3D.

Notre dernière galaxie de la Voie lactée en 3D.

Plus d’informations à explorer

Nous avons open-sourcer le code de l’application Galaxy Explorer et l’avons mis à la disposition des développeurs sur GitHub.

Vous souhaitez en savoir plus sur le processus de développement de Galaxy Explorer ? Consultez toutes nos dernières mises à jour de projet sur la chaîne YouTube Microsoft HoloLens.

À propos des auteurs

Photo de Karim Luccin à son bureau Karim Luccin est un ingénieur logiciel et un passionné de visuels de fantaisie. Il était ingénieur graphique pour Galaxy Explorer.
Photo du chef d’art Andy Zibits Andy Zibits est un directeur artistique et un passionné d’espace qui a géré l’équipe de modélisation 3D pour Galaxy Explorer et s’est battu pour encore plus de particules.

Voir aussi