Rendu de transparence par imposteur de profondeur

Le rendu d'objets transparents dans les jeux vidéo.

Par Peter Sikachev

Introduction
Les objets transparents ont toujours été difficiles à représenter dans les jeux vidéos, à commencer pour des questions de performance. Premièrement, la représentation d’objets transparents peut potentiellement demander trop de ressources pour un seul fragment.

Deuxièmement, les objets transparents ne sont pas compatibles avec toutes les techniques de rendu graphique. Avec les moteurs de rendu différé, il faut créer une passe spécifique en forward rendering; et ils peuvent poser des problèmes avec tous les post-traitements qui demandent des informations de profondeur : Occlusion ambiante et réflexions en screen space, profondeur de champ, pour ne citer que ceux-là. L’auto-ombrage des objets transparents peut aussi se révéler compliqué.

Et pour finir, le tri est l’obstacle principal de la transparence. Bien qu’il soit très simple de trier les instances d’un même objet (par exemple les particules d’un système), réaliser une composition correcte de différents objets transparents peut se révéler très difficile. (voir figure 1 et 2). Par exemple, un système de particules qui n’est que partiellement couvert par l’effet volumétrique n’est pas simple à trier, car certaines particules peuvent se placer avant les effets volumétriques et d’autres après.

Figure 1. Système de particules et objets transparents instanciés
Figure 2. Objets transparents dans un système de particules.
Dans cet article, nous proposons une approche qui pourrait être utilisée pour résoudre certains de ces problèmes. Nous nous intéressons particulièrement au tri des objets transparents et aux méthodes d’auto-ombrage.

Overview
Nous créons ici une render target avec 2 canaux supplémentaires que nous appelons imposteur de profondeur. Dans cette texture, nous stockons des valeurs minimum et maximum de profondeur linéaire d’une entité transparente. Quand une autre entité transparente est rendue, elle va chercher cette texture pour correctement superposer cette entité par-dessus elle-même, comme montré dans la figure 3.

 

Figure 3. RTIP simplifié.
Nous avons recours à la fonctionnalité independent blending, disponible via les API graphiques à partir de DirectX 10.1. Ainsi, nous pouvons remplir l’imposteur de profondeur pendant le rendu de l’entité transparente même, sans introduire de nouveaux draw calls. La render target de transparence par imposteur de profondeur passe par la fonction de blending MIN. Cela permet de conserver le minimum global dans le canal de profondeur minimum. Étant donné que la fonction de blending personnalisé par canal n’est pas disponible, nous devons utiliser le même blending MIN pour le canal de profondeur maximum. Pour que la fonction de blending MIN sélectionne la profondeur maximum, nous prenons la valeur opposée de la profondeur quand nous l’envoyons dans le canal max et prenons encore l’opposé pendant la récupération de la valeur.

Pendant le rendu d’une autre entité transparente qui doit être triée avec celle rendue, grâce à l’imposteur de profondeur, nous récupérons à la fois les textures de la couleur/opacité et de l’imposteur de profondeur. Les deux entités transparentes sont alors superposées, en supposant que l’opacité de l’entité s’accumule linéairement de la profondeur minimum à la profondeur maximum. Le code suivant montre comment le facteur de profondeur (depthFactor) est calculé :

float refZ = viewSpaceCoord.z;
float minZ = DepthProxyMap.Sample(textureSampler, texCoords).r;
float maxZ = -DepthProxyMap.Sample(textureSampler, texCoords).g;
float depthFactor = saturate((refZ – minZ) / abs(maxZ – minZ));

Dans les sections suivantes, nous présentons l’implémentation en détail, et les résultats de trois cas d’utilisation de la technique. Toutefois, cette technique ne se limite pas à ces cas, et nous encourageons le lecteur à explorer d’autres utilisations.

Étude de cas : Tri entre particules et maillages transparents
Faire le tri entre les particules et les objets transparents est particulièrement laborieux. Bien que théoriquement il soit possible de trier les émetteurs de particlules et les maillages transparents (si nous cassons l’instanciation, rendons les deux dans le même buffer, etc), il est quasiment impossible de trier les particules individuelles et les maillages transparents. Ainsi, on ne remarquerait du clignotement lorsqu’un objet transparent se déplace au sein d’un système de particules.

