FAQ Programmation 3D
FAQ Programmation 3DConsultez toutes les FAQ
Nombre d'auteurs : 7, nombre de questions : 67, dernière mise à jour : 14 juin 2021
- Qu'est-ce que le FFP ?
- Comment est organisé le pipeline 3D ?
- Comment passe-t-on du monde 3D à l'affichage 2D à l'écran ?
- Qu'est-ce qu'un shader/program ?
- Que représentent le color buffer, le back buffer, le depth buffer, le stencil buffer… ?
- Qu'est-ce que le batching ?
- Qu'est-ce que les mip-maps ?
- Comment fonctionne l'alpha-blending ?
- Qu'est-ce que l'antialiasing ?
- Qu'est-ce que le glow ?
- Qu'est-ce que le bloom ?
- Qu'est-ce que le Depth of Field (DOF) ?
- Qu'est-ce que le motion blur ?
- Qu'est-ce que le streak ?
- Qu'est-ce que le lens flare ?
Le FFP est le Fixed Function Pipeline, autrement dit les fonctions précablées dans le hardware, par lesquelles passe la géométrie afin d'être affichée à l'écran (voir https://jeux.developpez.com/faq/3d/?page=definitions#DEFINITIONS_pipeline).
Comme son nom l'indique, le FFP est fixe et est donc limité en fonctionnalités. Bien que suffisant pendant de nombreuses années, cette limitation a conduit il y a quelques années à l'évolution des shaders, qui sont par opposition au FFP des programmes directement exécutables par la carte graphique, et permettant donc de court-circuiter certaines parties du FFP.
Voir https://jeux.developpez.com/faq/3d/?page=definitions#DEFINITIONS_shaders.
Entre le moment où votre carte graphique reçoit la scène 3D, et le moment où elle la renvoie à l'écran sous forme d'une image 2D, la géométrie passe par différentes étapes bien précises. C'est ce que l'on appelle le pipeline 3D.
Un schéma étant plus parlant qu'un long discours, voici une illustration d'un pipeline 3D typique :
- Vertex data : votre scène telle que vous l'envoyez à la carte graphique.
- High order surface tesselation : étape facultative ne concernant que les primitives de type NURBS et autres surfaces paramétriques ; cela consiste simplement à découper celles-ci en triangles, afin que la carte graphique puisse les traiter comme le reste de la géométrie.
- Transform & lighting : étape durant laquelle les triangles sont transformés via les matrices de vue/projection, et éclairés si nécessaire.
- Programmable vertex shader : court-circuite l'étape « transform & lighting » si l'on utilise les vertex shaders, voir https://jeux.developpez.com/faq/3d/?page=definitions#DEFINITIONS_shaders.
- Backface culling : les triangles tournant le dos à la caméra sont éliminés (voir https://jeux.developpez.com/faq/3d/?page=culling#TECHNIQUES_CULLING_backface).
- Clipping : les triangles dépassant du champ de vision sont coupés, et les parties invisibles éliminées.
- Viewport transform : transformation de la géométrie afin de coller au viewport courant.
- Triangle setup : les triangles sont parcourus ligne par ligne et transformés en pixels. C'est à ce moment que l'on passe du traitement par vertex au traitement par pixel.
- Texturing, filtering & blending : les textures sont mappées, les différentes couleurs mélangées, et les filtres appliqués.
- Programmable pixel shader : court-circuite l'étape « texturing, filtering & blending » si l'on utilise les pixel shaders, voir https://jeux.developpez.com/faq/3d/?page=definitions#DEFINITIONS_shaders.
- Fog blending : ajout du brouillard via les équations correspondantes.
- Visibility testing : la profondeur du pixel est testée (via le depth-buffer), afin de savoir si celui-ci est visible ou caché.
- Frame buffer : étape ultime, à savoir l'écriture du pixel sur l'image qui sera envoyée à l'écran.
Les données de vos objets 3D subissent de nombreuses transformations avant d'arriver en 2D sur votre écran. Comprendre celles-ci n'est pas primordial étant donné que c'est un mécanisme entièrement géré par le hardware (partie « Transfom & Lighting », « Clipping » et « Viewport Transform » du pipeline), mais se faire une idée des traitements que subissent les données est toujours utile, notamment pour correctement paramétrer les matrices, viewports et autres au niveau de l'API 3D.
Au départ, les sommets que vous envoyez à votre API 3D sont dans ce que l'on appelle l'espace objet (-> object coordinates, ou world coordinates).
Vos sommets sont tout d'abord transformés par la matrice de modèle/vue (world * view chez DirectX, modelview chez OpenGL) : ils sont ainsi ramenés dans l'espace caméra (-> eye coordinates).
Ensuite, ces coordonnées sont transformées par la matrice de projection afin de leur faire subir l'effet de perspective (-> clip coordinates). Les coordonnées ainsi obtenues (X, Y, Z, W) sont toutes comprises entre -W et W.
Les coordonnées (X, Y, Z) obtenues précédemment sont divisées par la composante W afin d'être normalisées (-> normalized device coordinates). Elles se retrouvent donc entre -1 et 1.
Enfin, ces coordonnées normalisées sont décalées/rééchelonnées afin de coller au viewport courant (-> window coordinates). Pour rappel, le viewport permet d'afficher la scène sur n'importe quel endroit de la fenêtre de rendu.
Ce que l'on appelle shader chez DirectX, ou program chez OpenGL, sont des programmes, écrits avec une syntaxe assembleur ou dans un langage de plus haut niveau, directement exécutables par la carte graphique et remplaçant certaines parties du pipeline d'exécution habituel. Arrivés dans leur première version avec DirectX 8.0/OpenGL 1.4, les shaders ont apporté une incroyable flexibilité dans le rendu 3D, et la possibilité de développer de nouveaux effets toujours plus impressionnants.
Il existe deux types de shaders/programs. Sous DirectX, ce sont les vertex shaders et les pixel shaders, chez OpenGL ce sont les vertex programs et les fragment program, ou plus récemment les vertex shaders et les fragment shaders.
Les vertex shaders/programs remplacent les parties « transformation » et « éclairage » du pipeline de rendu. Ainsi ils prennent en entrée un vertex, et renvoient un vertex (il est donc impossible de traiter plusieurs vertices en même temps dans un vertex shader).
Les pixel/fragment shaders/programs remplacent les parties « texturage », « filtrage » et « mélange » du pipeline de rendu. De la même manière que les vertex shaders, ils prennent en entrée les informations sur le pixel courant (couleur, textures, coordonnées de textures…) et renvoient une couleur (le pixel à écrire sur le color buffer).
Il existe plusieurs langages pour écrire des shaders/programs. Les tout premiers langages possédaient une syntaxe pseudoassembleur, chaque API (DirectX/OpenGL) possédant sa propre syntaxe.
Bien que les shaders soient des programmes relativement courts et peu complexes, avec l'arrivée de nouvelles versions et de nouveaux effets leur écriture s'est révélée plus ardue, et des langages de plus haut niveau ont été développés.
Ainsi, il existe le HLSL (High Level Shading Langage) pour DirectX, le GLSL (OpenGL Shading Langage) pour OpenGL, et le Cg (C for Graphics) qui repose sur une bibliothèque développée par nVidia compatible avec les deux API. Tous ces langages possèdent une syntaxe proche du C, ce qui rend l'écriture de shaders bien plus rapide.
Lorsque l'on parle d'applications 3D, on a souvent affaire à tout un tas de buffers, mais et on ne sait pas toujours à quoi ils correspondent. Voici une petite liste descriptive qui vous permettra d'y voir plus clair.
- Le depth buffer (ou Z buffer) est la surface qui va contenir les informations de profondeur de chaque pixel (généralement sous forme de flottant entre 0 et 1), permettant ainsi d'éliminer les pixels cachés.
- Le stencil buffer est une surface auxiliaire permettant de stocker une valeur (généralement sur 8 bits) pour chaque pixel. Cette valeur n'a pas de sens particulier, mais on peut tout de même voir le stencil buffer comme un masque, un filtre qui permettrait d'afficher ou d'éliminer certains pixels. On peut du coup se servir du stencil buffer pour toutes sortes d'effets : balayages, reflets, ombres…
- Le back buffer est la surface offscreen sur laquelle sont affichés les pixels durant le rendu.
- Le front buffer est la surface qui reçoit du back buffer les pixels à afficher et les envoie sur l'écran. Ainsi pendant ce temps, le back buffer est libéré et le rendu peut continuer.
- Le color buffer est une autre désignation pour le couple back buffer/front buffer, désignant ainsi la surface sur laquelle on va écrire la couleur des pixels (par opposition au depth buffer, qui est la surface sur laquelle on écrit leur profondeur).
Ce qu'on appelle batching (groupement) est une technique courante d'optimisation, qui consiste à regrouper les ressources (buffers, textures, changements d'état…) afin de limiter les appels à l'API 3D, qui ont un coût non négligeable.
Ainsi, si l'on a plusieurs modèles utilisant la même texture, il vaudra mieux regrouper ces modèles et les rendre à la suite afin de ne pas changer inutilement de texture plusieurs fois. Autre exemple typique : on peut regrouper toute la géométrie statique dans de gros vertex buffers, plutôt que d'avoir un buffer par modèle. Ainsi cela limite les appels et les changements de buffers au sein de l'API.
Il est difficile de donner des techniques plus précises, car un bon batching dépendra beaucoup de votre application. Certaines auront intérêt à regrouper par texture, d'autres par tampon ou matériau. Mais souvenez-vous que quoi qu'il arrive, un bon batching sera une optimisation à ne pas négliger dans toute bonne application graphique.
Le terme mip-maps (pour mip-mappings) désigne une série d'images de différentes résolutions utilisées pour un même mapping. En fonction de l'éloignement de la surface texturée à afficher, le niveau de mip-map le plus approprié est sélectionné afin d'éliminer les effets de scintillement provoqués par l'éloignement des textures (voir https://jeux.developpez.com/faq/3d/?page=problemes#PROBLEMES_tex_scintillent).
Les avantages du mip-mapping sont donc d'éliminer ces effets de scintillement sur les textures lointaines, mais aussi d'obtenir parfois un gain de rapidité, étant donné qu'un objet éloigné n'utilisera pas une grande texture, mais prendra celle plus appropriée à la surface à afficher, et donc moins de données à transférer.
Du côté des inconvénients, le principal est la consommation mémoire supplémentaire pour les différentes résolutions de la texture.
L'utilisation des mip-maps provoque également de nettes coupures entre les différents niveaux (lorsque la carte change de mip-map). Pour remédier à cela, il est possible d'utiliser le filtrage de textures, qui limite ces changements brusques.
Note : le terme MIP vient du latin Multum In Parvo, qui signifie « une multitude dans un petit espace ».
Il faut voir l'alpha-blending (mélange alpha) comme un dessin avec deux couches : la couche destination est ce qui est déjà affiché dans le color buffer, et la couche source est ce que l'on est en train de dessiner.
Le résultat du blending dépend de l'opération utilisée selon l'équation suivante :
couleur finale = Pixel_src * Facteur_src + Pixel_dest * Facteur_dest
Pixel_src représente la couleur source (celle à écrire)
Pixel_dest représente la couleur de destination (celle déjà écrite)
Facteur_src représente le facteur de blending source
Facteur_dest représente le facteur de blending de destination
En clair, chaque composante de la couleur source est multipliée par le facteur de blending source, et chaque composante de la couleur de destination est multipliée par le facteur de blending de destination.
Les principaux facteurs de mélange sont les suivants (les noms sont purement informels, les véritables identifiants à utiliser varient bien sûr pour chaque API) :
- ZERO : (0, 0, 0, 0)
- ONE : (1, 1, 1, 1)
- SRC_COLOR : (Pixel_src.r, Pixel_src.g, Pixel_src.b, Pixel_src.a)
- INV_SRC_COLOR : (1 - Pixel_src.r, 1 - Pixel_src.g, 1 - Pixel_src.b, 1 - Pixel_src.a)
- DEST_COLOR : (Pixel_dest.r, Pixel_dest.g, Pixel_dest.b, Pixel_dest.a)
- INV_DEST_COLOR : (1 - Pixel_dest.r, 1 - Pixel_dest.g, 1 - Pixel_dest.b, 1 - Pixel_dest.a)
- SRC_ALPHA : (Pixel_src.a, Pixel_src.a, Pixel_src.a, Pixel_src.a)
- INV_SRC_ALPHA : (1 - Pixel_src.a, 1 - Pixel_src.a, 1 - Pixel_src.a, 1 - Pixel_src.a)
- DEST_ALPHA : (Pixel_dest.a, Pixel_dest.a, Pixel_dest.a, Pixel_dest.a)
- INV_DEST_ALPHA : (1 - Pixel_dest.a, 1 - Pixel_dest.a, 1 - Pixel_dest.a, 1 - Pixel_dest.a)
Ainsi, un blending dont le facteur source serait ONE et le facteur destination ZERO, serait équivalent à un affichage normal (ce qui serait donc inutile).
Voici quelques fonctions de blending intéressantes à connaître, car elles servent souvent :
- ONE, ONE : blending additif, l'image dessinée s'ajoute à l'image précédente. Plus on effectue d'affichages, plus les composantes RGBA tendent vers 1 (-> blanc) ;
- ZERO, ONE : désactive l'écriture dans le color buffer ;
- SRC_ALPHA, ONE : comme pour le blending additif, mais l'image que l'on affiche est modulée par sa composante alpha ;
- DEST_COLOR, SRC_COLOR : multiplication des deux images. Cette fonction est généralement utilisée avec l'une des images en niveau de gris, ainsi une des images est « noircie » par l'autre ;
- ONE, SRC_COLOR : encore une multiplication, mais additive cette fois-ci, ainsi on a un effet de saturation dans l'image obtenue ;
- SRC_ALPHA, INV_SRC_ALPHA : fonction de blending utilisée pour effectuer de la transparence réaliste, l'image de destination est modulée par l'inverse de l'image source. Attention néanmoins à l'utilisation de cette fonction ; pour qu'elle donne un résultat valide, il faut que les faces soient affichées de la plus lointaine à la plus proche, on parle de transparence dépendant de l'ordre d'affichage.
L'antialiasing (anticrénelage) est une technique qui consiste à supprimer les effets d'escalier sur les bordures des polygones.
À l'heure actuelle, il existe deux types d'algorithmes d'antialiasing :
1. Le super-sampling : c'est l'ancienne méthode créée à l'époque des Voodoo 4/5. Elle consiste à rendre la scène dans une surface plus grande que le back-buffer souhaité par l'utilisateur puis lors de l'affichage à l'écran, à réduire l'image avec un filtre qui va ainsi adoucir l'ensemble de l'image. Cette méthode a l'avantage d'être généraliste (elle marche aussi avec les textures alpha, mais elle a plusieurs désavantages :
- elle est très consommatrice en mémoire. Du super-sampling 2X multiplie par 2 les dimensions de l'image de rendu, ce qui consomme donc 4 fois plus de mémoire (16 fois pour du 4X),
- elle multiplie le temps de calcul proportionnellement à la taille de la surface utilisée (plus le temps de réduction / filtrage de l'image),
- l'ensemble de l'image est adouci, donc les textures aussi, ce qui peut donner un effet de flou général assez désagréable ;
2. Le multisampling : créée pour les GeForce 3, cette méthode utilise plusieurs surfaces qui contiennent des informations sur les pixels (les fameux samples). La façon de traiter les pixels avec cette méthode dépend complètement du constructeur, de la génération de la carte et même des drivers, mais en gros, l'algorithme consiste à stocker la couleur, la profondeur, le recouvrement et des pondérations pour les pixels sur les bords des polygones. Cette méthode a comme avantage par rapport au super-sampling d'avoir un rapport qualité d'image/coût en mémoire nettement meilleur, et comme l'algorithme ne traite que les bords des polygones, il est nettement plus rapide. Néanmoins, ce gain de rapidité a un prix : les textures alpha ne sont pas traitées et on peut donc voir apparaître de l'aliasing sur ces textures.
Il existe aussi des méthodes hybrides comme l'antialiasing Quincunx de nVidia, qui consiste à faire du multisampling 2X puis à effectuer du super-sampling local sur les bords.
Dans la réalité
Le glow dans la réalité est en fait le halo lumineux que l'on peut voir au niveau de sources lumineuses. Pour que nos yeux voient une couleur, il faut que des photons les « percutent ». Une source lumineuse va envoyer des photons dans une ou plusieurs directions (dans le cas d'un laser, ils sont envoyés dans une seule direction ce qui explique qu'on ne le voit pas, alors qu'une lampe va en envoyer dans toutes les directions et donc on la voit) et on voit un halo lumineux quand plusieurs de ces photons ont été déviés de leur trajectoire. En effet, il arrive que des photons percutent des atomes contenus dans l'air (ou dans l'eau), et ils sont donc déviés de leur trajectoire.
Le fait que le halo lumineux soit diffus est provoqué par le fait qu'il y a de moins en moins de photons qui suivent la même trajectoire.
Si par exemple un laser lance 10 000 photons (c'est bien plus que ça en réalité, mais c'est pour l'exemple), il se peut que 1000 photons subissent une même déviation, mais ensuite parmi ces 1000 photons peut-être que 500 vont être déviés de la même façon, alors que les autres vont être déviés dans une autre direction. Et donc finalement, il y aura de moins en moins de photons qui se retrouveront déviés au même endroit, ce qui explique ce halo diffus.
Voici ci-dessous un exemple de glow sur un laser :
On peut voir sur l'image qu'il y a un halo lumineux diffus autour du laser, nous avons donc notre glow. Vous remarquerez aussi que le glow est plus important quand il y a de la fumée ou du brouillard au niveau de la source lumineuse. Ce phénomène s'explique par le fait que dans de telles situations, il y a plus d'atomes susceptibles d'être percutés par les photons.
Dans les jeux vidéo
Dans les jeux vidéo, une des façons de reproduire cet effet est de flouter (rajouter du flou) l'image puis d'ajouter l'image floue à l'image de base en faisant bien attention de n'ajouter que les parties lumineuses. Ainsi on obtient une image finale sur laquelle les sources lumineuses sont entourées d'un halo lumineux.
Exemple en pseudo-code
On aurait par exemple une première passe dans le pixel shader, sur laquelle on va faire un render to texture (on stocke notre résultat directement dans une texture).
Voici à quoi pourrait ressembler cette passe en pseudo-code :
=> point d'entrée du pixel shader depuis lequel on reçoit les coordonnées de la texture (ici la texture est l'image finale,
donc le framebuffer, mais pour lequel on a extrait la lumière (pour ça vous pouvez utiliser une image servant de mask et
contenant les intensités de lumière pour chaque pixel, ou bien directement stocker cette information dans le canal alpha
(s'il ne vous sert pas)
- On stocke la couleur de la texture avec les coordonnées courantes.
- On ajoute à notre couleur la couleur de la texture avec des coordonnées
légèrement modifiées (par exemple TexCoord.x-0.01)
- On répète l'étape du dessus autant de fois que vous le voulez et avec des coordonnées différentes à chaque fois, mais qui restent proches des coordonnées actuelles
- On divise la couleur obtenue par le nombre de fois qu'on a modifié la couleur
(pour faire une moyenne et ainsi garder une couleur cohérente)
- On a donc notre pixel flouté, maintenant il faut l'enregistrer dans la texture créée
(car on fait un render to texture dans cette passe)
Une fois qu'on a une image contenant uniquement la lumière (le reste étant noir) et qui est floue, on peut passer à la deuxième passe.
Dans cette deuxième passe, on va simplement prendre notre image ainsi que le framebuffer et mélanger les deux ensemble :
=> point d'entrée du pixel shader.
- On va ajouter la couleur du pixel courant de notre image floue avec la couleur du pixel courant de notre framebuffer
Dans la réalité
L'effet bloom se produit lorsqu'une importante source lumineuse rencontre une surface très réfléchissante (comme du chrome). En effet à ce moment-là la surface réfléchissante pourra être assimilée à une source de lumière (plus ou moins importante en fonction de sa réflexion) et c'est ainsi qu'on obtient un effet de bloom.
Quand on a un effet de bloom, on a généralement avec lui un effet de glow.
Voici un exemple d'effet bloom :
Comme vous pouvez le voir depuis cette image, une partie de la théière est très éclairée ce qui provoque un effet de bloom.
Dans les jeux vidéo
Pour réaliser cet effet dans les jeux, il faut travailler avec des grandes intensités de couleur (HDRR). Ainsi au lieu de travailler avec des canaux sur 8 bits, vous allez travailler avec des canaux en nombres flottants sur 16 bits. De cette manière vous aurez une plus grande précision et un meilleur contraste avec les lumières intenses et le reste de la scène. Cependant les moniteurs ne supportent pas une telle précision, il vous faudra donc revenir sur des canaux 8 bits pour l'affichage, c'est le passage HDR -> LDR qu'on appelle le tone mapping.
Il va donc vous falloir pour votre effet une image vous servant de mask et stockant l'intensité de lumière pour chaque pixel (ou bien vous vous servez du canal alpha si vous n'en avez pas besoin). Depuis ce mask vous allez pouvoir transformer vos pixels qui sont sur des canaux 8 bits en pixels sur des canaux en nombres flottants 16 bits par une simple opération que vous choisissez (par exemple : nombre16bits = nombre8bits * intensité).
Vous avez donc vos pixels en HDR, il vous faut maintenant les transformer en pixels LDR avec une formule de tone mapping, par exemple : nombre8bits = pixelCourant16bits/plusGrandeValeurPixel16bits. Dans cet exemple pour chacun des pixels, vous divisez sa valeur par la plus grande valeur de pixel que vous avez trouvé dans l'image. (Attention cependant, car cette formule donne de mauvais résultats sur des images avec très peu de lumière très intense, car tous les pixels vont être divisés par cette couleur très intense, et donc l'image finale sera très sombre.)
Une fois que vous avez votre effet de bloom, vous pouvez y ajouter un effet de Glow pour rendre la scène encore plus réaliste. L'utilisation des deux effets peut s'appeler l'effet Glare (éblouissement).
Dans la réalité
Le depth of Field (DOF), ou encore champs de profondeur, est en fait un effet de flou généré sur des objets que nous ne regardons pas, mais qui sont dans notre champ de vision.
En fait vous avez surement dû remarquer que quand vous regardez un objet, les autres objets de votre champ de vision vous apparaissent flous. Si vous n'êtes toujours pas convaincus alors imaginez-vous dans votre voiture: vous avez devant vous le pare-brise et derrière celui-ci il y a la route. Habituellement vous regardez la route, donc celle-ci vous apparaît nette, mais maintenant regardez le pare-brise et seulement lui, eh bien vous verrez que cette fois la route vous apparaît floue.
Voici un exemple d'effet de champ de profondeur :
Dans les jeux vidéo
Pour implémenter cet effet, il faut d'abord, pour chaque pixel du framebuffer, savoir à quelle distance il se situe par rapport à la caméra.
Une fois que l'on connaît cette distance, on va calculer le coefficient de flou à lui appliquer en fonction de cette distance, et on stocke ce coefficient à la place du canal alpha par exemple (ou bien en créant une nouvelle texture avec cette information).
Une fois ceci fait, alors pour chaque pixel, on va appliquer un flou qui sera plus ou moins fort en fonction du coefficient de flou.
Cet algorithme suppose que vous regardiez l'objet le plus prêt de vous. Si ce n'est pas le cas, alors au lieu de calculer le coefficient de flou par rapport à la distance du pixel par rapport à la caméra, vous devez le calculer en fonction des informations qui vous intéressent.
Dans la réalité
Le motion blur est un effet de flou sur les objets en mouvement. Ce flou est provoqué par la persistance rétinienne de nos yeux. Ainsi quand nous voyons un objet son « fantôme » reste encore visible quelques secondes (voire moins) même quand l'objet en question n'est plus visible.
Voici un exemple de motion blur :
Dans les jeux vidéo
Il y a plusieurs façons d'implémenter cet effet, mais le plus simple qui me vient à l'esprit serait que pour chaque objet en mouvement, on stocke quelque part sa direction et sa vitesse (sa position étant déjà connue dans le vertex shader), et que par rapport à ces informations on crée un effet de flou qui suive la direction et qui aille plus ou moins loin en fonction de la vitesse de l'objet.
Dans la réalité
Pour vous mettre en condition, imaginez que vous êtes dans la rue et qu'il fait nuit. Cette rue est éclairée par des lampadaires, et si vous regardez leur source lumineuse, vous verrez que ça forme une étoile.
Voici un exemple :
Vous pouvez voir sur cette image un exemple d'effet streak (qui veut dire écart). En effet on voit bien que les rayons lumineux partent dans tous les sens et forment une sorte d'étoile.
Cet effet est en fait provoqué par toutes les cassures, impuretés, poussières, etc. qu'il y a sur le verre et qui provoquent donc une déviation de certains rayons lumineux, ce qui donne cet effet d'étoile.
Dans les jeux vidéo
Cet effet n'est pas très difficile à reproduire dans les jeux, et il y a plusieurs façons de le faire.
- Soit en se servant d'une image mask, sur laquelle ce qui est blanc (ou très clair) représente l'étoile, et ce qui est noir (ou très sombre) ne représente rien. Avec cette technique il suffit, lorsqu'on a une source de lumière dans la scène, de récupérer la couleur de la lumière et d'appliquer ce mask au niveau de la lumière en appliquant la couleur de celle-ci.
- Soit en faisant ça procéduralement (donc avec des calculs). Pour cette technique, il faut par exemple définir différentes directions à partir de la lumière, et appliquer un flou depuis la lumière dans les différentes directions (vous choisissez la distance jusqu'à laquelle aller). Ainsi vous obtiendrez plusieurs rayons (en fonction du nombre de directions que vous avez choisi) qui partent de la source lumineuse et qui vont dans les directions que vous aurez choisies.
Le lens flare (effet de lentille) est en fait l'apparition de plusieurs halos lumineux lorsqu'on regarde une forte source de lumière avec une caméra (ou autre objet avec une lentille comme un appareil photo).
Voici un exemple de lens flare :
Sur cette image vous pouvez voir qu'il y a plusieurs cercles lumineux, c'est donc l'effet lens flare. (Cette image est tirée d'un article qui montre la création de cet effet ce qui explique que dans la réalité ce n'est pas tout à fait comme ça.)
Cet effet est produit à cause de la réflexion de la lumière entre les lentilles de la caméra qui crée des images fantômes de cette source de lumière.
Dans les jeux vidéo
Cet effet a été l'un des premiers à être implémenté dans les jeux vidéo en raison de sa simplicité. En effet pour créer ce genre d'effet, il suffit d'avoir une image servant de mask représentant un cercle lumineux et d'appliquer ce mask aux bons endroits dans l'image.
Pour savoir où sont les bons endroits, il suffit de connaître la position de la source lumineuse et d'appliquer le cercle lumineux autant de fois que vous le voulez en direction de la caméra.