Choix des librairies

SharpDx

SharpDx est un wrapper .Net utilisé pour permettre l’interaction entre C# et DirectX. L’avantage de ce type de cette approche est double :

  • On retrouve tous les avantages de la plateforme .Net
  • On accède aux fonction DirectX comme si on le faisait en C++

OpenVR

OpenVR est la librairie de Steam utilisée pour permettre l’uniformisation des affichages sur les HMD et les interactions avec les contrôleurs.
Tout utilisateur de SteamVR peut utiliser l’application comme si elle avait été développée spécifiquement pour son matériel (HMD, Contrôleurs etc…).
Nativement, OpenVR est plutôt axé OpenGL mais la librairie supporte aussi DirectX 11+.

Les principaux casques du marché supporte OpenVR :

  • HTC Vive
  • Vive Cosmo
  • Windows Mixed Reality
  • Oculus Rift
  • Pimax
  • etc…

BEPU Physics

Les utilisateurs de Unity connaissent bien cette librairie.

BEPU est la librairie open source de simulation de physique la plus utilisée en .Net. Elle permet la gestion des éléments statiques et dynamiques, la détection et la résolution des collisions.

Open ASSIMP

Open ASSIMP permet le chargement de la plupart des modèles 3D. A l’aide de cette librairie, on peut s’abstraire du format de fichier utilisé pour les modèles.

J’utilise cette librairie uniquement pour charger le format FBX qui permet la gestion d’animation de maillage pré-calculés, chargement de bones, motion capture, etc…

Choix du moteur de rendu

Deferred shading / Deferred rendering

Maintenant que nous avons choisi les librairies graphiques, de physique et la librairie VR, nous devons choisir une technique.

Etant donné que les jeux VR sont gourmant en resource GPU, et afin de gagner du temps de développement, j’ai choisi de réaliser un nouveau moteur en rendu différé : Deferred Rendering.

Présentation du contenu du GBuffer

Sans entrer dans les détails de la réalisation d’un moteur graphique à rendu différé, il faut savoir que le contenu du GBuffer et le type et nombre des RenderTarget utilisés peuvent faire énormément varier la vitesse de rendu.

Après avoir réalisé de nombreux essais, et afin que le rendu soit aussi bon sur écran qu’en VR, je me suis arrêté sur le format : R16G16B16A16_Float. Comme son nom l’indique, il y a 4 channels, chacun possédant un flotant sur 16 bits.

Le GBuffer comporte 4 render targets :

Diffuse buffer : Contient l’albedo + le rouge de la couleur émissive du materiel

Position buffer : Contient la position X,Y,Z de chaque pixel + la distance à la caméra dans son clipping [0..1].

Normal buffer : Contient les coordonnées de la normale de chaque point. La composante A est restée libre (Spare).

Material buffer : Contient les données de surface du matériel (information de réflexion speculaire + émission de couleur).

Note : Le passage dans le GBuffer est restreint par un frustum culling de la caméra.

Render targets complémentaires

Light accumulation buffer

Le contenu du GBuffer permet d’avoir toutes les informations de géométrie et de textures. Mais il ne permet pas à lui seul d’avoir le rendu final sur l’écran (ou le casque VR). Pour cela, il faut passer par la phase d’éclairage.

2 render targets (buffer d’accumulation de lumière) sont créés pour accumuler la lumière diffuse et la lumière spéculaire. Le choix d’utiliser 2 render target est simple, l’un pourra être surexposé, alors que l’autre pourra être limité lors de la compilation finale et les effets post processing.

Bien entendue, les lumières sont dessinées avec une mesh approximant le volume de la source afin de profiter au maximum

Light depth buffers : Shadow Mapping

La technique usuelle de shadow mapping est utilisée. Pour cela, une lumière utilisera un render target de profondeur dans le cas d’une technique d’ombrage simple et 3 render targets dans le cas d’une technique de cascade mapping.

Bien entendu, les render targets des lumières sont ré-utilisés pour chaque lumière de la scène.

Ci-dessous, l’exemple de la scene vue par la lampe torche tenue dans la main gauche du joueur.

Note : Le passage dans le depth buffer est restreint par un frustum culling de la source lumineuse.

Particles buffer

Afin de pouvoir réaliser des effets de particules plus propres, et de pouvoir séparer le post processing, les générateurs de particules réaliseront leur rendu dans un render target spécifique.

Compilation opaque

Après le rendu des différents générateurs de particules, il est temps de compiler le résultat du rendu des surfaces opaques.

Afin de réaliser cette compilation, je dessine un rectangle en plein écran en utilisant un ensemble de render target créés dans les différentes séquences précédentes.

Le résultat obtenu contient toute la géométrie opaque, impactée par les lumières sur laquelle les particules sont ajoutées.

Objets transparents

Les objets transparents ne peuvent pas être réalisés lors d’un deferred shading. En effet, le GBuffer stock l’information du pixel le plus près de la caméra, empêchant l’utilisation du blending offert par les cartes graphiques.

Ainsi, un dernier render target est nécessaire pour réaliser, le rendu des polygons transparent (souvent en forward rendering). Nous ne rentrerons pas dans les détails mais l’ensemble des polygons transparents sont contenus dans ce render target.

Ci-dessous, un objet transparent (chute d’eau) contenant une texture de perturbation (diffraction par ondulation du contenu du render target de compilation opaque).

Compilation Finale

Maintenant que le shading est complet et que les objets transparents ont été rendus, il ne reste plus qu’à mettre le calque transparent par dessus le shading du GBuffer pour obtenir le rendu final à l’écran.

Mais lors de l’utilisation dans un casque VR, ce rendu doit être compilé dans une texture, il faut donc 2 render targets supplémentaires qui seront délivré à OpenVR pour être affiché sur votre casque préféré (HTC Vive, WMR, Occulus…)

Post processing

Le post processing peut être réalisé à différentes phase de la chaine mais le plus souvent, il est réalisé après la compilation finale. Par exemple, réaliser un effet de bloom, écrire du texte, afficher un HUD etc…

Cette partie se réalise comme dans n’importe quel moteur en forward rendering.

Conclusion

Si nous faisons le total du nombre de render target utilisés : 14 AU TOTAL dans le cas de la VR!

Il est clair que ce n’est pas optimum. Mais étant donné que le moteur doit être utilisé pour réaliser des expérimentations, cela permet d’accéder facilement à toutes les informations en bloquant la chaine de rendu à un certain point et en afficher l’information directement à l’écran.

Malgré ce détail architectural, sur un PC Windows 10 à 500€ de 4 ans (AMD FX3850 + 8 GO RAM), le framerate ne dessend jamais en dessous de 60 FPS en VR (comme l’ensemble des jeux auxquels je joue en détail minimum) et atteint 350 FPS en mode écran (pour une Map de 500000 triangles, une vingtaines de personnages animés et plus d’une dizaine de lumière actives simultanément).

Démonstration vidéo

Une courte vidéo montrant les capacités de ce type de moteur. La modélisation et l’animation est en majorité réalisée par mes soins. Je vous serais reconnaissant de ne pas juger de la qualité des modèles car il ne s’agit que d’un prototype de démonstration.

Désolé pour les pointeurs de souris qui se baladent, on ne les voit pas dans le casque…


Cette seconde vidéo montre le Gameplay. Le son est un peu décallé par moment (problème de ressources CPU).


Ci dessous, une scène de jour qui permet de visualiser certains effets difficiles à percevoir dans l’obscurité.