Nous pouvons résoudre le problème grâce au RTIP. Ainsi, nous associons une render target au format RG16F lors du rendu des particules. Le pixel shader des particules est alors modifié. En plus de la couleur en donnée de sortie, nous envoyons la profondeur linéaire en view-space à la render target du RTIP( la même valeur va dans les 2 canaux, on prend l’opposé du canal y).

Les maillages transparents échantillonnent à la fois les couleurs et les textures RTIP. Nous utilisons le code suivant pour superposer les particules et les maillages transparents :

color.a = 1.0f – (1.0f – color.a) * depthFactor;
output.fragColor.rgb = lerp(color.rgb, output.fragColor.rgb, color.a);

Le coût de cette méthode est d’environ 20% de la passe de rendu des particules, si les particules sont rendues dans une render target RGBA16F. Cela s’explique principalement par les limitations de la bande passante de sortie. La limite évidente de cette technique serait d’avoir plusieurs effets de particules (très éloignés les uns des autres) qui se projettent sur le même pixel et des maillages transparents au milieu.

 

 

Étude de cas : auto-ombrage des particules
Certaines particules comme la fumée projettent des ombres semi-transparentes. Bien qu’il soit facile de rendre les ombres des particules sur des surfaces solides, c’est bien plus compliqué de simuler l’auto-ombrage des particules. Toutefois, le RTIP peut aider.

Nous rendons l’opacité dans une render target au format R8UNORM. Tout comme pour le tri entre particules et maillages transparents, nous lions une render target au format RG16F supplémentaire lors du rendu des ombres de particules. Une fois les ombres translucides rendues, nous utilisons la valeur d’ombrage suivante dans la passe de rendu des particules

Comme les particules sont différentes en view space et en light space, nos imposteurs de profondeur ne correspondent pas totalement aux particules. Quand une particule disparaît, les profondeurs minimum et maximum changent subitement, produisant un clignotement de l’auto-ombrage.

translucentShadowTerm = 1.0f – pow(depthFactor, opacityPower) *
translucencyMap.SampleLevel(bilinearSampler, shadowTC.xy, 0.0f);

Pour compenser cela, nous utilisons une simple technique de re-projection d’auto-ombrage dans le pixel shader des ombres de particule, comme le montre cet extrait de code :

output.minMaxDepth = min(output.minMaxDepth,
minMaxParticleDepthPrev.SampleLevel(pointSampler, texCoord, 0.0f).rg + 0.1f);

Pour estimer l’impact sur les performances , nous avons fait un test sur environ 10000 à 20000 particules et une texture d’ombre de 2kx2k pixel sur une GeForce 1060 GTX. Alors qu’une texture d’ombre transparente traditionnelle prend environ 0.25ms, l’auto-ombrage grâce au RTIP prend 0.33ms, soit environ un surcoût de 30%.

Étude de cas : tri entre effets volumétriques et particules
Le Ray Marching sert depuis longtemps à simuler la diffusion de la lumière dans un volume de matière. Toutefois, cette méthode a quelques failles. Par exemple, on ne peut pas le séparer correctement des objets transparents qu’elle contient ; particules, maillages transparents, etc.

Pour régler le problème, l’éclairage volumétrique via Froxel a été introduit. Bien que cela résolve un certain nombre de problèmes, les implémentations pratiques actuelles sont de trop basse résolution pour certains effets, comme le faisceau lumineux d’une lampe torche.

Pendant le raytracing , nous avons assigné le point d’entrée du rayon comme profondeur minimum et son point de sortie comme profondeur maximum. Nous superposons les effets volumétriques et autres objets transparents grâce à la même équation que celles des particules et des objets transparents.

Côté performance, nous n’avons pas constaté de conséquences de l’utilisation du RTIP avec les effets volumétriques. Dans le cas qui nous concerne, ces effets ne sont pas limités par la bande passante de sortie, et les particules ne sont pas limitées par les appels aux textures. Cependant, Il se peut que vous ayez des résultats assez différents.

 

Conclusion
Nous avons présenté dans cet article une nouvelle méthode appelée « rendu de transparence par imposteur de profondeur ». Avec un coût minimum, elle permet de créer une plage de profondeur de remplacement pour un effet de transparence, qui peut ensuite être utilisée de plusieurs façons. Nous faisons la démonstration du RTIP sur quelques exemples de tri et de superposition de transparence ainsi qu’une méthode d’auto-ombrage. Nous encourageons nos lecteurs à chercher de nouvelles utilisations de cette technique.

Llama