Wikilivres
frwikibooks
https://fr.wikibooks.org/wiki/Accueil
MediaWiki 1.45.0-wmf.8
first-letter
Média
Spécial
Discussion
Utilisateur
Discussion utilisateur
Wikilivres
Discussion Wikilivres
Fichier
Discussion fichier
MediaWiki
Discussion MediaWiki
Modèle
Discussion modèle
Aide
Discussion aide
Catégorie
Discussion catégorie
Transwiki
Discussion Transwiki
Wikijunior
Discussion Wikijunior
TimedText
TimedText talk
Module
Discussion module
Wiki-hop/wikisphère/wikis professionnels
0
27267
746297
217389
2025-07-08T06:41:24Z
2A02:8440:8104:7704:0:50:9C0C:6E01
Le site wikiprof.net n'est plus accessible, projet similaire sur wikiprof.fr
746297
wikitext
text/x-wiki
Cette catégorie rassemble une sélection de projets éditoriaux animés par des professionnels pour des professionnels (enseignants, documentalistes, etc.) en relation avec l'éducation. Espaces d'échanges et de mutualisation, ces projets n'ont généralement pas pour vocation de constituer des produits finis.
===WikiEducator===
web : http://fr.wikieducator.org/Accueil
'''''Caractéristiques :'''''
Lancé à l'initiative du Commonwealth of Learning (CoL), WikiEducator se positionne comme un espace de réflexion et de mutualisation des ressources éducatives libres (REL).
'''''Eléments d'appréciation :'''''
==René Wiki==
web : http://wiki.univ-paris5.fr/
'''''Caractéristiques :'''''
Wiki de la cellule Tice de l'université Paris 5.
'''''Eléments d'appréciation :'''''
: Les inscriptions sont réservées aux membres de l'établissement.
==Bibliopedia==
web : http://www.bibliopedia.fr/
'''''Caractéristiques :'''''
'''''Eléments d'appréciation :'''''
==Exemples de wikis non francophones==
===Wikiversity : Portal Education===
web : http://en.wikiversity.org/wiki/Portal:Education
'''''Caractéristiques :'''''
'''''Eléments d'appréciation :'''''
===DIPF : BildungsLexicon===
web : http://wiki.bildungsserver.de/index.php/Hauptseite
'''''Caractéristiques :'''''
Ce dictionnaire de l'éducation et de la formation est un fork de Wikipédia.
'''''Eléments d'appréciation :'''''
==Exemples de fermes à wiki==
===Wikiprof===
web : https://www.wikiprof.fr
'''''Caractéristiques :'''''"Ecrivons nos cours ensemble", Un wiki expérimental qui a vocation de tester les potentialités pédagogiques de la création de cours assisté par l'intelligence artificielle et aussi de les mutualiser dans un wiki.
'''''Eléments d'appréciation :'''''
===Wikispaces (anglais)===
web : http://www.wikispaces.com/
'''''Caractéristiques :'''''
'''''Eléments d'appréciation :'''''
[[Catégorie:Wiki-hop]]
drz4e6dhjhw9s1s783tdr9leovdgfpt
Photographie/Personnalités/C/Julia Margaret Cameron
0
32767
746293
636742
2025-07-07T20:35:57Z
82.4.194.146
Replace Alice Liddell image with a higher-quality version
746293
wikitext
text/x-wiki
{{Ph s Personnalités}}
[[File:Julia Margaret Cameron MET DP114480 (cropped).jpg|thumb|240px|Julia Margaret Cameron par [[Oscar Gustave Rejlander]] ou [[Lewis Carroll]]]]
'''Julia Margaret Cameron''' était une photographe anglaise (1815 - 1879).
{{clr}}
== Galerie de photographies ==
<gallery widths="240px" heights="240px">
Robert Browning LACMA M.91.388.25.jpg|Robert Browning
Robert Browning. Photograph by Julia Margaret Cameron, 1865. Wellcome V0027592.jpg|Robert Browning
Mary Lady Cotton.png|Mary Lady Cotton
Charles Robert Darwin. Photograph by Julia Margaret Wellcome V0026273.jpg|Charles Darwin
Julia Margaret Cameron - John Herschel (Metropolitan Museum of Art copy, restored).jpg|John Herschel
JFW Herschel.jpg|John Herschel
John Frederick William Herschel, 1867 (7643162228).jpg|John Frederick William Herschel, 1867
Julia Margaret Cameron - John Herschel (Metropolitan Museum of Art copy, restored).png|John Herschel
Julia Margaret Cameron - John Herschel (Metropolitan Museum of Art copy, original scan UNRESTORED).jpg|John Herschel
JohnHerschel.png|John Herschel
Julia Jackson MET DT1121.jpg|Julia Jackson
Joseph Joachim, 1868 (7643162346).jpg|Joseph Joachim, 1868
Herr Joseph Joachim, 1868 (7643162442).jpg|Joseph Joachim, 1868
Benjamin Jowett by Julia Margaret Cameron.JPG|Benjamin Jowett
Kate Keown, 1867 (7643191456).jpg
May Prinsep. Photograph by Julia Margaret Cameron. Wellcome V0027589.jpg|Christabel (May Prinsep)
Christabel (May Prinsep), 1866 (7643162108).jpg|Christabel (May Prinsep), 1866
Julia Margaret Cameron - Valentine Prinsep, 1867.jpg|Valentine Prinsep, 1867
Hannah de Rothschild, later Countess of Rosebery, by Julia Margaret Cameron.jpg|Hannah de Rothschild
Marie Spartali Stilman, 1868.jpg|Marie Spartali Stilman, 1868
Marie Spartali Stillman, 1868.jpg|Marie Spartali Stilman, 1868
Henry Taylor, 1865 (7643190996).jpg|Henry Taylor, 1865
Alfred Tennyson, 1867 (7643190892).jpg|Alfred Tennyson, 1867
Lionel Tennyson, 1869 (7643161968).jpg|Lionel Tennyson, 1869
Samuel Wilberforce, bishop of Oxford. Photograph by Julia Ma Wellcome V0027591.jpg|Samuel Wilberforce
Philip Stanhope Worsley MET DP206679.jpg|Philip Stanhope Worsley
Zoe, Maid of Athens MET DT1174.jpg
Zuleika. Photograph by Julia Margaret Cameron, c.1864-1867. Wellcome V0027587.jpg|Zuleika, c. 1864-1867
Cameron VenusChidingCupidAndDeprivingHimOfHisWings MIA 95162.jpg|Venus Chiding Cupid And Depriving Him Of His Wings
Portrait of an unidentified girl (Mary Hillier?). Photograph Wellcome V0027588.jpg
A woman reading a letter. Photograph by Julia Margaret Camer Wellcome V0027590.jpg
I Wait, by Julia Margaret Cameron.jpg
The Little Novice and Queen Guinevere In The Holy House Of Almsbury LACMA M.2008.40.378.jpg
"A Bacchante", by Julia Margaret Cameron (1867).JPG|Une bacchante, 1867
La Madonna Riposata Resting in Hope (Mary Hillier and Percy Keown), 1864 (7643191158).jpg
Clio (Mary Hillier), 1866 (7643191312).jpg
Love In Idleness (Freddy Gould), 1866 (7643191572).jpg
George Frederick Watts, 1864 (7643190770).jpg
Young Astyanax (Freddy Gould), 1866 (7643236486).jpg
Henry Taylor, 1864 (7643236770).jpg
My Favourite Picture of All My Works (My niece Julia Jackson), 1867 (7643236620).jpg
Unknown subject (Mary Ryan and unknown man) c.1867 (7643237214).jpg
Mary Fisher, 1866-67 (7643236910).jpg
The Mountain Nymph Sweet Liberty (Mrs. Keene), 1866 (7643237480).jpg
Lord Hatherly, 1869 (7643237366).jpg
Margie Thackery, 1866-69 (7643237612).jpg
William Michael Rossetti, 1865 (7643237748).jpg
Left Robert Browning, 1865. Right Henry Halford Vaughan, 1864 -70 (7643237908).jpg
IWait.png
JohnHerschel.png|John Herschel
Baby "Pictet" (3588771149).jpg
Joseph Joachim 1868.jpg
King Lear allotting his Kingdom to his three daughters, by Julia Margaret Cameron (detail of King Lear).jpg
Alfred and Emily Tennyson with their sons at Farringford, by Oscar Gustave Rejlander.jpg
Vectis, by Julia Margaret Cameron.jpg
Suspense, by Julia Margaret Cameron.jpg
Isabel Bateman, by Julia Margaret Cameron.jpg
Hypatia, by Julia Margaret Cameron.jpg
A Holy Family, by Julia Margaret Cameron.jpg
Mary Mother, by Julia Margaret Cameron.jpg
Summer Days, by Julia Margaret Cameron.jpg
May Day, by Julia Margaret Cameron.jpg
The Kiss of Peace, by Julia Margaret Cameron.jpg
The Rose bud garden of girls, by Julia Margaret Cameron.jpg
Henry Herschel Hay Cameron, by Julia Margaret Cameron.jpg
Hon. Frank Charteris, by Julia Margaret Cameron.jpg
The Twilight hour, by Julia Margaret Cameron.jpg
The Wild Flower, by Julia Margaret Cameron.jpg
Ewen Wrottesley Hay Cameron, by Julia Margaret Cameron.jpg
Cherub and Seraph, by Julia Margaret Cameron.jpg
The Young Endymion, by Julia Margaret Cameron.jpg
Cupid's Pencil of Light, by Julia Margaret Cameron.jpg
Love in Idleness, by Julia Margaret Cameron.jpg
Unknown man 2, by Julia Margaret Cameron.jpg
Charles Norman and his Daughters, Adeline and Margaret, by Julia Margaret Cameron.jpg
Kate Keown Reading, by Julia Margaret Cameron.jpg
Kate Keown, by Julia Margaret Cameron.jpg
The Return after 3 days, by Julia Margaret Cameron.jpg
La Madonna Riposata Resting in hope, by Julia Margaret Cameron.jpg
Lionel Tennyson with bow & arrow, by Julia Margaret Cameron.jpg
Lionel Tennyson, by Julia Margaret Cameron.jpg
Two Little Girls Wearing Hats, by Julia Margaret Cameron.jpg
Ernest and Maggie, by Julia Margaret Cameron.jpg
A Child's Head 'Little Bee', by Julia Margaret Cameron.jpg
My Grandchild aged 2 years & 3 months, by Julia Margaret Cameron.jpg
I Wait, by Julia Margaret Cameron.jpg
Angel of the Nativity, by Julia Margaret Cameron.jpg
Déjatch Alámayou, King Theodore's Son, by Julia Margaret Cameron.jpg
'Our Royal Cousin', by Julia Margaret Cameron.jpg
Lionel Tennyson in the character of the Marquis de St Cast, by Julia Margaret Cameron.jpg
Beatrice Cenci, by Julia Margaret Cameron.jpg
Study of Beatrice Cenci, by Julia Margaret Cameron.jpg
Claud & Lady Florence Anson, by Julia Margaret Cameron.jpg
The Red and White Roses, by Julia Margaret Cameron.jpg
Blackberry Gathering, by Julia Margaret Cameron.jpg
King Ahasuerus & Queen Esther in Apocrypha, by Julia Margaret Cameron.jpg
King Lear allotting his Kingdom to his three daughters, by Julia Margaret Cameron.jpg
A Sibyl, by Julia Margaret Cameron.jpg
Gretchen, by Julia Margaret Cameron.jpg
The Mountain Nymph Sweet Liberty, by Julia Margaret Cameron.jpg
King Arthur, by Julia Margaret Cameron.jpg
The Parting of Sir Lancelot and Queen Guinevere, by Julia Margaret Cameron.jpg
Maud, by Julia Margaret Cameron.jpg
Elaine, by Julia Margaret Cameron.jpg
The Minstrel Group, by Julia Margaret Cameron.jpg
Queen Philippa interceding for the Burghers of Calais, by Julia Margaret Cameron.jpg
Alfred Tennyson, by Julia Margaret Cameron.jpg
Sir Henry Taylor, by Julia Margaret Cameron.jpg
Estate workers (Ceylon), by Julia Margaret Cameron.jpg
Untitled (Ceylon) 4, by Julia Margaret Cameron.jpg
Kalutara Peasant Man and Woman, by Julia Margaret Cameron.jpg
A Group of Kalutara Peasants 2, by Julia Margaret Cameron.jpg
A Group of Kalutara Peasants 1, by Julia Margaret Cameron.jpg
Untitled (Ceylon) 3, by Julia Margaret Cameron.jpg
Untitled (Ceylon) 2, by Julia Margaret Cameron.jpg
Untitled (Ceylon) 1, by Julia Margaret Cameron.jpg
Marianne North in Mrs Cameron's house in Ceylon, by Julia Margaret Cameron.jpg
Isabella Beeton, by Maull & Polyblank.jpg
Prince Albert of Saxe-Coburg-Gotha, by John Jabez Edwin Mayall.jpg
Deathbed Study, by Julia Margaret Cameron.jpg
Days at Freshwater, by Julia Margaret Cameron.jpg
Déjatch Alámayou and and Báshá Félika, by Julia Margaret Cameron.jpg
Young Astyanax, by Julia Margaret Cameron.jpg
The Bride, by Julia Margaret Cameron.jpg
The Angel at the Tomb, by Julia Margaret Cameron.jpg
La Contadina, by Julia Margaret Cameron.jpg
Call, I Follow, I Follow, Let Me Die, by Julia Margaret Cameron.jpg
Mary Ann Hillier, by Julia Margaret Cameron.jpg
The Dialogue, by Julia Margaret Cameron.jpg
The Echo, by Julia Margaret Cameron.jpg
Sisters, by Julia Margaret Cameron.jpg
Fichier:Sir John Herschel, by Julia Margaret Cameron.jpg|Sir [[Photographie/Personnalités/H/John Herschel|John Herschel]], 1867
The Angel in the House, by Julia Margaret Cameron.jpg
After the manner of Perugino, by Julia Margaret Cameron.jpg
The Angel at the Sepulchre, by Julia Margaret Cameron.jpg
The Gardener's Daughter, by Julia Margaret Cameron.jpg
Magdalene Brookfield, by Julia Margaret Cameron.jpg
A Study after the manner of Francia, by Julia Margaret Cameron.jpg
Image:Julia Margaret Cameron MET DP114480 (cropped).jpg|Self-portrait, ca. 1862
Image:Julia Margaret Cameron.jpg|By Henry Herschel Hay Cameron, 1870
Image:Julia Margaret Cameron by George Frederic Watts.jpg|By [[George Frederic Watts]], 1850-1852
Julia Margaret Cameron, by Henry Herschel Hay Cameron.jpg|By Henry Herschel Hay Cameron, 1870
Julia Margaret Cameron, by James Prinsep.jpg|James Prinsep
Image:Cameron julia jackson.jpg|Julia Jackson, 1867
Image:I Wait, by Julia Margaret Cameron.jpg|"''I wait''"
Image:Sir John Herschel with Cap by Julia Margaret Cameron (detail).jpg|[[:en:John Herschel|John Herschel]]
Image:Sir John Herschel, by Julia Margaret Cameron.jpg|[[:en:John Herschel|John Herschel]]
Image:Alice Liddell as Pomona by Julia Margaret Cameron.jpg|[[Alice Liddell]]
Image:Study of Beatrice Cenci, by Julia Margaret Cameron.jpg|Beatrice Cenci (May Prinsep)
Image:Beatrice Cenci, by Julia Margaret Cameron.jpg|Beatrice Cenci (Kate Keown)
Image:Charles_Darwin_by_Julia_Margaret_Cameron.jpg|[[Charles Darwin]]
Image:Marianne North01.jpg|<div style="text-align: center;">Marianne North</div>
Image:HenryWLongFellow1868.jpg|[[Henry Wadsworth Longfellow]] 1868
IWait.png
</gallery>
== Bibliographie ==
{{Ph Personnalités}}
[[Catégorie:Personnalités de la photographie]]
{{commonscat|Julia Margaret Cameron}}
{{DEFAULTSORT:Cameron, Julia mARGARET}}
4wp3ev9d3of01403lpw1qxm3da0axwj
Les cartes graphiques/Le multi-GPU
0
67391
746294
744822
2025-07-08T00:02:55Z
Mewtow
31375
/* L'implémentation matérielle du multi-GPU */
746294
wikitext
text/x-wiki
Les techniques dites de multi-GPU, tels le SLI et le Crossfire, permettent de mettre plusieurs cartes graphiques dans un PC pour gagner en performances. Le multi-GPU a eu son heure de gloire durant les années 2000. Dès 1998, il était possible de mettre dans un même PC deux cartes graphiques Voodoo 2, de marque 3dfx. Autre exemple : en 2006, le fabricant de cartes graphiques S3 avait introduit cette technologie pour ses cartes graphiques Chrome. Mais le multi-GPU est tombé en désuétude après 2010, du moins pour le grand public.
Le multi-GPU était destiné aux jeux vidéo, même si les applications de réalité virtuelle, l'imagerie médicale haute précision ou les applications de conception par ordinateur pouvaient en tirer profit. C'est ce genre de choses qui se cachent derrière les films d'animation ou les effets spéciaux créés par ordinateur : Pixar ou Disney ont vraiment besoin de rendre des images très complexes, avec beaucoup d'effets, ce qui demande la coopération de plusieurs cartes graphiques.
==La répartition des calculs sur les GPU==
Tout le problème des solutions multi-GPU est de répartir les calculs sur plusieurs cartes graphiques, ce qui est loin d'être chose facile. Il existe diverses techniques, chacune avec ses avantages et ses inconvénients, que nous allons aborder de suite. Mais elles peuvent être classées en deux types. Le ''Split Frame Rendering'' répartit le calcul d'une image sur plusieurs GPU. L'''Alternate Frame Rendering'' calcule une image sur un GPU, les GPU calculent chacun des images différentes.
===Le ''Split Frame Rendering''===
Le '''''Split Frame Rendering''''' (SFR) découpe l'image en morceaux, qui sont répartis sur des cartes graphiques différentes. Le SFR et l'AFR n'utilisent pas les GPU de la même manière. Le SFR demande d'utiliser au moins deux GPU, soit deux GPU sur une même carte imprimée, soit deux cartes graphiques séparées. Les GPU ont une organisation entre maitre-esclave : un GPU est un maitre, les autres sont des esclaves. Tous les GPU font des calculs de rendu pour un morceau de l'image final, mais seul le GPU maitre récupère les résultats calculés par les GPU esclaves et combine le tout pour donner l'imager finale. Pour faire la combinaison, le GPU contient des circuits de composition d'image dédié et c'est lui qui a le ''framebuffer'' final.
Historiquement, la première technique multi-GPU inventée est apparue sur les cartes graphiques Voodoo 2 et s'appelait le '''''Scan Line Interleave''''', ou SLI. Elle fonctionnait avec seulement deux GPU maximum. Le premier GPU rendait les lignes paires et l'autre les lignes impaires. Il faut noter qu'outre des performances améliorées, utiliser le SLI permettait de doubler la résolution, faisant passer d'une résolution maximale de 800 par 600 maximum pour une voodoo 2, à 1024 par 768. En théorie, on peut adapter la technique à un nombre arbitraire de GPU, en faisant calculer par chaque GPU une ligne sur 3, 4, 5, etc.
[[File:Scanline interleave.png|centre|vignette|upright=2.0|Scanline interleave]]
Il est aussi possible de simplement couper l'image en deux : la partie haute de l'image ira sur un GPU, et la partie basse sur l'autre. Cette technique peut être adaptée avec plusieurs GPU, en découpant l'image en autant de parties qu'il y a de GPU. Intuitivement, on se dit que l'écran est coupé en deux portions égales. Mais en faisant cela, des complications peuvent survenir dans certains jeux où le bas de l'image est plus chargé que le haut, les FPS notamment. Dans ces jeux, le haut représente le ciel ou un plafond assez vide de géométrie, toute la géométrie et les textures sont dans le bas de l'image. Ainsi, le rendu de la partie haute sera plus rapide que celui du bas, une des cartes 3D finira par attendre l'autre.
Mieux répartir les calculs devient alors nécessaire. Pour cela, on peut choisir un découpage statique adapté, dans lequel la partie haute envoyée au premier GPU est plus grande que la partie basse. Cela peut aussi être fait dynamiquement : le découpage de l'image est alors choisi à l’exécution, et la balance entre partie haute et basse s'adapte aux circonstances. Pour cela, le driver dispose d'algorithmes plus ou moins complexes capables de déterminer assez précisément comment découper l'image au mieux. Mais il va de soit que ces algorithmes ne sont pas parfaits.
[[File:Screen spliting.png|centre|vignette|upright=2.0|Screen spliting]]
La technique du '''''Checker Board''''' découpe l'image en carrés de plusieurs pixels, de taille identique. Le premier GPU calcule les carrés pairs, le second GPU calcule les carrés impairs. Les carrés ont une taille fixe, de 16 ou 32 pixels de largeur, identique pour tous les carrés d'une image. L'avantage est que la technique équilibre bien la charge de travail entre les deux GPU : les deux GPU calculent une portion égale de l'écran, autant en haut qu'en bas.
===L'''Alternate Frame Rendering''===
L''''alternate Frame Rendering''' (AFR) consiste à répartir des images complètes sur les différents GPUs. Dans sa forme la plus simple, un GPU calcule une image, et l'autre GPU calcule la suivante en parallèle. Les problèmes liés à la répartition des calculs entre cartes graphiques disparaissent alors. L'AFR a été inventé par ATI, sur ses cartes graphiques Rage Fury, afin de faire concurrence à la Geforce 256.
Un des défauts de cette approche est le '''micro-stuttering'''. Dans des situations où le processeur est peu puissant, les temps entre deux images peuvent se mettre à varier très fortement, et d'une manière beaucoup moins imprévisible. Le nombre d'images par seconde se met à varier rapidement sur de petites périodes de temps. Alors certes, on ne parle que de quelques millisecondes, mais cela se voit à l’œil nu. Cela cause une impression de micro-saccades, que notre cerveau peut percevoir consciemment, même si le temps entre deux images est très faible. Suivant les joueurs, des différences de 10 à 20 millisecondes peuvent rendre une partie de jeu injouable.
Pour diminuer l'ampleur de ce phénomène, les cartes graphiques récentes incorporent des circuits pour limiter la casse. Ceux-ci se basent sur un principe simple : pour égaliser le temps entre deux images, et éviter les variations, le mieux est d’empêcher des images de s'afficher trop tôt. Si une image a été calculée en très peu de temps, on retarde son affichage durant un moment. Le temps d'attente idéal est alors calculé en fonction de la moyenne du framerate mesuré précédemment.
Ensuite, il arrive que deux images soient dépendantes les unes des autres : les informations nées lors du calcul d'une image peuvent devoir être réutilisées dans le calcul des images suivantes. Cela arrive quand des données géométriques traitées par la carte graphique sont enregistrées dans des textures (dans les Streams Out Buffers pour être précis), dans l'utilisation de fonctionnalités de DirectX ou d'Open GL qu'on appelle le Render To Texture, ainsi que dans quelques autres situations. Évidemment, avec l'AFR, cela pose quelques problèmes : les deux cartes doivent synchroniser leurs calculs pour éviter que l'image suivante rate des informations utiles, et soit affichée n'importe comment. Sans compter qu'en plus, les données doivent être transférées dans la mémoire du GPU qui calcule l'image suivante.
==L'implémentation matérielle du multi-GPU==
Le multi-GPU peut se présenter sous plusieurs formes. Il est possible d'utiliser des GPU différents, des GPU identiques, de placer plusieurs GPU sur un même circuit imprimé, et j'en passe. Voyons ces méthodes en revue.
La plus simple des méthodes consiste à placer plusieurs GPU sur une même carte graphique. La technique a été utilisée dès les premières cartes accélératrices 2D. Par exemple, la Voodoo 5500 était une carte avec deux GPU sur son circuit imprimé et elle est sortie en Juin 2000. 3dfx a même envisagé des prototypes avec 4 GPU, portant le nom de Voodoo 5 6000, mais ils ne sont pas sortis dans le commerce. On parle alors de '''carte double GPU''' (''dual GPU'').
[[File:3dfx Voodoo 5500.jpg|centre|vignette|upright=2|3dfx Voodoo 5500]]
Il est aussi possible d'utiliser plusieurs cartes graphiques séparées, connectées à la carte mère via PCI-Express. Pour échanger des informations, les premières implémentations demandaient de connecter les deux cartes avec un connecteur spécialisé. ATI et NVIDIA faisaient ainsi sur les premières implémentations de leurs technologies Crossfire et SLI. Le connecteur n'était pas standardisé, dans le sens où ATI et NVIDIA avaient chacun leur connecteur dédié, incompatibles entre eux.
[[File:AMD CrossFireX – bridge connector-bottom oblique PNr°0369.jpg|centre|vignette|upright=2|Connecteur CrossFireX pour le multi-GPU ATI/AMD.]]
Par la suite, le connecteur SLI/CrossFire a rapidement été abandonné, pour laisser la place à des échanges passant par le PCI-Express. Le PCI Express permet en effet à deux périphériques de communiquer entre eux sans passer par l'intermédiaire du processeur, de la RAM, ou autre. En configurant des échanges DMA adéquats, plusieurs cartes graphiques dédiées peuvent communiquer entre elles via PCI-Express. ATI/AMD et NVIDIA utilisaient pour cela des technologies propriétaires, comme l'AMD DirectGMA.
[[File:AMD DirectGMA.svg|centre|vignette|upright=2|AMD DirectGMA.]]
Utiliser un connecteur dédié épargnait la bande passante PCI-Express, dans le sens où le connecteur fournissait de la bande passante en plus, utilisée uniquement pour la communication entre GPU. Les transferts PCI Express normaux n'entraient pas en compétition avec ceux du multi-GPU. Mais le gain était surtout pertinent sur les premières versions du PCI-Express dont le débit était limité. Sur les versions ultérieures du PCI-Express, le débit a augmenté suffisamment pour pouvoir gérer à la fois les transferts GPU normaux et les échanges multi-GPU sans trop de casse.
===L'ATI Crossfire===
La technologie multi-GPU d'ATI/AMD était appelée le Crossfire. Elle a été développée durant les années 2000 et a progressivement évolué dans le temps.
La toute première version n'était pas compatible avec toutes les cartes graphiques vendues par ATI. ATI vendait ses cartes graphiques en deux éditions : une édition Crossfire et une édition normale. Elle demandait d'utiliser une carte maitre avec une carte esclave. La carte esclave était une carte graphique normale ou Crossfire, mais la carte maitre devait obligatoirement être une carte Crossfire. La carte maitre était celle branchée sur l'écran. En tant que carte Crossfire, elle incorporait des circuits de composition d'image, afin de combiner les portions d'image calculée par elle-même avec celles calculées par la carte esclave.
Cependant, les circuits de composition d'image étaient un peu faibles. Par exemple, la Radeon XT 8500 ne supportait au maximum que des résolutions de 600×1200 - 60 Hz, or 1920×1440 - 52 Hz'''. Vu que le taux de rafraichissement était faible à de telles résolutions, surtout pour des écrans CRT qui ont tendance à faire mal aux yeux, le Crossfire était surtout utile pour les résolutions plus basses. Dommage pour du multi-GPU, censé aider pour les hautes résolutions.
Par la suite, les cartes Crossfire ont disparues et toutes les cartes graphiques ATI étaient compatibles Crossfire. La communication entre les GPU se faisait via le bus PCI Express. La technologie devint alors bien plus pratique, les cartes Crossfire étant rares et peu disponibles. Et ce malgré une petite perte en performance liée aux transferts via le PCI Express. Par la suite, l'introduction du CrossfireX ajouta le support d'un connecteur entre cartes graphiques, afin de passer outre le bus PCI Express. Cependant, cela ne dura que pour les générations des AMD HD 2000 à 7000. ATI/AMD a abandonné l'usage d'un connecteur CrossFire avec ses cartes utilisant le PCI-Express 3.0.
[[File:R700 interconnect.svg|centre|vignette|upright=2|Illustration du multi-GPU où deux cartes graphiques communiquent via un lien indépendant du bus PCI-Express. On voit que le débit du lien entre les deux cartes graphique est ajouté au débit du bus PCI-Express.]]
{{NavChapitre | book=Les cartes graphiques
| prev=L'antialiasing
| prevText=L'antialiasing
}}{{autocat}}
j8fgpydqi0nzpbq60y7xwz28soixofx
746295
746294
2025-07-08T00:05:03Z
Mewtow
31375
/* L'ATI Crossfire */
746295
wikitext
text/x-wiki
Les techniques dites de multi-GPU, tels le SLI et le Crossfire, permettent de mettre plusieurs cartes graphiques dans un PC pour gagner en performances. Le multi-GPU a eu son heure de gloire durant les années 2000. Dès 1998, il était possible de mettre dans un même PC deux cartes graphiques Voodoo 2, de marque 3dfx. Autre exemple : en 2006, le fabricant de cartes graphiques S3 avait introduit cette technologie pour ses cartes graphiques Chrome. Mais le multi-GPU est tombé en désuétude après 2010, du moins pour le grand public.
Le multi-GPU était destiné aux jeux vidéo, même si les applications de réalité virtuelle, l'imagerie médicale haute précision ou les applications de conception par ordinateur pouvaient en tirer profit. C'est ce genre de choses qui se cachent derrière les films d'animation ou les effets spéciaux créés par ordinateur : Pixar ou Disney ont vraiment besoin de rendre des images très complexes, avec beaucoup d'effets, ce qui demande la coopération de plusieurs cartes graphiques.
==La répartition des calculs sur les GPU==
Tout le problème des solutions multi-GPU est de répartir les calculs sur plusieurs cartes graphiques, ce qui est loin d'être chose facile. Il existe diverses techniques, chacune avec ses avantages et ses inconvénients, que nous allons aborder de suite. Mais elles peuvent être classées en deux types. Le ''Split Frame Rendering'' répartit le calcul d'une image sur plusieurs GPU. L'''Alternate Frame Rendering'' calcule une image sur un GPU, les GPU calculent chacun des images différentes.
===Le ''Split Frame Rendering''===
Le '''''Split Frame Rendering''''' (SFR) découpe l'image en morceaux, qui sont répartis sur des cartes graphiques différentes. Le SFR et l'AFR n'utilisent pas les GPU de la même manière. Le SFR demande d'utiliser au moins deux GPU, soit deux GPU sur une même carte imprimée, soit deux cartes graphiques séparées. Les GPU ont une organisation entre maitre-esclave : un GPU est un maitre, les autres sont des esclaves. Tous les GPU font des calculs de rendu pour un morceau de l'image final, mais seul le GPU maitre récupère les résultats calculés par les GPU esclaves et combine le tout pour donner l'imager finale. Pour faire la combinaison, le GPU contient des circuits de composition d'image dédié et c'est lui qui a le ''framebuffer'' final.
Historiquement, la première technique multi-GPU inventée est apparue sur les cartes graphiques Voodoo 2 et s'appelait le '''''Scan Line Interleave''''', ou SLI. Elle fonctionnait avec seulement deux GPU maximum. Le premier GPU rendait les lignes paires et l'autre les lignes impaires. Il faut noter qu'outre des performances améliorées, utiliser le SLI permettait de doubler la résolution, faisant passer d'une résolution maximale de 800 par 600 maximum pour une voodoo 2, à 1024 par 768. En théorie, on peut adapter la technique à un nombre arbitraire de GPU, en faisant calculer par chaque GPU une ligne sur 3, 4, 5, etc.
[[File:Scanline interleave.png|centre|vignette|upright=2.0|Scanline interleave]]
Il est aussi possible de simplement couper l'image en deux : la partie haute de l'image ira sur un GPU, et la partie basse sur l'autre. Cette technique peut être adaptée avec plusieurs GPU, en découpant l'image en autant de parties qu'il y a de GPU. Intuitivement, on se dit que l'écran est coupé en deux portions égales. Mais en faisant cela, des complications peuvent survenir dans certains jeux où le bas de l'image est plus chargé que le haut, les FPS notamment. Dans ces jeux, le haut représente le ciel ou un plafond assez vide de géométrie, toute la géométrie et les textures sont dans le bas de l'image. Ainsi, le rendu de la partie haute sera plus rapide que celui du bas, une des cartes 3D finira par attendre l'autre.
Mieux répartir les calculs devient alors nécessaire. Pour cela, on peut choisir un découpage statique adapté, dans lequel la partie haute envoyée au premier GPU est plus grande que la partie basse. Cela peut aussi être fait dynamiquement : le découpage de l'image est alors choisi à l’exécution, et la balance entre partie haute et basse s'adapte aux circonstances. Pour cela, le driver dispose d'algorithmes plus ou moins complexes capables de déterminer assez précisément comment découper l'image au mieux. Mais il va de soit que ces algorithmes ne sont pas parfaits.
[[File:Screen spliting.png|centre|vignette|upright=2.0|Screen spliting]]
La technique du '''''Checker Board''''' découpe l'image en carrés de plusieurs pixels, de taille identique. Le premier GPU calcule les carrés pairs, le second GPU calcule les carrés impairs. Les carrés ont une taille fixe, de 16 ou 32 pixels de largeur, identique pour tous les carrés d'une image. L'avantage est que la technique équilibre bien la charge de travail entre les deux GPU : les deux GPU calculent une portion égale de l'écran, autant en haut qu'en bas.
===L'''Alternate Frame Rendering''===
L''''alternate Frame Rendering''' (AFR) consiste à répartir des images complètes sur les différents GPUs. Dans sa forme la plus simple, un GPU calcule une image, et l'autre GPU calcule la suivante en parallèle. Les problèmes liés à la répartition des calculs entre cartes graphiques disparaissent alors. L'AFR a été inventé par ATI, sur ses cartes graphiques Rage Fury, afin de faire concurrence à la Geforce 256.
Un des défauts de cette approche est le '''micro-stuttering'''. Dans des situations où le processeur est peu puissant, les temps entre deux images peuvent se mettre à varier très fortement, et d'une manière beaucoup moins imprévisible. Le nombre d'images par seconde se met à varier rapidement sur de petites périodes de temps. Alors certes, on ne parle que de quelques millisecondes, mais cela se voit à l’œil nu. Cela cause une impression de micro-saccades, que notre cerveau peut percevoir consciemment, même si le temps entre deux images est très faible. Suivant les joueurs, des différences de 10 à 20 millisecondes peuvent rendre une partie de jeu injouable.
Pour diminuer l'ampleur de ce phénomène, les cartes graphiques récentes incorporent des circuits pour limiter la casse. Ceux-ci se basent sur un principe simple : pour égaliser le temps entre deux images, et éviter les variations, le mieux est d’empêcher des images de s'afficher trop tôt. Si une image a été calculée en très peu de temps, on retarde son affichage durant un moment. Le temps d'attente idéal est alors calculé en fonction de la moyenne du framerate mesuré précédemment.
Ensuite, il arrive que deux images soient dépendantes les unes des autres : les informations nées lors du calcul d'une image peuvent devoir être réutilisées dans le calcul des images suivantes. Cela arrive quand des données géométriques traitées par la carte graphique sont enregistrées dans des textures (dans les Streams Out Buffers pour être précis), dans l'utilisation de fonctionnalités de DirectX ou d'Open GL qu'on appelle le Render To Texture, ainsi que dans quelques autres situations. Évidemment, avec l'AFR, cela pose quelques problèmes : les deux cartes doivent synchroniser leurs calculs pour éviter que l'image suivante rate des informations utiles, et soit affichée n'importe comment. Sans compter qu'en plus, les données doivent être transférées dans la mémoire du GPU qui calcule l'image suivante.
==L'implémentation matérielle du multi-GPU==
Le multi-GPU peut se présenter sous plusieurs formes. Il est possible d'utiliser des GPU différents, des GPU identiques, de placer plusieurs GPU sur un même circuit imprimé, et j'en passe. Voyons ces méthodes en revue.
La plus simple des méthodes consiste à placer plusieurs GPU sur une même carte graphique. La technique a été utilisée dès les premières cartes accélératrices 2D. Par exemple, la Voodoo 5500 était une carte avec deux GPU sur son circuit imprimé et elle est sortie en Juin 2000. 3dfx a même envisagé des prototypes avec 4 GPU, portant le nom de Voodoo 5 6000, mais ils ne sont pas sortis dans le commerce. On parle alors de '''carte double GPU''' (''dual GPU'').
[[File:3dfx Voodoo 5500.jpg|centre|vignette|upright=2|3dfx Voodoo 5500]]
Il est aussi possible d'utiliser plusieurs cartes graphiques séparées, connectées à la carte mère via PCI-Express. Pour échanger des informations, les premières implémentations demandaient de connecter les deux cartes avec un connecteur spécialisé. ATI et NVIDIA faisaient ainsi sur les premières implémentations de leurs technologies Crossfire et SLI. Le connecteur n'était pas standardisé, dans le sens où ATI et NVIDIA avaient chacun leur connecteur dédié, incompatibles entre eux.
[[File:AMD CrossFireX – bridge connector-bottom oblique PNr°0369.jpg|centre|vignette|upright=2|Connecteur CrossFireX pour le multi-GPU ATI/AMD.]]
Par la suite, le connecteur SLI/CrossFire a rapidement été abandonné, pour laisser la place à des échanges passant par le PCI-Express. Le PCI Express permet en effet à deux périphériques de communiquer entre eux sans passer par l'intermédiaire du processeur, de la RAM, ou autre. En configurant des échanges DMA adéquats, plusieurs cartes graphiques dédiées peuvent communiquer entre elles via PCI-Express. ATI/AMD et NVIDIA utilisaient pour cela des technologies propriétaires, comme l'AMD DirectGMA.
[[File:AMD DirectGMA.svg|centre|vignette|upright=2|AMD DirectGMA.]]
Utiliser un connecteur dédié épargnait la bande passante PCI-Express, dans le sens où le connecteur fournissait de la bande passante en plus, utilisée uniquement pour la communication entre GPU. Les transferts PCI Express normaux n'entraient pas en compétition avec ceux du multi-GPU. Mais le gain était surtout pertinent sur les premières versions du PCI-Express dont le débit était limité. Sur les versions ultérieures du PCI-Express, le débit a augmenté suffisamment pour pouvoir gérer à la fois les transferts GPU normaux et les échanges multi-GPU sans trop de casse.
===L'ATI Crossfire===
La technologie multi-GPU d'ATI/AMD était appelée le Crossfire. Elle a été développée durant les années 2000 et a progressivement évolué dans le temps.
La toute première version n'était pas compatible avec toutes les cartes graphiques vendues par ATI. ATI vendait ses cartes graphiques en deux éditions : une édition Crossfire et une édition normale. Elle demandait d'utiliser une carte maitre avec une carte esclave. La carte esclave était une carte graphique normale ou Crossfire, mais la carte maitre devait obligatoirement être une carte Crossfire. La carte maitre était celle branchée sur l'écran. En tant que carte Crossfire, elle incorporait des circuits de composition d'image, afin de combiner les portions d'image calculée par elle-même avec celles calculées par la carte esclave.
Cependant, les circuits de composition d'image étaient un peu faibles. Par exemple, la Radeon XT 8500 ne supportait au maximum que des résolutions de 600×1200 - 60 Hz, or 1920×1440 - 52 Hz. Vu que le taux de rafraichissement était faible à de telles résolutions, surtout pour des écrans CRT qui ont tendance à faire mal aux yeux, le Crossfire était surtout utile pour les résolutions plus basses. Dommage pour du multi-GPU, censé aider pour les hautes résolutions.
Par la suite, les cartes Crossfire ont disparues et toutes les cartes graphiques ATI étaient compatibles Crossfire. La communication entre les GPU se faisait via le bus PCI Express. La technologie devint alors bien plus pratique, les cartes Crossfire étant rares et peu disponibles. Et ce malgré une petite perte en performance liée aux transferts via le PCI Express. Par la suite, l'introduction du CrossfireX ajouta le support d'un connecteur entre cartes graphiques, afin de passer outre le bus PCI Express. Cependant, cela ne dura que pour les générations des AMD HD 2000 à 7000. ATI/AMD a abandonné l'usage d'un connecteur CrossFire avec ses cartes utilisant le PCI-Express 3.0.
[[File:R700 interconnect.svg|centre|vignette|upright=2|Illustration du multi-GPU où deux cartes graphiques communiquent via un lien indépendant du bus PCI-Express. On voit que le débit du lien entre les deux cartes graphique est ajouté au débit du bus PCI-Express.]]
{{NavChapitre | book=Les cartes graphiques
| prev=L'antialiasing
| prevText=L'antialiasing
}}{{autocat}}
21806hv94114bzjt4kzkg5fjjbgwtzc
Photographie/Personnalités/P/Gunther Plüschow
0
74233
746292
631336
2025-07-07T20:07:28Z
Ziv
119944
([[c:GR|GR]]) [[c:COM:FR|File renamed]]: [[File:Botokuden-Girls - Kopie.jpg]] → [[File:Botokuden-Mädchen, 1928.jpg]] [[c:COM:FR#FR3|Criterion 3]] · corr, copy from what? removed this and the "Denglisch," added the year
746292
wikitext
text/x-wiki
{{Ph s Personnalités}}
[[Image:Plueschow-1.jpg|thumb|240px|Gunther Plüschow]]
== Biographie ==
'''Gunther Plüschow''' était un explorateur, écrivain, aviateur et photographe allemand, né le 8 février 1886 à Munich et décédé accidentellement le 28 janvier 1831 dans la Terre de Feu. Il a travaillé à Kiautschou, en Chine. Son corps a été inhumé à Lichterfelde, en Allemagne.
== Publications ==
== Galerie de photographies ==
<gallery widths="240px" heights="240px">
Botokuden-Mädchen, 1928.jpg|Botokuden-Girls
</gallery>
== Bibliographie ==
{{Ph Personnalités}}
{{DEFAULTSORT:Plüschow, Gunther}}
[[Catégorie:Personnalités de la photographie]]
e2ytbm1z3w7xuio6c57efvgtwooo0rp
Mathc matrices/26b
0
77632
746298
733402
2025-07-08T08:35:55Z
Xhungab
23827
746298
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
:
[[Mathc matrices/a259| '''Gass-Jordan''']]
{{Partie{{{type|}}}|L'équation d'un conique }}
:
Copier la bibliothèque dans votre répertoire de travail avec les fichiers des parties précédentes :
*[[Mathc matrices/h26b| '''d.h ..................... Déclaration des fichiers h''']]
Ne pas conserver le fichier d.h avec la bibliothèque après avoir testé les exemples.
'''Présentation :'''
<syntaxhighlight lang="C">
Trouver les coefficients a, b, c, d, e du conique,
ax**2 + by**2 + cx + dy + e = 0
qui passe par ces quatre points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3]) (x[4],y[4])
En utilisant les quatre points nous obtenons la matrice.
(a)x**2 (b)y**2 (c)x (d)y (e) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Ce système à quatre lignes et cinq inconnus (a, b, c, d, e).
C'est un système homogène, il a donc une infinité de solution.
Pour trouver une solution j'ai choisi de poser que a = 1.
Nous avons donc cinq lignes et cinq inconnus.
1 0 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Il suffit maintenant de résoudre le système.
</syntaxhighlight>
Deux exemples :
* [[Mathc matrices/c26b1|c01.c ]], [[Mathc matrices/06m| QR ]]
* [[Mathc matrices/c26b5|c02.c ]], [[Mathc matrices/06n| QR ]]
* [[Mathc matrices/c26b2|c03.c ]], [[Mathc matrices/06o| QR ]]
* [[Mathc matrices/c26b3|c04.c ]], [[Mathc matrices/06p| QR ]]
* [[Mathc matrices/c26b4|c05.c ]], [[Mathc matrices/06q| QR ]]
{{AutoCat}}
m5erdn79azcj6dba36ka9dekp4mo4oy
746299
746298
2025-07-08T08:36:43Z
Xhungab
23827
746299
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
:
[[Mathc matrices/a259| '''Gass-Jordan''']]
{{Partie{{{type|}}}|L'équation d'un conique }}
:
Copier la bibliothèque dans votre répertoire de travail avec les fichiers des parties précédentes :
*[[Mathc matrices/h26b| '''d.h ..................... Déclaration des fichiers h''']]
Ne pas conserver le fichier d.h avec la bibliothèque après avoir testé les exemples.
'''Présentation :'''
<syntaxhighlight lang="C">
Trouver les coefficients a, b, c, d, e du conique,
ax**2 + by**2 + cx + dy + e = 0
qui passe par ces quatre points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3]) (x[4],y[4])
En utilisant les quatre points nous obtenons la matrice.
(a)x**2 (b)y**2 (c)x (d)y (e) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Ce système à quatre lignes et cinq inconnus (a, b, c, d, e).
C'est un système homogène, il a donc une infinité de solution.
Pour trouver une solution j'ai choisi de poser que a = 1.
Nous avons donc cinq lignes et cinq inconnus.
1 0 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Il suffit maintenant de résoudre le système.
</syntaxhighlight>
Deux exemples :
* [[Mathc matrices/c26b1|c00a.c ]], [[Mathc matrices/06m| QR ]]
* [[Mathc matrices/c26b5|c00b.c ]], [[Mathc matrices/06n| QR ]]
* [[Mathc matrices/c26b2|c00c.c ]], [[Mathc matrices/06o| QR ]]
* [[Mathc matrices/c26b3|c00d.c ]], [[Mathc matrices/06p| QR ]]
* [[Mathc matrices/c26b4|c00e.c ]], [[Mathc matrices/06q| QR ]]
{{AutoCat}}
t2j6aqvtyy1ezeob2n3gnpudm0yahat
Mathc matrices/c26b1
0
77634
746300
736608
2025-07-08T08:38:30Z
Xhungab
23827
746300
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
'''Sommaire''' ◀ '''''Utilise la commande "Retour en Arrière" de ton navigateur.'''
Installer et compiler ces fichiers dans votre répertoire de travail.
*[[Mathc matrices/h26b| d.h ..................... L'équation d'un conique]]
{{Fichier|c00a.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00a.c */
/* ------------------------------------ */
#include "v_a.h"
#include "d.h"
/* --------------------------------- */
int main(void)
{
double xy[8] ={
1, 0,
2, 3,
3, 4,
4, 0 };
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **A = i_mR(R5,C5);
double **b = i_mR(R5,C1);
double **Ab = i_Abr_Ac_bc_mR(R5,C5,C1);
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y ");
i_A_b_with_XY_mR(XY,A,b);
c_A_b_Ab_mR(A,b,Ab);
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
printf(" The Gauss Jordan process will reduce this matrix to : \n");
gj_TP_mR(Ab);
p_mR(Ab,S7,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n");
p_eq_conica_mR(Ab);
stop();
clrscrn();
printf(" x y \n");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Verify the result : \n\n");
verify_XY_mR(Ab,XY[R1][C1],XY[R1][C2]);
verify_XY_mR(Ab,XY[R2][C1],XY[R2][C2]);
verify_XY_mR(Ab,XY[R3][C1],XY[R3][C2]);
verify_XY_mR(Ab,XY[R4][C1],XY[R4][C2]);
printf("\n\n\n");
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
------------------------------------
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 +0
+2 +3
+3 +4
+4 +0
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +0.00 +1.00 +0.00 +1.00 +0.00
+4.00 +9.00 +2.00 +3.00 +1.00 +0.00
+9.00 +16.00 +3.00 +4.00 +1.00 +0.00
+16.00 +0.00 +4.00 +0.00 +1.00 +0.00
Press return to continue.
The Gauss Jordan process will reduce this matrix to :
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 -0.17
-0.00 -0.00 +1.00 -0.00 -0.00 -5.00
+0.00 +0.00 +0.00 +1.00 +0.00 +1.17
+0.00 +0.00 +0.00 +0.00 +1.00 +4.00
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 -0.17y**2 -5.00x +1.17y +4.00 = 0
Press return to continue.
x y
+1 +0
+2 +3
+3 +4
+4 +0
Verify the result :
With x = +1.0 and y = +0.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +2.0 and y = +3.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +3.0 and y = +4.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +4.0 and y = +0.0 ax**2 + by**2 + cx+ dy + e = +0.00000
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
7qruputdiq5qekceqagagxo4uk7d3u0
Mathc matrices/c26b2
0
77635
746302
736609
2025-07-08T08:39:47Z
Xhungab
23827
746302
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00c.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00c.c */
/* ------------------------------------ */
#include "v_a.h"
#include "d.h"
/* --------------------------------- */
int main(void)
{
double xy[8] ={
1, -8,
2, 2,
3, 1,
4, 2 };
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **A = i_mR(R5,C5);
double **b = i_mR(R5,C1);
double **Ab = i_Abr_Ac_bc_mR(R5,C5,C1);
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y ");
i_A_b_with_XY_mR(XY,A,b);
c_A_b_Ab_mR(A,b,Ab);
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
printf(" The Gauss Jordan process will reduce this matrix to : \n");
gj_TP_mR(Ab);
p_mR(Ab,S7,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n");
p_eq_conica_mR(Ab);
stop();
clrscrn();
printf(" x y \n");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Verify the result : \n\n");
verify_XY_mR(Ab,XY[R1][C1],XY[R1][C2]);
verify_XY_mR(Ab,XY[R2][C1],XY[R2][C2]);
verify_XY_mR(Ab,XY[R3][C1],XY[R3][C2]);
verify_XY_mR(Ab,XY[R4][C1],XY[R4][C2]);
printf("\n\n\n");
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
------------------------------------
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 -8
+2 +2
+3 +1
+4 +2
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +64.00 +1.00 -8.00 +1.00 +0.00
+4.00 +4.00 +2.00 +2.00 +1.00 +0.00
+9.00 +1.00 +3.00 +1.00 +1.00 +0.00
+16.00 +4.00 +4.00 +2.00 +1.00 +0.00
Press return to continue.
The Gauss Jordan process will reduce this matrix to :
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 -0.14
+0.00 +0.00 +1.00 +0.00 +0.00 -6.00
+0.00 +0.00 +0.00 +1.00 +0.00 -0.57
+0.00 +0.00 +0.00 +0.00 +1.00 +9.71
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 -0.14y**2 -6.00x -0.57y +9.71 = 0
Press return to continue.
x y
+1 -8
+2 +2
+3 +1
+4 +2
Verify the result :
With x = +1.0 and y = -8.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +2.0 and y = +2.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +3.0 and y = +1.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +4.0 and y = +2.0 ax**2 + by**2 + cx+ dy + e = +0.00000
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
gwep9pcw0h930tojjpm4gymye6rozo7
Mathc matrices/c26b3
0
77636
746303
736610
2025-07-08T08:40:12Z
Xhungab
23827
746303
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00d.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00d.c */
/* ------------------------------------ */
#include "v_a.h"
#include "d.h"
/* --------------------------------- */
int main(void)
{
double xy[8] ={
1, 4,
2, 5,
3, -7,
4, 5 };
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **A = i_mR(R5,C5);
double **b = i_mR(R5,C1);
double **Ab = i_Abr_Ac_bc_mR(R5,C5,C1);
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y ");
i_A_b_with_XY_mR(XY,A,b);
c_A_b_Ab_mR(A,b,Ab);
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
printf(" The Gauss Jordan process will reduce this matrix to : \n");
gj_TP_mR(Ab);
p_mR(Ab,S7,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n");
p_eq_conica_mR(Ab);
stop();
clrscrn();
printf(" x y \n");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Verify the result : \n\n");
verify_XY_mR(Ab,XY[R1][C1],XY[R1][C2]);
verify_XY_mR(Ab,XY[R2][C1],XY[R2][C2]);
verify_XY_mR(Ab,XY[R3][C1],XY[R3][C2]);
verify_XY_mR(Ab,XY[R4][C1],XY[R4][C2]);
printf("\n\n\n");
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
------------------------------------
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 +4
+2 +5
+3 -7
+4 +5
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +16.00 +1.00 +4.00 +1.00 +0.00
+4.00 +25.00 +2.00 +5.00 +1.00 +0.00
+9.00 +49.00 +3.00 -7.00 +1.00 +0.00
+16.00 +25.00 +4.00 +5.00 +1.00 +0.00
Press return to continue.
The Gauss Jordan process will reduce this matrix to :
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +0.28
-0.00 -0.00 +1.00 -0.00 -0.00 -6.00
+0.00 +0.00 +0.00 +1.00 +0.00 +0.48
+0.00 -0.00 +0.00 +0.00 +1.00 -1.39
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +0.28y**2 -6.00x +0.48y -1.39 = 0
Press return to continue.
x y
+1 +4
+2 +5
+3 -7
+4 +5
Verify the result :
With x = +1.0 and y = +4.0 ax**2 + by**2 + cx+ dy + e = -0.00000
With x = +2.0 and y = +5.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +3.0 and y = -7.0 ax**2 + by**2 + cx+ dy + e = -0.00000
With x = +4.0 and y = +5.0 ax**2 + by**2 + cx+ dy + e = +0.00000
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
1c9a1bthgc8ddb5599xdlqymx5hau0f
Mathc matrices/c26b4
0
77637
746304
736611
2025-07-08T08:40:36Z
Xhungab
23827
746304
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00e.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00e.c */
/* ------------------------------------ */
#include "v_a.h"
#include "d.h"
/* --------------------------------- */
int main(void)
{
double xy[8] ={
1, 2,
2, -8,
3, -8,
4, -3 };
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **A = i_mR(R5,C5);
double **b = i_mR(R5,C1);
double **Ab = i_Abr_Ac_bc_mR(R5,C5,C1);
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y ");
i_A_b_with_XY_mR(XY,A,b);
c_A_b_Ab_mR(A,b,Ab);
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
printf(" The Gauss Jordan process will reduce this matrix to : \n");
gj_TP_mR(Ab);
p_mR(Ab,S7,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n");
p_eq_conica_mR(Ab);
stop();
clrscrn();
printf(" x y \n");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Verify the result : \n\n");
verify_XY_mR(Ab,XY[R1][C1],XY[R1][C2]);
verify_XY_mR(Ab,XY[R2][C1],XY[R2][C2]);
verify_XY_mR(Ab,XY[R3][C1],XY[R3][C2]);
verify_XY_mR(Ab,XY[R4][C1],XY[R4][C2]);
printf("\n\n\n");
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
------------------------------------
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 +2
+2 -8
+3 -8
+4 -3
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +4.00 +1.00 +2.00 +1.00 +0.00
+4.00 +64.00 +2.00 -8.00 +1.00 +0.00
+9.00 +64.00 +3.00 -8.00 +1.00 +0.00
+16.00 +9.00 +4.00 -3.00 +1.00 +0.00
Press return to continue.
The Gauss Jordan process will reduce this matrix to :
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +0.04
-0.00 -0.00 +1.00 -0.00 -0.00 -5.00
+0.00 +0.00 +0.00 +1.00 +0.00 +0.04
+0.00 +0.00 +0.00 +0.00 +1.00 +3.76
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +0.04y**2 -5.00x +0.04y +3.76 = 0
Press return to continue.
x y
+1 +2
+2 -8
+3 -8
+4 -3
Verify the result :
With x = +1.0 and y = +2.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +2.0 and y = -8.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +3.0 and y = -8.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +4.0 and y = -3.0 ax**2 + by**2 + cx+ dy + e = -0.00000
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
kzywjt4xz65gm2mtbjs4a8pgn4viz5s
Mathc matrices/c26b5
0
77638
746301
736612
2025-07-08T08:39:24Z
Xhungab
23827
746301
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00b.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00b.c */
/* ------------------------------------ */
#include "v_a.h"
#include "d.h"
/* --------------------------------- */
int main(void)
{
double xy[8] ={
1, 1,
2, 4,
3, 9,
4, 16 };
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **A = i_mR(R5,C5);
double **b = i_mR(R5,C1);
double **Ab = i_Abr_Ac_bc_mR(R5,C5,C1);
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y ");
i_A_b_with_XY_mR(XY,A,b);
c_A_b_Ab_mR(A,b,Ab);
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
printf(" The Gauss Jordan process will reduce this matrix to : \n");
gj_TP_mR(Ab);
p_mR(Ab,S7,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n");
p_eq_conica_mR(Ab);
stop();
clrscrn();
printf(" x y \n");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Verify the result : \n\n");
verify_XY_mR(Ab,XY[R1][C1],XY[R1][C2]);
verify_XY_mR(Ab,XY[R2][C1],XY[R2][C2]);
verify_XY_mR(Ab,XY[R3][C1],XY[R3][C2]);
verify_XY_mR(Ab,XY[R4][C1],XY[R4][C2]);
printf("\n\n\n");
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Le même problème résolue avec [[Mathc matrices/c23n| la méthode des Méthode des moindres carrés ]]
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
------------------------------------
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 +1
+2 +4
+3 +9
+4 +16
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +1.00 +1.00 +1.00 +1.00 +0.00
+4.00 +16.00 +2.00 +4.00 +1.00 +0.00
+9.00 +81.00 +3.00 +9.00 +1.00 +0.00
+16.00 +256.00 +4.00 +16.00 +1.00 +0.00
Press return to continue.
The Gauss Jordan process will reduce this matrix to :
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +0.00
+0.00 +0.00 +1.00 +0.00 +0.00 +0.00
-0.00 -0.00 +0.00 +1.00 +0.00 -1.00
+0.00 +0.00 +0.00 +0.00 +1.00 +0.00
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 -1.00y = 0
Press return to continue.
x y
+1 +1
+2 +4
+3 +9
+4 +16
Verify the result :
With x = +1.0 and y = +1.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +2.0 and y = +4.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +3.0 and y = +9.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +4.0 and y = +16.0 ax**2 + by**2 + cx+ dy + e = +0.00000
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
oe94asjg2jzzluw4a5xhosoajsf0b1e
746310
746301
2025-07-08T08:58:14Z
Xhungab
23827
746310
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00b.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00b.c */
/* ------------------------------------ */
#include "v_a.h"
#include "d.h"
/* --------------------------------- */
int main(void)
{
double xy[8] ={
1, 1,
2, 4,
3, 9,
4, 16 };
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **A = i_mR(R5,C5);
double **b = i_mR(R5,C1);
double **Ab = i_Abr_Ac_bc_mR(R5,C5,C1);
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y ");
i_A_b_with_XY_mR(XY,A,b);
c_A_b_Ab_mR(A,b,Ab);
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
printf(" The Gauss Jordan process will reduce this matrix to : \n");
gj_TP_mR(Ab);
p_mR(Ab,S7,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n");
p_eq_conica_mR(Ab);
stop();
clrscrn();
printf(" x y \n");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Verify the result : \n\n");
verify_XY_mR(Ab,XY[R1][C1],XY[R1][C2]);
verify_XY_mR(Ab,XY[R2][C1],XY[R2][C2]);
verify_XY_mR(Ab,XY[R3][C1],XY[R3][C2]);
verify_XY_mR(Ab,XY[R4][C1],XY[R4][C2]);
printf("\n\n\n");
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
------------------------------------
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 +1
+2 +4
+3 +9
+4 +16
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +1.00 +1.00 +1.00 +1.00 +0.00
+4.00 +16.00 +2.00 +4.00 +1.00 +0.00
+9.00 +81.00 +3.00 +9.00 +1.00 +0.00
+16.00 +256.00 +4.00 +16.00 +1.00 +0.00
Press return to continue.
The Gauss Jordan process will reduce this matrix to :
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +0.00
+0.00 +0.00 +1.00 +0.00 +0.00 +0.00
-0.00 -0.00 +0.00 +1.00 +0.00 -1.00
+0.00 +0.00 +0.00 +0.00 +1.00 +0.00
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 -1.00y = 0
Press return to continue.
x y
+1 +1
+2 +4
+3 +9
+4 +16
Verify the result :
With x = +1.0 and y = +1.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +2.0 and y = +4.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +3.0 and y = +9.0 ax**2 + by**2 + cx+ dy + e = +0.00000
With x = +4.0 and y = +16.0 ax**2 + by**2 + cx+ dy + e = +0.00000
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
6f0c6t6uppa46k03arjybuh3ue0d169
Mathc matrices/26c
0
77639
746311
733403
2025-07-08T09:58:25Z
Xhungab
23827
746311
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
:
[[Mathc matrices/a259| '''Gass-Jordan''']]
{{Partie{{{type|}}}|L'équation d'un cercle}}
:
Copier la bibliothèque dans votre répertoire de travail avec les fichiers des parties précédentes :
*[[Mathc matrices/h26c| '''d.h ..................... Déclaration des fichiers h''']]
Ne pas conserver le fichier d.h avec la bibliothèque après avoir testé les exemples.
'''Présentation :'''
<syntaxhighlight lang="C">
Calculons les coefficients a, b, c, d d'un cercle,
ax**2 + ay**2 + bx + cy + d = 0
Qui passe par ces trois points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3])
En utilisant ces trois points nous avons cette matrice.
(a)x**2 (a)y**2 (b)x (c)y (d) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Ce système a trois lignes et quatre inconnues.
Il est homogène, donc il a une infinité de solution.
Pour trouver une solution j'ai choisi que a = 1.
Nous obtenons cette matrice.
(a)x**2 (a)y**2
1 0 0 0 0 1
0 1 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Il suffit de resoudre le système.
</syntaxhighlight>
Deux exemples :
* [[Mathc matrices/c26c1|c00a.c ]], [[Mathc matrices/06r| QR ]]
* [[Mathc matrices/c26c2|c00b.c ]], [[Mathc matrices/06s| QR ]]
* [[Mathc matrices/c26c3|c00c.c ]], [[Mathc matrices/06t| QR ]]
* [[Mathc matrices/c26c4|c00d.c ]], [[Mathc matrices/06u| QR ]]
{{AutoCat}}
itxvcync8l8ktayevb67vfqz91xny39
Mathc matrices/c26c1
0
77641
746312
736613
2025-07-08T09:58:56Z
Xhungab
23827
746312
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
'''Sommaire''' ◀ '''''Utilise la commande "Retour en Arrière" de ton navigateur.'''
Installer et compiler ces fichiers dans votre répertoire de travail.
*[[Mathc matrices/h26c| d.h ..................... L'équation d'un cercle]]
{{Fichier|c00a.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00a.c */
/* ------------------------------------ */
#include "v_a.h"
#include "d.h"
/* ------------------------------------ */
int main(void)
{
double xy[6] ={
1, -2,
2, -3,
3, 6 };
double **XY = ca_A_mR(xy,i_mR(R3,C2));
double **A = i_mR(R5,C5);
double **b = i_mR(R5,C1);
double **Ab = i_Abr_Ac_bc_mR(R5,C5,C1);
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y ");
i_A_b_with_XY_mR(XY,A,b);
c_A_b_Ab_mR(A,b,Ab);
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
printf(" The Gauss Jordan process will reduce this matrix to : \n");
gj_TP_mR(Ab);
p_mR(Ab,S7,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n");
p_eq_circle_mR(Ab);
stop();
clrscrn();
printf(" x y \n");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Verify the result : \n\n");
verify_XY_mR(Ab,XY[R1][C1],XY[R1][C2]);
verify_XY_mR(Ab,XY[R2][C1],XY[R2][C2]);
verify_XY_mR(Ab,XY[R3][C1],XY[R3][C2]);
printf("\n\n\n");
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
------------------------------------
Find the coefficients a, b, c, d, of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three points.
x y
+1 -2
+2 -3
+3 +6
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +4.00 +1.00 -2.00 +1.00 +0.00
+4.00 +9.00 +2.00 -3.00 +1.00 +0.00
+9.00 +36.00 +3.00 +6.00 +1.00 +0.00
Press return to continue.
The Gauss Jordan process will reduce this matrix to :
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
-0.00 -0.00 +1.00 -0.00 -0.00 -10.40
+0.00 +0.00 +0.00 +1.00 +0.00 -2.40
+0.00 +0.00 +0.00 +0.00 +1.00 +0.60
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +1.00y**2 -10.40x -2.40y +0.60 = 0
Press return to continue.
x y
+1 -2
+2 -3
+3 +6
Verify the result :
With x = +1.0 and y = -2.0 ax**2 + ay**2 + cx+ dy + e = +0.00000
With x = +2.0 and y = -3.0 ax**2 + ay**2 + cx+ dy + e = +0.00000
With x = +3.0 and y = +6.0 ax**2 + ay**2 + cx+ dy + e = -0.00000
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
7w3hz8bu6el2ksj4ms6p7dgdnrmoszb
Mathc matrices/c26c2
0
77642
746313
736614
2025-07-08T09:59:45Z
Xhungab
23827
746313
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26c| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00b.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00b.c */
/* ------------------------------------ */
#include "v_a.h"
#include "d.h"
/* ------------------------------------ */
int main(void)
{
double xy[6] ={
1, -1,
2, -9,
3, -8 };
double **XY = ca_A_mR(xy,i_mR(R3,C2));
double **A = i_mR(R5,C5);
double **b = i_mR(R5,C1);
double **Ab = i_Abr_Ac_bc_mR(R5,C5,C1);
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y ");
i_A_b_with_XY_mR(XY,A,b);
c_A_b_Ab_mR(A,b,Ab);
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
printf(" The Gauss Jordan process will reduce this matrix to : \n");
gj_TP_mR(Ab);
p_mR(Ab,S7,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n");
p_eq_circle_mR(Ab);
stop();
clrscrn();
printf(" x y \n");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Verify the result : \n\n");
verify_XY_mR(Ab,XY[R1][C1],XY[R1][C2]);
verify_XY_mR(Ab,XY[R2][C1],XY[R2][C2]);
verify_XY_mR(Ab,XY[R3][C1],XY[R3][C2]);
printf("\n\n\n");
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
------------------------------------
Find the coefficients a, b, c, d, of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three points.
x y
+1 -1
+2 -9
+3 -8
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +1.00 +1.00 -1.00 +1.00 +0.00
+4.00 +81.00 +2.00 -9.00 +1.00 +0.00
+9.00 +64.00 +3.00 -8.00 +1.00 +0.00
Press return to continue.
The Gauss Jordan process will reduce this matrix to :
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+0.00 +0.00 +1.00 +0.00 +0.00 +1.44
+0.00 +0.00 -0.00 +1.00 +0.00 +10.56
+0.00 +0.00 +0.00 +0.00 +1.00 +7.11
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +1.00y**2 +1.44x +10.56y +7.11 = 0
Press return to continue.
x y
+1 -1
+2 -9
+3 -8
Verify the result :
With x = +1.0 and y = -1.0 ax**2 + ay**2 + cx+ dy + e = -0.00000
With x = +2.0 and y = -9.0 ax**2 + ay**2 + cx+ dy + e = -0.00000
With x = +3.0 and y = -8.0 ax**2 + ay**2 + cx+ dy + e = +0.00000
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
pn3wq9w9fqahszz05k3cp01ib74fgrj
Mathc matrices/c26c3
0
77643
746314
736615
2025-07-08T10:00:09Z
Xhungab
23827
746314
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26c| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00c.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00c.c */
/* ------------------------------------ */
#include "v_a.h"
#include "d.h"
/* ------------------------------------ */
int main(void)
{
double xy[6] ={
1, 10,
2, 1,
3, -10 };
double **XY = ca_A_mR(xy,i_mR(R3,C2));
double **A = i_mR(R5,C5);
double **b = i_mR(R5,C1);
double **Ab = i_Abr_Ac_bc_mR(R5,C5,C1);
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y ");
i_A_b_with_XY_mR(XY,A,b);
c_A_b_Ab_mR(A,b,Ab);
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
printf(" The Gauss Jordan process will reduce this matrix to : \n");
gj_TP_mR(Ab);
p_mR(Ab,S7,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n");
p_eq_circle_mR(Ab);
stop();
clrscrn();
printf(" x y \n");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Verify the result : \n\n");
verify_XY_mR(Ab,XY[R1][C1],XY[R1][C2]);
verify_XY_mR(Ab,XY[R2][C1],XY[R2][C2]);
verify_XY_mR(Ab,XY[R3][C1],XY[R3][C2]);
printf("\n\n\n");
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
------------------------------------
Find the coefficients a, b, c, d, of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three points.
x y
+1 +10
+2 +1
+3 -10
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +100.00 +1.00 +10.00 +1.00 +0.00
+4.00 +1.00 +2.00 +1.00 +1.00 +0.00
+9.00 +100.00 +3.00 -10.00 +1.00 +0.00
Press return to continue.
The Gauss Jordan process will reduce this matrix to :
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+0.00 +0.00 +1.00 +0.00 +0.00 +996.00
+0.00 +0.00 +0.00 +1.00 +0.00 +100.00
-0.00 -0.00 -0.00 -0.00 +1.00 -2097.00
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +1.00y**2 +996.00x +100.00y -2097.00 = 0
Press return to continue.
x y
+1 +10
+2 +1
+3 -10
Verify the result :
With x = +1.0 and y = +10.0 ax**2 + ay**2 + cx+ dy + e = -0.00000
With x = +2.0 and y = +1.0 ax**2 + ay**2 + cx+ dy + e = -0.00000
With x = +3.0 and y = -10.0 ax**2 + ay**2 + cx+ dy + e = -0.00000
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
ni501jmiv6qd8w46wbbjx4qb086xeuc
Mathc matrices/c26c4
0
77644
746315
736616
2025-07-08T10:00:38Z
Xhungab
23827
746315
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26c| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00d.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00d.c */
/* ------------------------------------ */
#include "v_a.h"
#include "d.h"
/* ------------------------------------ */
int main(void)
{
double xy[6] ={
10, 10,
-5, 1,
7, -10 };
double **XY = ca_A_mR(xy,i_mR(R3,C2));
double **A = i_mR(R5,C5);
double **b = i_mR(R5,C1);
double **Ab = i_Abr_Ac_bc_mR(R5,C5,C1);
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y ");
i_A_b_with_XY_mR(XY,A,b);
c_A_b_Ab_mR(A,b,Ab);
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
printf(" The Gauss Jordan process will reduce this matrix to : \n");
gj_TP_mR(Ab);
p_mR(Ab,S7,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n");
p_eq_circle_mR(Ab);
stop();
clrscrn();
printf(" x y \n");
p_mR(XY,S5,P0,C6);
printf("\n");
printf(" Verify the result : \n\n");
verify_XY_mR(Ab,XY[R1][C1],XY[R1][C2]);
verify_XY_mR(Ab,XY[R2][C1],XY[R2][C2]);
verify_XY_mR(Ab,XY[R3][C1],XY[R3][C2]);
printf("\n\n\n");
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
------------------------------------
Find the coefficients a, b, c, d, of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three points.
x y
+10 +10
-5 +1
+7 -10
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+100.00 +100.00 +10.00 +10.00 +1.00 +0.00
+25.00 +1.00 -5.00 +1.00 +1.00 +0.00
+49.00 +100.00 +7.00 -10.00 +1.00 +0.00
Press return to continue.
The Gauss Jordan process will reduce this matrix to :
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
-0.00 -0.00 +1.00 -0.00 +0.00 -11.07
+0.00 +0.00 +0.00 +1.00 +0.00 -0.89
-0.00 -0.00 -0.00 -0.00 +1.00 -80.44
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +1.00y**2 -11.07x -0.89y -80.44 = 0
Press return to continue.
x y
+10 +10
-5 +1
+7 -10
Verify the result :
With x = +10.0 and y = +10.0 ax**2 + ay**2 + cx+ dy + e = -0.00000
With x = -5.0 and y = +1.0 ax**2 + ay**2 + cx+ dy + e = +0.00000
With x = +7.0 and y = -10.0 ax**2 + ay**2 + cx+ dy + e = -0.00000
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
f4ksxq6p95h8gu3pcax6527giozd41v
Les moteurs de rendu des FPS en 2.5 D/Les généralités : le rendu 2D
0
81107
746216
746197
2025-07-07T12:57:45Z
Mewtow
31375
/* Le rendu de l'environnement : le rendu des murs */
746216
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées pour former des zones polygonales. Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes. L'essentiel est que les lignes sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique mémorisait quelles portions de l'image ont déjà été écrites, afin qu'elles ne soient pas modifiées ultérieurement. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée de l'objet correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée. L'occlusion est donc gérée convenablement.
L'avantage est que cet algorithme fait moins d'écriture. Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite marqués totalement par un objet plus proche. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Avec le rendu proche-vers-loin, chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait !
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème est que les rendus proche-vers-loin et du peintre demandent de trier les polygones d'une scène selon leur profondeur, du plus profond au moins profond. Et les cartes graphiques n'aiment pas ça, que ce soit les anciennes cartes graphiques comme les modernes. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé sur d'anciennes cartes graphiques, qui ne géraient pas la géométrie mais seulement les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Un problème est que trier les polygones demande d'utiliser un algorithme de tri, dont le nombre d'opérations est assez important. Le temps de calcul est proportionnel à <math>N \times log{N}</math>, d'après la théorie de la complexité algorithmique. Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul simplement proportionnel à N, linéaire. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrétement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
abv9o0v9y1gwfzuwp102x786u6of4b7
746219
746216
2025-07-07T13:00:48Z
Mewtow
31375
/* Le tri des polygones/lignes/objets selon la distance */
746219
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées pour former des zones polygonales. Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes. L'essentiel est que les lignes sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique mémorisait quelles portions de l'image ont déjà été écrites, afin qu'elles ne soient pas modifiées ultérieurement. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée de l'objet correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée. L'occlusion est donc gérée convenablement.
L'avantage est que cet algorithme fait moins d'écriture. Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite marqués totalement par un objet plus proche. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Avec le rendu proche-vers-loin, chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait !
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé à une époque où les cartes graphiques n'avaient pas de tampon de profondeur, elles ne géraient que les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Un problème est que trier les polygones demande d'utiliser un algorithme de tri, dont le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times log{N}</math>, d'après la théorie de la complexité algorithmique. Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrétement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
196k7hjwsqf3hx4tkemzekogjq86c5k
746223
746219
2025-07-07T13:29:27Z
Mewtow
31375
/* Le tri des polygones/lignes/objets selon la distance */
746223
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées pour former des zones polygonales. Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes. L'essentiel est que les lignes sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique mémorisait quelles portions de l'image ont déjà été écrites, afin qu'elles ne soient pas modifiées ultérieurement. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée de l'objet correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée. L'occlusion est donc gérée convenablement.
L'avantage est que cet algorithme fait moins d'écriture. Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite marqués totalement par un objet plus proche. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Avec le rendu proche-vers-loin, chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait !
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé à une époque où les cartes graphiques n'avaient pas de tampon de profondeur, elles ne géraient que les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Un problème est que trier les polygones demande d'utiliser un algorithme de tri, dont le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times log{N}</math>, d'après la théorie de la complexité algorithmique. Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrétement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
9fhwuu7rzufsz4nqsbtwbdfxvb2ntuz
746224
746223
2025-07-07T13:31:56Z
Mewtow
31375
/* L'usage de l'alpha-testing pour le rendu des sprites */
746224
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées pour former des zones polygonales. Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes. L'essentiel est que les lignes sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique mémorisait quelles portions de l'image ont déjà été écrites, afin qu'elles ne soient pas modifiées ultérieurement. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée de l'objet correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée. L'occlusion est donc gérée convenablement.
L'avantage est que cet algorithme fait moins d'écriture. Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite marqués totalement par un objet plus proche. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Avec le rendu proche-vers-loin, chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait !
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé à une époque où les cartes graphiques n'avaient pas de tampon de profondeur, elles ne géraient que les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Un problème est que trier les polygones demande d'utiliser un algorithme de tri, dont le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times log{N}</math>, d'après la théorie de la complexité algorithmique. Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrétement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
b1xslz91th1krmerwa8ercsheah1io5
746226
746224
2025-07-07T13:38:35Z
Mewtow
31375
/* La géométrie, les niveaux, les objets et la caméra */
746226
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'amélioration variait suivant le moteur de jeu utilisé, et donnait soit une technique dite de ''portal rendering'', soit un système de ''Binary Space Partionning'', assez complexes et difficiles à expliquer. Mais il ne s'agissait pas de jeux en 3D, les maps de ces jeux avaient des contraintes qui rendaient cette technique utilisable. Ils n'avaient pas de polygones qui se chevauchent, notamment.
L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique mémorisait quelles portions de l'image ont déjà été écrites, afin qu'elles ne soient pas modifiées ultérieurement. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée de l'objet correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée. L'occlusion est donc gérée convenablement.
L'avantage est que cet algorithme fait moins d'écriture. Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite marqués totalement par un objet plus proche. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Avec le rendu proche-vers-loin, chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait !
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé à une époque où les cartes graphiques n'avaient pas de tampon de profondeur, elles ne géraient que les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Un problème est que trier les polygones demande d'utiliser un algorithme de tri, dont le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times log{N}</math>, d'après la théorie de la complexité algorithmique. Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrétement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
4nlc0sh68w1uj7e4dxj4s7uujlgg0uq
746252
746226
2025-07-07T16:02:16Z
Mewtow
31375
/* L'algorithme du peintre inversé : le rendu proche-vers-lointain */
746252
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique mémorisait quelles portions de l'image ont déjà été écrites, afin qu'elles ne soient pas modifiées ultérieurement. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée de l'objet correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée. L'occlusion est donc gérée convenablement.
Avec le rendu proche-vers-loin, chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé à une époque où les cartes graphiques n'avaient pas de tampon de profondeur, elles ne géraient que les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Un problème est que trier les polygones demande d'utiliser un algorithme de tri, dont le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times log{N}</math>, d'après la théorie de la complexité algorithmique. Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrétement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
p8dp6n1j1ibaglohxvk5ro16dwbkts5
746253
746252
2025-07-07T16:07:50Z
Mewtow
31375
/* L'algorithme du peintre inversé : le rendu proche-vers-lointain */
746253
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
En théorie, un simple pixel suffit à cela, mais le tableau peut aussi mémoriser la transparence du pixel, à savoir si le pixel écrit est opaque, transparent, etc. Pour cela, un nombre proportionnel à la transparence du pixel est stocké : il vaut 0 pour un pixel totalement transparent, 255 pour un pixel totalement opaque, les valeurs intermédiaires disent à quel point le pixel est partiellement transparent.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé à une époque où les cartes graphiques n'avaient pas de tampon de profondeur, elles ne géraient que les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Un problème est que trier les polygones demande d'utiliser un algorithme de tri, dont le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times log{N}</math>, d'après la théorie de la complexité algorithmique. Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrétement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
id8vx6xj8mfl5u3t7l2oas7dhvs1bz0
746254
746253
2025-07-07T16:08:14Z
Mewtow
31375
/* L'algorithme du peintre inversé : le rendu proche-vers-lointain */
746254
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Il s'agit généralement d'une tâche qui est réalisée par le processeur, le CPU, qui est plus efficace que le GPU pour trier des trucs. Aussi, l'algorithme du peintre était utilisé à une époque où les cartes graphiques n'avaient pas de tampon de profondeur, elles ne géraient que les textures et quelques effets de post-processing. Avec ces GPU, les jeux vidéo calculaient la géométrie et la triait sur le CPU, puis effectuaient le reste de la rastérisation sur le GPU.
Un problème est que trier les polygones demande d'utiliser un algorithme de tri, dont le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times log{N}</math>, d'après la théorie de la complexité algorithmique. Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrétement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
5l2b2qfreag26gja6il0mzhdwlx4ni4
746255
746254
2025-07-07T16:52:38Z
Mewtow
31375
/* Le tri des polygones/lignes/objets selon la distance */
746255
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Trier les polygones demande d'utiliser un algorithme de tri, qui est exécuté par le processeur. Le CPU est en effet plus efficace que le GPU pour trier des trucs. Par contre, le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times log{N}</math>, d'après la théorie de la complexité algorithmique. Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrétement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
67r3o2mohblpcenyzurwk02lt1vwsjn
746256
746255
2025-07-07T16:52:52Z
Mewtow
31375
/* Le tri des polygones/lignes/objets selon la distance */
746256
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Trier les polygones demande d'utiliser un algorithme de tri, qui est exécuté par le processeur. Le CPU est en effet plus efficace que le GPU pour trier des trucs. Par contre, le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times \log{N}</math>, d'après la théorie de la complexité algorithmique. Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrétement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
tuylog0psn9nprae74x6khu9l59nylf
746257
746256
2025-07-07T16:53:09Z
Mewtow
31375
/* Le tri des polygones/lignes/objets selon la distance */
746257
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Trier les polygones demande d'utiliser un algorithme de tri, qui est exécuté par le processeur. Le CPU est en effet plus efficace que le GPU pour trier des trucs. Par contre, le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times \log{N}</math>, d'après la théorie de la complexité algorithmique.
Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrètement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des ''portals''. Il y avait un ''portal'' pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portals visibles apr le joueur. Pour chaque ''portal visible'', il rendait alors la pièce voisine visible depuis ce ''portal'' et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
lbgbt4qica5l46ph4faryw65ktzlygf
746258
746257
2025-07-07T16:53:54Z
Mewtow
31375
/* Le tri des polygones/lignes/objets selon la distance */
746258
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Trier les polygones demande d'utiliser un algorithme de tri, qui est exécuté par le processeur. Le CPU est en effet plus efficace que le GPU pour trier des trucs. Par contre, le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times \log{N}</math>, d'après la théorie de la complexité algorithmique.
Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrètement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des portails. Il y avait un portail pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portails visibles par le joueur. Pour chaque portail visible, il rendait alors la pièce voisine visible depuis ce portail et continuait récursivement le rendu dans les pièces voisines.
Le défaut des portal et des BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile, mais loin d'être facile hors-scripts.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
32odp3vzbv2n44jgx4vj8e0q1kjutp7
746268
746258
2025-07-07T17:30:32Z
Mewtow
31375
/* Le tri des polygones/lignes/objets selon la distance */
746268
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Trier les polygones demande d'utiliser un algorithme de tri, qui est exécuté par le processeur. Le CPU est en effet plus efficace que le GPU pour trier des trucs. Par contre, le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times \log{N}</math>, d'après la théorie de la complexité algorithmique.
Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Par contre, ces optimisations font que l'ordre de rendu n'est pas parfaitement conservé. Les objets sont globalement rendus du plus proche au plus lointain, mais l'ordre est cependant approximatif. De nombreux jeux de l'époque utilisaient néanmoins ces optimisations, car le gain en performance en valait la peine. Mais le défaut est que les approximations pouvaient causer quelques défauts d'affichage. Il arrivait que les objets deviennent visibles à travers des murs opaques ou invisibles à travers des surfaces transparentes. Mais ces artefacts graphiques étaient assez mineurs.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrètement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des portails. Il y avait un portail pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portails visibles par le joueur. Pour chaque portail visible, il rendait alors la pièce voisine visible depuis ce portail et continuait récursivement le rendu dans les pièces voisines.
Le défaut du BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
2kpgg8lasinfoec6u6enybnv99akpqp
746286
746268
2025-07-07T18:30:22Z
Mewtow
31375
/* L'algorithme du peintre : le rendu loin-vers-proche */
746286
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
Il existe quelques jeux qui ont utilisé ce système de rendu. Par exemple, la version SP1 de DOOM utilise l'algorithme du peintre, contrairement à la version PC qui fonctionne totalement différemment.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Le tri des polygones/lignes/objets selon la distance===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Trier les polygones demande d'utiliser un algorithme de tri, qui est exécuté par le processeur. Le CPU est en effet plus efficace que le GPU pour trier des trucs. Par contre, le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times \log{N}</math>, d'après la théorie de la complexité algorithmique.
Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace.
Par contre, ces optimisations font que l'ordre de rendu n'est pas parfaitement conservé. Les objets sont globalement rendus du plus proche au plus lointain, mais l'ordre est cependant approximatif. De nombreux jeux de l'époque utilisaient néanmoins ces optimisations, car le gain en performance en valait la peine. Mais le défaut est que les approximations pouvaient causer quelques défauts d'affichage. Il arrivait que les objets deviennent visibles à travers des murs opaques ou invisibles à travers des surfaces transparentes. Mais ces artefacts graphiques étaient assez mineurs.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrètement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des portails. Il y avait un portail pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portails visibles par le joueur. Pour chaque portail visible, il rendait alors la pièce voisine visible depuis ce portail et continuait récursivement le rendu dans les pièces voisines.
Le défaut du BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
0db2rneieu2zf1081l7p5x4osc31bxn
746287
746286
2025-07-07T18:41:58Z
Mewtow
31375
/* Le tri des polygones/lignes/objets selon la distance */
746287
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
Il existe quelques jeux qui ont utilisé ce système de rendu. Par exemple, la version SP1 de DOOM utilise l'algorithme du peintre, contrairement à la version PC qui fonctionne totalement différemment.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Les approximations rapides de l'algorithme du peintre==
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Trier les polygones demande d'utiliser un algorithme de tri, qui est exécuté par le processeur. Le CPU est en effet plus efficace que le GPU pour trier des trucs. Par contre, le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times \log{N}</math>, d'après la théorie de la complexité algorithmique.
Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace. Par contre, ces optimisations font que l'ordre de rendu n'est pas parfaitement conservé. Les objets sont globalement rendus du plus proche au plus lointain, mais l'ordre est cependant approximatif. Les optimisations profitent du fait que dans certains cas, l'ordre de rendu n'a pas d'impact sur le rendu final.
De nombreux jeux de l'époque utilisaient néanmoins ces optimisations, car le gain en performance en valait la peine. Mais le défaut est que les approximations pouvaient occasionnellement causer quelques défauts d'affichage. Il arrivait que les objets deviennent visibles à travers des murs opaques ou invisibles à travers des surfaces transparentes. Mais ces artefacts graphiques étaient assez mineurs. Et la plupart du temps, c'était le signe que le concepteur du niveau avait laissé une coquille dans le niveau.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrètement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des portails. Il y avait un portail pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portails visibles par le joueur. Pour chaque portail visible, il rendait alors la pièce voisine visible depuis ce portail et continuait récursivement le rendu dans les pièces voisines.
Le défaut du BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
kfuzkjiq0vxzj7qz8fqsjmvd6y8s5sl
746288
746287
2025-07-07T18:42:04Z
Mewtow
31375
/* =Les approximations rapides de l'algorithme du peintre */
746288
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
Il existe quelques jeux qui ont utilisé ce système de rendu. Par exemple, la version SP1 de DOOM utilise l'algorithme du peintre, contrairement à la version PC qui fonctionne totalement différemment.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Les approximations rapides de l'algorithme du peintre===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Trier les polygones demande d'utiliser un algorithme de tri, qui est exécuté par le processeur. Le CPU est en effet plus efficace que le GPU pour trier des trucs. Par contre, le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times \log{N}</math>, d'après la théorie de la complexité algorithmique.
Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace. Par contre, ces optimisations font que l'ordre de rendu n'est pas parfaitement conservé. Les objets sont globalement rendus du plus proche au plus lointain, mais l'ordre est cependant approximatif. Les optimisations profitent du fait que dans certains cas, l'ordre de rendu n'a pas d'impact sur le rendu final.
De nombreux jeux de l'époque utilisaient néanmoins ces optimisations, car le gain en performance en valait la peine. Mais le défaut est que les approximations pouvaient occasionnellement causer quelques défauts d'affichage. Il arrivait que les objets deviennent visibles à travers des murs opaques ou invisibles à travers des surfaces transparentes. Mais ces artefacts graphiques étaient assez mineurs. Et la plupart du temps, c'était le signe que le concepteur du niveau avait laissé une coquille dans le niveau.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrètement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets du plus lointain au plus proche. le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des portails. Il y avait un portail pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portails visibles par le joueur. Pour chaque portail visible, il rendait alors la pièce voisine visible depuis ce portail et continuait récursivement le rendu dans les pièces voisines.
Le défaut du BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
exbepu78zunj492pxdqrali1nxn4znm
746289
746288
2025-07-07T18:52:30Z
Mewtow
31375
/* Les approximations rapides de l'algorithme du peintre */
746289
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
DOOM 11 et 2 faisaient le rendu d'un mur colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
Il existe quelques jeux qui ont utilisé ce système de rendu. Par exemple, la version SP1 de DOOM utilise l'algorithme du peintre, contrairement à la version PC qui fonctionne totalement différemment.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Les approximations rapides de l'algorithme du peintre===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Trier les polygones demande d'utiliser un algorithme de tri, qui est exécuté par le processeur. Le CPU est en effet plus efficace que le GPU pour trier des trucs. Par contre, le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times \log{N}</math>, d'après la théorie de la complexité algorithmique.
Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace. Par contre, ces optimisations font que l'ordre de rendu n'est pas parfaitement conservé. Les objets sont globalement rendus du plus proche au plus lointain, mais l'ordre est cependant approximatif. Les optimisations profitent du fait que dans certains cas, l'ordre de rendu n'a pas d'impact sur le rendu final.
De nombreux jeux de l'époque utilisaient néanmoins ces optimisations, car le gain en performance en valait la peine. Mais le défaut est que les approximations pouvaient occasionnellement causer quelques défauts d'affichage. Il arrivait que les objets deviennent visibles à travers des murs opaques ou invisibles à travers des surfaces transparentes. Mais ces artefacts graphiques étaient assez mineurs. Et la plupart du temps, c'était le signe que le concepteur du niveau avait laissé une coquille dans le niveau.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrètement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets selon leur profondeur. Le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3. DOOM PSX utilisait un BSP pour accélérer un rendu loin vers proche, alors que DOOM PC faisait l'inverse.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des portails. Il y avait un portail pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portails visibles par le joueur. Pour chaque portail visible, il rendait alors la pièce voisine visible depuis ce portail et continuait récursivement le rendu dans les pièces voisines.
Le défaut du BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
q44zlzzev1q8v20szt7pt6cgzq4uasw
746290
746289
2025-07-07T18:54:07Z
Mewtow
31375
/* Le rendu de l'environnement : le rendu des murs */
746290
wikitext
text/x-wiki
[[File:Anarch short gameplay.gif|vignette|Anarch short gameplay]]
Les tout premiers First Person Shooters, comme DOOM ou Wolfenstein 3D, avaient un rendu relativement simpliste. Et contrairement à ce que voient nos yeux, le rendu n'était pas de la vraie 3D, mais une méthode de rendu hybride entre 2D et 3D. Quelques rares moteurs utilisaient un moteur en 3D, avec rendu d'objets en polygones, comme le moteur d'Ultima Underworld, mais ils sont l'exception qui confirme la règle. Pour comprendre comment ces moteurs de rendu fonctionnaient, il faut faire une petite introduction sur les méthodes de rendu 3D en général.
==Le rendu en 2.5D : les bases communes à tous les moteurs==
Calculer un monde en 3D ou en 2.5D, demande de faire beaucoup de choses différentes : calculer la géométrie des niveaux, appliquer les textures sur les modèles 3D, calculer les lumières, les ombres, etc. La distinction entre géométrie, textures et éclairage est assez intuitive, surtout que les moteurs de jeu savent relativement bien gérer ces trois aspects. Voyons ce qu'il en est pour les jeux en 2.5D.
===La géométrie, les niveaux, les objets et la caméra===
Une map dans un FPS en 2.5D est un environnement en deux dimensions, à savoir qu'elle est décrite sur un plan 2D, dans lequel le moteur d’un jeu vidéo place des objets et les fait bouger. Cette scène est, en première approche, un simple rectangle. Un des coins de ce rectangle sert d’origine à un système de coordonnées : il est à la position (0, 0), et les axes partent de ce point en suivant les arêtes. Dans cette scène 2D, on place des murs infranchissables, des objets, des ennemis, etc. Les objets sont placés à des coordonnées bien précises dans ce parallélogramme. Ils ont une coordonnée bien précise, ce sont des points sur la map 2D.
Le fait que la scène soit en 2D fait que la carte n'a qu'un seul niveau : pas d'escalier, d'ascenseur, ni de différence de hauteur. Du moins, sans améliorations notables du moteur graphique. Il est en fait possible de tricher et de simuler des étages, mais nous en parlerons plus tard.
La géométrie du niveau est généralement très simple, à savoir qu'elle est composées de lignes, qui sont connectées entre elles et ne laissent pas de trous. Elles délimitent un espace fermé, le niveau, duquel on ne peut pas sortir. Pour donner un exemple, voici un exemple de map dans le jeu libre FreeDOOM :
[[File:Freedoom005 001.png|centre|vignette|upright=2|Exemple d'une map de Freedoom.]]
Tous les jeux, sauf ceux utilisant le moteur de Wolfenstein 3D, découpaient la map en zone polygones appelées '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un bâtiment : chaque pièce aura une forme rectangulaire. Une pièce rectangulaire correspond à un polygone, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, dont les ouvertures sont des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur est définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===La caméra et l'écran===
Outre les objets proprement dit, on trouve une '''caméra''', qui représente les yeux du joueur. Cette caméra est définie au minimum par la position du joueur et la direction de son regard. La position du joueur est un simple point, avec donc deux coordonnées, mais la direction du regarde est un vecteur.
La caméra est complétée par le champ de vision du joueur (un angle). Il y a aussi un plan qui représente l'écran du joueur. En effet, vous êtes à une certaine distance de votre écran, et cela doit se refléter dans le jeu. Ce que vous voyez sur votre écran est une image, qui a la même résolution que votre écran. Et cette image est prise à une certaine distance de la caméra, qui correspond approximativement à la distance entre vous et l'écran, mais transposée dans le niveau du jeu. Si vous êtes à 50 cm de votre écran en moyenne, le plan correspondant à l'écran est lui aussi à l'équivalent de 50 cm dans le niveau.
[[File:Raycasting 2D.png|centre|vignette|upright=2|Caméra et écran en 2D.]]
Il faut noter que vu que le jeu est en 2D, la caméra est censée rester à l'horizontale. De nombreux FPS 2.5D, comme Wolfenstein 3D ou DOOM, ne permettent pas la visée libre, aussi appelée ''free look'', à laquelle vous êtes habitués. Avec la visée libre, on peut regarder à gauche, à droite, mais aussi en haut et en bas. Mais vu que les jeux 2.5D ne sont pas de la vraie 3D, ils ne permettaient pas de regarder en haut et en bas. Les joueurs de DOOM savent que ces jeux utilisaient une mal-nommée "visée automatique" qui permettait de tirer sur des ennemis en hauteur ou en contrebas. Le regard restait à la verticale, mais les tirs touchaient les ennemis en haut ou en bas. Vous tiriez sur un mur, mais l'ennemi situé à la vertical du tir était touché.
Initialement, c'est comme cela que ça marchait, l'absence de visée libre était systématique. Mais quelques petites modifications des moteurs de rendu ont permis d'ajouter un système de visée libre aux moteurs de jeu en 2.5D. Des jeux comme Heretic ou Hexen en étaient capables. Cependant, cette visée libre n'est pas parfaite : la perspective est faussée. Le tout est illustré avec l'animation ci-dessous. A gauche, ce qu'on devrait obtenir avec un rendu en 3D, à droite le résultat avec un moteur de rendu en 2.5D.
[[File:Camera Rotation vs Shearing.gif|centre|vignette|upright=2|Camera Rotation vs Shearing]]
Concrètement, l'astuce utilisée pour gérer la visée libre était rudimentaire. Le moteur rendait le jeu avec une résolution verticale plus importante que la normale, mais n'affichait que ce qui était dans une fenêtre de même résolution que l'écran. La fenêtre pouvait se déplace verticalement dans le ''framebuffer'', afin de simuler un regard qui se déplace vers le haut ou le bas. Vous remarquez qu'à droite, le jeu se contente de déplacer l'image affichée sur la verticale, en la faisant monter ou descendre.
Un défaut ce cette méthode est qu'une ligne verticale reste verticale en regardant en haut ou en bas, au lieu de pencher comme à gauche. On devine assez facilement que le rendu en 2.5D se fait colonne par colonne, à savoir qu'on déplace verticalement les textures/murs/objets, sans gérer la moindre perspective en 3D. Et cela nous donne des indices sur la manière dont est réalisé un rendu en 2.5D. Le caractère colonne par colonne est primordial pour un rendu en 2.5D. Toujours est-il que cela permet de faire la différence entre un moteur en vraie 3D et en 2.5D :
: '''La perspective est gérée en 2D avec un moteur 2.5D, ce qui pose des problèmes avec la perspective verticale'''
===Les ''sprites'' et l'environnement===
Un point important des FPS 2.5D est qu'ils font une distinction entre l'environnement et les objets/ennemis. L''''environnement''' correspond au niveau lui-même. A l'écran, il correspond aux murs, au ciel, au sol, au plafond, etc. Il s'agit souvent d'éléments statiques, encore que certains jeux de l'époque avaient des environnement dynamiques. Duke Nukem 3D avait des éléments dynamiques, les maps pouvaient changer en temps réels, encore que tout était scripté. Mais pour simplifier, l'environnement sert de décor statique dans lequel on place des objets dynamiques.
Par contre, un niveau contient des éléments dynamiques qui ne peuvent pas être gérés par des scripts. Les ennemis, les objets, items, power-up, medikits et autres. Prenez un médikit pour vous soigner, il va disparaitre, et doit disparaitre en temps réel. Idem avec par exemple les ennemis : ils se déplacent en temps réel, on peut les tuer. Pour gérer ces éléments dynamiques, le jeu utilise des '''''sprites''''', de simples images placées au-dessus de l'environnement. Le meilleur moyen pour s'en rendre compte étant de tourner autour d'un objet : la forme de l'objet ne change pas du tout. Les objets sont de simples pancartes sur lesquelles on plaque une image.
[[File:Pac Man.svg|vignette|upright=0.5|Pac Man]]
Les ''sprites'' sont partiellement transparents, pour ne pas écraser l'environnement qui est derrière. Par exemple, prenez le ''sprite'' de Pacman ci-contre. Le Pacman jaune est colorié, mais tout ce qu'il y a autour est transparent. Vous ne le voyez pas car la transparence afficher la page web derrière, mais passer en dark mode ou en light mode et vous devriez voir que ce qu'il y autour du Pacman change de couleur avec l'arrière-plan.
Les ennemis sont généralement animés : ils bougent, ils sont "stun" quand on tire dessus, ils peuvent tirer des projectiles, ils ont une animation de mort, etc. Et il y a une animation pour chaque action. Pareil pour certains objets de l'environnement : pensez aux fameux barils explosifs qui explosent quand on tire dessus ! Ces animations sont réalisées en enchainant une succession de ''sprites'', qui est toujours identique. Il suffit de dérouler la bonne succession de ''sprite'' et le tour est joué !
[[File:Cube screenshot 199627.jpg|vignette|Cube screenshot 199627]]
L'arme du joueur est aussi un ''sprite'', qui est rendu comme tous les autres ''sprites''. Mais il y a une différence avec les autres ''sprites'' : il est toujours à l'avant-plan, devant tout le reste de l'image. Et il en est de même pour le HUD, qui est souvent rendu avec des ''sprites'' 2D placés à l'avant-plan.
Un HUD de FPS normal ressemble à ce qu'il y a dans la capture d'écran à droite : quelques chiffres et icônes superposées sur le reste du rendu. Les icônes pour la vie, l'armure et les munitions ; ne sont ni plus moins que des ''sprites''. Ce n'est pas évident, mais c'est pareil pour les chiffres. Le HUD est mis à jour à chaque fois que le joueur tire (compteur de munitions), perd de la vie, prend de l'armure, etc. Il est idéalement rendu à la toute fin du rendu, son sprite étant superposé à tout le reste.
[[File:Advanced raycasting demo 2.gif|vignette|Advanced raycasting demo 2]]
Mais dans Wolfenstein 3D et quelques jeux anciens, on a ce qui est illustré dans l'animation de droite. Le HUD est un gros rectangle qui prend tout le bas de l'écran. On peut bouger autant qu'on veut, le bas de l'écran associé au HUD reste le même. Le HUD n'a pas de transparence, les textures et l'environnement se se voient pas à travers. Avec cette contrainte, on peut dessiner le HUD et le reste de l'image séparément. Le HUD est donc rendu à part du reste. Cela signifie aussi que l'image calculée est en réalité plus petite. Si le HUD prend 10% de l'écran, alors on a juste à dessiner les 90% restants. Sans cette contrainte, on doit calculer 100% de l'image, pour ensuite superposer un HUD partiellement transparent.
==Le ''framebuffer'' et son remplissage==
Le rendu calcule une image, qui est affichée à l'écran. Le rendu doit se faire de manière à avoir 30 images par secondes, voire plus, toujours est-il qu'un jeu affiche une succession ultra-rapide d'images qui donne une impression de mouvement. L'image à afficher est enregistrée soit dans la mémoire RAM de l'ordinateur, soit dans la mémoire vidéo. Toujours est-il qu'elle est mémorisée dans une portion de la mémoire dédiée, qui s'appelle le '''''framebuffer'''''.
Vous pouvez voir le ''framebuffer'' comme une sorte de tableau sur lequel le moteur du jeu dessine. Le tableau a la même taille que l'écran. Le tableau est en réalité un tableau de pixel, qui a la même résolution que l'écran. Par exemple, pour un écran de résolution 1920 par 1080, le ''framebuffer'' est un tableau rectangulaire de 1920 pixels de large et de 1080 pixels de haut. Le moteur du jeu dessine dans le ''frambuffer'' soit pixel par pixel, soit par blocs de pixels. Tours est-il qu'il colorie les pixels, il remplit chaque pixel du ''frambuffer'' avec une couleur.
Pour simplifier, le ''framebuffer'' est initialement vide, rempli avec une couleur d'arrière-plan. Le rendu s'effectue alors comme suit :
* Le moteur graphique calcule une image sans les ''sprites'', avec seulement l'environnement, et l'enregistre dans le ''framebuffer''.
* Puis, il dessine les ''sprites'' sur l'image de l'environnement, sauf là où les ''sprites'' sont transparents.
* L'arme et le HUD étant à l'avant-plan, il sont rendus dans une phase à part du reste, souvent après ou avant tout le reste.
Le rendu d'un FPS en 2.5D est donc réalisé en deux-trois phases de dessin : le rendu de l'environnement, le rendu des ''sprites''. Il y a parfois des étapes en plus. Par exemple, DOOM calcule l'environnement en deux phases : une pour dessiner les murs, une autre pour dessiner le sol et le plafond/ciel.
===Le rendu des ''sprites'' : la mise à l'échelle===
Cependant, il ne suffit pas de superposer des ''sprites'' sur l'environnement pour que cela fonctionne. Il faut aussi les mettre à l'échelle, en fonction de leur distance. Rappelons que plus un objet/ennemi est loin, plus il nous parait petit à l'écran. Et cela vaut pour les ''sprites'', mais aussi pour les murs de l'environnement.
Les ''sprites'' doivent donc être mis à l'échelle suivant la distance : rapetissés pour les ennemis lointains, et zoomés pour les ennemis proches. Pour cela, on utilise une relation mathématique très simple : la loi de Thalès.
Dans un jeu vidéo, et comme dans le monde réel, si on multiplie la distance d'un objet par deux, trois ou quatre, celui-ci devient respectivement deux, trois ou quatre fois plus petit. Dit autrement, un objet de hauteur H situé à une distance D aura une hauteur perçue identique à celle d'un objet de hauteur double/triple/quadruple situé deux/trois/quatre fois plus loin. En clair, pour un objet de hauteur <math>h_1</math>, situé à une distance <math>d_1</math>, et un autre objet de hauteur <math>h_2</math> et de distance <math>d_2</math>, les deux auront la même hauteur perçue :
: <math>\frac{h_1}{d_1} = \frac{h_2}{d_2}</math>
Tout ''sprite'' est une image, avec une résolution. Elle a un certain nombre de pixels à l'horizontale, un autre à la verticale. La taille verticale en pixel du ''sprite'' dépend du ''sprite'', mettons qu'elle est égale à 50 pixels de large pour 60 de haut. Il s'agit de la taille réelle du sprite déterminée lors de la conception du jeu (aussi bien en vertical et horizontal). Nous allons noter sa taille en vertical comme suit : <math>T_s</math>.
Cette taille correspond à une distance précise. Pour un ''sprite'' 50 pixels de large pour 60 de haut, le ''sprite'' aura la même taille à l'écran à une certaine distance, que nous allons noter <math>D_s</math>.
Maintenant, d'après la relation vue plus haut, on peut calculer la taille affichée à l'écran du ''sprite'', notée H. Pour cela, il suffit de connaitre la distance D, et on a la relation :
: <math>\frac{T}{D} = \frac{T_s}{D_s}</math>
On peut la reformuler comme suit :
: <math>T = D \times \frac{T_s}{D_s}</math>
Quelques multiplications, et le tour est joué. Le terme <math>\frac{T_s}{D_s}</math> peut même être mémorisé à l'avance pour chaque ''sprite'', ce qui économise quelques calculs.
===Le rendu de l'environnement : le rendu des murs===
Le rendu de l'environnement est assez simple. Il consiste à dessiner les murs dans le ''framebuffer'', puis le ciel/plafond et le sol. Les deux sont souvent séparés, notamment dans le moteur de DOOM qui dessine les murs avant de dessiner les ''visplanes'' (ciel, plafond, sol). Par contre, Wolfenstein 3D fait le rendu colonne par colonne, sans distinguer les deux. Cependant, le rendu de l'environnement est gouverné par le rendu des murs. Pour simplifier, on dessine d'abord le mur, le plafond et le sol correspondent à ce qui reste une fois le mur rendu. Le ciel/plafond correspond à ce qui est au-dessus du mur, le sol est en-dessous.
[[File:Ciel et sol en raycasting 2D.jpg|centre|vignette|upright=2|Ciel et sol en raycasting 2D]]
Le rendu des murs est assez simple. Partons du principe que les murs ont une certaine hauteur, qui est encodée pour chaque mur. Aussi, le sol et le plafond sont plats. Le sol et le plafond peuvent avoir leur propre hauteur, mais laissons-cela de côté pour le moment. Cela garantit que le plafond est situé au sommet de l'écran, le sol est en bas de l'écran, et les murs sont au milieu. À partir de ces contraintes et de la carte en 2D, le moteur graphique peut afficher des graphismes de ce genre :
[[File:Exemple de rendu en raycasting.jpg|centre|vignette|upright=2|Exemple de rendu en raycasting]]
Les murs aussi subissent un processus de mise à l'échelle, avec quelques subtilités. Partons du principe que le regard du joueur est à une hauteur fixe au-dessus du sol, généralement au milieu de l'écran. La taille du mur est mise à l'échelle en fonction de la distance, et on place le mur au milieu de l'écran. En pratique, c'est ce qui était dans Wolfenstein 3D, mais DOOM autorisait d'avoir des murs plus hauts que d'autres. Le mur n'était alors pas centré sur l'écran, mais dépassait plus vers le haut que vers le bas. Les maths pour ça sont assez simples, mais nous allons les mettre de côté.
[[File:Hauteur d'un mur en fonction de la distance en raycasting 2D.png|centre|vignette|upright=2|Hauteur d'un mur en fonction de la distance en raycasting 2D]]
Pour comprendre comment se fait la mise à l'échelle, nous allons voir deux cas : celui où l'écran est devant nous, perpendiculaire au regard. Nous verrons ensuite comment passer au cas général, où le mur est de biais. La raison est que le premier cas est plus simple et introduit les concepts importants pour comprendre le cas général.
Voyons d'abord le cas où vous avez un mur plat devant vous, à votre perpendiculaire. Le mur est rectangulaire, avec une certaine largeur et une certaine hauteur. Il occupe un certain rectangle à l'écran, vu qu'il est vu à la perpendiculaire, il n'y a pas d'angle. La hauteur du mur perçue sur l'écran dépend de sa distance, par effet de perspective : plus le mur est loin, plus le rectangle visible à l'écran sera petit. Il faut donc le mettre à l'échelle en fonction de sa distance. La formule pour calculer la taille d'un mur à l'écran est la même que pour les ''sprites'' : on utilise le théorème de Thalès pour calculer la taille du mur à l'écran.
[[File:Détermination de la hauteur percue d'un mur en raycasting 2D.png|centre|vignette|upright=2|Détermination de la hauteur perçue d'un mur en raycasting 2D]]
Mais il s'agit là d'un cas idéal. Dans le cas général, le mur est vu avec un certain angle. Et cet angle modifie implique un effet de perspective. Les portions du mur plus proche de nous seront perçues comme plus grandes, le mur donnera l'impression de rétrécir avec la distance. Le mur rectangulaire est alors un trapèze sur l'écran.
[[File:Perspective mur en rendu 2.5D.png|centre|vignette|upright=2|Perspective mur en rendu 2.5D]]
Pour gérer cela, il y a plusieurs méthodes. Wolfenstein 3D faisait le rendu colonne par colonne sur l'écran, et il calculait la hauteur perçue avec Thalès, pour chaque colonne de l'écran. Mais ce n'était possible que parce qu'il utilisait la technique du ''raycasting''. Les autres jeux faisaient le rendu mur par mur. Pour chaque mur, ils calculaient la position des quatre sommets du trapèze qu'occupe le mur à l'écran, avec un algorithme de rastérisation. Puis la texture du mur était dessinée dans ce trapèze, après mise à l'échelle. Pour cela, prenaient la largeur du mur, sa position (à partir du milieu du mur), calculaient l'angle que fait le mur avec l'écran.
Les FPS 2.5D dessinaient les murs colonne par colonne. Le rendu était donc mur par mur, colonne par colonne. Le fait de rendre un mur colonne par colonne facilite la mise à l'échelle, qui se fait en modifiant la hauteur, donc la verticale du mur. D'ailleurs, afin de faciliter la mise à l'échelle, les textures sont mémorisées colonnes par colonnes en mémoire. Et même sur le disque dur, les fichiers WAD mémorisent les textures colonnes par colonne. L'habitude pour l'époque était de mémoriser les textures et images ligne par ligne, afin de faciliter l'affichage sur un écran CRT, qui affichait chaque image ligne par ligne, mais DOOM faisait l'inverse.
==La détermination des surfaces visibles==
Enfin, il faut parler d'un point très important, qui est absolument central pour le rendu 2D comme 3D : la '''visibilité''' des objets à l'écran. Un moteur de jeu doit déterminer ce qui est visible à l'écran : quels murs sont visibles, quels sont les objets visibles à l'écran, quels ennemis sont visibles, etc. Et cela regroupe trois choses différents :
* Un objet/mur en-dehors du champ de vision doit être éliminé du rendu : c'est ce qu'on appelle le ''frustrum cliping''.
* Un objet/mur a une face visible et une face cachée qui fait dos à la caméra : la face cachée n'est pas rendue grâce à des techniques de ''back-face culling''.
* Si un objet/mur est masqué par un autre, totalement ou partiellement, il ne faut pas rendre ce qui est masqué. : on parle d'''occlusion culling''.
Et ce qui est intéressant, c'est que la détermination de la visibilité est un problème central, qui détermine comment fonctionne le moteur d'un jeu. Un moteur de jeu est souvent construit autour d'un algorithme qui détermine la visibilité des objets, le reste se greffant autour. Après tout, avant de se demander comment afficher quelque chose, le moteur doit d'abord savoir quoi afficher ! Les autres problèmes sont en quelque sorte secondaire, la manière dont un moteur de jeu fonctionne dans les grandes lignes est gouvernée par ce problème de visibilité.
A ce propos, il est intéressant de regarder ce qu'en dit Michael Abrash, un des programmeurs ayant codé les moteurs de Quake et d'autres jeux Id Software aux côtés de John Carmack, dont la postérité n'a pas retenu son nom. Voici une citation tirée de son livre "Graphics Programming Black Book Special Edition", où il parle de son expérience sur le moteur de Quake:
: ''...but for the here and now I want to talk about what is, in my opinion, the toughest 3-D problem of all: visible surface determination (drawing the proper surface at each pixel), and its close relative, culling (discarding non-visible polygons as quickly as possible, a way of accelerating visible surface determination). In the interests of brevity, I’ll use the abbreviation VSD to mean both visible surface determination and culling from now on.''
: ''Why do I think VSD is the toughest 3-D challenge? Although rasterization issues such as texture mapping are fascinating and important, they are tasks of relatively finite scope, and are being moved into hardware as 3-D accelerators appear; also, they only scale with increases in screen resolution, which are relatively modest.''
: ''In contrast, VSD is an open-ended problem, and there are dozens of approaches currently in use. Even more significantly, the performance of VSD, done in an unsophisticated fashion, scales directly with scene complexity, which tends to increase as a square or cube function, so this very rapidly becomes the limiting factor in rendering realistic worlds. I expect VSD to be the increasingly dominant issue in realtime PC 3-D over the next few years, as 3-D worlds become increasingly detailed. Already, a good-sized Quake level contains on the order of 10,000 polygons, about three times as many polygons as a comparable DOOM level.''
Voyons maintenant dans le détail comment la visibilité des objets/environnements est déterminée.
===Les méthodes de détermination des surfaces visibles en 3D===
Les solutions à ce problème de visibilité sont assez nombreuses. Heureusement, elles peuvent se classer en quelque grand types assez larges. Les techniques les plus utilisées sont le tampon de profondeur (''z-buffer''), le tri de polygones par profondeur (''Depth sorting''), le lancer de rayon (''raytracing''). Elles résolvent le problème de la visibilité d'une manière fort différente.
Les jeux 3D modernes utilisent un '''tampon de profondeur''', aussi appelé ''z-buffer'', car c'est une technique supportée par les cartes graphiques modernes. Elles peuvent donc l'utiliser avec de bonnes performances, vu que le GPU fait la grosse partie du travail. Mais les premières cartes graphiques ne géraient pas de tampon de profondeur, ce n'est que vers la fin des années 90 que la technique a été intégrée aux GPU. Et une implémentation logicielle du tampon de profondeur aurait été beaucoup trop lente.
A vrai dire, les GPU de l'époque ne géraient pas grand chose. Les cartes accélératrices 3D n'existaient pas encore et les GPU ne faisaient que de la 2D. Et cette 2D était souvent très rudimentaire. Les cartes graphiques des PC de l'époque étaient optimisées pour rendre du texte, avec un support minimal du rendu d'images par pixel. En conséquence, les jeux vidéos de l'époque devaient faire tous les calculs graphiques sur le CPU, on parlait alors de rendu logiciel, de rendu ''software''. Et les contraintes faisaient qu'ils devaient utiliser des algorithmes particuliers pour résoudre la visibilité.
Le moteur de Wolfenstein 3D a été le seul à utiliser la technique du '''lancer de rayons''', bien qu'adaptée à des maps 2D. Et encore, il ne l'utilisait que d'une manière bien particulière, avec des optimisations qui rendaient son calcul bien plus simple : les niveaux étaient alignés sur une grille 2D, le moteur découpait le niveaux en blocs de taille fixe, et j'en passe. Mais la technique était très gourmande en puissance de calcul. Mais le vrai problème est que les optimisations appliquées bridaient les concepteurs de niveaux. Impossible de faire autre chose qu'un labyrinthe aux murs à 90°...
Tous les autres jeux vidéos, que ce soit DOOM ou le Build engine, ou tous les autres moteurs de l'époque, utilisaient des méthodes dites de '''rendu ordonné''', qui consistent à rendre les objets à rendre du plus proche au plus lointain ou inversement. La méthode la plus simple pour cela est celle de l’algorithme du peintre. Et les premiers FPS utilisaient une version fortement améliorée de cet algorithme.
===L'algorithme du peintre : le rendu loin-vers-proche===
Pour rappel, la base d'un rendu en 2D ou 2.5D est de superposer des images 2D pré-calculées les unes au-dessus des autres, pour obtenir l'image finale. Par exemple, on peut avoir une image pour l’arrière-plan (le décor), une image pour le monstre qui vous fonce dessus, une image pour le dessin de votre personnage, une image pour chaque mur, etc. L'image final est rendue dans une portion de mémoire RAM appelée le ''framebuffer'', superposer une image dessus revient à la copier dans cette zone de mémoire. La copie peut être partielle ou totale, tout dépend des besoins.
Pour éliminer les surfaces cachées, la solution la plus simple consiste simplement à rendre les objets à rendre du plus lointain au plus proche. L'idée est que si deux objets se recouvrent totalement ou partiellement, on doit dessiner celui qui est derrière, puis celui qui est devant. Le dessin du second va recouvrir le premier. Il s'agit de l''''algorithme du peintre'''.
[[File:Painter's algorithm.svg|centre|vignette|upright=2.0|Exemple de rendu 2D utilisant l'algorithme du peintre.]]
Un problème est que la solution ne marche pas avec certaines configurations particulières, dans le cas où des polygones un peu complexes se chevauchent plusieurs fois. Il se présente rarement dans un rendu 3D normal, mais c'est quand même un cas qu'il faut gérer. Le problème est suffisant pour que cette solution ne soit plus utilisée dans le rendu 3D normal.
{|
|[[File:Polygons cross.svg|vignette|Polygons cross]]
|[[File:Painters problem.svg|vignette|Painters problem]]
|}
Avec l'algorithme du peintre, il arrive qu'on dessine des objets qui sont ensuite écrasés par un objet plus proche qui redessine par dessus. Ou encore, on dessine un objet en entier, mais une partie de celui-ci est ensuite masquée par un objet plus proche. On dessine donc inutilement. Et ces dessins correspondent à écrire des pixels dans le ''framebuffer'', donc à de la puissance de calcul, des transferts mémoire inutiles. Des pixels sont écrits pour ensuite être écrasés. C'est le problème de l''''''overdraw''''', que nous traduiront en français par le volontairement ridicule terme de '''sur-dessinage'''. Mais ce défaut peut être mitigé avec une variante de l'algorithme du peintre, appelée l'algorithme du peintre inversé, qu'on va voir dans ce qui suit.
Il existe quelques jeux qui ont utilisé ce système de rendu. Par exemple, la version SP1 de DOOM utilise l'algorithme du peintre, contrairement à la version PC qui fonctionne totalement différemment.
===L'algorithme du peintre inversé : le rendu proche-vers-lointain===
Les anciens jeux en 2.5D comme DOOM ou les DOOM-like, utilisaient une variante de l'algorithme du peintre. L'idée était de rendre l'image dans le sens inverse de l'algorithme du peintre, à savoir du plus proche au plus lointain. En conséquence, nous allons la désigner sous le terme d''''algorithme du peintre inversé'''. La technique fonctionnait car elle ne modifiait pas les portions de l'image déjà dessinée. Les techniques pour cela sont assez nombreuses, mais elles garantissaient que chaque pixel est écrit une seule et unique fois, le sur-dessinnage disparait ! L'avantage est que cet algorithme fait moins d'écriture.
Une des ces techniques mémorisait quelles pixels de l'image ont déjà été écrits, afin qu'ils ne soient pas modifiés ultérieurement. A chaque fois que le moteur de jeu veut modifier la couleur d'un pixel, il vérifie si celui-ci est déjà occupé ou s'il est vide. Le dessin du pixel ne se fait que s'il est vide. Ainsi, si on veut rendre un objet lointain partiellement caché par un objet proche, la portion non-cachée correspondra à une portion vierge de l'image, mais la portion cachée correspondra à une portion déjà écrite. La portion non-cachée écrira ses pixels dans le ''framebuffer'', pas la portion cachée.
L'occlusion est donc gérée convenablement. Mais cela demande de mémoriser, pour chaque pixel, s'il a déjà été remplit ou s'il est vide. Pour cela, le moteur du jeu utilise un '''tableau d'occlusion''', à savoir un tableau qui a les mêmes dimensions que l'écran, les mêmes dimensions que le ''framebuffer''. Il mémorise, pour chaque pixel, s'il a déjà été remplis ou s'il est vide.
Cependant, l'algorithme du peintre inversé échoue si les objets rendus sont transparents. Dès que de la transparence est impliquée, l'algorithme du peintre inversé ne marche plus. DOOM gérait la situation assez simplement en mélangeant algorithme du peintre normal et inversé. les murs étaient rendus avec l'algorithme du peintre inversé, alors que les ''sprites'' et les murs transparents étaient rendus avec l'algorithme du peintre normal.
===Les approximations rapides de l'algorithme du peintre===
Un problème de l'algorithme du peintre est qu'il demande de trier les murs/''sprites'' d'une scène selon leur profondeur, du plus profond au moins profond. Trier les polygones demande d'utiliser un algorithme de tri, qui est exécuté par le processeur. Le CPU est en effet plus efficace que le GPU pour trier des trucs. Par contre, le nombre d'opérations est assez important. Pour trier N entités, le temps de calcul est proportionnel à <math>N \times \log{N}</math>, d'après la théorie de la complexité algorithmique.
Heureusement, quelques optimisations permettent de réduire ce nombre d'opération d'une manière assez drastique et de passer à un temps de calcul dit linéaire, simplement proportionnel à N. De plus, ces optimisations permettent de faire ce tri très facilement, sans avoir à tout retrier quand le joueur tourne la caméra ou se déplace. Par contre, ces optimisations font que l'ordre de rendu n'est pas parfaitement conservé. Les objets sont globalement rendus du plus proche au plus lointain, mais l'ordre est cependant approximatif. Les optimisations profitent du fait que dans certains cas, l'ordre de rendu n'a pas d'impact sur le rendu final.
De nombreux jeux de l'époque utilisaient néanmoins ces optimisations, car le gain en performance en valait la peine. Mais le défaut est que les approximations pouvaient occasionnellement causer quelques défauts d'affichage. Il arrivait que les objets deviennent visibles à travers des murs opaques ou invisibles à travers des surfaces transparentes. Mais ces artefacts graphiques étaient assez mineurs. Et la plupart du temps, c'était le signe que le concepteur du niveau avait laissé une coquille dans le niveau.
Une optimisation de ce type est l'usage du '''''Binary Space Partionning'''''. Concrètement, elle est utilisée pour précalculer des informations spatiales, qui permettent de trier les objets selon leur profondeur. Le BSP est formellement un arbre binaire dont le parcours permet de trier naturellement les objets/murs soit du plus proche au plus loin, soit, au contraire du plus loin au plus proche. Tout dépend de comment on le parcours, il y a deux méthodes différentes, une par sens de transfert. Elle a été utilisée dans le moteur de DOOM 1 et 2, qui étaient formellement en 2.5D, mais aussi dans des jeux 3D d'Id Software comme Quake 1, Quake 2, et même DOOM 3. DOOM PSX utilisait un BSP pour accélérer un rendu loin vers proche, alors que DOOM PC faisait l'inverse.
Les autres jeux en 2.5D de l'époque de DOOM faisaient autrement. Ils utilisaient la technique du '''''portal rendering'''''. Elle aussi précalculait des informations spatiales qui permettaient de trier naturellement les objets du plus lointain au plus proche. Pour simplifier, la map était coupées en pièces, connectées les unes aux autres par des portails. Il y avait un portail pour chaque porte, fenêtre, ouverture dans une pièce. Le moteur commençait le rendu dans la pièce actuellement occupée et rendait les objets. Puis, il déterminait les ''portal''visibles depuis la caméra, les portails visibles par le joueur. Pour chaque portail visible, il rendait alors la pièce voisine visible depuis ce portail et continuait récursivement le rendu dans les pièces voisines.
Le défaut du BSP est qu'ils marchent bien quand la géométrie du niveau est statique, qu'elle n'est pas modifiée. Par contre, si le niveau doit subir des modifications dynamiques, c'est plus compliqué. Avec un BSP, les niveaux dynamiques sont compliqués à concevoir, car modifier un BSP dynamiquement n'est pas chose facile. A la rigueur, si les modifications peuvent être scriptées, les choses sont plus faciles. Avec des portals, modifier un niveau est plus simple, plus facile.
Dans un FPS, il y a une classe d'objets qui ne peuvent pas être rendus avec la technique du BSP ou du portal rendering : les ennemis. Ils bougent d'une manière qui n'est pas prévue par un script, mais par un système d'IA, aussi rudimentaire soit-il. Impossible de précalculer le mouvement des ennemis ou autres. Autant les murs peuvent être rendus avec un BSP ou des portals, autant il faut trouver une autre solution pour les ennemis. La solution retenue est de rendre les ennemis à part du reste. DOOm faisait ainsi : il rendait les murs, puis les ennemis et autres objets basiques, en utilsant des ''sprites''.
Le rendu des ''sprites'' se fait une fois que l'environnement a été dessiné, c'est à dire après les murs, le sol et les plafonds. Les ''sprites'' des ennemis et items sont donc superposé sur l'arrière-plan calculée par l'étape précédente. Cependant, certains ''sprites'' peuvent se recouvrir : il faut impérativement que le ''sprite'' le plus proche soit affiché au-dessus de l'autre. Pour cela, les ''sprites'' sont superposés avec l'algorithme du peintre, à savoir du plus lointain au plus proche, même si l'environnement est rendu dans l'autre sens. Faire demande évidemment de trier les ''sprites'' à rendre en fonction de la distance des objets/ennemis.
===L'usage de l'alpha-testing pour le rendu des ''sprites''===
Néanmoins, précisons que ce n'est pas systématique, surtout sur le matériel moderne. Par exemple, DOOM a été porté sur de nombreuses machines, au point où le même "''can it run DOOM ?''" est né. Et chaque port utilise les possibilités du hardware pour simplifier le moteur. DOOM et Dukem 3D ont par exemple leurs ports sources qui fonctionnent avec au choix un rendu logiciel, ou un rendu sous Open Gl, voir parfois un rendu sous Vulkan pour les port source les plus avancés. Ils peuvent alors profiter des fonctionnalités du matériel moderne pour simplifier le rendu.
La plupart des ports altèrent fortement le rendu des ''sprites''. Les ports source modernes ne se préoccupent pas de rendre les ''sprites'' dans l'ordre, du plus lointain au plus proche. En effet, les cartes graphiques gérent la transparence nativement. Elles peuvent superposer plusieurs ''sprites'' ou textures partiellement transparents, dans le ''framebuffer'', grâce à une technique appelée l'''alpha blending''. En conséquence, les ''sprites'' sont rendus dans le désordre, sans que cela ne pose le moindre problème.
Un exemple est le cas du portage de DOOM sur iPhone, qui rend les textures dans un ordre tout sauf intuitif. Les textures du jeu sont numérotées, chacune ayant un identifiant de texture ou ''Texture ID''. Le jeu rend les textures par ''Texture ID'' croissant, sans se préoccuper de leur profondeur. Au passage, le processus est illustré dans les animations à la fin de cet article, qui décrit en détail le fonctionnement de ce portage : [https://fabiensanglard.net/doomIphone/index.php].
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=L'historique des moteurs de FPS
| prevText=L'historique des moteurs de FPS
| next=Le moteur de Wolfenstein 3D
| nextText=Le moteur de Wolfenstein 3D
}}{{autocat}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
dgzlx4s69ry7y6acgaq427b5h2f96db
Les moteurs de rendu des FPS en 2.5 D/Le portal rendering
0
81489
746220
723782
2025-07-07T13:14:22Z
Mewtow
31375
746220
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
s80m2yhcoq7xps2nqh7x20hon65maef
746228
746220
2025-07-07T13:59:42Z
Mewtow
31375
746228
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des mirs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité.
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
dilfnd19wu1ymr2k49twtdzxtsypd9d
746232
746228
2025-07-07T14:08:10Z
Mewtow
31375
/* Les secteurs et les portails */
746232
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des mirs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
===Les secteurs doivent être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, mais celles-ci sont découpées en plusieurs sous-secteurs convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc. Les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
dtr1ry424fh1cjk5r7auk5j2smtiyjs
746233
746232
2025-07-07T14:08:26Z
Mewtow
31375
/* Les secteurs et les portails */
746233
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des mirs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
===Les secteurs doivent être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, mais celles-ci sont découpées en plusieurs sous-secteurs convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc. Les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
bid4nderoz2zh6kh9pgrstbyhg4u3df
746234
746233
2025-07-07T14:08:42Z
Mewtow
31375
/* Les secteurs doivent être convexes */
746234
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des mirs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
===Les secteurs doivent être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, mais celles-ci sont découpées en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc. Les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
m6d4m2oa0vxoni2uk9dcozyl78xxkd3
746236
746234
2025-07-07T14:13:13Z
Mewtow
31375
/* Les portails */
746236
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des mirs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
===Les secteurs doivent être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, mais celles-ci sont découpées en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc. Les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
1n42usc8bmx5rwc36u59iwz9789wway
746238
746236
2025-07-07T14:15:47Z
Mewtow
31375
/* Les secteurs doivent être convexes */
746238
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des mirs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
pcmvybm1yusuvhdywxr77kiebmtf35o
746240
746238
2025-07-07T14:28:21Z
Mewtow
31375
/* Les secteurs doivent idéalement être convexes */
746240
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des mirs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
===Le rendu par portail est une implémentation de l'algorithme du peintre===
Le rendu par portail permet de naturellement faire le rendu en allant du plus proche au plus lointain, ou inversement. L'idée est la suivante : le moteur du jeu parcourt les secteurs du plus proche au plus lointain. Il part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite.
Le moteur n'est cependant pas stupide et ne parcours pas tous les secteurs de la map. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte.
==Le ''Build Engine''==
Le ""Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
obyk1rn9on7bx13reuc8632xatz2z4q
746241
746240
2025-07-07T14:37:09Z
Mewtow
31375
/* Les secteurs et les portails */
746241
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
===Le rendu par portail est une implémentation de l'algorithme du peintre===
Le rendu par portail permet de naturellement faire le rendu en allant du plus proche au plus lointain, ou inversement. L'idée est la suivante : le moteur du jeu parcourt les secteurs du plus proche au plus lointain. Il part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite.
Le moteur n'est cependant pas stupide et ne parcours pas tous les secteurs de la map. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte.
==Le ''Build Engine''==
Le ""Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
jk6fyh1ts9tgz1gz6j2m08gvj6pil91
746242
746241
2025-07-07T14:42:08Z
Mewtow
31375
/* Les portails */
746242
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
===Le rendu par portail est une implémentation de l'algorithme du peintre===
Le rendu par portail permet de naturellement faire le rendu en allant du plus proche au plus lointain, ou inversement. L'idée est la suivante : le moteur du jeu parcourt les secteurs du plus proche au plus lointain. Il part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite.
Le moteur n'est cependant pas stupide et ne parcours pas tous les secteurs de la map. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte.
==Le ''Build Engine''==
Le ""Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
5gxwx50i6kn5ewnnytcuitmaqwxakqz
746243
746242
2025-07-07T14:42:19Z
Mewtow
31375
/* Les portails */
746243
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
===Le rendu par portail est une implémentation de l'algorithme du peintre===
Le rendu par portail permet de naturellement faire le rendu en allant du plus proche au plus lointain, ou inversement. L'idée est la suivante : le moteur du jeu parcourt les secteurs du plus proche au plus lointain. Il part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite.
Le moteur n'est cependant pas stupide et ne parcours pas tous les secteurs de la map. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte.
==Le ''Build Engine''==
Le ""Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
jk6fyh1ts9tgz1gz6j2m08gvj6pil91
746244
746243
2025-07-07T14:42:30Z
Mewtow
31375
/* Le rendu par portail est une implémentation de l'algorithme du peintre */
746244
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
===Le rendu par portail est une implémentation de l'algorithme du peintre===
Le rendu par portail permet de naturellement faire le rendu en allant du plus proche au plus lointain, ou inversement. L'idée est la suivante : le moteur du jeu parcourt les secteurs du plus proche au plus lointain. Il part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite.
Le moteur n'est cependant pas stupide et ne parcours pas tous les secteurs de la map. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte.
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
==Le ''Build Engine''==
Le ""Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
pfps8k1csdg8xxekeep1lh3pqypi8gz
746245
746244
2025-07-07T14:42:36Z
Mewtow
31375
/* Le Build Engine */
746245
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
===Le rendu par portail est une implémentation de l'algorithme du peintre===
Le rendu par portail permet de naturellement faire le rendu en allant du plus proche au plus lointain, ou inversement. L'idée est la suivante : le moteur du jeu parcourt les secteurs du plus proche au plus lointain. Il part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite.
Le moteur n'est cependant pas stupide et ne parcours pas tous les secteurs de la map. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte.
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
==Le ''Build Engine''==
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
ltmf2vlvrc2offnfw4mhszq14nl608n
746246
746245
2025-07-07T14:54:23Z
Mewtow
31375
/* Le rendu par portail est une implémentation de l'algorithme du peintre */
746246
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
===Le rendu par portail est une implémentation de l'algorithme du peintre===
Le rendu par portail permet de naturellement faire le rendu en allant du plus proche au plus lointain, ou inversement. L'idée est la suivante : le moteur du jeu parcourt les secteurs du plus proche au plus lointain. Il part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite.
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
==Le ''Build Engine''==
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
htr6v9d03ccpp7d6s9n4h3upelvlbts
746247
746246
2025-07-07T14:58:41Z
Mewtow
31375
/* Le rendu par portail est une implémentation de l'algorithme du peintre */
746247
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une implémentation de l'algorithme du peintre==
Le rendu par portail permet de naturellement faire le rendu en allant du plus proche au plus lointain, ou inversement. L'idée est la suivante : le moteur du jeu parcourt les secteurs du plus proche au plus lointain. Il part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite.
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
==Le ''Build Engine''==
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
hi7wo0a70u0l5rxd3s6tcpaayw734j1
746248
746247
2025-07-07T15:47:42Z
Mewtow
31375
/* Le rendu par portail est une implémentation de l'algorithme du peintre */
746248
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre, que ce soit sa forme normale ou inversée. Et il peut le faire de deux manières différentes. En effet, il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur.
Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
==Le ''Build Engine''==
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
6t7rj34trwk8sptqypihwo20mf4zh4c
746249
746248
2025-07-07T15:48:03Z
Mewtow
31375
/* Le rendu par portail est une approximation de l'algorithme du peintre */
746249
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre, que ce soit sa forme normale ou inversée. Et il peut le faire de deux manières différentes. En effet, il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur.
Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Les optimisations liées à la visibilité des portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
==Le ''Build Engine''==
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
hz228dh9vhazruzh8yr48jpz7xnfbbg
746250
746249
2025-07-07T15:49:36Z
Mewtow
31375
/* Le Build Engine */
746250
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre, que ce soit sa forme normale ou inversée. Et il peut le faire de deux manières différentes. En effet, il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur.
Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Les optimisations liées à la visibilité des portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
hpmp5t97bkzgsb4mel1l3o7betr6exc
746251
746250
2025-07-07T15:59:29Z
Mewtow
31375
/* Les optimisations liées à la visibilité des portails */
746251
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre, que ce soit sa forme normale ou inversée. Et il peut le faire de deux manières différentes. En effet, il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur.
Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Les optimisations liées à la visibilité des portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
Une optimisation intéressante, appliquée par le Build Engine, était que les portails n'étaient pas pris en compte si une des deux conditions n'était pas réunie : ils sont en-dehors du cône de vision du joueur, ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
bwbeqchl2x6hz27qw2mplg1i5mptaia
746259
746251
2025-07-07T17:06:29Z
Mewtow
31375
/* Les optimisations liées à la visibilité des portails */
746259
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre, que ce soit sa forme normale ou inversée. Et il peut le faire de deux manières différentes. En effet, il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur.
Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail.
L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
Une optimisation intéressante, appliquée par le Build Engine, était que les portails n'étaient pas pris en compte si une des deux conditions n'était pas réunie : ils sont en-dehors du cône de vision du joueur, ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
4bmc5ihhwgs0i50jgpl1qf4ex6c6dat
746260
746259
2025-07-07T17:06:43Z
Mewtow
31375
/* La visibilité à travers les portails */
746260
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre, que ce soit sa forme normale ou inversée. Et il peut le faire de deux manières différentes. En effet, il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur.
Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
Une optimisation intéressante, appliquée par le Build Engine, était que les portails n'étaient pas pris en compte si une des deux conditions n'était pas réunie : ils sont en-dehors du cône de vision du joueur, ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
6f9re3jn6edsxsu78vrcxtsemmil1v3
746261
746260
2025-07-07T17:08:06Z
Mewtow
31375
/* La visibilité à travers les portails */
746261
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre, que ce soit sa forme normale ou inversée. Et il peut le faire de deux manières différentes. En effet, il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur.
Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
La map peut aussi contenir des informations de visibilité entre portail. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
dcxnep94yt2am9rz9942flr6rldx9my
746262
746261
2025-07-07T17:08:12Z
Mewtow
31375
/* La visibilité à travers les portails */
746262
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre, que ce soit sa forme normale ou inversée. Et il peut le faire de deux manières différentes. En effet, il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur.
Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
La map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Un joueur situé à l'extérieur du secteur peut regarder à travers un portail, il ne verra pas l'autre. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Pour chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
2v91uic9vaxfwtdzzj1imwt30o0bpk6
746263
746262
2025-07-07T17:09:33Z
Mewtow
31375
/* La visibilité à travers les portails */
746263
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre, que ce soit sa forme normale ou inversée. Et il peut le faire de deux manières différentes. En effet, il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur.
Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
0xgn2v3jm8cet7ne116kbb2g8isrtzl
746264
746263
2025-07-07T17:18:41Z
Mewtow
31375
/* Le rendu par portail est une approximation de l'algorithme du peintre */
746264
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
jazij2ou3kljewnav0kmxvqnb1kbxds
746265
746264
2025-07-07T17:23:20Z
Mewtow
31375
/* Le rendu par portail est une approximation de l'algorithme du peintre */
746265
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaines vers la plus proche.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
o3etj3dgjbs5lamtm0uv8dw6lof6knd
746266
746265
2025-07-07T17:25:53Z
Mewtow
31375
/* En savoir plus */
746266
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaines vers la plus proche.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
hsc583f8qptzhb2awz1kvv6mkbvcogd
746267
746266
2025-07-07T17:27:42Z
Mewtow
31375
/* Le rendu par portail est une approximation de l'algorithme du peintre */
746267
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
nmzi82m7xfmgr1b4f8o9nuqvup9x9js
746269
746267
2025-07-07T17:34:04Z
Mewtow
31375
/* En savoir plus */
746269
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
cdiw5o8kdi2y5d05g0y04chr25a92zt
746270
746269
2025-07-07T17:42:15Z
Mewtow
31375
/* Le support des portails à géométrie non-euclidienne */
746270
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Les effets graphiques liés aux portails==
===Voir, à travers un portail de téléporation===
===Le support des portails à géométrie non-euclidienne==
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
h99tokp6ov0x1airyts7esckjqa9ka7
746271
746270
2025-07-07T17:42:29Z
Mewtow
31375
/* =Le support des portails à géométrie non-euclidienne */
746271
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Les effets graphiques liés aux portails==
===Voir, à travers un portail de téléporation===
===Le support des portails à géométrie non-euclidienne===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
t6tm5lu4knsm25hvz4ecf6v71peh1ma
746272
746271
2025-07-07T17:42:50Z
Mewtow
31375
/* Les effets graphiques liés aux portails */
746272
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et les portails==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les portails===
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
kd7mc721t3pal7nwk38w3d9yn7kc5bt
746273
746272
2025-07-07T17:48:12Z
Mewtow
31375
/* Les secteurs et les portails */
746273
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, rien de plus simple. Prenez par exemple le rendu du secteur où se trouve le joueur. Le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
ttrk3raarb7xu1sobshm0hfykwt2fzi
746274
746273
2025-07-07T17:50:48Z
Mewtow
31375
/* Les secteurs et leur rendu */
746274
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre. En clair, peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Et cela doit marcher même si le joueur est placé en-dehors du secteur et regarde celui-ci de loin, à travers un portail, voire une succession de portails alignés. pour cela, il faut que le portail soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
dst06n4pud9lbcv8wf0oe88kovsm9nv
746275
746274
2025-07-07T17:51:55Z
Mewtow
31375
/* Les secteurs doivent idéalement être convexes */
746275
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
mwd8gyxp3elyny12cmu54lf39nei8er
746276
746275
2025-07-07T17:52:03Z
Mewtow
31375
/* Les sous-secteurs reliés par des portails */
746276
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec un secteur concave, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs, et la liste de ses portail. Il peut dessiner les murs dans le désordre, avant de passer au rendu des portails.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
79wkumhli95v1qbikdmr8vdnfmxhdux
746277
746276
2025-07-07T17:52:54Z
Mewtow
31375
/* Les secteurs doivent idéalement être convexes */
746277
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec des secteurs garantis convexes, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs et des ouvertures vers les autres pièces. Il peut dessiner les murs dans le désordre, avant de passer au rendu des ouvertures.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures et pour les portes est très importante pour ce qui est du gameplay, mais elle ne nous intéressera pas ici. Pour le moment, nous allons nous intéresser aux lignes opaques et aux lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
1018v648b3ransxcybpvir0ctkfh3vt
746278
746277
2025-07-07T17:53:38Z
Mewtow
31375
/* Les portails */
746278
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec des secteurs garantis convexes, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs et des ouvertures vers les autres pièces. Il peut dessiner les murs dans le désordre, avant de passer au rendu des ouvertures.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures pour les fenêtres/portes est très importante pour ce qui est du gameplay, mais aussi pour le rendu à portail. Elle colle assez bien à la distinction entre lignes opaques et lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont donc des lignes qui relient deux pièces entre elles ou une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
irhmc8omrksm9trkih31m91hm3rxdst
746279
746278
2025-07-07T17:54:09Z
Mewtow
31375
/* Les portails */
746279
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec des secteurs garantis convexes, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs et des ouvertures vers les autres pièces. Il peut dessiner les murs dans le désordre, avant de passer au rendu des ouvertures.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures pour les fenêtres/portes est très importante pour ce qui est du gameplay, mais aussi pour le rendu à portail. Elle colle assez bien à la distinction entre lignes opaques et lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont des lignes qui correspondent à des ouvertures, relient soit deux pièces entre elles, soit une pièce avec l'extérieur.
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
gqbsc9e2tjanjkds0m7xay9vzpr0yqb
746280
746279
2025-07-07T17:54:30Z
Mewtow
31375
/* Les portails */
746280
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec des secteurs garantis convexes, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs et des ouvertures vers les autres pièces. Il peut dessiner les murs dans le désordre, avant de passer au rendu des ouvertures.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures pour les fenêtres/portes est très importante pour ce qui est du gameplay, mais aussi pour le rendu à portail. Elle colle assez bien à la distinction entre lignes opaques et lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont des lignes qui correspondent à des ouvertures, relient soit deux pièces entre elles, soit une pièce avec l'extérieur.
===Le rendu par portail se fait secteur par secteur===
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
80wxw6bu3wufjy5y54dmf6suah4hzde
746281
746280
2025-07-07T18:03:01Z
Mewtow
31375
/* Le Build Engine et l'usage d'une pile de parcours */
746281
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec des secteurs garantis convexes, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs et des ouvertures vers les autres pièces. Il peut dessiner les murs dans le désordre, avant de passer au rendu des ouvertures.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures pour les fenêtres/portes est très importante pour ce qui est du gameplay, mais aussi pour le rendu à portail. Elle colle assez bien à la distinction entre lignes opaques et lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont des lignes qui correspondent à des ouvertures, relient soit deux pièces entre elles, soit une pièce avec l'extérieur.
===Le rendu par portail se fait secteur par secteur===
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
===Le ''Build Engine'' et l'usage d'une pile de parcours===
Le ''Build Engine'' utilise une version assez étrange du rendu par portail. Son rendu se fait en plusieurs phases. La toute première détermine quels sont les murs potentiellement visibles. Elle se fait en parcourant tous les secteurs, en traversant les portails, à partir du secteur occupé par le joueur. Mais n'allez pas croire qu'il s'agit d'une phase de rendu : rien n'est dessiné. Mais l'algorithme d'élimination des murs invisibles n'est pas très efficace. Il part du principe qu'un mur est potentiellement visible s'il est dans le cone de vision du joueur, qui fait 90°. Il élimine aussi les murs qui font dos au joueur. En somme, la première étape est juste une phase de ''clipping'' et de ''back-face culling''.
ne rend pas les murs quand il visite un secteur. A la place, il mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
rdp1j5amuk6ye415ml9fjhra5k8s801
746282
746281
2025-07-07T18:06:05Z
Mewtow
31375
/* Le Build Engine et l'usage d'une pile de parcours */
746282
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec des secteurs garantis convexes, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs et des ouvertures vers les autres pièces. Il peut dessiner les murs dans le désordre, avant de passer au rendu des ouvertures.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures pour les fenêtres/portes est très importante pour ce qui est du gameplay, mais aussi pour le rendu à portail. Elle colle assez bien à la distinction entre lignes opaques et lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont des lignes qui correspondent à des ouvertures, relient soit deux pièces entre elles, soit une pièce avec l'extérieur.
===Le rendu par portail se fait secteur par secteur===
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
==Le ''Build Engine''==
Le ''Build Engine'' utilise une version assez étrange du rendu par portail. Son rendu se fait en plusieurs phases. La toute première détermine quels sont les murs potentiellement visibles. Elle se fait en parcourant tous les secteurs, en traversant les portails, à partir du secteur occupé par le joueur. Mais n'allez pas croire qu'il s'agit d'une phase de rendu : rien n'est dessiné. Il s'agit juste de faire la liste des murs potentiellement visibles.
L'algorithme d'élimination des murs invisibles pour cette première phase n'est pas très efficace. Il part du principe qu'un mur est potentiellement visible s'il est dans le cone de vision du joueur, qui fait 90°. Il élimine aussi les murs qui font dos au joueur. En somme, la première étape est juste une phase de ''clipping'' et de ''back-face culling''.
===L'usage d'une pile de parcours===
La première phase mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
Précisons cependant que les explications sont simplifiées, dans le sens où le moteur du jeu ne mémorise pas des murs individuels, mais des paquets de murs, avec un paquet par secteur. Un paquet correspond aux murs potentiellement visibles dans un secteur.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
shxlma5ba95ym7946s917iu5bu1e4d3
746283
746282
2025-07-07T18:07:28Z
Mewtow
31375
/* L'usage d'une pile de parcours */
746283
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec des secteurs garantis convexes, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs et des ouvertures vers les autres pièces. Il peut dessiner les murs dans le désordre, avant de passer au rendu des ouvertures.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures pour les fenêtres/portes est très importante pour ce qui est du gameplay, mais aussi pour le rendu à portail. Elle colle assez bien à la distinction entre lignes opaques et lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont des lignes qui correspondent à des ouvertures, relient soit deux pièces entre elles, soit une pièce avec l'extérieur.
===Le rendu par portail se fait secteur par secteur===
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
==Le ''Build Engine''==
Le ''Build Engine'' utilise une version assez étrange du rendu par portail. Son rendu se fait en plusieurs phases. La toute première détermine quels sont les murs potentiellement visibles. Elle se fait en parcourant tous les secteurs, en traversant les portails, à partir du secteur occupé par le joueur. Mais n'allez pas croire qu'il s'agit d'une phase de rendu : rien n'est dessiné. Il s'agit juste de faire la liste des murs potentiellement visibles.
L'algorithme d'élimination des murs invisibles pour cette première phase n'est pas très efficace. Il part du principe qu'un mur est potentiellement visible s'il est dans le cone de vision du joueur, qui fait 90°. Il élimine aussi les murs qui font dos au joueur. En somme, la première étape est juste une phase de ''clipping'' et de ''back-face culling''.
===La première étape : l'usage d'une pile de parcours===
La première phase mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
Précisons cependant que les explications sont simplifiées, dans le sens où le moteur du jeu ne mémorise pas des murs individuels, mais des paquets de murs, avec un paquet par secteur. Un paquet correspond aux murs potentiellement visibles dans un secteur.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
mlutgg71gi5txcpmnvc6xlk7nrdg3qi
746284
746283
2025-07-07T18:13:27Z
Mewtow
31375
/* Le Build Engine */
746284
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec des secteurs garantis convexes, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs et des ouvertures vers les autres pièces. Il peut dessiner les murs dans le désordre, avant de passer au rendu des ouvertures.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures pour les fenêtres/portes est très importante pour ce qui est du gameplay, mais aussi pour le rendu à portail. Elle colle assez bien à la distinction entre lignes opaques et lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont des lignes qui correspondent à des ouvertures, relient soit deux pièces entre elles, soit une pièce avec l'extérieur.
===Le rendu par portail se fait secteur par secteur===
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
==Le ''Build Engine''==
Le ''Build Engine'' utilise une version assez étrange du rendu par portail. Son rendu se fait en plusieurs phases.
La toute première étape détermine quels sont les murs potentiellement visibles. Elle se fait en parcourant tous les secteurs, en traversant les portails, à partir du secteur occupé par le joueur. Mais n'allez pas croire qu'il s'agit d'une phase de rendu : rien n'est dessiné. Il s'agit juste de faire la liste des murs potentiellement visibles. L'algorithme d'élimination des murs invisibles pour cette première phase n'est pas très efficace. Il part du principe qu'un mur est potentiellement visible s'il est dans le cône de vision du joueur, qui fait 90°. Il élimine aussi les murs qui font dos au joueur. En somme, la première étape est juste une phase de ''clipping'' et de ''back-face culling''.
Par la suite, les paquets de murs sont rendus du plus proche au plus lointain, avec cependant un ordre approximatif. Précisément, il commence par rendre le premier mur qui n'est occludé par aucun mur. Ce n'est pas forcément le mur le plus proche, mais c'est généralement un mur assez proche, qui appartient au secteur occupé par le joueur.
Le moteur utilise un tableau d'occlusion, qui indique quelles portions de l'écran ont déjà été écrites. Lors du rendu d'un mur, le moteur dessine dans le ''framebuffer'' colonne par colonne. Pour chaque colonne, il rend d'abord le plafond/ciel, puis le sol, puis le mure. Il marque ensuite la colonne complète comme écrite, à savoir que chaque pixel de la colonne est marqué comme écrit. Lors du rendu d'un portail, le ciel/plafond et le sol sont rendus, puis le portail est rendu en regardant ce qu'il y a dans le secteur lié, et ainsi de suite. Le tableau d'occlusion marque alors les pixels de la colonne qui ont été rendus, et ce n'est pas forcément toute la colonne.
===La première étape : l'usage d'une pile de parcours===
La première phase mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
Précisons cependant que les explications sont simplifiées, dans le sens où le moteur du jeu ne mémorise pas des murs individuels, mais des paquets de murs, avec un paquet par secteur. Un paquet correspond aux murs potentiellement visibles dans un secteur.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
cvl4j4lt95f9mqeqvuqll5fsbicmmdi
746285
746284
2025-07-07T18:17:13Z
Mewtow
31375
/* Le Build Engine */
746285
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec des secteurs garantis convexes, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs et des ouvertures vers les autres pièces. Il peut dessiner les murs dans le désordre, avant de passer au rendu des ouvertures.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures pour les fenêtres/portes est très importante pour ce qui est du gameplay, mais aussi pour le rendu à portail. Elle colle assez bien à la distinction entre lignes opaques et lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont des lignes qui correspondent à des ouvertures, relient soit deux pièces entre elles, soit une pièce avec l'extérieur.
===Le rendu par portail se fait secteur par secteur===
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
==Le ''Build Engine''==
Le ''Build Engine'' utilise une version assez étrange du rendu par portail. Son rendu se fait en plusieurs phases.
La toute première étape détermine quels sont les murs potentiellement visibles. Elle se fait en parcourant tous les secteurs, en traversant les portails, à partir du secteur occupé par le joueur. Mais n'allez pas croire qu'il s'agit d'une phase de rendu : rien n'est dessiné. Il s'agit juste de faire la liste des murs potentiellement visibles. L'algorithme d'élimination des murs invisibles pour cette première phase n'est pas très efficace. Il part du principe qu'un mur est potentiellement visible s'il est dans le cône de vision du joueur, qui fait 90°. Il élimine aussi les murs qui font dos au joueur. En somme, la première étape est juste une phase de ''clipping'' et de ''back-face culling''.
Par la suite, les paquets de murs sont rendus du plus proche au plus lointain, avec cependant un ordre approximatif. Précisément, il commence par rendre le premier mur qui n'est occludé par aucun mur. Ce n'est pas forcément le mur le plus proche, mais c'est généralement un mur assez proche, qui appartient au secteur occupé par le joueur. Le moteur utilise un tableau d'occlusion, qui indique quelles portions de l'écran ont déjà été écrites. Lors du rendu d'un mur, le moteur dessine dans le ''framebuffer'' colonne par colonne pour les murs. Le tableau d'occlusion marque alors les pixels de la colonne qui ont été rendus, et ce n'est pas forcément toute la colonne.
===La première étape : l'usage d'une pile de parcours===
La première phase mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
Précisons cependant que les explications sont simplifiées, dans le sens où le moteur du jeu ne mémorise pas des murs individuels, mais des paquets de murs, avec un paquet par secteur. Un paquet correspond aux murs potentiellement visibles dans un secteur.
==Le support des portails à géométrie non-euclidienne==
===Voir, à travers un portail de téléportation===
===Les portails à la ''portal''===
Supporté par Unreal, je sais pas pour les autres jeux de l'époque.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
evjnvzyxxkbbzht2a6w4qgigfq87tw7
746291
746285
2025-07-07T19:17:26Z
Mewtow
31375
/* Le support des portails à géométrie non-euclidienne */
746291
wikitext
text/x-wiki
Le rendu à portails, aussi appelé ''portal rendering'', était la méthode de rendu dominante durant les années 90. Il a notamment été utilisé sur le ''Build Engine'', le moteur de jeu de Duke Nukem 3D, Shadow Warrior, Blood, et quelques autres excellents jeux des années 90. Elle a aussi été utilisée dans le ''jedi engine'' des FPS lucas art, à savoir Star Wars Dark Forces et Outlaws, et pour les jeux Marathon et Pathways to Darkness de Bungie. Le rendu à portail était moins performant que l'usage du ''Binary Space Partionning'', mais permettait d'avoir des niveaux dynamiques et des maps à la géométrie non-euclidienne. Elle gère notamment les portails de téléportation naturellement, là où le moteur de DOOM, seul à utiliser les BSP, ne permettait pas de voir à travers un portail.
Dans ce chapitre, nous allons étudier le rendu à portail en prenant l'exemple du Build Engine, dont le code source est disponible depuis les années 2000.
==Les secteurs et leur rendu==
Le rendu à portail demande que de nombreuses informations concernant la map soient précalculées. L'idée est de découper la map 2D en polygones appelés '''secteurs''' (dans la doc de DOOM) ou encore ''polygones'' (dans la doc du ''Build Engine''). Par exemple, prenez un niveau qui se passe dans un étage de bâtiment. le niveau est composé de couloirs reliant des pièces rectangulaires. Une pièce rectangulaire correspond à un polygone/secteur, dont les murs et la porte d'entrée sont représentés par une ligne. Mais des niveaux plus complexes, comme des forêts, des niveaux d'enfer ou autres, peuvent utiliser des polygones plus complexes.
Un secteur est globalement une pièce fermée, délimitée par des murs, avec des ouvertures comme des portes, des fenêtres transparentes, des ouvertures vers l'extérieur, etc. Un secteur définit plusieurs lignes qui ferment le secteur. Les murs qui l'entourent sont des lignes infranchissables, sauf à activer un cheat passe-muraille. D'autres lignes infranchissables permettent de définir des fenêtres et autre ouvertures qui permettent de voir une autre pièce ou l'extérieur. Mais il faut aussi utiliser une ligne pour chaque porte, qui sont cette fois-ci des lignes que le joueur peut traverser. De telles lignes franchissables sont nécessaires pour découper la map en polygones.
Le rendu se fait secteur par secteur, mais nous laissons cela de côté pour le moment. Ce qui va nous intéresser est comment le jeu rend un secteur. Pour simplifier les explications, nous allons partir du principe que le joueur est physiquement dans un secteur, et que le jeu veut rendre ce secteur précisément. Le rendu qui nous pré-occupe est celui des murs du secteur, les ouvertures sont justement traitées à part. Nous verrons que ce sont elles qui permettent de passer d'un secteur à l'autre.
===Les secteurs doivent idéalement être convexes===
Pour rendre un secteur, le rendu affiche les murs, généralement en dessinant les murs un par un dans le ''framebuffer''. Puis, il rend les ''sprites'', du plus lointain au plus proche dans la pièce. Le rendu des murs doit être assez rapide. Et pour cela, il se fait généralement sans tenir compte des informations de profondeur. On dessine les murs un par un, dans le désordre. Mais cela demande qu'une condition bien précise soit respectée : le secteur n'a pas de murs qui en cache un autre.
[[File:Simple polygon.svg|vignette|Polygone concave.]]
Peu importe l'angle de vue, un mur ne doit pas en cacher un autre, même partiellement. Pour cela, il faut que le secteur soit convexe, les secteurs concaves sont interdits. Pour éviter de rentrer dans la définition mathématique de ce qu'est un truc concave, on va dire qu'il ne doit pas y avoir de "pointe" ou un angle qui rentre dans le polygone, et qui pourrait masquer un mur derrière. Par exemple, le polygone ci-contre est concave, en raison de la petite pointe en bas à gauche.
Avec des secteurs garantis convexes, il est certain qu'aucun mur n'en masque un autre. Pas besoin de trier les murs selon leur profondeur. Un secteur est alors simplement définit par la liste de ses murs et des ouvertures vers les autres pièces. Il peut dessiner les murs dans le désordre, avant de passer au rendu des ouvertures.
==Les portails==
La distinction entre lignes pour les murs, pour les ouvertures pour les fenêtres/portes est très importante pour ce qui est du gameplay, mais aussi pour le rendu à portail. Elle colle assez bien à la distinction entre lignes opaques et lignes invisibles. Les lignes opaques sont concrètement les lignes des murs, infranchissables, n'ont pas de nom. Mais elles sont opposées aux lignes pour les ouvertures, qui sont en quelque sorte "transparentes". On peut voir à travers une fenêtre, une porte ouverte, ou toute autre ouverture. Elles ne sont pas forcément traversables : on peut traverser une porte ouverte, mais le jeu ne permettra pas de traverser une vitre ou une fenêtre fermée. Le jeu peut permettre de la casser, mais encore faut-il que les programmeurs aient prévu le coup. Les '''portails''' sont des lignes qui correspondent à des ouvertures, relient soit deux pièces entre elles, soit une pièce avec l'extérieur.
===Le rendu par portail se fait secteur par secteur===
L'idée du rendu par portail est de faire le rendu pièce par pièce, ou mieux dit : secteur par secteur. L'idée est de commencer le rendu dans le secteur où se trouve le joueur, puis de passer aux secteurs voisins, puis aux secteurs encore voisins, et ainsi de suite. Le rendu est donc récursif, terme barbare qui recouvre un concept assez simple. Chaque pièce pointe vers plusieurs pièces, qui pointent vers plusieurs pièces, qui elles-mêmes pointent vers plusieurs pièces, et ainsi de suite. Une fois qu'on a rendu une pièce, il ne faut, pas oublier de revenir en arrière, à la pièce précédente, pour reprendre le rendu à partir de celle-ci. Les programmeurs savent comment faire ce genre de chose : une fonction récursive, l'usage d'une pile de pièce, rien de compliqué.
Pour cela, la map doit contenir des informations de connectivité, à savoir : quel secteur est voisin de quel autre. La map mémorise, pour chaque secteur, la liste de ses portails. Et chaque portail indique vers quelle autre pièce il donne de la visibilité. Le rendu utilise les informations sur les portails pour passer d'une pièce à la suivante.
Avec les explications que je viens de donner, vous avez peut-être trouvé un défaut à cette méthode : elle marche bien pour les environnements fermés, où on ne voit pas l'extérieur. Mais dès qu'un niveau a beaucoup d'environnement en extérieur, sans rien pour bloquer la vision, ce mode de rendu ne fonctionne plus vraiment. Aussi, cette méthode de rendu a été utilisé pour quelques jeux bien précis. Les anciens FPS en 2.5D avaient peu d’environnement ouverts, et les rares environnements à l'extérieur simulaient ceux-ci avec des secteurs entourés de bâtiments. Dans Duke Nukem 3D, les environnements ouverts étaient surtout des rues, entourées de bâtiments, qui étaient bloquées à leurs extrémités.
Il faut noter que la méthode marche aussi en trois dimensions. Des jeux 3D utilisaient cette technique, bien que cela demandait un level-design adéquat. L'exemple caractéristique est le jeu en vraie 3D texturée Descent, paru quelques mois avant ce qui est souvent considéré à tord comme le premier en vraie 3D : Quake !
===Les sous-secteurs reliés par des portails===
[[File:Concave Polygon Fan Triangulation.svg|vignette|Concave Polygon Fan Triangulation]]
Il est en théorie possible de gérer des pièces concaves, en les découpant en plusieurs '''sous-secteurs''' convexes, connectés entre eux par des portails. Les mathématiques nous disent que c'est toujours possible. Un polygone concave peut toujours être découpé en plusieurs triangles collés les uns aux autres. Et un triangle est naturellement convexe. Sans compter qu'on peut combiner plusieurs triangles en polygones convexes de plus grande taille, comme des rectangles, des quadrilatères, etc.
Mais les performances s'en ressentent, car le rendu d'une pièce demande alors de rendre un portail, voire plusieurs. Et les portails ne sont pas gratuits en termes de performance. En pratique, le cout en performance sera assez important. Il est aussi possible de trier les murs d'un secteur en fonction de leur distance, pour les rendre avec l'algorithme du peintre ou l'algorithme du peintre inversé. Le ''Build Engine'' triait les murs du plus proche au plus lointain, pour chaque secteur. Le cout en performance était moindre, avec le rendu par portail.
==Le rendu par portail est une approximation de l'algorithme du peintre==
Le rendu par portail permet d'approximer l'algorithme du peintre inversée. Et si j'ai dit approximer, c'est car l'ordre de rendu des objets n'est pas exactement du plus proche au plus lointain. Le rendu se fait secteur par secteur et le moteur tend à rendre les secteurs du plus proche au plus lointain. Par contre, le rendu des murs dans un secteur ne se fait pas forcément du plus proche au plus lointain. Tout dépend du moteur : le ''Build Engine'' trie bien les murs selon leur distance, d'autres moteurs s'en passent. Tout dépend de si les secteurs sont garantis convexes ou non.
De plus, même en rendant les murs dans l'ordre et les secteurs dans l'ordre, on peut avoir un résultat pas totalement ordonné. Imaginez l'exemple suivant : le joueur voit deux secteurs à travers deux portails séparés. Le secteur 1 contient un mur à 5 mètres et un autre à 50 mètres, le secteur 2 a un mur à 1 mètre et un autre à 15 mètres. L'ordre de rendu sera alors : mur à 5 mètre, puis à 50, puis celui à 1 mètre et celui à 15 mètres. Ou alors, on aura mur à à 1 mètre et celui à 15 mètres, puis celui à 5 mètres et celui à 50. L'ordre de profondeur n'est pas totalement conservé.
Mais le rendu à portail garantit cependant que ce n'est pas un problème. En effet, les deux portails sont par définition deux portions de l'écran qui ne se recouvrent pas. Par contre, si deux portails sont alignés, le rendu à portail garantir que l'on traitera les deux portails dans l'ordre. En fait, deux portails non-alignés sont des portions indépendantes de l'image, qui ne contiennent pas de mur ou de ''sprites'' commun. On peut les rendre indépendamment les uns des autres, sans problème.
Quelques moteurs graphiques utilisaient cependant l'algorithme du peintre, pas sa version inversée. Par exemple, le jeu Thief était un jeu en 3D qui utilisait une version 3D du rendu à portail. Et il dessinait ses surfaces en allant de la plus lointaine vers la plus proche. De plus, il ne séparait pas le rendu des murs et celui des objets, vu que c'était plus simple avec un algorithme du peintre normal, surtout qu'en plus le jeu était rendu en 3D.
===La visibilité à travers les portails===
Idéalement, le moteur n'est cependant pas stupide et ne parcours pas les secteurs invisibles. Quand il visite un secteur, il détermine si quels sont les murs et portails visibles par le joueur, depuis sa position initiale. Si un portail n'est pas visible par le joueur, la pièce voisine associée ne sera pas visitée. Et les murs du secteur qui ne sont pas visibles ne sont pas pris en compte. Le moteur du jeu Thief, un FPS orienté infiltration en 3D, appliquait cette optimisation. Lors du parcours secteur par secteur, le moteur du jeu vérifiait pour chaque secteur s'il était visible par le joueur. Si ce n'était pas le cas, le jeu abandonnait ce secteur et passait au secteur suivant. Bizarrement, le Build Engine n'appliquait pas cette optimisation.
Le rendu à portail résout un problème très important : déterminer ce qui est visible à l'écran. Le moteur du jeu analyse en premier le premier secteur, celui où se trouve le joueur. Il regarde alors quels sont les murs et les ''sprites'' qui sont dans le cône de vision du joueur. Le moteur de jeu dispose d'algorithmes de ''view frustrum clipping'' qui éliminent tout ce qui est en-dehors de ce cône de vision. Et ces algorithmes peuvent être adaptés pour déterminer ce qui est visible à travers un portail. L'idée est que pour chaque portail, le moteur détermine un cône de vision dont les bords sont ceux du portail. Il ré-utilise alors l'algorithme de ''view frustrum clipping'' pour éliminer ce qui est en-dehors de ce cône de vision à travers le portail. En faisant cela,
[[File:Visibilité à travers les portails.png|centre|vignette|upright=2.5|Visibilité à travers les portails]]
Le Build Engine n'utilisait pas cette méthode. Il faisait le strict minimum lors du rendu et se contentait d'éliminer les portails en-dehors du champ de vision du joueur. Par contre, il ajoutait une optimisation : les portails n'étaient pas pris en compte si ils font dos au joueur. En effet, un portail n'est pas qu'une ligne : il a aussi un sens, une direction qui indique par où le joueur peut regarder. Les portails pour les portes ont un double sens : ils font toujours face au joueur. Mais d'autres portails comme les fenêtres vers un extérieur inaccessible, n'ont qu'un seul sens. Si le portail fait dos au joueur, c'est signe qu'un mur est situé entre le joueur et le portail.
Pour accélérer l'élimination des portails invisibles, la map peut aussi contenir des informations de visibilité entre portails. Par exemple, imaginez une pièce avec deux portails, placés de telle sorte que si le joueur regarde à travers un portail, l'autre est invisible. Cette information est très importante, car elle indique que si le joueur regarde à travers le premier portail, le second n'est pas à prendre en compte. Cela fait un portail en moins à analyser avec le ''clipping''. Pour en profiter, chaque portail d'un secteur, la map mémorise ce qui est potentiellement visible depuis ce portail. De telles informations sont regroupées sous le terme de '''''Potentially visible set''''' : l'ensemble potentiellement visible. Le potentiellement est important, car ce qui est visible dépend de la position du joueur et de l'angle que fait sa caméra.
===L'ordre de parcours des secteurs===
Il y a deux grandes méthodes pour parcourir les secteurs du jeu : le parcours en largeur et le parcours en profondeur. Le parcours en largeur part du secteur occupé par le joueur, puis passe aux secteurs voisins, accessibles directement via un portail connecté au premier secteur. Ils les traite alors tous, avant de passer aux secteurs voisins des voisins, en regardant les portails de chaque secteur voisin. Et ainsi de suite. Le parcours en profondeur traite la situation portail par portail, en traitant chaque portail l'un à la suite de l'autre, chaque portail impliquant la visite de tous les sous-portails et les sous-sous-portails.
Les deux schémas ci-dessous illustrent les deux parcours. On part d'un secteur, celui où se trouve le joueur, qui est relié à trois secteurs voisins, dont deux d'entre eux sont reliés à des voisins, et ainsi de suite. Il y a donc les secteurs immédiatement voisins avec un portail commun, les secteurs voisins secondaires qui demandent de traverser deux portails, des secteurs tertiaires qui demandent de traverser trois portails, etc. Le parcours en largeur visite tous les secteurs voisins , puis les secteurs secondaires, puis les secteurs tertiaires, et ainsi de suite.
{|
|[[File:Depth-first-tree.svg|vignette|upright=1.5|Parcours en profondeur.]]
|[[File:Breadth-first-tree.svg|vignette|upright=1.5|Parcours en largeur.]]
|}
Le parcours en largeur est une approximation de l'algorithme du peintre inversé. Le moteur du jeu parcourt les secteurs du plus proche au plus lointain, mais le dessin des murs dans un secteur ne suit pas un ordre de profondeur. Deux murs dans deux secteurs différents peuvent être à des distances très différentes, leur ordre de dessin ne compte pas. Par exemple, si je dessine le secteur 1 avant le secteur 2, peu importe qu'un des murs du secteur 1 soit plus loin qu'un mur du secteur 2 : ce n'est pas pris en compte. En cela, le parcours en largeur est une approximation de l'algorithme du peintre inversé.
==Le ''Build Engine''==
Le ''Build Engine'' utilise une version assez étrange du rendu par portail. Son rendu se fait en plusieurs phases.
La toute première étape détermine quels sont les murs potentiellement visibles. Elle se fait en parcourant tous les secteurs, en traversant les portails, à partir du secteur occupé par le joueur. Mais n'allez pas croire qu'il s'agit d'une phase de rendu : rien n'est dessiné. Il s'agit juste de faire la liste des murs potentiellement visibles. L'algorithme d'élimination des murs invisibles pour cette première phase n'est pas très efficace. Il part du principe qu'un mur est potentiellement visible s'il est dans le cône de vision du joueur, qui fait 90°. Il élimine aussi les murs qui font dos au joueur. En somme, la première étape est juste une phase de ''clipping'' et de ''back-face culling''.
Par la suite, les paquets de murs sont rendus du plus proche au plus lointain, avec cependant un ordre approximatif. Précisément, il commence par rendre le premier mur qui n'est occludé par aucun mur. Ce n'est pas forcément le mur le plus proche, mais c'est généralement un mur assez proche, qui appartient au secteur occupé par le joueur. Le moteur utilise un tableau d'occlusion, qui indique quelles portions de l'écran ont déjà été écrites. Lors du rendu d'un mur, le moteur dessine dans le ''framebuffer'' colonne par colonne pour les murs. Le tableau d'occlusion marque alors les pixels de la colonne qui ont été rendus, et ce n'est pas forcément toute la colonne.
===La première étape : l'usage d'une pile de parcours===
La première phase mémorise les murs et ''sprites'' à rendre dans une structure de donnée appelée une '''pile de données''', ou encore une pile. Les piles tirent leur nom de la comparaison avec une pile d'assiette. Les données ajoutées les plus récemment sont au sommet de la pile, les plus anciennes sont au fond de la pile. Il est possible d'ajouter une donnée : on dit qu'on empile la donnée. Il est aussi possible de récupérer la donnée qui est au sommet de la pile, à savoir la plus récemment ajoutée.
[[File:PrimitivesPile.png|centre|vignette|upright=2|Primitives de gestion d'une pile.]]
Ce système d'empilement/dépilement garantit que dans une pile, les données sont triées selon leur ordre d'ajout. Vu qu'on ne peut qu'enlever la donnée la plus récemment ajoutée.
[[File:Stack (data structure) LIFO.svg|centre|vignette|upright=2|Stack (data structure) LIFO.]]
Le ''build engine'' utilise deux piles : une pour les ''sprites'' et une autre pour les murs. Quand il visite un secteur, il trie les murs par ordre de profondeur et ajoute un par un dans la pile des murs. Il fait pareil avec les ''sprites'' : il les trie selon leur profondeur et les ajoute un par un dans la pile. Il vérifie alors les portails et place les portails du secteur dans une liste de portails à rendre plus tard.
Précisons cependant que les explications sont simplifiées, dans le sens où le moteur du jeu ne mémorise pas des murs individuels, mais des paquets de murs, avec un paquet par secteur. Un paquet correspond aux murs potentiellement visibles dans un secteur.
==En savoir plus==
Pour en savoir plus sur le build engine, je conseille fortement la lecture des articles du blog de Fabien Sanglard :
* [https://fabiensanglard.net/duke3d/ Duke Nukem 3D Code Review: INTRODUCTION]
* [https://fabiensanglard.net/duke3d/build_engine_internals.php Duke Nukem 3D: BUILD ENGINE INTERNALS (PART 2 OF 4) ]
* [https://fabiensanglard.net/duke3d/code_legacy.php Duke Nukem 3D: CODE LEGACY (PART 3 OF 4)]
* [https://fabiensanglard.net/duke3d/chocolate_duke_nukem_3D.php Duke Nukem 3D: Chocolate Duke Nukem 3D (PART 4 OF 4)]
Le peu d'information publique vulgarisée sur le Jedi Engine est disponible via ce lien :
* [https://theforceengine.github.io/2020/05/16/DFRender1.html Dark Forces (DOS) Rendering, Part I]
Documentation incomplète du moteur de Thief, réalisée par un ancien programmeur du moteur :
* [https://nothings.org/gamedev/thief_rendering.html The 3D Software Rendering Technology of 1998's Thief: The Dark Project]
<noinclude>
{{NavChapitre | book=Les moteurs de rendu des FPS en 2.5 D
| prev=Le moteur de Wolfenstein 3D
| prevText=Le moteur de Wolfenstein 3D
| next=Le DOOM engine
| nextText=Le DOOM engine
}}
</noinclude>
[[Catégorie:Les moteurs de rendu des FPS en 2.5 D (livre)]]
gu86v2o9m8rretmhvz6bnrl5m9k23cm
Discussion Wikilivres:Le Bistro/2025
5
82063
746296
745639
2025-07-08T00:05:32Z
MediaWiki message delivery
36013
/* Actualités techniques n° 2025-28 */ nouvelle section
746296
wikitext
text/x-wiki
== Actualités techniques n° 2025-03 ==
<section begin="technews-2025-W03"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/03|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Le système de connexion utilisateur unique (SUL) va être mis à jour durant les prochains mois. Il permet aux utilisateurs et utilisatrices d’être connectés sur tous les sites en même temps après avoir renseigné leurs identifiants sur un site Wikimedia. La mise à jour est nécessaire car les navigateurs restreignent de plus en plus les témoins de connexion inter-domaines. Pour s’adapter à ces restrictions, les pages de connexion et de création de compte seront déplacées vers un domaine central, mais cela apparaitra toujours comme si vous étiez sur le wiki d’origine. Le code mis à jour sera activé cette semaine pour les utilisations sur les wikis de test. Ce changement devrait être déployé pour tous durant février et mars. Consultez [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3#Deployment|la page du projet SUL3]] pour plus d’informations et un calendrier.
'''Actualités pour la contribution'''
* Sur les wikis ayant [[mw:Special:MyLanguage/Extension:PageAssessments|PageAssessments]] (évaluation des pages) installée, vous pouvez désormais [[mw:Special:MyLanguage/Extension:PageAssessments#Search|filtrer les résultats de recherche]] aux pages dans un projet donné à l’aide du mot-clé <code dir=ltr>inproject:</code>. (Ces wikis : {{int:project-localized-name-arwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-enwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-enwikivoyage/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-frwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-huwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-newiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-trwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-zhwiki/fr}}.) [https://phabricator.wikimedia.org/T378868]
* Un nouveau wiki a été créé : une Wikipédia en [[d:Q34129|tigré]] ([[w:tig:|<code>w:tig:</code>]]) [https://phabricator.wikimedia.org/T381377]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:35|la tâche soumise|les {{formatnum:35}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:35||s}} la semaine dernière]]. Par exemple, il y avait un beugue de mise à jour du compteur de modifications de quelqu’un effectuant une annulation d’une autre modification : cela est maintenant corrigé. [https://phabricator.wikimedia.org/T382592]
'''Actualités pour la contribution technique'''
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] Les utilisateurs et utilisatrices de l’API REST de Wikimedia (par exemple pour des robots ou des outils) peuvent être impactés par des mises à jour en cours. À partir de la semaine du 13 janvier, nous commencerons à rediriger [[phab:T374683|certains points terminaux de contenu de page]] depuis RESTbase vers les nouveaux points terminaux de l’API REST de MediaWiki pour tous les projets wiki. Ce changement était disponible sur testwiki, et ne devrait pas affecter les fonctionnalités existantes, mais les utilisateurs actifs des points terminaux concernés peuvent signaler directement à l’[[phab:project/view/6931/|équipe des interfaces de MediaWiki]] tout problème qui arriverait.
* Les personnes maintenant des outils sur Toolforge peuvent désormais partager leurs retour sur Toolforge UI, un projet visant à fournir une plateforme web pour la création et la gestion d’outils Toolforge depuis une interface graphique, en plus des processus existant par ligne de commande. Ce projet vise à simplifier les tâches des mainteneurs et mainteneuses actifs, ainsi qu’à rendre l’inscription et les procédures de déploiement plus accessibles aux nouveaux et nouvelles créatrices d’outils. Le projet en est encore à ses balbutiements et l’équipe des services en infonuage recueille des retours de la communauté Toolforge pour aiderà concevoir la solution correspondant à leurs besoins. [[wikitech:Wikimedia Cloud Services team/EnhancementProposals/Toolforge UI|En savoir plus et donner son avis sur Toolforge UI]].
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] <span class="mw-translate-fuzzy">Pour le développement d’outil et bibliothèque qui utilisent le système OAuth : le point terminal d’identité utilisé pour [[mw:Special:MyLanguage/OAuth/For Developers#Identifying the user|OAuth 1]] et [[mw:Special:MyLanguage/OAuth/For Developers#Identifying the user 2|OAuth 2]] retournait un objet JSON avec un entier dans le sous-champ, ce qui était incorrect (le champ doit toujours être une chaine de caractère); Cela a été corrigé ; le correctif sera déployé sur les wikis Wikimedia la semaine du 13 janvier.</span> [https://phabricator.wikimedia.org/T382139]
* De nombreux wikis utilisent actuellement le [[:mw:Parsoid/Parser Unification/Cite CSS|CSS de Cite]] pour insérer des marqueurs de note de bas de page personnalisés dans la sortie de Parsoid. À partir du 20 janvier, ces règles seront désactivées, mais les développeurs vous demandent de ''ne pas'' nettoyer votre <bdi lang="en" dir="ltr">[[MediaWiki:Common.css]]</bdi> avant le 20 février pour éviter des problèmes pendant la migration. Vos wikis rencontreront peut-être des petits changements dans les marqueurs de notes de bas page dans l’éditeur visuel ou en utilisant le mode de lecture expérimental Parsoid, mais s’il y a des changements, ils devraient garder le rendu cohérent avec la sortie de l’analyseur classique. [https://phabricator.wikimedia.org/T370027]
'''Rencontres et évènements'''
* Les prochaines réunions de la série des [[c:Special:MyLanguage/Commons:WMF support for Commons/Commons community calls|Discussions communautaires entre Wikimedia Foundation et la communauté de Wikimedia Commons]] aura lieu le [[m:Special:MyLanguage/Event:Commons community discussion - 15 January 2025 08:00 UTC|15 janvier à 8 h UTC]] et [[m:Special:MyLanguage/Event:Commons community discussion - 15 January 2025 16:00 UTC|à 16 h UTC]]. Le sujet de cette conférence porte sur la définition des priorités d’investissement en outils pour Commons. Les contributeurs et contributrices de tous les wikis sont les bienvenus pour participer, notamment celles et ceux qui maintiennent des outils pour Commons.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/03|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W03"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 14 janvier 2025 à 02:42 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28048614 -->
== Launching! Join Us for Wiki Loves Ramadan 2025! ==
Dear All,
We’re happy to announce the launch of [[m:Wiki Loves Ramadan 2025|Wiki Loves Ramadan 2025]], an annual international campaign dedicated to celebrating and preserving Islamic cultures and history through the power of Wikipedia. As an active contributor to the Local Wikipedia, you are specially invited to participate in the launch.
This year’s campaign will be launched for you to join us write, edit, and improve articles that showcase the richness and diversity of Islamic traditions, history, and culture.
* Topic: [[m:Event:Wiki Loves Ramadan 2025 Campaign Launch|Wiki Loves Ramadan 2025 Campaign Launch]]
* When: Jan 19, 2025
* Time: 16:00 Universal Time UTC and runs throughout Ramadan (starting February 25, 2025).
* Join Zoom Meeting: https://us02web.zoom.us/j/88420056597?pwd=NdrpqIhrwAVPeWB8FNb258n7qngqqo.1
* Zoom meeting hosted by [[m:Wikimedia Bangladesh|Wikimedia Bangladesh]]
To get started, visit the [[m:Wiki Loves Ramadan 2025|campaign page]] for details, resources, and guidelines: Wiki Loves Ramadan 2025.
Add [[m:Wiki Loves Ramadan 2025/Participant|your community here]], and organized Wiki Loves Ramadan 2025 in your local language.
Whether you’re a first-time editor or an experienced Wikipedian, your contributions matter. Together, we can ensure Islamic cultures and traditions are well-represented and accessible to all.
Feel free to invite your community and friends too. Kindly reach out if you have any questions or need support as you prepare to participate.
Let’s make Wiki Loves Ramadan 2025 a success!
For the [[m:Wiki Loves Ramadan 2025/Team|International Team]] 16 janvier 2025 à 13:08 (CET)
<!-- Message envoyé par User:ZI Jony@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=27568454 -->
== Actualités techniques n° 2025-04 ==
<section begin="technews-2025-W04"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/04|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Administrators can mass-delete multiple pages created by a user or IP address using [[mw:Special:MyLanguage/Extension:Nuke|Extension:Nuke]]. It previously only allowed deletion of pages created in the last 30 days. It can now delete pages from the last 90 days, provided it is targeting a specific user or IP address.</span> [https://phabricator.wikimedia.org/T380846]
* <span lang="en" dir="ltr" class="mw-content-ltr">On [[phab:P72148|wikis that use]] the [[mw:Special:MyLanguage/Help:Patrolled edits|Patrolled edits]] feature, when the rollback feature is used to revert an unpatrolled page revision, that revision will now be marked as "manually patrolled" instead of "autopatrolled", which is more accurate. Some editors that use [[mw:Special:MyLanguage/Help:New filters for edit review/Filtering|filters]] on Recent Changes may need to update their filter settings.</span> [https://phabricator.wikimedia.org/T302140]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:31|la tâche soumise|les {{formatnum:31}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:31||s}} la semaine dernière]]. <span lang="en" dir="ltr" class="mw-content-ltr">For example, the Visual Editor's "Insert link" feature did not always suggest existing pages properly when an editor started typing, which has now been [[phab:T383497|fixed]].</span>
'''Actualités pour la contribution technique'''
* <span lang="en" dir="ltr" class="mw-content-ltr">The Structured Discussion extension (also known as Flow) is being progressively removed from the wikis. This extension is unmaintained and causes issues. It will be replaced by [[mw:Special:MyLanguage/Help:DiscussionTools|DiscussionTools]], which is used on any regular talk page. [[mw:Special:MyLanguage/Structured Discussions/Deprecation#Deprecation timeline|The last group of wikis]] ({{int:project-localized-name-cawikiquote/en}}{{int:comma-separator/en}}{{int:project-localized-name-fiwikimedia/en}}{{int:comma-separator/en}}{{int:project-localized-name-gomwiki/en}}{{int:comma-separator/en}}{{int:project-localized-name-kabwiki/en}}{{int:comma-separator/en}}{{int:project-localized-name-ptwikibooks/en}}{{int:comma-separator/en}}{{int:project-localized-name-sewikimedia/en}}) will soon be contacted. If you have questions about this process, please ping [[m:User:Trizek (WMF)|Trizek (WMF)]] at your wiki.</span> [https://phabricator.wikimedia.org/T380912]
* <span lang="en" dir="ltr" class="mw-content-ltr">The latest quarterly [[mw:Technical_Community_Newsletter/2025/January|Technical Community Newsletter]] is now available. This edition includes: updates about services from the Data Platform Engineering teams, information about Codex from the Design System team, and more.</span>
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/04|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W04"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 21 janvier 2025 à 02:36 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28129769 -->
== Universal Code of Conduct annual review: provide your comments on the UCoC and Enforcement Guidelines ==
<div lang="en" dir="ltr" class="mw-content-ltr">
My apologies for writing in English.
{{Int:Please-translate}}.
I am writing to you to let you know the annual review period for the Universal Code of Conduct and Enforcement Guidelines is open now. You can make suggestions for changes through 3 February 2025. This is the first step of several to be taken for the annual review.
[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find a conversation to join on the UCoC page on Meta]].
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 24 janvier 2025 à 02:10 (CET)
</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=27746256 -->
== Actualités techniques n° 2025-05 ==
<section begin="technews-2025-W05"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/05|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Patrollers and admins - what information or context about edits or users could help you to make patroller or admin decisions more quickly or easily? The Wikimedia Foundation wants to hear from you to help guide its upcoming annual plan. Please consider sharing your thoughts on this and [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Product & Technology OKRs|13 other questions]] to shape the technical direction for next year.</span>
'''Actualités pour la contribution'''
* <span lang="en" dir="ltr" class="mw-content-ltr">iOS Wikipedia App users worldwide can now access a [[mw:Special:MyLanguage/Wikimedia Apps/Team/iOS/Personalized Wikipedia Year in Review/How your data is used|personalized Year in Review]] feature, which provides insights based on their reading and editing history on Wikipedia. This project is part of a broader effort to help welcome new readers as they discover and interact with encyclopedic content.</span>
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] <span lang="en" dir="ltr" class="mw-content-ltr">Edit patrollers now have a new feature available that can highlight potentially problematic new pages. When a page is created with the same title as a page which was previously deleted, a tag ('Recreated') will now be added, which users can filter for in [[{{#special:RecentChanges}}]] and [[{{#special:NewPages}}]].</span> [https://phabricator.wikimedia.org/T56145]
* <span lang="en" dir="ltr" class="mw-content-ltr">Later this week, there will be a new warning for editors if they attempt to create a redirect that links to another redirect (a [[mw:Special:MyLanguage/Help:Redirects#Double redirects|double redirect]]). The feature will recommend that they link directly to the second redirect's target page. Thanks to the user SomeRandomDeveloper for this improvement.</span> [https://phabricator.wikimedia.org/T326056]
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] <span lang="en" dir="ltr" class="mw-content-ltr">Wikimedia wikis allow [[w:en:WebAuthn|WebAuthn]]-based second factor checks (such as hardware tokens) during login, but the feature is [[m:Community Wishlist Survey 2023/Miscellaneous/Fix security key (WebAuthn) support|fragile]] and has very few users. The MediaWiki Platform team is temporarily disabling adding new WebAuthn keys, to avoid interfering with the rollout of [[mw:MediaWiki Platform Team/SUL3|SUL3]] (single user login version 3). Existing keys are unaffected.</span> [https://phabricator.wikimedia.org/T378402]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:30|la tâche soumise|les {{formatnum:30}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:30||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* <span lang="en" dir="ltr" class="mw-content-ltr">For developers that use the [[wikitech:Data Platform/Data Lake/Edits/MediaWiki history dumps|MediaWiki History dumps]]: The Data Platform Engineering team has added a couple of new fields to these dumps, to support the [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|Temporary Accounts]] initiative. If you maintain software that reads those dumps, please review your code and the updated documentation, since the order of the fields in the row will change. There will also be one field rename: in the <bdi lang="zxx" dir="ltr"><code>mediawiki_user_history</code></bdi> dump, the <bdi lang="zxx" dir="ltr"><code>anonymous</code></bdi> field will be renamed to <bdi lang="zxx" dir="ltr"><code>is_anonymous</code></bdi>. The changes will take effect with the next release of the dumps in February.</span> [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/LKMFDS62TXGDN6L56F4ABXYLN7CSCQDI/]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/05|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W05"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 27 janvier 2025 à 23:14 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28149374 -->
== Reminder: first part of the annual UCoC review closes soon ==
<div lang="en" dir="ltr" class="mw-content-ltr">
My apologies for writing in English.
{{Int:Please-translate}}.
This is a reminder that the first phase of the annual review period for the Universal Code of Conduct and Enforcement Guidelines will be closing soon. You can make suggestions for changes through [[d:Q614092|the end of day]], 3 February 2025. This is the first step of several to be taken for the annual review.
[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find a conversation to join on the UCoC page on Meta]]. After review of the feedback, proposals for updated text will be published on Meta in March for another round of community review.
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 3 février 2025 à 01:48 (CET)
</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28198931 -->
== <span lang="en" dir="ltr">Tech News: 2025-06</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W06"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/06|Translations]] are available.
'''Updates for editors'''
* Editors who use the "Special characters" editing-toolbar menu can now see the 32 special characters you have used most recently, across editing sessions on that wiki. This change should help make it easier to find the characters you use most often. The feature is in both the 2010 wikitext editor and VisualEditor. [https://phabricator.wikimedia.org/T110722]
* Editors using the 2010 wikitext editor can now create sublists with correct indentation by selecting the line(s) you want to indent and then clicking the toolbar buttons.[https://phabricator.wikimedia.org/T380438] You can now also insert <code><nowiki><code></nowiki></code> tags using a new toolbar button.[https://phabricator.wikimedia.org/T383010] Thanks to user stjn for these improvements.
* Help is needed to ensure the [[mw:Special:MyLanguage/Citoid/Enabling Citoid on your wiki|citation generator]] works properly on each wiki.
** (1) Administrators should update the local versions of the page <code dir=ltr>MediaWiki:Citoid-template-type-map.json</code> to include entries for <code dir=ltr>preprint</code>, <code dir=ltr>standard</code>, and <code dir=ltr>dataset</code>; Here are example diffs to replicate [https://en.wikipedia.org/w/index.php?title=MediaWiki%3ACitoid-template-type-map.json&diff=1189164774&oldid=1165783565 for 'preprint'] and [https://en.wikipedia.org/w/index.php?title=MediaWiki%3ACitoid-template-type-map.json&diff=1270832208&oldid=1270828390 for 'standard' and 'dataset'].
** (2.1) If the citoid map in the citation template used for these types of references is missing, [[mediawikiwiki:Citoid/Enabling Citoid on your wiki#Step 2.a: Create a 'citoid' maps value for each citation template|one will need to be added]]. (2.2) If the citoid map does exist, the TemplateData will need to be updated to include new field names. Here are example updates [https://en.wikipedia.org/w/index.php?title=Template%3ACitation%2Fdoc&diff=1270829051&oldid=1262470053 for 'preprint'] and [https://en.wikipedia.org/w/index.php?title=Template%3ACitation%2Fdoc&diff=1270831369&oldid=1270829480 for 'standard' and 'dataset']. The new fields that may need to be supported are <code dir=ltr>archiveID</code>, <code dir=ltr>identifier</code>, <code dir=ltr>repository</code>, <code dir=ltr>organization</code>, <code dir=ltr>repositoryLocation</code>, <code dir=ltr>committee</code>, and <code dir=ltr>versionNumber</code>. [https://phabricator.wikimedia.org/T383666]
* One new wiki has been created: a {{int:project-localized-name-group-wikipedia/en}} in [[d:Q15637215|Central Kanuri]] ([[w:knc:|<code>w:knc:</code>]]) [https://phabricator.wikimedia.org/T385181]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:27}} community-submitted {{PLURAL:27|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the [[mediawikiwiki:Special:MyLanguage/Help:Extension:Wikisource/Wikimedia OCR|OCR (optical character recognition) tool]] used for Wikisource now supports a new language, Church Slavonic. [https://phabricator.wikimedia.org/T384782]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/06|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W06"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 4 février 2025 à 01:08 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28203495 -->
== <span lang="en" dir="ltr">Tech News: 2025-07</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W07"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/07|Translations]] are available.
'''Weekly highlight'''
* The Product and Technology Advisory Council (PTAC) has published [[m:Special:MyLanguage/Product and Technology Advisory Council/February 2025 draft PTAC recommendation for feedback|a draft of their recommendations]] for the Wikimedia Foundation's Product and Technology department. They have recommended focusing on [[m:Special:MyLanguage/Product and Technology Advisory Council/February 2025 draft PTAC recommendation for feedback/Mobile experiences|mobile experiences]], particularly contributions. They request community [[m:Talk:Product and Technology Advisory Council/February 2025 draft PTAC recommendation for feedback|feedback at the talk page]] by 21 February.
'''Updates for editors'''
* The "Special pages" portlet link will be moved from the "Toolbox" into the "Navigation" section of the main menu's sidebar by default. This change is because the Toolbox is intended for tools relating to the current page, not tools relating to the site, so the link will be more logically and consistently located. To modify this behavior and update CSS styling, administrators can follow the instructions at [[phab:T385346|T385346]]. [https://phabricator.wikimedia.org/T333211]
* As part of this year's work around improving the ways readers discover content on the wikis, the Web team will be running an experiment with a small number of readers that displays some suggestions for related or interesting articles within the search bar. Please check out [[mw:Special:MyLanguage/Reading/Web/Content Discovery Experiments#Experiment 1: Display article recommendations in more prominent locations, search|the project page]] for more information.
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Advanced item]] Template editors who use TemplateStyles can now customize output for users with specific accessibility needs by using accessibility related media queries (<code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion prefers-reduced-motion]</code>, <code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-transparency prefers-reduced-transparency]</code>, <code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast prefers-contrast]</code>, and <code dir=ltr>[https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors forced-colors]</code>). Thanks to user Bawolff for these improvements. [https://phabricator.wikimedia.org/T384175]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:22}} community-submitted {{PLURAL:22|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the global blocks log will now be shown directly on the {{#special:CentralAuth}} page, similarly to global locks, to simplify the workflows for stewards. [https://phabricator.wikimedia.org/T377024]
'''Updates for technical contributors'''
* Wikidata [[d:Special:MyLanguage/Help:Default values for labels and aliases|now supports a special language as a "default for all languages"]] for labels and aliases. This is to avoid excessive duplication of the same information across many languages. If your Wikidata queries use labels, you may need to update them as some existing labels are getting removed. [https://phabricator.wikimedia.org/T312511]
* The function <code dir="ltr">getDescription</code> was invoked on every Wiki page read and accounts for ~2.5% of a page's total load time. The calculated value will now be cached, reducing load on Wikimedia servers. [https://phabricator.wikimedia.org/T383660]
* As part of the RESTBase deprecation [[mw:RESTBase/deprecation|effort]], the <code dir="ltr">/page/related</code> endpoint has been blocked as of February 6, 2025, and will be removed soon. This timeline was chosen to align with the deprecation schedules for older Android and iOS versions. The stable alternative is the "<code dir="ltr">morelike</code>" action API in MediaWiki, and [[gerrit:c/mediawiki/services/mobileapps/+/982154/13/pagelib/src/transform/FooterReadMore.js|a migration example]] is available. The MediaWiki Interfaces team [[phab:T376297|can be contacted]] for any questions. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/GFC2IJO7L4BWO3YTM7C5HF4MCCBE2RJ2/]
'''In depth'''
* The latest quarterly [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/January|Language and Internationalization newsletter]] is available. It includes: Updates about the "Contribute" menu; details on some of the newest language editions of Wikipedia; details on new languages supported by the MediaWiki interface; updates on the Community-defined lists feature; and more.
* The latest [[mw:Extension:Chart/Project/Updates#January 2025: Better visibility into charts and tabular data usage|Chart Project newsletter]] is available. It includes updates on the progress towards bringing better visibility into global charts usage and support for categorizing pages in the Data namespace on Commons.
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/07|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W07"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 11 février 2025 à 01:11 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28231022 -->
== Actualités techniques n° 2025-08 ==
<section begin="technews-2025-W08"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/08|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Les communautés utilisant les outils de croissance peuvent désormais mettre en avant un évènement pour les nouveaux contributeurs sur la <code>{{#special:Homepage}}</code>. Cette fonctionnalité aidera les nouveaux venus à être informés des activités d'édition auxquels ils peuvent participer. Les administrateurs peuvent ajouter un nouvel évènement à mettre en avant sur <code>{{#special:CommunityConfiguration}}</code>. Pour en apprendre davantage sur cette nouvelle fonctionnalité, vous pouvez lire [[diffblog:2025/02/12/community-updates-module-connecting-newcomers-to-your-initiatives/|l'annonce sur Diff]], la [[mw:Special:MyLanguage/Help:Growth/Tools/Community updates module|documentation]] ou [[mw:Talk:Growth|contacter l'équipe Croissance]].
'''Actualités pour la contribution'''
[[File:Page Frame Features on desktop.png|thumb|Mise en évidence des améliorations aux pages de discussion]]
* À partir de la semaine prochaine, les pages de discussions des wikis suivants recevront [[diffblog:2024/05/02/making-talk-pages-better-for-everyone/|une nouvelle présentation]] : {{int:project-localized-name-eswiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-frwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-itwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-jawiki/fr}}. Ce changement a été largement testé en tant que fonctionnalité beta et il s'agit de la dernière étape des [[mw:Special:MyLanguage/Talk pages project/Feature summary|améliorations aux pages de discussion]]. [https://phabricator.wikimedia.org/T379102]
* Vous pouvez désormais visualiser une page de redirection directement depuis ses pages d'action, comme la page d'historique. Auparavant, vous étiez automatiquement redirigé vers la page cible et deviez manuellement revenir à la page de redirection. Ce changement devrait aider les rédacteurs travaillant avec les redirections. Merci à stjn pour cette amélioration. [https://phabricator.wikimedia.org/T5324]
* Quand une référence est utilisée de nombreuses fois, les wikis affichent actuellement des nombres comme 1.23 ou des marqueurs avec des lettres comme a, b, c dans la liste de références. Avant, quand le nombre de références était trop important et que toutes les lettres avaient été utilisées, un [[MediaWiki:Cite error references no backlink label|message d'erreur]] était affiché. Dans le cadre des travaux pour la [[phab:T383036|modernisation de la personnalisation des références]], ces erreurs ne seront plus affichées et des marqueurs numériques comme 1.27 seront utilisés par défaut après épuisement des marqueurs alphabétiques.
* Les entrées de journal pour chaque changement aux groupes utilisateur d'un éditeur ont été clarifiés pour indiquer exactement ce qui a été modifié. Elles contenaient auparavant les deux listes des groupes avant et après le changement. Les traducteurs sont invités à [[phab:T369466|aider à traduire les messages système associés]]. Merci à Msz2001 pour ces améliorations.
* Un nouveau filtre a été ajouté à [[{{#special:Nuke}}]] — outil permettant aux administrateurs de supprimer en masse des pages — pour permettre aux utilisateurs de filtrer les pages en fonction de leur taille en octets. Cela permet par exemple de supprimer uniquement des pages inférieures à une certaine taille. [https://phabricator.wikimedia.org/T378488]
* Les non-administrateurs peuvent maintenant voir quelles pages peuvent être supprimées à l'aide de [[{{#special:Nuke}}]]. Merci à MolecularPilot pour cette amélioration et les précédentes. [https://phabricator.wikimedia.org/T376378]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:25|la tâche soumise|les {{formatnum:25}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:25||s}} la semaine dernière]]. Par exemple, un bug a été corrigé dans la configuration du format de fichier vidéo AV1, ce qui permet à ces fichiers d'être lus à nouveau. [https://phabricator.wikimedia.org/T382193]
'''Actualités pour la contribution technique'''
* Parsoid Read Views sera déployé sur la plupart des Wiktionnaires dans les prochaines semaines, à la suite de la transition avec succès des Wikivoyage à Parsoid l'année dernière. Pour davantage d'informations, voir la page du projet [[mw:Special:MyLanguage/Parsoid/Parser Unification|Parsoid/Parser Unification]]. [https://phabricator.wikimedia.org/T385923][https://phabricator.wikimedia.org/T371640]
* Les développeurs d'outils sur wiki sont informés que <code dir=ltr>mw.Uri</code> est obsolète. Les outils nécessitant <code dir=ltr>mw.Uri</code> doivent déclarer explicitement <code dir=ltr>mediawiki.Uri</code> comme une dépendance de ResourceLoader, et devraient migrer vers l'API <code dir=ltr>URL</code> native du navigateur prochainement. [https://phabricator.wikimedia.org/T384515]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/08|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W08"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 17 février 2025 à 22:16 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28275610 -->
== <span lang="en" dir="ltr"> Upcoming Language Community Meeting (Feb 28th, 14:00 UTC) and Newsletter</span> ==
<div lang="en" dir="ltr">
<section begin="message"/>
Hello everyone!
[[File:WP20Symbols WIKI INCUBATOR.svg|right|frameless|150x150px|alt=An image symbolising multiple languages]]
We’re excited to announce that the next '''Language Community Meeting''' is happening soon, '''February 28th at 14:00 UTC'''! If you’d like to join, simply sign up on the '''[[mw:Wikimedia_Language_and_Product_Localization/Community_meetings#28_February_2025|wiki page]]'''.
This is a participant-driven meeting where we share updates on language-related projects, discuss technical challenges in language wikis, and collaborate on solutions. In our last meeting, we covered topics like developing language keyboards, creating the Moore Wikipedia, and updates from the language support track at Wiki Indaba.
'''Got a topic to share?''' Whether it’s a technical update from your project, a challenge you need help with, or a request for interpretation support, we’d love to hear from you! Feel free to '''reply to this message''' or add agenda items to the document '''[[etherpad:p/language-community-meeting-feb-2025|here]]'''.
Also, we wanted to highlight that the sixth edition of the Language & Internationalization newsletter (January 2025) is available here: [[:mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/January|Wikimedia Language and Product Localization/Newsletter/2025/January]]. This newsletter provides updates from the October–December 2024 quarter on new feature development, improvements in various language-related technical projects and support efforts, details about community meetings, and ideas for contributing to projects. To stay updated, you can subscribe to the newsletter on its wiki page: [[:mw:Wikimedia Language and Product Localization/Newsletter|Wikimedia Language and Product Localization/Newsletter]].
We look forward to your ideas and participation at the language community meeting, see you there!
<section end="message"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 22 février 2025 à 09:28 (CET)
<!-- Message envoyé par User:SSethi (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28217779 -->
== Actualités techniques n° 2025-09 ==
<section begin="technews-2025-W09"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/09|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les administrateurs peuvent désormais personnaliser la manière dont les catégories de [[m:Special:MyLanguage/User language|Babel]] sont créées en utilisant [[{{#special:CommunityConfiguration/Babel}}]]. Ils peuvent renommer les catégories de langues, choisir si elles doivent être créées automatiquement et ajuster d'autres paramètres. [https://phabricator.wikimedia.org/T374348]
* Le portail <bdi lang="en" dir="ltr">[https://www.wikimedia.org/ wikimedia.org]</bdi> a été mis à jour pour moderniser et améliorer l'accessibilité de nos pages de portail. Il dispose désormais d'un meilleur support pour les mises en page mobiles, de meilleures formulations et liens et d'un support linguistique amélioré. De plus, tous les portails du projet Wikimedia, comme <bdi lang="en" dir="ltr">[https://wikibooks.org wikibooks.org]</bdi>, prennent maintenant en charge le mode sombre lorsqu'un lecteur utilise ce paramètre système. [https://phabricator.wikimedia.org/T373204][https://phabricator.wikimedia.org/T368221][https://meta.wikimedia.org/wiki/Project_portals]
* Un nouveau wiki a été créé : un {{int:project-localized-name-group-wiktionary/fr}} en [[d:Q33965|Santali]] ([[wikt:sat:|<code>wikt:sat:</code>]]) [https://phabricator.wikimedia.org/T386619]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:30|la tâche soumise|les {{formatnum:30}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:30||s}} la semaine dernière]]. Par exemple, un bogue qui empêchait de cliquer sur les résultats de recherche de l'interface web sur certaines configurations mobiles avec Firefox a été corrigé. [https://phabricator.wikimedia.org/T381289]
'''Rencontres et évènements'''
* La prochaine rencontre de la communauté linguistique aura lieu le 28 février à [https://zonestamp.toolforge.org/1740751200 14:00 UTC]. La rencontre de cette semaine couvrira : les points importants et mises-à-jour techniques pour les langues samis, les contributions à translatewiki.net de la part de la communauté Bahasa Lampung en Indonésie et une FAQ technique. Si vous souhaitez participer, inscrivez-vous sur la [[mw:Wikimedia Language and Product Localization/Community meetings#28 February 2025|page wiki]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/09|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W09"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 25 février 2025 à 01:41 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28296129 -->
== Actualités techniques n° 2025-10 ==
<section begin="technews-2025-W10"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/10|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les utilisateurs et utilisatrices connectés utilisant l’affichage mobile peuvent désormais modifier une page complète. Le lien « {{int:Minerva-page-actions-editfull}} » est accessible dans le menu « {{int:minerva-page-actions-overflow}} » de la barre d’outils. Ce lien était auparavant disponible uniquement lorsque le [[mw:Special:MyLanguage/Reading/Web/Advanced mobile contributions|mode avancé]] était activé. [https://phabricator.wikimedia.org/T387180]
* Les admins d’interface peuvent désormais retirer les utilisations de la classe CSS « <code dir="ltr">mw-ref</code> » de leur <bdi lang="en" dir="ltr">[[MediaWiki:Common.css]]</bdi> local. Cette classe de l’extension <span lang="en">Cite</span> est obsolète. La liste des wikis l’utilisant peut être trouvée par [https://global-search.toolforge.org/?q=mw-ref%5B%5E-a-z%5D®ex=1&namespaces=8&title=.*css cette recherche globale] et dans [https://ace.wikipedia.org/w/index.php?title=MediaWiki:Common.css&oldid=145662#L-139--L-144 cet exemple]. D’autres informations sur les manières d’aider sont données sur la [[mw:Parsoid/Parser Unification/Cite CSS|page du projet de migration du CSS]]. Les appels de note (<code dir="ltr">[1]</code>) sont désormais rendus par [[mw:Special:MyLanguage/Parsoid|Parsoid]] ; le CSS obsolète n’est plus nécessaire. Le CSS pour les rétroliens « <code dir="ltr">mw:referencedBy</code> » doit rester en place pour le moment. Ce nettoyage ne devrait pas avoir d’effet visible pour les lecteurs et lectrices. Merci d’aider à retirer ce code avant le 30 mars, après quoi l’équipe de développement le fera pour vous.
* Lorsque les contributeurs ajoutent un fichier (par exemple <code><nowiki>[[File:MediaWiki.png]]</nowiki></code>) sur une page protégée par une protection en cascade, le logiciel ne restreindra plus les modifications à la page de description du fichier, mais uniquement aux nouveaux téléchargements de fichiers. [https://phabricator.wikimedia.org/T24521] A l’inverse, la transclusion d’une page de description de fichier (par exemple <code><nowiki>{{:File:MediaWiki.png}}</nowiki></code>) dans une page protégée en cascade provoquera désormais une restriction des modifications à la page.[https://phabricator.wikimedia.org/T62109]
* Remettre un fichier dans une version antérieure nécessitera désormais les mêmes autorisations que le téléchargement d'une nouvelle version du fichier. Le logiciel vérifie désormais la possession des droits « <i lang="en">reupload</i> » ou « <i lang="en">reuplod-own</i> » [https://phabricator.wikimedia.org/T304474], et respecte la protection en cascade. [https://phabricator.wikimedia.org/T140010]
* Lorsque les admins listent des pages à supprimer avec l’outil Nuke, ils peuvent désormais également répertorier les pages de discussion associées et les redirections à supprimer, en plus des pages créées par la cible, plutôt que de devoir supprimer manuellement ces pages. [https://phabricator.wikimedia.org/T95797]
* La mise à jour [[m:Special:MyLanguage/Tech/News/2025/03|mentionnée précédemment]] de la connexion utilisateur unifiée, qui prendra en compte les restrictions du navigateur sur les cookies inter-domaines en déplaçant la connexion et la création de compte vers un domaine central, sera déployée pour tous les utilisateurs et utilisatrices en mars et avril. L'équipe prévoit de l'activer pour toutes les nouvelles créations de compte sur les wikis du [[wikitech:Deployments/Train#Tuesday|groupe 0]] cette semaine. Consultez la [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3#Deployment|page du projet SUL3]] pour plus de détails et un calendrier mis à jour.
* Depuis la semaine dernière, un bogue cause l'affichage de certaines icônes d'interface sous forme de carrés noirs jusqu'à ce que la page soit entièrement chargée. Cela sera corrigé cette semaine. [https://phabricator.wikimedia.org/T387351]
* Un nouveau wiki a été créé : une {{int:project-localized-name-group-wikipedia/fr}} en [[d:Q2044560|sylheti]] ([[w:syl:|<code>w:syl:</code>]]) [https://phabricator.wikimedia.org/T386441]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]]. Par exemple, un bogue avec le chargement d'images dans de très anciennes versions du navigateur Firefox sur mobile a été corrigé. [https://phabricator.wikimedia.org/T386400]
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.19|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/10|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W10"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 4 mars 2025 à 03:30 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28334563 -->
== Universal Code of Conduct annual review: proposed changes are available for comment ==
<div lang="en" dir="ltr" class="mw-content-ltr">
My apologies for writing in English.
{{Int:Please-translate}}.
I am writing to you to let you know that [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/Proposed_Changes|proposed changes]] to the [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Universal Code of Conduct (UCoC) Enforcement Guidelines]] and [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|Universal Code of Conduct Coordinating Committee (U4C) Charter]] are open for review. '''[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/Proposed_Changes|You can provide feedback on suggested changes]]''' through the [[d:Q614092|end of day]] on Tuesday, 18 March 2025. This is the second step in the annual review process, the final step will be community voting on the proposed changes.
[[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review|Read more information and find relevant links about the process on the UCoC annual review page on Meta]].
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] (U4C) is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|you may review the U4C Charter]].
Please share this information with other members in your community wherever else might be appropriate.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] 7 mars 2025 à 19:50 (CET)
</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28307738 -->
== Actualités techniques n° 2025-11 ==
<section begin="technews-2025-W11"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/11|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Les contributeurs qui utilisent des gestionnaires de mots de passe sur plusieurs wikis peuvent remarquer des changements à l’avenir. La manière dont nos wikis fournissent des informations aux gestionnaires de mots de passe sur la réutilisation des mots de passe entre les domaines a été récemment mise à jour, de sorte que certains gestionnaires de mots de passe peuvent maintenant vous proposer des identifiants de connexion que vous avez sauvegardés pour un autre site Wikimedia. Certains gestionnaires de mots de passe l'ont déjà fait, et le font maintenant pour d'autres domaines de Wikimedia. Cela fait partie du projet [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3|SUL3]] qui vise à améliorer le fonctionnement de notre connexion unifiée et à la rendre compatible avec les changements en cours dans les navigateurs web que nous utilisons. [https://phabricator.wikimedia.org/T385520][https://phabricator.wikimedia.org/T384844]
* L'équipe des applications Wikipédia invite les utilisateurs intéressés à contribuer à l'amélioration de l'utilisation de Wikipédia hors ligne ou en internet limité. Après les discussions de [[m:Afrika Baraza|Afrika Baraza]] et la dernière [[m:Special:MyLanguage/ESEAP Hub/Meetings|conférence ESEAP]], des défis clés comme la recherche, la modification et l'accès hors ligne sont explorés, avec des groupes de discussion à venir pour approfondir ces sujets. Toutes les langues sont les bienvenues et des interprètes seront disponibles. Vous souhaitez partager vos idées ? [[mw:Special:MyLanguage/Wikimedia Apps/Improving Wikipedia Mobile Apps for Offline & Limited Internet Use|Participez à la discussion]] ou envoyez un courriel à <bdi lang="en" dir="ltr">aramadan@wikimedia.org</bdi> !
* Tous les wikis seront en lecture seule pendant quelques minutes le 19 mars, à [https://zonestamp.toolforge.org/1742392800 14 h UTC]. De plus amples informations seront publiées dans les ''Actualités techniques'' et seront également publiées sur chaque wiki dans les semaines à venir.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.20|MediaWiki]]
'''En détails'''
* La dernière [[mw:Special:MyLanguage/Growth/Newsletters/33|infolettre trimestrielle du département Croissance]] est disponible. Elle présente : le lancement du module Actualités de la communauté, les dernières modifications de la configuration communautaire et le test à venir des suggestions d'articles pour les personnes contribuant pour la première fois.
* Une ancienne API utilisée dans l'application Android Wikipedia sera supprimée à la fin du mois de mars. Il n'y a pas d'utilisation logicielle en cours, mais les utilisateurs de l'application dont la version date de plus de 6 mois au moment de la suppression (2025-03-31), n'auront plus accès à la fonction Suggested Edits, jusqu'à ce qu'ils mettent à jour leur application. Vous pouvez [[diffblog:2025/02/24/sunset-of-wikimedia-recommendation-api/|lire plus de détails sur ce changement]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/11|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W11"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 11 mars 2025 à 00:09 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28372257 -->
== Votre wiki sera bientôt en lecture seule ==
<section begin="server-switch"/><div class="plainlinks">
[[:m:Special:MyLanguage/Tech/Server switch|Lire ce message dans une autre langue]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-Tech%2FServer+switch&language=&action=page&filter= {{int:please-translate}}]
La [[foundation:|Fondation Wikimedia]] va basculer le trafic entre ses centres de données. Cela permettra de s’assurer que Wikipédia et les autres wikis de Wikimedia peuvent rester en ligne même après une catastrophe.
Le trafic sera basculé le '''{{#time:j xg|2025-03-19|fr}}'''. La bascule débutera à '''[https://zonestamp.toolforge.org/{{#time:U|2025-03-19T14:00|en}} {{#time:H:i e|2025-03-19T14:00}}]'''.
Malheureusement, en raison de certaines limites de [[mw:Special:MyLanguage/Manual:What is MediaWiki?|MediaWiki]], toutes les modifications de pages devront être arrêtées durant le passage d’un centre de données à l’autre. Nous nous excusons pour ce dérangement, que nous nous efforçons de réduire pour le futur.
Une bannière sera affichée sur tous les wikis 30 minutes avant le début de l’opération. Cette bannière restera visible jusqu’à la fin de l’opération.
'''Pendant une courte période, vous pourrez lire les wikis mais pas les modifier.'''
*Vous ne pourrez pas effectuer de modification pendant une durée pouvant aller jusqu’à une heure, le {{#time:l j xg Y|2025-03-19|fr}}.
*Si vous essayez de faire une modification ou de sauvegarder pendant cette période, vous verrez un message d’erreur. Nous espérons qu’aucune modification ne sera perdue durant ce temps, mais nous ne pouvons le garantir. Si vous voyez un message d’erreur, merci de patienter jusqu’au retour à la normale. Vous pourrez alors enregistrer votre modification. Nous vous conseillons cependant de faire une copie de votre modification avant, au cas où.
''Autres conséquences :''
*Les tâches de fond seront ralenties et certaines pourraient être stoppées. Les liens rouges ne seront pas mis à jour aussi vite que d’habitude. Si vous créez un article qui est déjà lié depuis une autre page, le lien rouge pourrait rester rouge plus longtemps que d’habitude. Certains scripts ayant un long temps d’exécution devront être stoppés.
* Le déploiement de code devrait se dérouler comme chaque semaine. Cependant, certains codes particuliers pourraient être gelés si l’opération le nécessitait.
* [[mw:Special:MyLanguage/GitLab|GitLab]] sera indisponible durant environ 90 minutes.
Ce projet pourra être reporté si nécessaire. Vous pouvez [[wikitech:Switch_Datacenter|consulter le calendrier sur wikitech.wikimedia.org]]. Tout changement sera annoncé dans le calendrier.
'''Merci de partager ces informations avec votre communauté.'''</div><section end="server-switch"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 15 mars 2025 à 00:14 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Non-Technical_Village_Pumps_distribution_list&oldid=28307742 -->
== <span lang="en" dir="ltr">Tech News: 2025-12</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W12"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/12|Translations]] are available.
'''Weekly highlight'''
* Twice a year, around the equinoxes, the Wikimedia Foundation's Site Reliability Engineering (SRE) team performs [[m:Special:MyLanguage/Tech/Server switch|a datacenter server switchover]], redirecting all traffic from one primary server to its backup. This provides reliability in case of a crisis, as we can always fall back on the other datacenter. [http://listen.hatnote.com/ Thanks to the Listen to Wikipedia] tool, you can hear the switchover take place: Before it begins, you'll hear the steady stream of edits; Then, as the system enters a brief read-only phase, the sound stops for a couple of minutes, before resuming after the switchover. You can [[diffblog:2025/03/12/hear-that-the-wikis-go-silent-twice-a-year/|read more about the background and details of this process on the Diff blog]]. If you want to keep an ear out for the next server switchover, listen to the wikis on [https://zonestamp.toolforge.org/1742392800 March 19 at 14:00 UTC].
'''Updates for editors'''
* The [https://test.wikipedia.org/w/index.php?title=Special:ContentTranslation&filter-type=automatic&filter-id=previous-edits&active-list=suggestions&from=en&to=es improved Content Translation tool dashboard] is now available in [[phab:T387820|10 Wikipedias]] and will be available for all Wikipedias [[phab:T387821|soon]]. With [[mw:Special:MyLanguage/Content translation#Improved translation experience|the unified dashboard]], desktop users can now: Translate new sections of an article; Discover and access topic-based [https://ig.m.wikipedia.org/w/index.php?title=Special:ContentTranslation&active-list=suggestions&from=en&to=ig&filter-type=automatic&filter-id=previous-edits article suggestion filters] (initially available only for mobile device users); Discover and access the [[mw:Special:MyLanguage/Translation suggestions: Topic-based & Community-defined lists|Community-defined lists]] filter, also known as "Collections", from wiki-projects and campaigns.
* On Wikimedia Commons, a [[c:Commons:WMF support for Commons/Upload Wizard Improvements#Improve category selection|new system to select the appropriate file categories]] has been introduced: if a category has one or more subcategories, users will be able to click on an arrow that will open the subcategories directly within the form, and choose the correct one. The parent category name will always be shown on top, and it will always be possible to come back to it. This should decrease the amount of work for volunteers in fixing/creating new categories. The change is also available on mobile. These changes are part of planned improvements to the UploadWizard.
* The Community Tech team is seeking wikis to join a pilot for the [[m:Special:MyLanguage/Community Wishlist Survey 2023/Multiblocks|Multiblocks]] feature and a refreshed Special:Block page in late March. Multiblocks enables administrators to impose multiple different types of blocks on the same user at the same time. If you are an admin or steward and would like us to discuss joining the pilot with your community, please leave a message on the [[m:Talk:Community Wishlist Survey 2023/Multiblocks|project talk page]].
* Starting March 25, the Editing team will test a new feature for Edit Check at [[phab:T384372|12 Wikipedias]]: [[mw:Special:MyLanguage/Help:Edit check#Multi-check|Multi-Check]]. Half of the newcomers on these wikis will see all [[mw:Special:MyLanguage/Help:Edit check#ref|Reference Checks]] during their edit session, while the other half will continue seeing only one. The goal of this test is to see if users are confused or discouraged when shown multiple Reference Checks (when relevant) within a single editing session. At these wikis, the tags used on edits that show References Check will be simplified, as multiple tags could be shown within a single edit. Changes to the tags are documented [[phab:T373949|on Phabricator]]. [https://phabricator.wikimedia.org/T379131]
* The [[m:Special:MyLanguage/Global reminder bot|Global reminder bot]], which is a service for notifying users that their temporary user-rights are about to expire, now supports using the localized name of the user-rights group in the message heading. Translators can see the [[m:Global reminder bot/Translation|listing of existing translations and documentation]] to check if their language needs updating or creation.
* The [[Special:GlobalPreferences|GlobalPreferences]] gender setting, which is used for how the software should refer to you in interface messages, now works as expected by overriding the local defaults. [https://phabricator.wikimedia.org/T386584]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:26}} community-submitted {{PLURAL:26|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the Wikipedia App for Android had a bug fixed for when a user is browsing and searching in multiple languages. [https://phabricator.wikimedia.org/T379777]
'''Updates for technical contributors'''
* Later this week, the way that Codex styles are loaded will be changing. There is a small risk that this may result in unstyled interface message boxes on certain pages. User generated content (e.g. templates) is not impacted. Gadgets may be impacted. If you see any issues [[phab:T388847|please report them]]. See the linked task for details, screenshots, and documentation on how to fix any affected gadgets.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.44/wmf.21|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/12|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W12"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 18 mars 2025 à 00:48 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28412594 -->
== Actualités techniques n° 2025-13 ==
<section begin="technews-2025-W13"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/13|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* La Fondation Wikimédia souhaite recueillir vos commentaires sur les [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Product & Technology OKRs|ébauches des objectifs et des résultats-clés qui façonneront les priorités de la Fondation en matière de produits et de technologies]] pour la prochaine année fiscale (commençant en juillet). Les objectifs sont des domaines généraux et les résultats-clés permettent de mesurer leur réalisation. N'hésitez pas à partager vos commentaires sur la page de discussion, dans n'importe quelle langue, idéalement avant fin avril.
'''Actualités pour la contribution'''
* L'extension [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|CampaignEvents]] sera déployée sur plusieurs wikis (voir le [[m:Special:MyLanguage/CampaignEvents/Deployment status#Global Deployment Plan|plan de déploiement]] pour plus de détails) en avril 2025, et l'équipe a commencé le processus d'engagement des communautés sur les wikis identifiés. L'extension fournit des outils pour organiser, gérer et promouvoir des activités collaboratives (comme des événements, des edit-a-thons et des WikiProjects) sur les wikis. L'extension comporte trois outils : [[m:Special:MyLanguage/Event Center/Registration|Inscription à l'événement]], [[m:Special:MyLanguage/CampaignEvents/Collaboration list|Liste de collaboration]] et [[m:Special:MyLanguage/Campaigns/Foundation Product Team/Invitation list|Liste d'invitation]]. Elle est actuellement présente sur 13 Wikipédias, dont la Wikipédia en anglais, la Wikipédia en français et la Wikipédia en espagnol, ainsi que sur Wikidata. Les questions ou demandes peuvent être adressées sur la [[mw:Help talk:Extension:CampaignEvents|page de discussion de l'extension]] ou sur Phabricator (avec l'étiquette <bdi lang="en" dir="ltr" style="white-space: nowrap;">#campaigns-product-team</bdi>).
* À partir de la semaine du 31 mars, les wikis pourront définir quels groupes d'utilisateurs peuvent voir les inscriptions privées dans [[m:Special:MyLanguage/Event Center/Registration|Inscription à l'événement]], dans le cadre de l'extension [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|CampaignEvents]]. Par défaut, les organisateurs d'événements et les administrateurs du wiki local pourront voir les inscriptions privées. Il s'agit d'un changement par rapport au réglage actuel qui permet seulement aux organisateurs de l'événement de voir les inscriptions privées. Les wikis peuvent modifier la configuration par défaut en demandant un changement de configuration dans Phabricator (et en ajoutant l'étiquette <bdi lang="en" dir="ltr" style="white-space: nowrap;">#campaigns-product-team</bdi>). Les participants aux événements passés peuvent annuler leur inscription à tout moment.
* Les administrateurs des wikis qui disposent d'une barre latérale <bdi lang="en" dir="ltr">[[MediaWiki:Sidebar]]</bdi> personnalisée doivent vérifier si elle contient une entrée pour la liste {{int:specialpages}}. Si ce n'est pas le cas, ils doivent l'ajouter en utilisant <code dir=ltr style="white-space: nowrap;">* specialpages-url|specialpages</code>. Les wikis disposant d'une barre latérale par défaut verront le lien déplacé de la boîte à outils de la page vers le menu de la barre latérale en avril. [https://phabricator.wikimedia.org/T388927]
* L'habillage Minerva (web mobile) combine les notifications d'avis et d'alertes dans l'icône de cloche ([[File:OOjs UI icon bell.svg|16px|link=|class=skin-invert]]). Il existait depuis longtemps un bogue qui faisait qu'une indication de nouvelles notifications n'était affichée que si vous aviez des alertes que vous n'avez pas vues. Ce problème est désormais résolu. À l'avenir, les utilisateurs de Minerva remarqueront un compteur au-dessus de l'icône de la cloche lorsque vous avez un ou plusieurs notifications et/ou alertes non vues. [https://phabricator.wikimedia.org/T344029]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:23|la tâche soumise|les {{formatnum:23}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:23||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* VisualEditor a introduit un [[mw:VisualEditor/Hooks|nouveau hook côté client]] pour les développeurs à utiliser lors de l'intégration avec le cycle de vie de la cible VisualEditor. Ce hook devrait remplacer les hooks existants liés au cycle de vie et être plus cohérent entre les différentes plateformes. De plus, le nouveau hook s'appliquera aux utilisations de VisualEditor en dehors de l'édition complète d'articles, permettant aux gadgets d'interagir avec l'éditeur dans DiscussionTools également. L'équipe d'édition a l'intention de déprécier et éventuellement de supprimer les hooks de l'ancien cycle de vie, donc tous les cas d'utilisation que ce nouveau hook ne couvre pas seraient intéressants pour l'équipe et peuvent être [[phab:T355555|partagés dans la tâche]].
* Les développeurs qui utilisent la bibliothèque JavaScript <code dir=ltr>mw.Api</code> peuvent désormais identifier l'outil qui l'utilise avec le paramètre <code dir=ltr>userAgent</code> : <code dir=ltr>var api = new mw.Api( { userAgent: 'GadgetNameHere/1.0.1' } );</code>. Si vous gérez un gadget ou un script utilisateur, veuillez définir un agent utilisateur, car cela facilite la maintenance de la bibliothèque et du serveur et permet de différencier le trafic légitime du trafic illégitime. [https://phabricator.wikimedia.org/T373874][https://foundation.wikimedia.org/wiki/Policy:Wikimedia_Foundation_User-Agent_Policy]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.22|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/13|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W13"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 24 mars 2025 à 23:42 (CET)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28443127 -->
== Actualités techniques n° 2025-14 ==
<section begin="technews-2025-W14"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/14|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* L'équipe Contribution travaille sur une nouvelle [[mw:Special:MyLanguage/Edit Check|vérification des modifications]] : le [[mw:Special:MyLanguage/Edit check#26 March 2025|contrôle des éloges]]. L'objectif de cette vérification est d’identifier les termes non neutres saisis lors de la modification d’une page Wikipédia, afin d’informer l’auteur ou autrice que son texte devrait peut-être être modifié avant publication. Ce projet n’en est qu’à ses débuts ; l’équipe a besoin de l’avis des communautés. Dans [[phab:T389445|cette tâche Phabricator]], l’équipe rassemble les recommendations internes des wikis, les modèles utilisés pour étiqueter les articles non neutres et les termes (jargon et mots-clés) utilisés dans les résumés de modification pour les langues étudiées actuellement. Vous pouvez participer en modifiant le tableau sur Phabricator, en commentant la tâche ou en envoyant directement un message à [[m:user:Trizek (WMF)|Trizek (WMF)]].
* La [[mw:Special:MyLanguage/MediaWiki Platform Team/SUL3|connexion utilisateur unique]] (SUL) a été mise à jour sur tous les wikis afin de déplacer la connexion et la création de compte vers un domaine central. Cela rend la connexion des contributeurs compatible avec les restrictions des navigateurs sur les cookies inter-domaines, qui ont empêché les utilisateurs de certains navigateurs de rester connectés.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:35|la tâche soumise|les {{formatnum:35}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:35||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* À partir du 31 mars, l'équipe MediaWiki Interfaces va lancer une version limitée des spécifications OpenAPI générées et une expérience de bac à sable basée sur SwaggerUI pour [[mw:Special:MyLanguage/API:REST API|MediaWiki REST APIs]]. L'équipe invite les développeurs d'un groupe limité de communautés Wikipédia non anglophones (arabe, allemand, français, hébreu, interlingua, néerlandais, chinois) à consulter la documentation et à expérimenter le bac à sable dans leur langue de choix. En plus de ces projets Wikipédia spécifiques, le bac à sable et la spécification OpenAPI seront disponibles sur la [[testwiki:Special:RestSandbox|page spéciale test wiki REST Sandbox]] pour les développeurs dont l'anglais est la langue préférée. Pendant la période de prévisualisation, l'équipe MediaWiki Interfaces invite également les développeurs à [[mw:MediaWiki Interfaces Team/Feature Feedback/REST Sandbox|partager leur retour d'expérience]]. L'aperçu durera environ 2 semaines, après quoi le bac à sable et les spécifications OpenAPI seront mis à la disposition de tous les projets wiki.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.23|MediaWiki]]
'''En détails'''
* Parfois, un petit changement de code d'une ligne peut avoir une grande importance : dans ce cas, cela signifie que pour la première fois depuis des années, nous sommes en mesure de faire fonctionner toute la pile qui sert <bdi lang="en" dir="ltr">[http://maps.wikimedia.org/ maps.wikimedia.org]</bdi> - un hôte dédié à servir nos wikis et leurs besoins en cartes multilingues - à partir d'un seul centre de données, ce que nous testons à chaque fois que nous effectuons un [[m:Special:MyLanguage/Tech/Server switch|basculement de centre de données]]. C'est important car cela signifie que si l'un de nos centres de données est affecté par une catastrophe, nous serons toujours en mesure de servir le site. Ce changement est le résultat d'un [[phab:T216826|travail intensif]] de deux développeurs sur le portage du dernier composant de la pile de cartes sur [[w:fr:Kubernetes|kubernetes]], où nous pouvons allouer des ressources plus efficacement qu'auparavant, ce qui nous permet de supporter plus de trafic dans un seul centre de données. Ce travail a nécessité beaucoup d'étapes compliquées car ce logiciel et les bibliothèques logicielles qu'il utilise nécessitaient de nombreuses mises à jour attendues depuis longtemps. Ce type de travail rend l'infrastructure de Wikimedia plus durable.
'''Rencontres et évènements'''
* La [[mw:Special:MyLanguage/MediaWiki Users and Developers Workshop Spring 2025|Conférence des utilisateurs et développeurs de MediaWiki printemps 2025]] se déroulera à Sandusky, aux États-Unis, et en ligne, du 14 au 16 mai 2025. La conférence proposera des discussions autour de l'utilisation du logiciel MediaWiki par et au sein d'entreprises de différents secteurs, et inspirera et embarquera de nouveaux utilisateurs. L'inscription et l'enregistrement des présentations sont maintenant disponibles sur le site web de la conférence.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/14|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W14"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 1 avril 2025 à 02:05 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28473566 -->
== Final proposed modifications to the Universal Code of Conduct Enforcement Guidelines and U4C Charter now posted ==
<div lang="en" dir="ltr" class="mw-content-ltr">
The proposed modifications to the [[foundation:Special:MyLanguage/Policy:Universal_Code_of_Conduct/Enforcement_guidelines|Universal Code of Conduct Enforcement Guidelines]] and the U4C Charter [[m:Universal_Code_of_Conduct/Annual_review/2025/Proposed_Changes|are now on Meta-wiki for community notice]] in advance of the voting period. This final draft was developed from the previous two rounds of community review. Community members will be able to vote on these modifications starting on 17 April 2025. The vote will close on 1 May 2025, and results will be announced no later than 12 May 2025. The U4C election period, starting with a call for candidates, will open immediately following the announcement of the review results. More information will be posted on [[m:Special:MyLanguage//Universal_Code_of_Conduct/Coordinating_Committee/Election|the wiki page for the election]] soon.
Please be advised that this process will require more messages to be sent here over the next two months.
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review was planned and implemented by the U4C. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|review the U4C Charter]].
Please share this message with members of your community so they can participate as well.
-- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|talk]]) 4 avril 2025 à 04:04 (CEST)
</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28469465 -->
== Actualités techniques n° 2025-15 ==
<section begin="technews-2025-W15"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/15|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* Désormais, les [[m:Special:MyLanguage/Interface administrators|admins d’interface]] et [[m:Special:MyLanguage/Central notice administrators|admins des annonces centrales]] sont contraint techniquement d’activer l’[[m:Special:MyLanguage/Help:Two-factor authentication|authentification à deux facteurs]] avant de pouvoir utiliser leurs privilèges. À l’avenir, cela pourrait être étendu à d’autres groupes ayant des droits avancés. [https://phabricator.wikimedia.org/T150898]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:20|la tâche soumise|les {{formatnum:20}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:20||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* L’équipe Système design prépare la sortie de la nouvelle version majeur de Codex (v2.0.0) pour le 29 avril. Les contributeurices et développeurs et développeuses qui utilisent du CSS de Codex devraient consulter la [[mw:Codex/Release Timeline/2.0|documentation sur l’arrivée de la v2]], elle inclut un guidage pour les ruptures introduites dans cette version, par exemple pour <code dir=ltr style="white-space: nowrap;">font-size</code>, <code dir=ltr style="white-space: nowrap;">line-height</code> et <code dir=ltr style="white-space: nowrap;">size-icon</code>.
* Les résultats de [[mw:Developer Satisfaction Survey/2025|l’enquête 2025 sur la satisfaction des développeurs et développeuses]] est désormais disponible. Merci à tous les participants ! Ces résultats aident Wikimedia à décider ce sur quoi orienter le travail et à évaluer le travail récent.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.24|MediaWiki]]
'''Rencontres et évènements'''
* Le [[mw:Special:MyLanguage/Wikimedia Hackathon 2025|Hackathon Wikimedia 2025]] aura lieu à Istanbul en Turquie, du 2 au 4 mai. Les inscriptions pour participer en présentiel ont lieu jusqu’au 13 avril. Avant de vous inscrire, sachez qu’il vous faudra peut-être un [https://www.mfa.gov.tr/turkish-representations.en.mfa visa] ou un [https://www.mfa.gov.tr/visa-information-for-foreigners.en.mfa e-visa] pour entrer dans le pays.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/15|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W15"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 7 avril 2025 à 20:52 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28507470 -->
== Wikidata and Sister Projects: An online community event ==
''(Apologies for posting in English)''
Hello everyone, I am excited to share news of an upcoming online event called '''[[d:Event:Wikidata_and_Sister_Projects|Wikidata and Sister Projects]]''' celebrating the different ways Wikidata can be used to support or enhance with another Wikimedia project. The event takes place over 4 days between '''May 29 - June 1st, 2025'''.
We would like to invite speakers to present at this community event, to hear success stories, challenges, showcase tools or projects you may be working on, where Wikidata has been involved in Wikipedia, Commons, WikiSource and all other WM projects.
If you are interested in attending, please [[d:Special:RegisterForEvent/1291|register here]].
If you would like to speak at the event, please fill out this Session Proposal template on the [[d:Event_talk:Wikidata_and_Sister_Projects|event talk page]], where you can also ask any questions you may have.
I hope to see you at the event, in the audience or as a speaker, - [[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 11 avril 2025 à 11:18 (CEST)
<!-- Message envoyé par User:Danny Benjafield (WMDE)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Danny_Benjafield_(WMDE)/MassMessage_Send_List&oldid=28525705 -->
== <span lang="en" dir="ltr">Tech News: 2025-16</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W16"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/16|Translations]] are available.
'''Weekly highlight'''
* Later this week, the default thumbnail size will be increased from 220px to 250px. This changes how pages are shown in all wikis and has been requested by some communities for many years, but wasn't previously possible due to technical limitations. [https://phabricator.wikimedia.org/T355914]
* File thumbnails are now stored in discrete sizes. If a page specifies a thumbnail size that's not among the standard sizes (20, 40, 60, 120, 250, 330, 500, 960), then MediaWiki will pick the closest larger thumbnail size but will tell the browser to downscale it to the requested size. In these cases, nothing will change visually but users might load slightly larger images. If it doesn't matter which thumbnail size is used in a page, please pick one of the standard sizes to avoid the extra in-browser down-scaling step. [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images#Thumbnail_sizes][https://phabricator.wikimedia.org/T355914]
'''Updates for editors'''
* The Wikimedia Foundation are working on a system called [[m:Edge Uniques|Edge Uniques]] which will enable [[:w:en:A/B testing|A/B testing]], help protect against [[:w:en:Denial-of-service attack|Distributed denial-of-service attacks]] (DDoS attacks), and make it easier to understand how many visitors the Wikimedia sites have. This is so that they can more efficiently build tools which help readers, and make it easier for readers to find what they are looking for.
* To improve security for users, a small percentage of logins will now require that the account owner input a one-time password [[mw:Special:MyLanguage/Help:Extension:EmailAuth|emailed to their account]]. It is recommended that you [[Special:Preferences#mw-prefsection-personal-email|check]] that the email address on your account is set correctly, and that it has been confirmed, and that you have an email set for this purpose. [https://phabricator.wikimedia.org/T390662]
* "Are you interested in taking a short survey to improve tools used for reviewing or reverting edits on your Wiki?" This question will be [[phab:T389401|asked at 7 wikis starting next week]], on Recent Changes and Watchlist pages. The [[mw:Special:MyLanguage/Moderator Tools|Moderator Tools team]] wants to know more about activities that involve looking at new edits made to your Wikimedia project, and determining whether they adhere to your project's policies.
* On April 15, the full Wikidata graph will no longer be supported on <bdi lang="zxx" dir="ltr">[https://query.wikidata.org/ query.wikidata.org]</bdi>. After this date, scholarly articles will be available through <bdi lang="zxx" dir="ltr" style="white-space:nowrap;">[https://query-scholarly.wikidata.org/ query-scholarly.wikidata.org]</bdi>, while the rest of the data hosted on Wikidata will be available through the <bdi lang="zxx" dir="ltr">[https://query.wikidata.org/ query.wikidata.org]</bdi> endpoint. This is part of the scheduled split of the Wikidata Graph, which was [[d:Special:MyLanguage/Wikidata:SPARQL query service/WDQS backend update/September 2024 scaling update|announced in September 2024]]. More information is [[d:Wikidata:SPARQL query service/WDQS graph split|available on Wikidata]].
* The latest quarterly [[m:Special:MyLanguage/Wikimedia Apps/Newsletter/First quarter of 2025|Wikimedia Apps Newsletter]] is now available. It covers updates, experiments, and improvements made to the Wikipedia mobile apps.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:30}} community-submitted {{PLURAL:30|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]].
'''Updates for technical contributors'''
* The latest quarterly [[mw:Technical Community Newsletter/2025/April|Technical Community Newsletter]] is now available. This edition includes: an invitation for tool maintainers to attend the Toolforge UI Community Feedback Session on April 15th; recent community metrics; and recent technical blog posts.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.44/wmf.25|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/16|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W16"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 15 avril 2025 à 02:24 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28540654 -->
== Vote now on the revised UCoC Enforcement Guidelines and U4C Charter ==
<div lang="en" dir="ltr" class="mw-content-ltr">
The voting period for the revisions to the Universal Code of Conduct Enforcement Guidelines ("UCoC EG") and the UCoC's Coordinating Committee Charter is open now through the end of 1 May (UTC) ([https://zonestamp.toolforge.org/1746162000 find in your time zone]). [[m:Special:MyLanguage/Universal_Code_of_Conduct/Annual_review/2025/Voter_information|Read the information on how to participate and read over the proposal before voting]] on the UCoC page on Meta-wiki.
The [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee (U4C)]] is a global group dedicated to providing an equitable and consistent implementation of the UCoC. This annual review of the EG and Charter was planned and implemented by the U4C. Further information will be provided in the coming months about the review of the UCoC itself. For more information and the responsibilities of the U4C, you may [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Charter|review the U4C Charter]].
Please share this message with members of your community so they can participate as well.
In cooperation with the U4C -- [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|talk]]) 17 avril 2025 à 02:34 (CEST)
</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28469465 -->
== Actualités techniques n° 2025-17 ==
<section begin="technews-2025-W17"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/17|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* [[f:Special:MyLanguage/Wikifunctions:Main Page|Wikifunctions]] est désormais intégré à la [[w:dag:Solɔɣu|Wikipédia en dagbani]] depuis le 15 avril. C'est le premier projet qui pourra appeler des [[f:Special:MyLanguage/Wikifunctions:Introduction|fonctions de Wikifonctions]] et les intégrer dans des articles. Une fonction est quelque chose qui prend une ou plusieurs entrées et les transforme en un résultat souhaité, comme l'addition de deux nombres, la conversion de miles en mètres, le calcul du temps écoulé depuis un événement, ou la déclinaison d'un mot en une casse. Les Wikifonctions permettront aux utilisateurs de faire cela par un simple appel d'[[f:Special:MyLanguage/Wikifunctions:Catalogue|une fonction stable et globale]], plutôt que par l'intermédiaire d'un modèle local. [https://www.wikifunctions.org/wiki/Special:MyLanguage/Wikifunctions:Status_updates/2025-04-16]
* Un nouveau type d'erreur ''lint'' a été créé : [[Special:LintErrors/empty-heading|{{int:linter-category-empty-heading}}]] ([[mw:Special:MyLanguage/Help:Lint errors/empty-heading|documentation]]). L'objectif de l'extension [[mw:Special:MyLanguage/Help:Extension:Linter|Linter]] est d'identifier les éléments de wikitexte qui doivent ou peuvent être corrigés dans les pages et de fournir des conseils sur les problèmes posés par ces éléments et sur la manière de les corriger. [https://phabricator.wikimedia.org/T368722]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:37|la tâche soumise|les {{formatnum:37}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:37||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* À la suite de sa publication sur ''HuggingFace'', l'ensemble de données « ''Structured Contents'' », développé par Wikimedia Enterprise, est [https://enterprise.wikimedia.com/blog/kaggle-dataset/ maintenant également disponible sur Kaggle]. Cette initiative bêta vise à rendre les données de Wikimedia plus lisibles par les machines pour les réutilisateurs de gros volumes. Cette version bêta est publiée à un emplacement déjà utilisé par les communautés de données ouvertes, afin de recueillir des commentaires qui permettront d'améliorer le produit en vue d'une future diffusion à plus grande échelle. Vous pouvez en savoir plus sur le projet ''[https://enterprise.wikimedia.com/blog/structured-contents-snapshot-api/#open-datasets Structured Contents]'', et sur la [https://enterprise.wikimedia.com/blog/structured-contents-wikipedia-infobox/ première version librement utilisable].
* Il n'y a pas de nouvelle version de MediaWiki cette semaine.
'''Rencontres et évènements'''
* Les équipes de rédaction et d'apprentissage automatique (''Editing and Machine Learning Teams'') invitent les bénévoles intéressés à une visioconférence pour discuter de la [[mw:Special:MyLanguage/Edit check/Peacock check|vérification « ''peacock'' »]] <small>(NdT : litt. « paon » mais également une expression idiomatique pour le langage vaniteux, prétentieux)</small>, qui est la dernière [[mw:Special:MyLanguage/Edit check|vérification de modification]] qui détectera le langage « trop promotionnel » ou « non neutre » pendant qu'un rédacteur est en train de taper. Les rédacteurs qui travaillent avec les nouveaux arrivants, ou qui aident à corriger ce type d'écriture, ou qui sont intéressés par la manière dont nous utilisons l'intelligence artificielle dans nos projets, sont invités à participer à cette réunion. La [[mw:Special:MyLanguage/Editing team/Community Conversations#Next Conversation|réunion aura lieu le 28 avril 2025]] à [https://zonestamp.toolforge.org/1745863200 18:00-19:00 UTC] et sera hébergée sur Zoom.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/17|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W17"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 21 avril 2025 à 23:00 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28578245 -->
== Actualités techniques n° 2025-18 ==
<section begin="technews-2025-W18"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/18|D’autres traductions]] sont disponibles.
'''Actualités pour la contribution'''
* <span lang="en" dir="ltr" class="mw-content-ltr">Event organizers who host collaborative activities on [[m:Special:MyLanguage/CampaignEvents/Deployment status#Global Deployment Plan|multiple wikis]], including Bengali, Japanese, and Korean Wikipedias, will have access to the [[mw:Special:MyLanguage/Extension:CampaignEvents|CampaignEvents extension]] this week. Also, admins in the Wikipedia where the extension is enabled will automatically be granted the event organizer right soon. They won't have to manually grant themselves the right before they can manage events as [[phab:T386861|requested by a community]].</span>
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:19|la tâche soumise|les {{formatnum:19}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:19||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* <span lang="en" dir="ltr" class="mw-content-ltr">The release of the next major version of [[mw:Special:MyLanguage/Codex|Codex]], the design system for Wikimedia, is scheduled for 29 April 2025. Technical editors will have access to the release by the week of 5 May 2025. This update will include a number of [[mw:Special:MyLanguage/Codex/Release_Timeline/2.0#Breaking_changes|breaking changes]] and minor [[mw:Special:MyLanguage/Codex/Release_Timeline/2.0#Visual_changes|visual changes]]. Instructions on handling the breaking and visual changes are documented on [[mw:Special:MyLanguage/Codex/Release Timeline/2.0#|this page]]. Pre-release testing is reported in [[phab:T386298|T386298]], with post-release issues tracked in [[phab:T392379|T392379]] and [[phab:T392390|T392390]].</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">Users of [[wikitech:Special:MyLanguage/Help:Wiki_Replicas|Wiki Replicas]] will notice that the database views of <code dir="ltr">ipblocks</code>, <code dir="ltr">ipblocks_ipindex</code>, and <code dir="ltr">ipblocks_compat</code> are [[phab:T390767|now deprecated]]. Users can query the <code dir="ltr">[[mw:Special:MyLanguage/Manual:Block_table|block]]</code> and <code dir="ltr">[[mw:Special:MyLanguage/Manual:Block_target_table|block_target]]</code> new views that mirror the new tables in the production database instead. The deprecated views will be removed entirely from Wiki Replicas in June, 2025.</span>
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.27|MediaWiki]]
'''En détails'''
* <span lang="en" dir="ltr" class="mw-content-ltr">The latest quarterly [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/April|Language and Internationalization Newsletter]] is now available.</span> <span lang="en" dir="ltr" class="mw-content-ltr">This edition includes an overview of the improved [https://test.wikipedia.org/w/index.php?title=Special:ContentTranslation&campaign=contributionsmenu&to=es&filter-type=automatic&filter-id=previous-edits&active-list=suggestions&from=en#/ Content Translation Dashboard Tool], [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/April#Language Support for New and Existing Languages|support for new languages]], [[mw:Special:MyLanguage/Wikimedia Language and Product Localization/Newsletter/2025/April#Wiki Loves Ramadan Articles Made In Content Translation Mobile Workflow|highlights from the Wiki Loves Ramadan campaign]], [[m:Special:MyLanguage/Research:Languages Onboarding Experiment 2024 - Executive Summary|results from the Language Onboarding Experiment]], an analysis of topic diversity in articles, and information on upcoming community meetings and events.</span>
'''Rencontres et évènements'''
* <span lang="en" dir="ltr" class="mw-content-ltr">The [[Special:MyLanguage/Grants:Knowledge_Sharing/Connect/Calendar|Let's Connect Learning Clinic]] will take place on [https://zonestamp.toolforge.org/1745937000 April 29 at 14:30 UTC]. This edition will focus on "Understanding and Navigating Conflict in Wikimedia Projects". You can [[m:Special:MyLanguage/Event:Learning Clinic %E2%80%93 Understanding and Navigating Conflict in Wikimedia Projects (Part_1)|register now]] to attend.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">The [[mw:Special:MyLanguage/Wikimedia Hackathon 2025|2025 Wikimedia Hackathon]], which brings the global technical community together to connect, brainstorm, and hack existing projects, will take place from May 2 to 4th, 2025, at Istanbul, Turkey.</span>
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/18|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W18"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 28 avril 2025 à 21:31 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28585685 -->
== Vote sur les modifications proposées aux lignes directrices de l'UCoC et à la charte de l'U4C ==
<section begin="announcement-content" />
La période de vote pour les révisions des directives d'application du Code de conduite universel et de la Charte U4C se termine le 1er mai 2025 à 23:59 UTC ([https://zonestamp.toolforge.org/1746162000 trouver dans votre fuseau horaire]). [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2025/Voter information|Lisez les informations sur la façon de participer et lisez la proposition avant de voter]] sur la page UCoC de Méta-wiki.
Le [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee|Comité de coordination du code de conduite universel (U4C)]] est un groupe mondial qui se consacre à la mise en œuvre équitable et cohérente de l'UCoC. Cet examen annuel a été planifié et mis en œuvre par l'U4C. Pour plus d'informations et pour connaître les responsabilités de l'U4C, vous pouvez [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Charter|réviser la charte U4C]].
Veuillez partager ce message avec les membres de votre communauté dans votre langue, le cas échéant, afin qu'ils puissent également y participer.
En coopération avec l'U4C -- <section end="announcement-content" />
<div lang="en" dir="ltr" class="mw-content-ltr">
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 29 avril 2025 à 05:40 (CEST)</div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 -->
== Actualités techniques n° 2025-19 ==
<section begin="technews-2025-W19"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/19|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* La Wikimedia Foundation a partagé le dernier projet de mise à jour de son [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026|plan annuel]] pour l'année prochaine (juillet 2025-juin 2026). Cela comprend un [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026|résumé exécutif]] (également sur [[diffblog:2025/04/25/sharing-the-wikimedia-foundations-2025-2026-draft-annual-plan/|Diff]]), des détails sur les trois principaux [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Goals|objectifs]] ([[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Product & Technology OKRs|Infrastructure]], [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Goals/Volunteer Support|Soutien aux bénévoles]] et [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Goals/Effectiveness|Efficacité]]), [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Global Trends|tendances mondiales]], ainsi que le [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Budget Overview|budget]] et le [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026/Financial Model|modèle financier]]. Les réactions et les questions sont les bienvenues sur la [[m:Talk:Wikimedia Foundation Annual Plan/2025-2026|page de discussion]] jusqu'à la fin du mois de mai.
'''Actualités pour la contribution'''
* Pour les wikis qui ont l'extension [[m:Special:MyLanguage/CampaignEvents/Deployment status|CampaignEvents]] activée, deux nouvelles améliorations de fonctionnalités ont été publiées :
** Les administrateurs peuvent désormais choisir les espaces de noms autorisés pour [[m:Special:MyLanguage/Event Center/Registration|Inscription à un événement]] via [[mw:Special:MyLanguage/Community Configuration|Configuration de la communauté]] ([[mw:Special:MyLanguage/Help:Extension:CampaignEvents/Registration/Permitted namespaces|documentation]]). Par défaut, l'enregistrement d'un événement est autorisé dans l'espace de noms Event, mais d'autres espaces de noms (tels que l'espace de noms du projet ou l'espace de noms WikiProject) peuvent désormais être ajoutés. Grâce à cette modification, les communautés telles que les WikiProjets peuvent désormais utiliser plus facilement l'enregistrement d'événements pour leurs activités collaboratives.
** Les éditeurs peuvent désormais [[mw:Special:MyLanguage/Transclusion|transclure]] la liste de collaboration sur une page wiki ([[mw:Special:MyLanguage/Help:Extension:CampaignEvents/Collaboration list/Transclusion|documentation]]). La liste de collaboration est une liste automatisée d'événements et de wikiprojets sur les wikis, accessible via {{#special:AllEvents}} ([[w:en:Special:AllEvents|exemple]]). Désormais, la liste de collaboration peut être ajoutée à toutes sortes de pages wiki, telles que : une page principale wiki, une page WikiProjet, une page d'affiliation, une page d'événement, ou même une page d'utilisateur.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les développeurs qui utilisent la bibliothèque <code dir=ltr>moment</code> dans les gadgets et les scripts utilisateur doivent réviser leur code pour utiliser des alternatives comme la bibliothèque <code dir=ltr>Intl</code> ou la nouvelle bibliothèque <code dir=ltr>mediawiki.DateFormatter</code>. La bibliothèque <code dir=ltr>moment</code> a été dépréciée et commencera à enregistrer des messages dans la console du développeur. Vous pouvez voir une recherche globale pour les utilisations actuelles, et [[phab:T392532|posez des questions connexes dans cette tâche Phabricator]].
* Les développeurs qui maintiennent un outil qui interroge les tables de stockage de termes de Wikidata (<code dir=ltr style="white-space: nowrap;">wbt_*</code>) doivent mettre à jour leur code pour se connecter à une grappe de base de données séparée. Ces tables sont réparties dans une grappe de base de données distincte. Les outils qui interrogent ces tables via les répliques du wiki doivent être adaptés pour se connecter à la nouvelle grappe. [[wikitech:News/2025 Wikidata term store database split|La documentation et des liens connexes sont à votre disposition]]. [https://phabricator.wikimedia.org/T390954]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.44/wmf.28|MediaWiki]]
'''En détails'''
* La dernière [[mw:Special:MyLanguage/Extension:Chart/Project/Updates|lettre d’information du projet Chart]] est disponible. Il comprend des mises à jour sur la préparation de l'extension du déploiement à d'autres wikis dès cette semaine (à partir du 6 mai) et sur la mise à l'échelle au cours des semaines suivantes, ainsi que sur l'exploration du filtrage et de la transformation des données sources.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/19|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W19"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 6 mai 2025 à 02:14 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28665011 -->
== We will be enabling the new Charts extension on your wiki soon! ==
''(Apologies for posting in English)''
Hi all! We have good news to share regarding the ongoing problem with graphs and charts affecting all wikis that use them.
As you probably know, the [[:mw:Special:MyLanguage/Extension:Graph|old Graph extension]] was disabled in 2023 [[listarchive:list/wikitech-l@lists.wikimedia.org/thread/EWL4AGBEZEDMNNFTM4FRD4MHOU3CVESO/|due to security reasons]]. We’ve worked in these two years to find a solution that could replace the old extension, and provide a safer and better solution to users who wanted to showcase graphs and charts in their articles. We therefore developed the [[:mw:Special:MyLanguage/Extension:Chart|Charts extension]], which will be replacing the old Graph extension and potentially also the [[:mw:Extension:EasyTimeline|EasyTimeline extension]].
After successfully deploying the extension on Italian, Swedish, and Hebrew Wikipedia, as well as on MediaWiki.org, as part of a pilot phase, we are now happy to announce that we are moving forward with the next phase of deployment, which will also include your wiki.
The deployment will happen in batches, and will start from '''May 6'''. Please, consult [[:mw:Special:MyLanguage/Extension:Chart/Project#Deployment Timeline|our page on MediaWiki.org]] to discover when the new Charts extension will be deployed on your wiki. You can also [[:mw:Special:MyLanguage/Extension:Chart|consult the documentation]] about the extension on MediaWiki.org.
If you have questions, need clarifications, or just want to express your opinion about it, please refer to the [[:mw:Special:MyLanguage/Extension_talk:Chart/Project|project’s talk page on Mediawiki.org]], or ping me directly under this thread. If you encounter issues using Charts once it gets enabled on your wiki, please report it on the [[:mw:Extension_talk:Chart/Project|talk page]] or at [[phab:tag/charts|Phabricator]].
Thank you in advance! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 6 mai 2025 à 17:07 (CEST)
<!-- Message envoyé par User:Sannita (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=28663781 -->
== Actualités techniques n° 2025-20 ==
<section begin="technews-2025-W20"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/20|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Le lien [[m:Special:MyLanguage/Wikimedia URL Shortener|« Obtenir une URL raccourcie »]] dans la barre latérale inclut désormais un [[phab:T393309|code QR]]. Les utilisateurs des sites Wikimedia peuvent maintenant l’utiliser en le scannant ou en le téléchargeant pour partager et accéder rapidement au contenu partagé des sites Wikimedia, de manière pratique.
'''Actualités pour la contribution'''
* La Wikimedia Foundation travaille sur un système appelé [[m:Edge Uniques|« ''Edge Uniques'' »]], qui permettra de réaliser des [[w:en:A/B testing|tests A/B]], d’aider à se protéger contre les [[w:en:Denial-of-service attack|attaques par déni de service distribué]] (attaques DDoS), et de mieux comprendre combien de visiteurs les sites Wikimedia reçoivent. Cela vise à construire plus efficacement des outils utiles aux lecteurs, et de les aider à trouver ce qu'ils cherchent. Les Actualités techniques en ont [[m:Special:MyLanguage/Tech/News/2025/16|déjà parlé]]. Le déploiement sera progressif. Certains pourraient voir le cookie « ''Edge Uniques'' » à partir de la semaine du 19 mai. Vous pouvez en discuter sur la [[m:Talk:Edge Uniques|page de discussion]].
* À partir du 19 mai 2025, les organisateurs d’événements sur les wikis disposant de l’[[mw:Special:MyLanguage/Help:Extension:CampaignEvents|extension « CampaignEvents »]] pourront utiliser l’[[m:Special:MyLanguage/Event Center/Registration|inscription aux événements]] dans l’espace de noms du projet (par exemple, l’espace Wikipédia, l’espace Wikidata). Avec ce changement, les communautés n’ont plus besoin d’administrateurs pour utiliser cette fonctionnalité. Toutefois, les wikis qui ne souhaitent pas ce changement peuvent retirer et ajouter les espaces de noms autorisés sur [[Special:CommunityConfiguration/CampaignEvents]].
* Le projet Wikipédia dispose désormais d’un {{int:project-localized-name-group-wikipedia/fr}} en [[d:Q36720|nupe]] ([[w:nup:|<code>w:nup:</code>]]). Il s’agit d’une langue principalement parlée dans la région du centre-nord du Nigeria. Les locuteurs de cette langue sont invités à contribuer à la [[w:nup:Tatacin feregi|nouvelle Wikipédia]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les développeurs peuvent désormais accéder à la Wikipédia néerlandaise pré-analysée, parmi d’autres (anglais, allemand, français, espagnol, italien et portugais), via les [https://enterprise.wikimedia.com/docs/snapshot/#structured-contents-snapshot-bundle-info-beta instantanés « ''Structured Contents'' » (bêta)]. Le contenu comprend des résumés Wikipédia analysés, des descriptions, des images principales, des infoboxes, des sections d’articles et des références.
* Le point de terminaison de l’API REST <code dir="ltr">/page/data-parsoid</code> n’est plus utilisé et sera obsolète. Il est [[phab:T393557|prévu qu’il soit désactivé]] le 7 juin 2025.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.1|MediaWiki]]
'''En détails'''
* Le [https://wikitech.wikimedia.org/wiki/News/2025_Cloud_VPS_VXLAN_IPv6_migration support IPv6] est un nouveau réseau virtuel Cloud qui améliore considérablement l’évolutivité, la sécurité et la préparation des plateformes Wikimedia pour l’avenir. Si vous êtes un contributeur technique curieux d’en savoir plus, consultez [https://techblog.wikimedia.org/2025/05/06/wikimedia-cloud-vps-ipv6-support/ ce billet de blogue] pour un aperçu détaillé de la transition vers l’IPv6.
'''Rencontres et évènements'''
* La deuxième édition de 2025 de [[m:Special:MyLanguage/Afrika Baraza|« Afrika Baraza »]], une plateforme virtuelle permettant aux Wikimédiens africains de se connecter, aura lieu le [https://zonestamp.toolforge.org/1747328400 15 mai à 17 h UTC]. Cette édition sera axée sur des discussions concernant la [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2025-2026|planification annuelle et les avancées de Wikimedia]].
* La [[m:Special:MyLanguage/MENA Connect Community Call|« ''MENA Connect Community Call'' »]], une réunion virtuelle permettant aux Wikimédiens de la [[w:fr:MENA|région Moyen-Orient et Afrique du Nord]] (MENA) de se rencontrer, aura lieu le [https://zonestamp.toolforge.org/1747501200 17 mai à 17 h UTC]. Vous pouvez [[m:Event:MENA Connect (Wiki_Diwan) APP Call|vous inscrire dès maintenant]] pour y assister.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/20|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W20"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 13 mai 2025 à 00:37 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28714188 -->
== Appel aux candidatures pour le Comité de Coordination du Code de Conduite Universel (U4C). ==
<section begin="announcement-content" />
Les résultats du vote sur les directives d'application et la charte du Comité de Coordination du Code de Conduite Universel (U4C) sont disponibles [[m:Special:MyLanguage/Universal Code of Conduct/Annual review/2025#Results|sur Méta-wiki]].
Vous pouvez désormais [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025/Candidates|soumettre votre candidature pour siéger à l'U4C]] <s>à partir du 29 mai 2025 à 12:00 UTC</s>. Des informations sur [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025|l'éligibilité, le processus, et la chronologie sont disponibles sur Méta-wiki]]. Le vote sur les candidats sera ouvert le 1<sup>er</sup> juin 2025 et durera deux semaines, se finissant donc le 15 juin 2025 à 12:00 UTC.
Si vous avez des questions, vous pouvez les poser sur [[m:Talk:Universal Code of Conduct/Coordinating Committee/Election/2025|la page de discussion pour l'élection]]. -- en coopération avec l'U4C, </div><section end="announcement-content" />
<bdi lang="en" dir="ltr">[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User_talk:Keegan (WMF)|discussion]])</bdi> 16 mai 2025 à 00:06 (CEST)
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 -->
:Rectification : Vous pouvez désormais [[m:Special:MyLanguage/Universal Code of Conduct/Coordinating Committee/Election/2025/Candidates|soumettre votre candidature pour siéger à l'U4C]] du 14 mai au 28 mai.
:{{BlocCitation|You can submit your candidacy from May 14 until May 28, 2025}}
:-- ◄ [[Utilisateur:DavidL|'''D'''avid '''L''']] • [[Discussion Utilisateur:DavidL|discuter]] ► 16 mai 2025 à 16:36 (CEST)
== <span lang="en" dir="ltr">Tech News: 2025-21</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W21"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/21|Translations]] are available.
'''Weekly highlight'''
* The Editing Team and the Machine Learning Team are working on a new check for newcomers: [[mw:Edit check/Peacock check|Peacock check]]. Using a prediction model, this check will encourage editors to improve the tone of their edits, using artificial intelligence. We invite volunteers to review the first version of the Peacock language model for the following languages: Arabic, Spanish, Portuguese, English, and Japanese. Users from these wikis interested in reviewing this model are [[mw:Edit check/Peacock check/model test|invited to sign up at MediaWiki.org]]. The deadline to sign up is on May 23, which will be the start date of the test.
'''Updates for editors'''
* From May 20, 2025, [[m:Special:MyLanguage/Oversight policy|oversighters]] and [[m:Special:MyLanguage/Meta:CheckUsers|checkusers]] will need to have their accounts secured with two-factor authentication (2FA) to be able to use their advanced rights. All users who belong to these two groups and do not have 2FA enabled have been informed. In the future, this requirement may be extended to other users with advanced rights. [[m:Special:MyLanguage/Mandatory two-factor authentication for users with some extended rights|Learn more]].
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Wishlist item]] [[m:Special:MyLanguage/Community Wishlist Survey 2023/Multiblocks|Multiblocks]] will begin mass deployment by the end of the month: all non-Wikipedia projects plus Catalan Wikipedia will adopt Multiblocks in the week of May 26, while all other Wikipedias will adopt it in the week of June 2. Please [[m:Talk:Community Wishlist Survey 2023/Multiblocks|contact the team]] if you have concerns. Administrators can test the new user interface now on your own wiki by browsing to [{{fullurl:Special:Block|usecodex=1}} {{#special:Block}}?usecodex=1], and can test the full multiblocks functionality [[testwiki:Special:Block|on testwiki]]. Multiblocks is the feature that makes it possible for administrators to impose different types of blocks on the same user at the same time. See the [[mw:Special:MyLanguage/Help:Manage blocks|help page]] for more information. [https://phabricator.wikimedia.org/T377121]
* Later this week, the [[{{#special:SpecialPages}}]] listing of almost all special pages will be updated with a new design. This page has been [[phab:T219543|redesigned]] to improve the user experience in a few ways, including: The ability to search for names and aliases of the special pages, sorting, more visible marking of restricted special pages, and a more mobile-friendly look. The new version can be [https://meta.wikimedia.beta.wmflabs.org/wiki/Special:SpecialPages previewed] at Beta Cluster now, and feedback shared in the task. [https://phabricator.wikimedia.org/T219543]
* The [[mw:Special:MyLanguage/Extension:Chart|Chart extension]] is being enabled on more wikis. For a detailed list of when the extension will be enabled on your wiki, please read the [[mw:Special:MyLanguage/Extension:Chart/Project#Deployment Timeline|deployment timeline]].
* [[f:Special:MyLanguage/Wikifunctions:Main Page|Wikifunctions]] will be deployed on May 27 on five Wiktionaries: [[wikt:ha:|Hausa]], [[wikt:ig:|Igbo]], [[wikt:bn:|Bengali]], [[wikt:ml:|Malayalam]], and [[wikt:dv:|Dhivehi/Maldivian]]. This is the second batch of deployment planned for the project. After deployment, the projects will be able to call [[f:Special:MyLanguage/Wikifunctions:Introduction|functions from Wikifunctions]] and integrate them in their pages. A function is something that takes one or more inputs and transforms them into a desired output, such as adding up two numbers, converting miles into metres, calculating how much time has passed since an event, or declining a word into a case. Wikifunctions will allow users to do that through a simple call of [[f:Special:MyLanguage/Wikifunctions:Catalogue|a stable and global function]], rather than via a local template.
* Later this week, the Wikimedia Foundation will publish a hub for [[diffblog:2024/07/09/on-the-value-of-experimentation/|experiments]]. This is to showcase and get user feedback on product experiments. The experiments help the Wikimedia movement [[diffblog:2023/07/13/exploring-paths-for-the-future-of-free-knowledge-new-wikipedia-chatgpt-plugin-leveraging-rich-media-social-apps-and-other-experiments/|understand new users]], how they interact with the internet and how it could affect the Wikimedia movement. Some examples are [[m:Special:MyLanguage/Future Audiences/Generated Video|generated video]], the [[m:Special:MyLanguage/Future Audiences/Roblox game|Wikipedia Roblox speedrun game]] and [[m:Special:MyLanguage/Future Audiences/Discord bot|the Discord bot]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:29}} community-submitted {{PLURAL:29|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, there was a bug with creating an account using the API, which has now been fixed. [https://phabricator.wikimedia.org/T390751]
'''Updates for technical contributors'''
* Gadgets and user scripts that interact with [[{{#special:Block}}]] may need to be updated to work with the new [[mw:Special:MyLanguage/Help:Manage blocks|manage blocks interface]]. Please review the [[mw:Help:Manage blocks/Developers|developer guide]] for more information. If you need help or are unable to adapt your script to the new interface, please let the team know on the [[mw:Help talk:Manage blocks/Developers|talk page]]. [https://phabricator.wikimedia.org/T377121]
* The <code dir=ltr>mw.title</code> object allows you to get information about a specific wiki page in the [[w:en:Wikipedia:Lua|Lua]] programming language. Starting this week, a new property will be added to the object, named <code dir=ltr>isDisambiguationPage</code>. This property allows you to check if a page is a disambiguation page, without the need to write a custom function. [https://phabricator.wikimedia.org/T71441]
* [[File:Octicons-tools.svg|15px|link=|class=skin-invert|Advanced item]] User script developers can use a [[toolforge:gitlab-content|new reverse proxy tool]] to load javascript and css from [[gitlab:|gitlab.wikimedia.org]] with <code dir=ltr>mw.loader.load</code>. The tool's author hopes this will enable collaborative development workflows for user scripts including linting, unit tests, code generation, and code review on <bdi lang="zxx" dir="ltr">gitlab.wikimedia.org</bdi> without a separate copy-and-paste step to publish scripts to a Wikimedia wiki for integration and acceptance testing. See [[wikitech:Tool:Gitlab-content|Tool:Gitlab-content on Wikitech]] for more information.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.45/wmf.2|MediaWiki]]
'''Meetings and events'''
* The 12th edition of [[m:Special:MyLanguage/Wiki Workshop 2025|Wiki Workshop 2025]], a forum that brings together researchers that explore all aspects of Wikimedia projects, will be held virtually on 21-22 May. Researchers can [https://pretix.eu/wikimedia/wikiworkshop2025/ register now].
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/21|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W21"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 20 mai 2025 à 01:12 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28724712 -->
== RfC ongoing regarding Abstract Wikipedia (and your project) ==
<div lang="en" dir="ltr" class="mw-content-ltr">
''(Apologies for posting in English, if this is not your first language)''
Hello all! We opened a discussion on Meta about a very delicate issue for the development of [[:m:Special:MyLanguage/Abstract Wikipedia|Abstract Wikipedia]]: where to store the abstract content that will be developed through functions from Wikifunctions and data from Wikidata. Since some of the hypothesis involve your project, we wanted to hear your thoughts too.
We want to make the decision process clear: we do not yet know which option we want to use, which is why we are consulting here. We will take the arguments from the Wikimedia communities into account, and we want to consult with the different communities and hear arguments that will help us with the decision. The decision will be made and communicated after the consultation period by the Foundation.
You can read the various hypothesis and have your say at [[:m:Abstract Wikipedia/Location of Abstract Content|Abstract Wikipedia/Location of Abstract Content]]. Thank you in advance! -- [[User:Sannita (WMF)|Sannita (WMF)]] ([[User talk:Sannita (WMF)|<span class="signature-talk">{{int:Talkpagelinktext}}</span>]]) 22 mai 2025 à 17:26 (CEST)
</div>
<!-- Message envoyé par User:Sannita (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Sannita_(WMF)/Mass_sending_test&oldid=28768453 -->
== Actualités techniques n° 2025-22 ==
<section begin="technews-2025-W22"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/22|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Une discussion communautaire à l’échelle du mouvement est désormais ouverte sur Meta au sujet d’un point très délicat pour le développement de [[m:Special:MyLanguage/Abstract Wikipedia|« Abstract Wikipedia »]] : où stocker le contenu abstrait qui sera développé à partir des fonctions de Wikifunctions et des données de Wikidata. La discussion est ouverte jusqu’au 12 juin sur [[m:Special:MyLanguage/Abstract Wikipedia/Location of Abstract Content|Abstract Wikipedia/Location of Abstract Content]], et tous les avis sont les bienvenus. La décision sera prise et communiquée par la Fondation à l’issue de la période de consultation.
'''Actualités pour la contribution'''
* Depuis la semaine dernière, sur tous les wikis excepté [[phab:T388604|les vingt plus grands]], les utilisateurs de l’éditeur visuel mobile disposent de [[phab:T385851|nouveaux outils dans la barre de menu]], accessibles via le nouveau bouton de la barre d’outils <code>+</code>. Pour commencer, le nouveau menu proposera des options pour ajouter : des références, des hiéroglyphes et des blocs de code. Le déploiement sur les autres wikis est [[phab:T388605|prévu]] pour le mois de juin.
* [[File:Octicons-tools.svg|12px|link=|class=skin-invert|Sujet technique]] La fonction d’analyse <code dir=ltr>[[mw:Special:MyLanguage/Help:Extension:ParserFunctions##ifexist|#ifexist]]</code> ne créera plus de lien vers sa page cible. Cela améliorera l’utilité de [[{{#special:WantedPages}}]], qui ne listera à terme que les pages réellement ciblées par un lien rouge. Ce changement se fera progressivement à mesure que les pages sources seront mises à jour. [https://phabricator.wikimedia.org/T14019]
* Cette semaine, l’équipe des outils de modération va lancer [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|un nouveau filtre dans les modifications récentes]], en commençant par la Wikipédia en indonésien. Ce nouveau filtre met en évidence les modifications susceptibles d’être annulées. L’objectif est d’aider les patrouilleurs des modifications récentes à repérer les contributions potentiellement problématiques. D’autres wikis bénéficieront de ce filtre à l’avenir.
* Lorsqu’ils cliquent sur une barre de recherche vide, les utilisateurs non connectés verront des suggestions d’articles à lire. Cette fonctionnalité sera disponible à la fois sur ordinateur et sur mobile. Les lecteurs des Wikipédias en catalan, hébreu et italien ainsi que de certains projets frères recevront ce changement entre le 21 mai et la mi-juin. Les lecteurs des autres wikis le recevront plus tard. L’objectif est d’encourager les utilisateurs à lire davantage les wikis. [[mw:Special:MyLanguage/Reading/Web/Content Discovery Experiments/Search Suggestions|En savoir plus]].
* Certains utilisateurs de l’application Wikipédia sur Android peuvent utiliser une nouvelle fonctionnalité destinée aux lecteurs : [[mw:Special:MyLanguage/Wikimedia Apps/Team/Android/TrivaGame|« WikiGames »]], un jeu quotidien de quiz basé sur des événements historiques réels. Le déploiement a commencé sous forme de test A/B, disponible pour 50 % des utilisateurs dans les langues suivantes : anglais, français, portugais, russe, espagnol, arabe, chinois et turc.
* L’[[mw:Special:MyLanguage/Extension:Newsletter|extension « Newsletter »]] disponible sur MediaWiki.org permet la création de [[mw:Special:Newsletters|divers bulletins d’information]] à destination des utilisateurs de tous les wikis. L’extension peut désormais publier de nouveaux numéros sous forme de liens vers des sections sur une page existante, au lieu de nécessiter une nouvelle page pour chaque numéro. [https://phabricator.wikimedia.org/T393844]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:32|la tâche soumise|les {{formatnum:32}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:32||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les champs <code dir=ltr>[[mw:Special:MyLanguage/Manual:Ipblocks table|ipblocks]]</code>, précédemment déclarés obsolètes, seront supprimés début juin dans les [[wikitech:Help:Wiki Replicas|Wiki Replicas]]. Il est recommandé aux utilisateurs d’interroger les nouveaux champs <code dir=ltr>[[mw:Special:MyLanguage/Manual:Block table|block]]</code> et <code dir=ltr>[[mw:Special:MyLanguage/Manual:Block target table|block_target]]</code> à la place.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.3|MediaWiki]]
'''Rencontres et évènements'''
* [[d:Special:MyLanguage/Event:Wikidata and Sister Projects|« Wikidata et les projets frères »]] est un événement en ligne de plusieurs jours qui portera sur l’intégration de Wikidata à Wikipédia et aux autres projets Wikimedia. L’événement se déroulera du 29 mai au 1<sup>er</sup> juin. Vous pouvez [[d:Special:MyLanguage/Event:Wikidata and Sister Projects#Sessions|consulter le programme]] et [[d:Special:RegisterForEvent/1291|vous inscrire]].
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/22|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W22"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 26 mai 2025 à 22:04 (CEST)
<!-- Message envoyé par User:UOzurumba (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28788673 -->
== Sélection 2025 du conseil d'administration de la fondation Wikimédia et appel à questions ==
<section begin="announcement-content" />
:''[[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Announcement/Selection announcement|{{int:interlanguage-link-mul}}]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Wikimedia Foundation elections/2025/Announcement/Selection announcement}}&language=&action=page&filter= {{int:please-translate}}]''
Chers tous et toutes,
Cette année, le mandat de deux administrateurs sélectionnés par la communauté et les affiliés au conseil d'administration de la Fondation Wikimédia prendra fin [1]. Le conseil invite l'ensemble du mouvement à participer au processus de sélection de cette année et à voter pour pourvoir ces sièges.
Le Comité des élections supervisera ce processus avec le soutien du personnel de la Fondation [2]. Le Comité de gouvernance, composé de membres du Conseil d'administration non-candidats au processus de sélection des membres du Conseil d'administration 2025 sélectionnés par la communauté et les affiliés (Raju Narisetti, Shani Evenstein Sigalov, Lorenzo Losa, Kathy Collins, Victoria Doronina et Esra'a Al Shafei) [3], est chargé de superviser le processus de sélection des membres du Conseil d'administration 2025 et de tenir le Conseil d'administration au courant de la situation. Pour plus de détails sur les rôles de la commission des élections, du conseil d'administration et du personnel, cliquez ici [4].
En voici les dates clés :
* 22 mai - 5 juin : Annonce ( la présente communication) et période d'appel à questions. [6]
* 17 juin - 1er juillet 2025 : Appel à candidatures
* Juillet 2025 : Si nécessaire, les affiliés votent pour présélectionner les candidats si 10 d'entre eux ou plus se présentent [5].
* Août 2025 : Période de la campagne
* Août - septembre 2025 : Période de vote communautaire de deux semaines
* Octobre - novembre 2025 : Vérification des antécédents des candidats sélectionnés
* Réunion du conseil d'administration en décembre 2025 : Installation des nouveaux membres du conseil d'administration
Pour en savoir plus sur le processus de sélection de 2025 - y compris le calendrier détaillé, le processus de candidature, les règles de la campagne et les critères d'éligibilité des électeurs -, veuillez consulter cette page Meta-wiki. [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2025|[link]]].
'''Appel à questions'''
Lors de chaque processus de sélection, la communauté a la possibilité de soumettre des questions auxquelles les candidats au conseil d'administration devront répondre. Le comité électoral sélectionne les questions à partir de la liste établie par la communauté pour que les candidats y répondent. Les candidats doivent répondre à toutes les questions posées dans le dossier de candidature pour être éligibles, faute de quoi leur candidature sera rejetée. Cette année, le comité électoral sélectionnera 5 questions auxquelles les candidats devront répondre. Les questions sélectionnées peuvent être une combinaison de celles qui ont été soumises par la communauté, si elles sont similaires ou liées. [[m:Special:MyLanguage/Wikimedia_Foundation_elections/2025/Questions_for_candidates|[link]]]
'''Bénévoles des élections'''
Une autre façon de participer au processus de sélection de 2025 est de devenir bénévole des élections. Les bénévoles électoraux constituent un pont entre le comité électoral et leur communauté respective. Ils veillent à ce que leur communauté soit représentée et les incitent à voter. Pour en savoir plus sur le programme et les modalités d'adhésion, consultez cette page Meta-wiki. [[m:Wikimedia_Foundation_elections/2025/Election_volunteers|[link]]].
Je vous remercie !
[1] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2022/Results
[2] https://foundation.wikimedia.org/wiki/Committee:Elections_Committee_Charter
[3] https://foundation.wikimedia.org/wiki/Resolution:Committee_Membership,_December_2024
[4] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections_committee/Roles
[5] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2025/FAQ
[6] https://meta.wikimedia.org/wiki/Wikimedia_Foundation_elections/2025/Questions_for_candidates
Bien à vous,
Victoria Doronina
Liaison du conseil d'administration avec le comité des élections
Comité de gouvernance<section end="announcement-content" />
[[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 28 mai 2025 à 05:07 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28618011 -->
== Actualités techniques n° 2025-23 ==
<section begin="technews-2025-W23"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/23|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* L'[[mw:Special:MyLanguage/Extension:Chart|extension Chart]] est maintenant disponible sur tous les wikis Wikimedia. Les éditeurs peuvent utiliser cette nouvelle extension pour créer des visualisations de données interactives comme des diagrammes à barres, à lignes, avec des zones, et circulaires. Chart a été créée pour remplacer la plupart des utilisations de l'ancienne [[mw:Special:MyLanguage/Extension:Graph|extension Graph]].
'''Actualités pour la contribution'''
* Il est maintenant plus simple de configurer les citations automatiques pour votre wiki dans le [[mw:Special:MyLanguage/Citoid/Enabling Citoid on your wiki|générateur de citations]] de l'éditeur visuel. Les administrateurs peuvent maintenant définir un modèle par défaut en utilisant la clé <code dir=ltr>_default</code> dans la page locale <bdi lang="en" dir="ltr">[[MediaWiki:Citoid-template-type-map.json]]</bdi> ([[mw:Special:Diff/6969653/7646386|exemple de modification]]). Définir ce réglage par défaut permettra aussi de pérenniser vos configurations existantes lorsque de [[phab:T347823|nouveaux types d'objets]] seront ajoutés à l'avenir. Vous pouvez toujours définir des modèles pour des types d'objets individuels et ils seront prioritaires par rapport au modèle par défaut. [https://phabricator.wikimedia.org/T384709]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:20|la tâche soumise|les {{formatnum:20}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:20||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* À partir de la semaine du 2 juin, les robots qui utilisent <code dir=ltr>action=login</code> ou <code dir=ltr>action=clientlogin</code> pour s'authentifier auront un taux d'échec plus fréquent. Cela est dû à des protections plus fortes contre les connexions suspectes. Les robots qui utilisent des [[mw:Special:MyLanguage/Manual:Bot passwords|mots de passe de robots]] ou une authentification sans connexion telle que [[mw:Special:MyLanguage/OAuth/Owner-only consumers|OAuth]] ne seront pas affectés. Si votre bot n'utilise aucun des deux, vous devriez le mettre à jour ; utiliser <code dir=ltr>action=login</code> sans un mot de passe de robot a été rendu désuet [[listarchive:list/wikitech-l@lists.wikimedia.org/message/3EEMN7VQX5G7WMQI5K2GP5JC2336DPTD/|en 2016]]. Pour la plupart des robots, cela nécessite seulement de changer quel mot de passe ce dernier utilise. [https://phabricator.wikimedia.org/T395205]
* À partir de cette semaine, les wikis Wikimedia permettront des fonctionnalités ES2017 dans le code JavaScript pour le code officiel, les gadgets et les scripts utilisateurs. La fonctionnalité la plus visible d'ES2017 est la syntaxe <bdi lang="zxx" dir="ltr"><code>async</code>/<code>await</code></bdi>, ce qui permet un code plus facile à lire. Jusqu'à cette semaine, la plateforme ne permettait que jusqu'à ES2016, et quelques mois plus tôt, jusqu'à ES2015. [https://phabricator.wikimedia.org/T381537]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.4|MediaWiki]]
'''Rencontres et évènements'''
* Les demandes de bourse d'études pour participer à la [[m:Special:MyLanguage/GLAM Wiki 2025|conférence GLAM 2025]] sont maintenant ouvertes. La conférence aura lieu du 30 octobre au 1er novembre, à Lisbonne, au Portugal. Les contributeurs GLAM qui n'ont pas les moyens de financer leur participation peuvent [[m:Special:MyLanguage/GLAM Wiki 2025/Scholarships|faire une demande ici]]. La date limite de candidature est le 7 juin.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/23|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W23"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 3 juin 2025 à 01:54 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28819186 -->
== Actualités techniques n° 2025-24 ==
<section begin="technews-2025-W24"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/24|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* L’[[mw:Special:MyLanguage/Trust and Safety Product|équipe produits Confiance et sûreté]] finalise les travaux nécessaires au déploiement des [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] sur les grandes Wikipédias plus tard ce mois-ci. L’équipe a collaboré avec les stewards et d’autres utilisateurs disposant de droits étendus afin d’anticiper et de traiter de nombreux cas d’usage qui pourraient se présenter sur les wikis de grande taille, afin que les membres des communautés puissent continuer à modérer et à patrouiller efficacement les comptes temporaires. Il s’agira de la deuxième des trois phases de déploiement ; la dernière aura lieu en septembre au plus tôt. Pour plus d’informations sur les développements récents du projet, [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Updates|voir cette mise à jour]]. Si vous avez des commentaires ou des questions, écrivez sur la [[mw:Talk:Trust and Safety Product/Temporary Accounts|page de discussion]] et [[m:Event:CEE Catch up Nr. 10 (June 2025)|rejoignez un « CEE Catch Up »]] ce mardi.
'''Actualités pour la contribution'''
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] La fonctionnalité [[mw:Special:MyLanguage/Help:Watchlist expiry|« expiration de la liste de suivi »]] permet aux contributeurs de suivre des pages pendant une durée limitée. Une fois ce délai écoulé, la page est automatiquement retirée de votre liste de suivi. À partir de cette semaine, vous pouvez définir une préférence pour la durée par défaut pendant laquelle vous souhaitez suivre les pages. Les [[Special:Preferences#mw-prefsection-watchlist-pageswatchlist|préférences]] permettent également de définir différentes durées par défaut selon que vous modifiez une page existante, que vous en créez une nouvelle ou que vous utilisez l’annulation rapide (''rollback''). [https://phabricator.wikimedia.org/T265716]
[[File:Talk pages default look (April 2023).jpg|thumb|alt=Capture d'écran des améliorations visuelles apportées aux pages de discussion|Exemple d'une page de discussion avec les améliorations, en français.]]
* L’apparence des pages de discussion va changer sur la quasi-totalité des Wikipédias ([[m:Special:MyLanguage/Tech/News/2024/19|certaines]] ont déjà reçu ce nouveau design, [[phab:T379264|quelques-unes]] le recevront plus tard). Vous pouvez lire les détails concernant ces changements [[diffblog:2024/05/02/making-talk-pages-better-for-everyone/|sur ''Diff'']]. Il est possible de désactiver ces modifications [[Special:Preferences#mw-prefsection-editing-discussion|dans les préférences utilisateur]] (« {{int:discussiontools-preference-visualenhancements}} »). [https://phabricator.wikimedia.org/T319146][https://phabricator.wikimedia.org/T392121]
* Les utilisateurs disposant de certains droits étendus (y compris les administrateurs, bureaucrates, vérificateurs, masqueurs et stewards) peuvent désormais voir les adresses IP de tous les comptes temporaires [[phab:T358853|révélées automatiquement]] pendant des périodes limitées, lorsqu’ils doivent lutter contre du vandalisme rapide impliquant des changements fréquents de compte. Cette fonctionnalité a été demandée par les stewards. [https://phabricator.wikimedia.org/T386492]
* Cette semaine, les équipes des outils de modération et d’apprentissage automatique poursuivent le déploiement [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|d’un nouveau filtre dans les Modifications récentes]], en l’étendant à plusieurs autres Wikipédias. Ce filtre utilise le modèle « Revert Risk », développé par l’équipe de recherche, pour mettre en évidence les modifications susceptibles d’être annulées et aider les patrouilleurs à repérer les contributions potentiellement problématiques. La fonctionnalité sera déployée sur les Wikipédias suivantes : {{int:project-localized-name-afwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-bewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-bnwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-cywiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-hawwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-iswiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-kkwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-simplewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-trwiki/fr}}. Le déploiement se poursuivra dans les semaines à venir pour inclure [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|le reste des Wikipédias concernées par ce projet]]. [https://phabricator.wikimedia.org/T391964]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Les éditeurs de filtres anti-abus actifs sur Meta-Wiki et les grandes Wikipédias sont priés de mettre à jour les filtres pour les rendre compatibles avec les comptes temporaires. Un lien vers les instructions ainsi que vers les listes privées des filtres à vérifier est [[phab:T369611|disponible sur Phabricator]].
* Les modules Lua ont désormais accès au nom de l’image miniature associée à une page, et sur [https://gerrit.wikimedia.org/g/operations/mediawiki-config/+/2e4ab14aa15bb95568f9c07dd777065901eb2126/wmf-config/InitialiseSettings.php#10849 certains wikis], aux informations d’évaluation des WikiProjets. Cela est possible grâce à deux nouvelles propriétés des objets [[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#added-by-extensions|mw.title]], nommées <code dir=ltr>pageImage</code> et <code dir=ltr>pageAssessments</code>. [https://phabricator.wikimedia.org/T131911][https://phabricator.wikimedia.org/T380122]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.5|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/24|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W24"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 10 juin 2025 à 03:16 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28846858 -->
== Vote now in the 2025 U4C Election ==
<div lang="en" dir="ltr" class="mw-content-ltr">
Apologies for writing in English.
{{Int:Please-translate}}
Eligible voters are asked to participate in the 2025 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2025|2025 Election information page]]. The vote closes on 17 June 2025 at [https://zonestamp.toolforge.org/1750161600 12:00 UTC].
Please vote if your account is eligible. Results will be available by 1 July 2025. -- In cooperation with the U4C, [[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 14 juin 2025 à 01:00 (CEST) </div>
<!-- Message envoyé par User:Keegan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28848819 -->
== <span lang="en" dir="ltr">Tech News: 2025-25</span> ==
<div lang="en" dir="ltr">
<section begin="technews-2025-W25"/><div class="plainlinks">
Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2025/25|Translations]] are available.
'''Updates for editors'''
* You can [https://wikimediafoundation.limesurvey.net/359761?lang=en nominate your favorite tools] for the sixth edition of the [[m:Special:MyLanguage/Coolest Tool Award|Coolest Tool Award]]. Nominations are anonymous and will be open until June 25. You can re-use the survey to nominate multiple tools.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:33}} community-submitted {{PLURAL:33|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]].
'''Updates for technical contributors'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.45/wmf.6|MediaWiki]]
'''In depth'''
* Foundation staff and technical volunteers use Wikimedia APIs to build the tools, applications, features, and integrations that enhance user experiences. Over the coming years, the MediaWiki Interfaces team will be investing in Wikimedia web (HTTP) APIs to better serve technical volunteer needs and protect Wikimedia infrastructure from potential abuse. You can [https://techblog.wikimedia.org/2025/06/12/apis-as-a-product-investing-in-the-current-and-next-generation-of-technical-contributors/ read more about their plans to evolve the APIs in this Techblog post].
'''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]] • [[m:Special:MyLanguage/Tech/News#contribute|Contribute]] • [[m:Special:MyLanguage/Tech/News/2025/25|Translate]] • [[m:Tech|Get help]] • [[m:Talk:Tech/News|Give feedback]] • [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].''
</div><section end="technews-2025-W25"/>
</div>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 17 juin 2025 à 01:38 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28870688 -->
== Conseil d'administration de la Fondation Wikimédia 2025 - Appel à candidatures ==
<section begin="announcement-content" />
:''<div class="plainlinks">[[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Announcement/Call for candidates|{{int:interlanguage-link-mul}}]] • [https://meta.wikimedia.org/w/index.php?title=Special:Translate&group=page-{{urlencode:Wikimedia Foundation elections/2025/Announcement/Call for candidates}}&language=&action=page&filter= {{int:please-translate}}]</div>
Bonjour à toutes et à tous,
L'appel [[m:Special:MyLanguage/Wikimedia Foundation elections/2025|à candidatures pour la sélection du Conseil d'administration de la Fondation Wikimédia 2025]] est désormais ouvert du 17 juin 2025 au 2 juillet 2025 à 11:59 UTC [1]. Le conseil d'administration est chargé de superviser le travail de la Fondation Wikimédia, et chaque membre du conseil d'administration est nommé pour un mandat de trois ans [2]. Il s'agit d'un rôle bénévole.
Cette année, la communauté Wikimédia votera entre la fin du mois d'août et le mois de septembre 2025 pour élire deux (2) membres du Conseil d'administration de la Fondation. Seriez-vous - ou connaissez-vous quelqu'un - susceptible de rejoindre le conseil d'administration de la Fondation Wikimedia ? [3]
Apprenez-en plus sur les exigences pour briguer ces postes de direction et sur la manière de soumettre votre candidature sur [[m:Special:MyLanguage/Wikimedia Foundation elections/2025/Candidate application|cette page Meta-wiki]] ou encouragez quelqu’un d’autre à se présenter à l’élection de cette année.
Bien à vous,
Abhishek Suryawanshi<br />
Président du Comité des élections
Au nom du comité des élections et du comité de gouvernance
[1] https://meta.wikimedia.org/wiki/Special:MyLanguage/Wikimedia_Foundation_elections/2025/Call_for_candidates
[2] https://foundation.wikimedia.org/wiki/Legal:Bylaws#(B)_Term.
[3] https://meta.wikimedia.org/wiki/Special:MyLanguage/Wikimedia_Foundation_elections/2025/Resources_for_candidates<section end="announcement-content" />
[[Utilisateur:MediaWiki message delivery|MediaWiki message delivery]] ([[Discussion utilisateur:MediaWiki message delivery|discussion]]) 17 juin 2025 à 19:43 (CEST)
<!-- Message envoyé par User:RamzyM (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=28866958 -->
== Actualités techniques n° 2025-26 ==
<section begin="technews-2025-W26"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/26|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Cette semaine, les équipes des outils de modération et d'apprentissage automatique poursuivront le déploiement d'[[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|un nouveau filtre pour les modifications récentes]], le lançant à la troisième et dernière série de Wikipédia. Ce filtre utilise le modèle de risque de réversion, qui a été créé par l'équipe de recherche, pour mettre en avant les modifications susceptibles d'être annulées et aider les patrouilleurs des modifications récentes à identifier les contributions potentiellement problématiques. La fonctionnalité sera déployée sur les Wikipédias suivantes : {{int:project-localized-name-azwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-lawiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-mkwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-mlwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-mrwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-nnwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-pawiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-swwiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-tewiki/fr}}{{int:comma-separator/fr}}{{int:project-localized-name-tlwiki/fr}}. Le déploiement se poursuivra dans les semaines à venir pour inclure [[mw:Special:MyLanguage/2025 RecentChanges Language Agnostic Revert Risk Filtering|le reste des Wikipédias de ce projet]]. [https://phabricator.wikimedia.org/T391964]
'''Actualités pour la contribution'''
* La semaine dernière, des [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|comptes temporaires]] ont été déployés sur les Wikipédias tchèque, coréenne et turque. Cette semaine et la semaine prochaine, des déploiements sur des Wikipédias plus importantes suivront. [[mw:Talk:Trust and Safety Product/Temporary Accounts|Partagez vos pensées]] sur le projet. [https://phabricator.wikimedia.org/T340001]
* Plus tard cette semaine, l'équipe d'Édition publiera ''[[mw:Special:MyLanguage/Help:Edit check#Multi check|Multi Check]]'' sur toutes les Wikipédias (à l'exception de la Wikipédia en anglais). Cette fonctionnalité affiche plusieurs [[mw:Special:MyLanguage/Help:Edit check#Reference check|vérifications de références]] dans l'interface d'édition. Elle encourage les utilisateurs à ajouter des citations lorsqu'ils ajoutent plusieurs nouveaux paragraphes à un article Wikipédia. Cette fonctionnalité était auparavant disponible en tant que test A/B. [https://analytics.wikimedia.org/published/reports/editing/multi_check_ab_test_report_final.html#summary-of-results Le test montre] que les utilisateurs qui voient plusieurs vérifications sont 1,3 fois plus susceptibles d'ajouter une référence à leur modification, et que leur modification est moins susceptible d'être annulée (-34,7 %). [https://phabricator.wikimedia.org/T395519]
* Quelques pages doivent être renommées en raison de mises à jour logicielles et pour correspondre à des normes Unicode plus récentes. Tous ces changements sont liés aux modifications de la capitalisation des titres. Environ 71 pages et 3 fichiers seront renommés, sur 15 wikis ; la liste complète se trouve dans [[phab:T396903|la tâche]]. Les développeurs renommeront ces pages la semaine prochaine, et ils corrigeront les redirections et les liens de fichiers intégrés quelques minutes plus tard via une mise à jour des paramètres système.
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:24|la tâche soumise|les {{formatnum:24}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:24||s}} la semaine dernière]]. Par exemple, un bug qui avait fait défiler les pages vers le haut lorsque du texte près du haut était sélectionné a été corrigé. [https://phabricator.wikimedia.org/T364023]
'''Actualités pour la contribution technique'''
* Les éditeurs peuvent désormais utiliser des modules Lua pour filtrer et transformer des données tabulaires à utiliser avec [[mw:Special:MyLanguage/Extension:Chart|Extension:Chart]]. Cela peut être utilisé pour des choses comme sélectionner un sous-ensemble de lignes ou de colonnes à partir des données sources, convertir entre des unités, le traitement statistique et de nombreuses autres transformations utiles. [[mw:Special:MyLanguage/Extension:Chart/Transforms|Des informations sur la façon d'utiliser les transformations sont disponibles]]. [https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:Chart/Project/Updates]
* La variable <code dir=ltr>all_links</code> dans [[Special:AbuseFilter|AbuseFilter]] a été renommée <code dir=ltr>new_links</code> pour plus de cohérence avec les autres variables. Les anciennes utilisations continueront de fonctionner. [https://phabricator.wikimedia.org/T391811]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.7|MediaWiki]]
'''En détails'''
* Le dernier [[mw:Special:MyLanguage/Growth/Newsletters/34|bulletin de croissance]] trimestriel est disponible. Il contient : les mises à jour récentes pour la tâche "Ajouter un lien", deux nouvelles fonctionnalités d'engagement des nouveaux arrivants et des mises à jour de la configuration de la communauté.
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/26|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W26"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 24 juin 2025 à 01:20 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28870688 -->
== Commentaires sur les projets de Wikimédia ==
<section begin="message"/>
Chère communauté Wikimédia,
[[m:Wikimedia Foundation Community Affairs Committee|Le Comité des affaires communautaires (CAC)]] du conseil d'administration de la Fondation Wikimedia a chargé [[m:Wikimedia Foundation Community Affairs Committee/Sister Projects Task Force|le groupe de travail sur les projets frères (SPTF)]] de mettre à jour et d’implémenter une procédure d'évaluation du cycle de vie des projets frères, c'est-à-dire [[m:Wikimedia projects|des projets wiki soutenus par la Fondation Wikimédia (WMF)]].
La vision d'une connaissance libre, pertinente, accessible et percutante a toujours guidé le mouvement Wikimédia. Alors que l’environnement des projets Wikimédia continue d'évoluer, il est crucial de réévaluer régulièrement les projets existants afin de nous assurer qu'ils correspondent toujours à nos objectifs et aux capacités de la communauté.
Malgré leurs nobles intentions, certains projets peuvent ne plus remplir efficacement leur objectif initial. '''Examiner de tels projets ne signifie pas les abandonner, mais plutôt gérer de manière responsable les ressources partagées'''. Le temps des bénévoles, le soutien du personnel, les infrastructures et l'attention de la communauté sont limités, et les coûts non techniques ont tendance à augmenter considérablement à mesure que notre écosystème entre dans une ère d'Internet différente de celle de nos débuts. Soutenir des projets inactifs ou qui ne répondent pas à nos ambitions peut involontairement détourner ces ressources de domaines à plus fort impact potentiel.
De plus, maintenir des projets qui ne reflètent plus la qualité et la fiabilité que Wikimédia représente comporte un risque pour la réputation. Un projet abandonné ou moins fiable affecte la confiance dans le mouvement Wikimédia.
Enfin, '''ne pas abandonner ou repenser les projets qui ne fonctionnent plus peut rendre beaucoup plus difficile le lancement de nouveaux projets'''. Lorsque la communauté se sent liée à toutes les décisions passées, aussi obsolètes soient-elles, nous risquons la stagnation. Un environnement sain doit permettre l'évolution, l'adaptation et, si nécessaire, de lâcher-prise. Si nous créons l'attente que chaque projet doit exister indéfiniment, nous limitons notre capacité d'expérimentation et d'innovation.
C'est pourquoi le SPTF a examiné des demandes concernant le cycle de vie de deux projets frères afin d'analyser et de démontrer le processus d'évaluation. Nous avons choisi Wikispore comme étude de cas pour l'ouverture éventuelle d'un nouveau projet frère, et Wikinews comme étude de cas pour l'évaluation d'un projet existant. Les conclusions préliminaires ont été discutées lors de la réunion du CAC du 11 septembre 2024, et le CAC a recommandé une consultation communautaire sur les deux propositions.
=== Wikispore ===
La [[m:Wikispore|demande d'admission de Wikispore]] comme nouveau projet frère a été soumise en 2019. Le SPTF a décidé d'examiner cette demande plus en détail, car, plutôt que de se concentrer sur un sujet spécifique, comme le sont la plupart des propositions de nouveaux projets frères, Wikispore a le potentiel de soutenir plusieurs projets frères en cours de démarrage.
Après mûre réflexion, le SPTF a décidé de '''ne pas recommander''' Wikispore comme projet frère Wikimedia. Compte tenu du niveau d'activité actuel, la structure en cours offre une '''plus grande flexibilité''' et plus d’expérimentation, tandis que la WMF fournit le soutien infrastructurel de base.
Nous reconnaissons le potentiel de l'initiative et sollicitons l'avis de la communauté sur ce qui constituerait un niveau d'activité et d'engagement suffisant pour reconsidérer son statut à l'avenir.
Dans le cadre de ce processus, nous avons partagé la décision avec la communauté Wikispore et invité l'un de ses responsables, Pharos, à une réunion du SPTF.
En ce moment, nous sollicitons particulièrement vos commentaires sur des critères mesurables indiquant l'état de préparation du projet, tels que le nombre de contributeurs, le volume de contenu et le soutien durable de la communauté. Cela clarifiera les critères nécessaires à l'ouverture d'un nouveau projet frère, y compris une éventuelle nouvelle candidature de Wikispore. Cependant, les chiffres restent seulement indicatifs, car ils peuvent toujours être manipulés.
=== Wikinews ===
Nous avons choisi d'évaluer Wikinews parmi les projets frères existants, car c'est celui pour lequel nous avons observé le plus grand niveau d'inquiétude à plusieurs égards.
Depuis la création du SPTF en 2023, ses membres ont sollicité l'avis de la communauté lors de conférences et d'appels communautaires concernant les projets frères qui n'avaient pas tenu leurs promesses au sein du mouvement Wikimédia.[https://commons.wikimedia.org/wiki/File:WCNA_2024._Sister_Projects_-_opening%3F_closing%3F_merging%3F_splitting%3F.pdf <nowiki>[1]</nowiki>][https://meta.wikimedia.org/wiki/Wikimedia_Foundation_Community_Affairs_Committee/Sister_Projects_Task_Force#Wikimania_2023_session_%22Sister_Projects:_past,_present_and_the_glorious_future%22 <nowiki>[2]</nowiki>][https://meta.wikimedia.org/wiki/WikiConvention_francophone/2024/Programme/Quelle_proc%C3%A9dure_pour_ouvrir_ou_fermer_un_projet_%3F <nowiki>[3]</nowiki>] Wikinews était le principal candidat à une évaluation, car il avait été proposé par des personnes issues de plusieurs communautés linguistiques. De plus, selon la plupart des indicateurs, il s'agit du projet frère le moins actif, affichant la plus forte baisse d'activité au fil des ans.
Bien que le comité des langues ouvre et ferme régulièrement les versions linguistiques des projets frères dans des « petites » langues, aucune proposition valable n'a jamais été présentée pour fermer Wikipédia dans les langues majeures, ni aucun projet en anglais. Ce n'est pas le cas de Wikinews, où une proposition de fermeture de Wikinews en anglais a été formulée, laquelle a suscité un certain intérêt, mais n'a donné lieu à aucune action,[https://meta.wikimedia.org/wiki/Proposals_for_closing_projects/Closure_of_English_Wikinews <nowiki>[4]</nowiki>][https://meta.wikimedia.org/wiki/WikiConvention_francophone/2024/Programme/Quelle_proc%C3%A9dure_pour_ouvrir_ou_fermer_un_projet_%3F voir également la section 5 de <nowiki>[5]</nowiki>], ainsi qu'un projet de proposition visant à fermer Wikinews dans toutes les langues[https://meta.wikimedia.org/wiki/Talk:Proposals_for_closing_projects/Archive_2#Close_Wikinews_completely,_all_languages? <nowiki>[6]</nowiki>].
[[:c:File:Sister Projects Taskforce Wikinews review 2024.pdf|Les indicateurs initiaux]] compilés par l'équipe de la WMF soutiennent également les inquiétudes de la communauté du mouvement concernant Wikinews.
Sur la base de ce rapport, la SPTF recommande une réévaluation communautaire de Wikinews. Nous concluons que sa structure actuelle et son niveau d'activité sont les plus faibles parmi les projets frères existants. La SPTF recommande également de suspendre l'ouverture de nouvelles éditions linguistiques pendant la durée de la consultation.
La SPTF soumet cette analyse à la discussion et encourage les discussions sur d'autres solutions, notamment d'éventuelles restructurations ou une intégration avec d'autres initiatives de Wikimédia.
Les options évoquées jusqu'à présent (peuvent être appliquées uniquement aux langues peu actives ou à toutes les langues) incluent, sans s'y limiter:
*Restructurer le fonctionnement de Wikinews et l'associer aux autres projets d'actualité;
*Fusionner le contenu de Wikinews dans les Wikipédias des langues concernées, éventuellement dans un nouvel espace de noms,
*Fusionner le contenu dans des projets externes sous licence compatible,
*Archiver les projets Wikinews.
Vos idées et points de vue sont précieux pour façonner l'avenir de ces projets. Nous encourageons tous les membres de la communauté intéressés à partager leurs réflexions sur les pages de discussion correspondantes ou via les autres canaux de commentaires dédiés.
<span id="Feedback_and_next_steps"></span>
=== Commentaires et prochaines étapes ===
Nous vous serons reconnaissants si vous pouvez participer à une discussion sur l'avenir de ces projets et sur le processus d'évaluation. Nous créons actuellement deux pages de projets: [[m:Public consultation about Wikispore|Public consultation about Wikispore]] et [[m:Public consultation about Wikinews|Public consultation about Wikinews]]. Merci de participer entre le 27 juin et le 27 juillet, date à laquelle nous résumerons la discussion pour la suite. Vous pouvez écrire dans votre langue.
J'animerai également une discussion communautaire le mercredi 16 juillet à 11 h UTC et le jeudi 17 juillet à 17 h UTC (les liens pour se connecter seront communiqués prochainement) et je serai présente à Wikimania pour poursuivre les discussions.
<section end="message"/>
-- [[User:Victoria|Victoria]] on behalf of the Sister Project Task Force, 27 juin 2025 à 22:56 (CEST)
<!-- Message envoyé par User:Johan (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=User:Johan_(WMF)/Sister_project_MassMassage_on_behalf_of_Victoria/Target_list&oldid=28911188 -->
== Actualités techniques n° 2025-27 ==
<section begin="technews-2025-W27"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/27|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* La [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|extension CampaignEvents]] a été activée sur toutes les Wikipédias. L'extension facilite l'organisation et la participation à des activités collaboratives, telles que les edit-a-thons et les WikiProjects, sur les wikis. L'extension a trois fonctionnalités : [[m:Special:MyLanguage/Event Center/Registration|Inscription à un événement]], [[m:Special:MyLanguage/CampaignEvents/Collaboration list|Liste de collaboration]], et [[m:Campaigns/Foundation Product Team/Invitation list|Liste d'invitation]]. Pour demander l'extension pour votre wiki, visitez la [[m:Special:MyLanguage/CampaignEvents/Deployment status#How to Request the CampaignEvents Extension for your wiki|page d'information sur le déploiement]].
'''Actualités pour la contribution'''
* Les mainteneurs des filtres de abus peuvent maintenant [[mw:Special:MyLanguage/Extension:IPReputation/AbuseFilter variables|faire correspondre avec les données de réputation IP]] dans [[mw:Special:MyLanguage/Extension:AbuseFilter|Filtres d'abus]]. Les données de réputation IP sont des informations sur les proxys et les VPN associés à l'adresse IP de l'utilisateur. Ces données ne sont pas affichées publiquement et ne sont pas générées pour les actions effectuées par des comptes enregistrés. [https://phabricator.wikimedia.org/T354599]
* Le contenu caché qui se trouve dans [[mw:Special:MyLanguage/Manual:Collapsible elements|des parties réductibles des pages wiki]] sera désormais révélé lorsqu'une personne recherche la page en utilisant la fonction « Rechercher dans la page » du navigateur Web (Ctrl+F ou ⌘F) dans les navigateurs compatibles. [https://phabricator.wikimedia.org/T327893][https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/hidden#browser_compatibility]
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] Une nouvelle fonctionnalité, appelée [[mw:Special:MyLanguage/Help:TemplateData/Template discovery|Modèles favoris]], sera déployée plus tard cette semaine sur tous les projets (sauf la Wikipédia en anglais, qui recevra la fonctionnalité la semaine prochaine), suite à une phase de test sur les Wikipédias polonaise et arabe, et sur les Wikisource italien et anglais. Cette fonctionnalité offrira une meilleure façon pour les contributeurs nouveaux et expérimentés de se souvenir et de découvrir des modèles via la boîte de dialogue des modèles, en permettant aux utilisateurs de mettre des modèles sur une "liste de favoris" spéciale. La fonctionnalité fonctionne à la fois avec l'éditeur visuel et l'éditeur wikitext. Cette fonctionnalité est un [[m:Special:MyLanguage/Community Wishlist/Focus areas/Template recall and discovery|domaine d'intérêt de la wishlist de la communauté]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:31|la tâche soumise|les {{formatnum:31}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:31||s}} la semaine dernière]]. Par exemple, un bug a été corrigé qui avait causé l'envoi de certaines notifications plusieurs fois. [https://phabricator.wikimedia.org/T397103]
'''Actualités pour la contribution technique'''
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.8|MediaWiki]]
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/27|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W27"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 1 juillet 2025 à 01:40 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28917415 -->
== Actualités techniques n° 2025-28 ==
<section begin="technews-2025-W28"/><div class="plainlinks">
Dernières '''[[m:Special:MyLanguage/Tech/News|actualités techniques]]''' de la communauté technique de Wikimedia. N’hésitez pas à informer les autres utilisateurs de ces changements. Certains changements ne vous concernent pas. [[m:Special:MyLanguage/Tech/News/2025/28|D’autres traductions]] sont disponibles.
'''En lumière cette semaine'''
* Des [[mw:Special:MyLanguage/Help:Temporary accounts|comptes temporaires]] ont été déployés sur 18 Wikipédias larges et de taille moyenne, y compris l'allemand, japonais, français et chinois. Maintenant, environ un tiers de toute l'activité des utilisateurs non connectés sur les wikis provient des comptes temporaires. Les utilisateurs impliqués dans la surveillance peuvent être intéressés par deux nouvelles pages de documentation : [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Access to IP|Accès à IP]], expliquant tout ce qui concerne l'accès aux adresses IP des comptes temporaires, et [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts/Repository|Dépôt]] avec une liste de nouveaux gadgets et scripts utilisateur.
'''Actualités pour la contribution'''
* Tout le monde peut jouer à un nouveau jeu expérimental, [[mw:Special:MyLanguage/New Engagement Experiments/WikiRun|WikiRun]], qui vous permet de courir à travers Wikipédia en cliquant d'un article à l'autre, dans le but d'atteindre une page cible en aussi peu d'étapes et en aussi peu de temps que possible. L'objectif du projet est d'explorer de nouvelles façons d'engager les lecteurs. [https://wikirun-game.toolforge.org/ Essayez de jouer au jeu] et faites savoir à l'équipe ce que vous en pensez [[mw:Talk:New Engagement Experiments/WikiRun|sur la page de discussion]].
* Les utilisateurs de l'application Android de Wikipédia dans certaines langues peuvent désormais jouer au nouveau [[mw:Special:MyLanguage/Wikimedia Apps/Team/Android/TrivaGame|jeu de trivia]]. ''Lequel est arrivé en premier ?'' est un simple jeu d'histoire où vous devinez lequel de deux événements s'est produit plus tôt à la date du jour. Il était auparavant disponible en tant que test A/B. Il est maintenant disponible pour tous les utilisateurs en anglais, allemand, français, espagnol, portugais, russe, arabe, turc et chinois. L'objectif de cette fonctionnalité est d'aider à engager de nouvelles générations de lecteurs. [https://meta.wikimedia.org/wiki/Special:MyLanguage/Tech/News/2025/22]
* Les utilisateurs de l'application Wikipédia iOS dans certaines langues peuvent voir une nouvelle fonctionnalité de navigation par onglets qui vous permet d'ouvrir plusieurs onglets pendant que vous lisez. Cette fonctionnalité facilite l'exploration de sujets connexes et le passage d'un article à l'autre. Le test A/B est actuellement en cours en arabe, en anglais et en japonais dans des régions sélectionnées. Plus de détails sont disponibles sur la [[mw:Special:MyLanguage/Wikimedia Apps/Team/iOS/Tabbed Browsing (Tabs)|page du projet Navigation par onglets]].
* Les bureaucrates sur les wikis Wikimédia peuvent maintenant utiliser [[{{#special:VerifyOATHForUser}}]] pour vérifier si les utilisateurs ont activé [[mw:Special:MyLanguage/Help:Two-factor authentication|l'authentification à deux facteurs]]. [https://phabricator.wikimedia.org/T265726]
* [[File:Octicons-gift.svg|12px|link=|class=skin-invert|Concerne un souhait]] Une nouvelle fonctionnalité liée à [[m:Special:MyLanguage/Community Wishlist/Focus areas/Template recall and discovery|Rappel et découverte de modèle]] sera déployée plus tard cette semaine sur tous les projets Wikimédia : un [[mw:Special:MyLanguage/Help:TemplateData/Template discovery#Template categories|navigateur de catégories de modèles]] sera introduit pour aider les utilisateurs à trouver des modèles à ajouter à leur liste de « favoris ». Le navigateur permettra aux utilisateurs de parcourir une liste de modèles qui ont été organisés dans un arbre de catégories donné. La fonctionnalité a été demandée par la communauté [[m:Special:MyLanguage/Community Wishlist/Wishes/Select templates by categories|à travers la liste de souhaits de la communauté]].
* Il est désormais possible d'accéder aux préférences de la liste de surveillance depuis la page de la liste de surveillance. De plus, le bouton redondant pour modifier la liste de surveillance a été retiré. [https://www.mediawiki.org/wiki/Moderator_Tools/Watchlist]
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Voir {{PLURAL:27|la tâche soumise|les {{formatnum:27}} tâches soumises}} par la communauté [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|résolue{{PLURAL:27||s}} la semaine dernière]].
'''Actualités pour la contribution technique'''
* Dans le cadre de [[mw:MediaWiki_1.44|MediaWiki 1.44]], il existe désormais un système de notifications intégré et unifié qui facilite l'envoi, la gestion et la personnalisation des notifications pour les développeurs. Consultez la documentation mise à jour à [[mw:Manual:Notifications|Manuel:Notifications]], des informations sur la migration dans [[phab:T388663|T388663]] et des détails sur les hooks obsolètes dans [[phab:T389624|T389624]].
* [[File:Octicons-sync.svg|12px|link=|class=skin-invert|Sujet récurrent]] Détail des mises-à-jour à venir cette semaine : [[mw:MediaWiki 1.45/wmf.9|MediaWiki]]
'''Rencontres et évènements'''
* [[d:Special:MyLanguage/Event:WikidataCon 2025|WikidataCon 2025]], la conférence dédiée à Wikidata est désormais ouverte pour [https://pretalx.com/wikidatacon-2025/cfp propositions de sessions] et pour [[d:Special:RegisterForEvent/1340|l'inscription]]. L'événement de cette année se tiendra en ligne du 31 octobre au 02 novembre et explorera le thème « Connecter les gens grâce aux données ouvertes liées ».
'''''[[m:Special:MyLanguage/Tech/News|Actualités techniques]]''' préparées par les [[m:Special:MyLanguage/Tech/News/Writers|rédacteurs des actualités techniques]] et postées par [[m:Special:MyLanguage/User:MediaWiki message delivery|robot]]. [[m:Special:MyLanguage/Tech/News#contribute|Contribuer]] • [[m:Special:MyLanguage/Tech/News/2025/28|Traduire]] • [[m:Tech|Obtenir de l’aide]] • [[m:Talk:Tech/News|Donner son avis]] • [[m:Global message delivery/Targets/Tech ambassadors|S’abonner ou se désabonner]].''
</div><section end="technews-2025-W28"/>
<bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> 8 juillet 2025 à 02:05 (CEST)
<!-- Message envoyé par User:Quiddity (WMF)@metawiki en utilisant la liste sur https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=28930584 -->
n8dyiuz7gv6awk5ofarvxijn6wn0xdq
Mathc complexes/069
0
82609
746221
745989
2025-07-07T13:22:59Z
Xhungab
23827
746221
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc complexes (livre)]]
:
[[Mathc complexes/a26| '''Gauss-Jordan''']]
:
{{Partie{{{type|}}}|L'équation d'un polynôme}}
:
Copier la bibliothèque dans votre répertoire de travail avec les fichiers des parties précédentes :
*[[Mathc complexes/062| '''d.h ..................... Déclaration des fichiers h''']]
Ne pas conserver le fichier d.h avec la bibliothèque après avoir testé les exemples.
'''Présentation :'''
<syntaxhighlight lang="c">
Calculons les coefficients d'un polynôme.
y = ax**2 + bx + c
qui passe par ces trois points.
x[1], y[1]
x[2], y[2]
x[3], y[3]
En utilisant les points nous obtenons la matrice :
x**2 x**1 x**0 y
x[1]**2 x[1]**1 x[1]**0 y[1]
x[2]**2 x[2]**1 x[2]**0 y[2]
x[3]**2 x[3]**1 x[3]**0 y[3]
Que nous pouvons écrire :
x**2 x 1 y
x[1]**2 x[1] 1 y[1]
x[2]**2 x[2] 1 y[2]
x[3]**2 x[3] 1 y[3]
Utilisons la fonction gj_TP_mR(Ab); pour résoudre
le système qui va nous donner les coefficients a,b,c
</syntaxhighlight>
Les exemples :
* [[Mathc complexes/063|c01.c ]], [[Mathc complexes/06w| QR ]]..... y = ax**2 + bx + c
* [[Mathc complexes/064|c02.c ]], [[Mathc complexes/06x| QR ]]
* [[Mathc complexes/065|c03.c ]] ..... y = ax**3 + bx**2 + cx + d
* [[Mathc complexes/066|c04.c ]]
* [[Mathc complexes/067|c05.c ]] ..... y = ax**4 + bx**3 + cx**2 + dx + e
* [[Mathc complexes/068|c06.c ]]
{{AutoCat}}
hv9v3de5ui9yv1tewyl1wa8kmb59l90
746227
746221
2025-07-07T13:56:11Z
Xhungab
23827
746227
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc complexes (livre)]]
:
[[Mathc complexes/a26| '''Gauss-Jordan''']]
:
{{Partie{{{type|}}}|L'équation d'un polynôme}}
:
Copier la bibliothèque dans votre répertoire de travail avec les fichiers des parties précédentes :
*[[Mathc complexes/062| '''d.h ..................... Déclaration des fichiers h''']]
Ne pas conserver le fichier d.h avec la bibliothèque après avoir testé les exemples.
'''Présentation :'''
<syntaxhighlight lang="c">
Calculons les coefficients d'un polynôme.
y = ax**2 + bx + c
qui passe par ces trois points.
x[1], y[1]
x[2], y[2]
x[3], y[3]
En utilisant les points nous obtenons la matrice :
x**2 x**1 x**0 y
x[1]**2 x[1]**1 x[1]**0 y[1]
x[2]**2 x[2]**1 x[2]**0 y[2]
x[3]**2 x[3]**1 x[3]**0 y[3]
Que nous pouvons écrire :
x**2 x 1 y
x[1]**2 x[1] 1 y[1]
x[2]**2 x[2] 1 y[2]
x[3]**2 x[3] 1 y[3]
Utilisons la fonction gj_TP_mR(Ab); pour résoudre
le système qui va nous donner les coefficients a,b,c
</syntaxhighlight>
Les exemples :
* [[Mathc complexes/063|c01.c ]], [[Mathc complexes/06w| QR ]]..... y = ax**2 + bx + c
* [[Mathc complexes/064|c02.c ]], [[Mathc complexes/06x| QR ]]
* [[Mathc complexes/065|c03.c ]], [[Mathc complexes/06y| QR ]] ..... y = ax**3 + bx**2 + cx + d
* [[Mathc complexes/066|c04.c ]], [[Mathc complexes/06z| QR ]]
* [[Mathc complexes/067|c05.c ]] ..... y = ax**4 + bx**3 + cx**2 + dx + e
* [[Mathc complexes/068|c06.c ]]
{{AutoCat}}
4eqqv7m06dcvpkfkb18l9n3janq9s41
746235
746227
2025-07-07T14:11:46Z
Xhungab
23827
746235
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc complexes (livre)]]
:
[[Mathc complexes/a26| '''Gauss-Jordan''']]
:
{{Partie{{{type|}}}|L'équation d'un polynôme}}
:
Copier la bibliothèque dans votre répertoire de travail avec les fichiers des parties précédentes :
*[[Mathc complexes/062| '''d.h ..................... Déclaration des fichiers h''']]
Ne pas conserver le fichier d.h avec la bibliothèque après avoir testé les exemples.
'''Présentation :'''
<syntaxhighlight lang="c">
Calculons les coefficients d'un polynôme.
y = ax**2 + bx + c
qui passe par ces trois points.
x[1], y[1]
x[2], y[2]
x[3], y[3]
En utilisant les points nous obtenons la matrice :
x**2 x**1 x**0 y
x[1]**2 x[1]**1 x[1]**0 y[1]
x[2]**2 x[2]**1 x[2]**0 y[2]
x[3]**2 x[3]**1 x[3]**0 y[3]
Que nous pouvons écrire :
x**2 x 1 y
x[1]**2 x[1] 1 y[1]
x[2]**2 x[2] 1 y[2]
x[3]**2 x[3] 1 y[3]
Utilisons la fonction gj_TP_mR(Ab); pour résoudre
le système qui va nous donner les coefficients a,b,c
</syntaxhighlight>
Les exemples :
* [[Mathc complexes/063|c00a.c ]], [[Mathc complexes/06w| QR ]]..... y = ax**2 + bx + c
* [[Mathc complexes/064|c00b.c ]], [[Mathc complexes/06x| QR ]]
* [[Mathc complexes/065|c00c.c ]], [[Mathc complexes/06y| QR ]] ..... y = ax**3 + bx**2 + cx + d
* [[Mathc complexes/066|c00d.c ]], [[Mathc complexes/06z| QR ]]
* [[Mathc complexes/067|c00e.c ]], [[Mathc complexes/070| QR ]] ..... y = ax**4 + bx**3 + cx**2 + dx + e
* [[Mathc complexes/068|c00f.c ]], [[Mathc complexes/071| QR ]]
{{AutoCat}}
ev5ltrvfiaktfgt15d1nbgjqjvgqthp
Mathc complexes/06m
0
82629
746213
746206
2025-07-07T12:50:53Z
Xhungab
23827
746213
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc complexes (livre)]]
:
[[Mathc complexes/a26| '''Gauss-Jordan''']]
:
{{Partie{{{type|}}}|L'équation d'un cercle}}
:
Copier la bibliothèque dans votre répertoire de travail avec les fichiers des parties précédentes :
*[[Mathc complexes/06n| '''d.h ..................... Déclaration des fichiers h''']]
Ne pas conserver le fichier d.h avec la bibliothèque après avoir testé les exemples.
'''Présentation :'''
<syntaxhighlight lang="C">
Calculons les coefficients a, b, c, d d'un cercle,
ax**2 + ay**2 + bx + cy + d = 0
Qui passe par ces trois points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3])
En utilisant ces trois points nous avons cette matrice.
(a)x**2 (a)y**2 (b)x (c)y (d) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Ce système a trois lignes et quatre inconnues.
Il est homogène, donc il a une infinité de solution.
Pour trouver une solution j'ai choisi que a = 1.
Nous obtenons cette matrice.
(a)x**2 (a)y**2
1 0 0 0 0 1
0 1 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Il suffit de resoudre le système.
</syntaxhighlight>
Deux exemples :
* [[Mathc complexes/06o|c00a.c ]], [[Mathc complexes/06s| QR ]]
* [[Mathc complexes/06p|c00b.c ]], [[Mathc complexes/06t| QR ]]
* [[Mathc complexes/06q|c00c.c ]], [[Mathc complexes/06u| QR ]]
* [[Mathc complexes/06r|c00d.c ]], [[Mathc complexes/06v| QR ]]
{{AutoCat}}
9yvvhph0583tz2nr9zhq9ikdfwd6pmi
Mathc complexes/06s
0
82635
746214
2025-07-07T12:53:28Z
Xhungab
23827
news
746214
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc complexes (livre)]]
:
[[Mathc complexes/06m| '''Application''']]
:
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00a.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00a.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R3*(C2*C2)] ={
1,0, -2,0,
2,0, -3,0,
3,0, 6,0 };
double ab[RA*((CA+Cb)*C2)]={
/* x**2 y**2 x y e = +0 */
+1,+0, +0,+0, +0,+0, +0,+0, +0,+0, +1,+0,
+0,+0, +1,+0, +0,+0, +0,+0, +0,+0, +1,+0,
+1,+0, +4,+0, +1,+0, -2,+0, +1,+0, +0,+0,
+4,+0, +9,+0, +2,+0, -3,+0, +1,+0, +0,+0,
+9,+0, +36,+0, +3,+0, +6,+0, +1,+0, +0,+0
};
double **XY = ca_A_mZ(xy,i_mZ(R3,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y c");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
stop();
clrscrn();
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three XY.
x y
+1 -2
+2 -3
+3 +6
Using the given XY, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y c
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +4.00 +1.00 -2.00 +1.00 +0.00
+4.00 +9.00 +2.00 -3.00 +1.00 +0.00
+9.00 +36.00 +3.00 +6.00 +1.00 +0.00
Press return to continue.
Q :
+0.1005 -0.4928 -0.5780 -0.3248 +0.5544
+0.0000 +0.1340 +0.0472 -0.9182 -0.3696
+0.1005 +0.0433 +0.7229 -0.2169 +0.6468
+0.4020 -0.7650 +0.3370 +0.0544 -0.3696
+0.9045 +0.3899 -0.1659 +0.0360 +0.0308
R :
+9.9499 +36.5834 +3.6181 +4.0202 +1.4071
-0.0000 +7.4603 -0.3168 +4.5480 -0.3317
-0.0000 -0.0000 +0.8993 -3.4522 +0.8940
+0.0000 +0.0000 -0.0000 +0.4863 -0.1264
+0.0000 +0.0000 +0.0000 +0.0000 +0.3080
Press return to continue.
Q_T :
+1.0050e-01 +0.0000e+00 +1.0050e-01 +4.0202e-01 +9.0453e-01
-4.9284e-01 +1.3404e-01 +4.3327e-02 -7.6499e-01 +3.8994e-01
-5.7800e-01 +4.7225e-02 +7.2290e-01 +3.3703e-01 -1.6589e-01
-3.2485e-01 -9.1825e-01 -2.1687e-01 +5.4449e-02 +3.5992e-02
+5.5444e-01 -3.6962e-01 +6.4684e-01 -3.6962e-01 +3.0802e-02
invR :
+1.0050e-01 -4.9284e-01 -5.7800e-01 -3.2485e-01 +5.5444e-01
+0.0000e+00 +1.3404e-01 +4.7225e-02 -9.1825e-01 -3.6962e-01
+0.0000e+00 +0.0000e+00 +1.1120e+00 +7.8932e+00 +1.2321e-02
-0.0000e+00 +0.0000e+00 -0.0000e+00 +2.0561e+00 +8.4398e-01
+0.0000e+00 +0.0000e+00 +0.0000e+00 -0.0000e+00 +3.2465e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+1.00
-10.40
-2.40
+0.60
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +1.00y**2 -10.40x -2.40y +0.60 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
pa0rnxb9cz6d7w2tsjbonoervd5ap51
Mathc complexes/06t
0
82636
746215
2025-07-07T12:55:50Z
Xhungab
23827
news
746215
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc complexes (livre)]]
:
[[Mathc complexes/06m| '''Application''']]
:
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00b.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00b.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R3*(C2*C2)] ={
1,0, -1,0,
2,0, -9,0,
3,0, -8,0 };
double ab[RA*((CA+Cb)*C2)]={
/* x**2 y**2 x y e = +0 */
+1,+0, +0,+0, +0,+0, +0,+0, +0,+0, +1,+0,
+0,+0, +1,+0, +0,+0, +0,+0, +0,+0, +1,+0,
+1,+0, +1,+0, +1,+0, -1,+0, +1,+0, +0,+0,
+4,+0, +81,+0, +2,+0, -9,+0, +1,+0, +0,+0,
+9,+0, +64,+0, +3,+0, -8,+0, +1,+0, +0,+0
};
double **XY = ca_A_mZ(xy,i_mZ(R3,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y c");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
stop();
clrscrn();
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three XY.
x y
+1 -1
+2 -9
+3 -8
Using the given XY, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y c
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +1.00 +1.00 -1.00 +1.00 +0.00
+4.00 +81.00 +2.00 -9.00 +1.00 +0.00
+9.00 +64.00 +3.00 -8.00 +1.00 +0.00
Press return to continue.
Q :
+0.1005 -0.1835 -0.3374 -0.6212 +0.6757
+0.0000 +0.0202 -0.0143 +0.7368 +0.6757
+0.1005 -0.1634 +0.9344 -0.1904 +0.2323
+0.4020 +0.8993 +0.0633 -0.1202 +0.1056
+0.9045 -0.3612 -0.0944 +0.1436 -0.1478
R :
+9.9499 +90.5539 +3.6181 -10.9549 +1.4071
+0.0000 +49.5882 +0.5518 -5.0413 +0.3748
-0.0000 -0.0000 +0.7776 -0.7482 +0.9032
-0.0000 -0.0000 -0.0000 +0.1234 -0.1670
-0.0000 -0.0000 -0.0000 +0.0000 +0.1900
Press return to continue.
Q_T :
+1.0050e-01 +0.0000e+00 +1.0050e-01 +4.0202e-01 +9.0453e-01
-1.8353e-01 +2.0166e-02 -1.6337e-01 +8.9933e-01 -3.6116e-01
-3.3742e-01 -1.4312e-02 +9.3436e-01 +6.3264e-02 -9.4444e-02
-6.2115e-01 +7.3679e-01 -1.9038e-01 -1.2020e-01 +1.4359e-01
+6.7567e-01 +6.7567e-01 +2.3226e-01 +1.0557e-01 -1.4780e-01
invR :
+1.0050e-01 -1.8353e-01 -3.3742e-01 -6.2115e-01 +6.7567e-01
-0.0000e+00 +2.0166e-02 -1.4312e-02 +7.3679e-01 +6.7567e-01
+0.0000e+00 +0.0000e+00 +1.2861e+00 +7.7945e+00 +7.3667e-01
-0.0000e+00 +0.0000e+00 -0.0000e+00 +8.1005e+00 +7.1180e+00
+0.0000e+00 +0.0000e+00 +0.0000e+00 -0.0000e+00 +5.2623e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+1.00
+1.44
+10.56
+7.11
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +1.00y**2 +1.44x +10.56y +7.11 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
clgc4o1zfctlxvs2r5owislb6f1v71x
Mathc complexes/06u
0
82637
746217
2025-07-07T12:58:05Z
Xhungab
23827
news
746217
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc complexes (livre)]]
:
[[Mathc complexes/06m| '''Application''']]
:
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00c.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00c.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R3*(C2*C2)] ={
1,0, 10,0,
2,0, 1,0,
3,0, -10,0 };
double ab[RA*((CA+Cb)*C2)]={
/* x**2 y**2 x y e = +0 */
+1,+0, +0,+0, +0,+0, +0,+0, +0,+0, +1,+0,
+0,+0, +1,+0, +0,+0, +0,+0, +0,+0, +1,+0,
+1,+0, +100,+0, +1,+0, +10,+0, +1,+0, +0,+0,
+4,+0, +1,+0, +2,+0, +1,+0, +1,+0, +0,+0,
+9,+0, +100,+0, +3,+0, -10,+0, +1,+0, +0,+0
};
double **XY = ca_A_mZ(xy,i_mZ(R3,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y c");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
stop();
clrscrn();
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three XY.
x y
+1 +10
+2 +1
+3 -10
Using the given XY, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y c
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +100.00 +1.00 +10.00 +1.00 +0.00
+4.00 +1.00 +2.00 +1.00 +1.00 +0.00
+9.00 +100.00 +3.00 -10.00 +1.00 +0.00
Press return to continue.
Q :
+0.1005 -0.1023 -0.3709 +0.9175 -0.0082
+0.0000 +0.0101 -0.0043 -0.0095 -0.9999
+0.1005 +0.9068 +0.3402 +0.2277 +0.0055
+0.4020 -0.3993 +0.7909 +0.2310 -0.0096
+0.9045 +0.0881 -0.3481 -0.2299 +0.0046
R :
+9.9499 +100.9058 +3.6181 -7.6383 +1.4071
-0.0000 +99.0960 +0.3725 +7.7879 +0.5956
-0.0000 +0.0000 +0.8777 +7.6742 +0.7830
+0.0000 -0.0000 +0.0000 +4.8075 +0.2288
+0.0000 +0.0000 +0.0000 +0.0000 +0.0005
Press return to continue.
Q_T :
+1.0050e-01 +0.0000e+00 +1.0050e-01 +4.0202e-01 +9.0453e-01
-1.0234e-01 +1.0091e-02 +9.0678e-01 -3.9927e-01 +8.8069e-02
-3.7088e-01 -4.2823e-03 +3.4023e-01 +7.9089e-01 -3.4810e-01
+9.1750e-01 -9.5115e-03 +2.2771e-01 +2.3104e-01 -2.2993e-01
-8.1722e-03 -9.9989e-01 +5.5283e-03 -9.6144e-03 +4.5668e-03
invR :
+1.0050e-01 -1.0234e-01 -3.7088e-01 +9.1750e-01 +0.0000e+00
+0.0000e+00 +1.0091e-02 -4.2823e-03 -9.5115e-03 -9.9989e-01
+0.0000e+00 -0.0000e+00 +1.1393e+00 -1.8187e+00 -9.9009e+02
+0.0000e+00 +0.0000e+00 +0.0000e+00 +2.0801e-01 -9.9012e+01
+0.0000e+00 +0.0000e+00 +0.0000e+00 +0.0000e+00 +2.0802e+03
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+0.99
+1.00
+996.00
+100.00
-2097.00
The coefficients a, b, c, d, e, of the curve are :
+0.99x**2 +1.00y**2 +996.00x +100.00y -2097.00 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
f7wvuxq0fou77nln6jwcro2kqii9vlj
Mathc complexes/06v
0
82638
746218
2025-07-07T13:00:36Z
Xhungab
23827
news
746218
wikitext
text/x-wiki
__NOTOC__
[[Catégorie:Mathc complexes (livre)]]
:
[[Mathc complexes/06m| '''Application''']]
:
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00d.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00d.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R3*(C2*C2)] ={
10,0, 10,0,
-5,0, 1,0,
7,0, -10,0 };
double ab[RA*((CA+Cb)*C2)]={
/* x**2 y**2 x y e = +0 */
+1,+0, +0,+0, +0,+0, +0,+0, +0,+0, +1,+0,
+0,+0, +1,+0, +0,+0, +0,+0, +0,+0, +1,+0,
+100,+0, +100,+0, +10,+0, +10,+0, +1,+0, +0,+0,
+25,+0, +1,+0, -5,+0, +1,+0, +1,+0, +0,+0,
+49,+0, +100,+0, +7,+0, -10,+0, +1,+0, +0,+0
};
double **XY = ca_A_mZ(xy,i_mZ(R3,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" x**2 y**2 x y c");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
stop();
clrscrn();
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three XY.
x y
+10 +10
-5 +1
+7 -10
Using the given XY, we obtain this matrix.
(a = 1. This is my choice)
x**2 y**2 x y c
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+100.00 +100.00 +10.00 +10.00 +1.00 +0.00
+25.00 +1.00 -5.00 +1.00 +1.00 +0.00
+49.00 +100.00 +7.00 -10.00 +1.00 +0.00
Press return to continue.
Q :
+0.0088 -0.0213 +0.0046 -0.6732 -0.7391
+0.0000 +0.0186 -0.0191 +0.7387 -0.6735
+0.8761 -0.2704 +0.3985 +0.0196 +0.0028
+0.2190 -0.5131 -0.8298 +0.0014 +0.0109
+0.4293 +0.8141 -0.3901 -0.0270 +0.0039
R :
+114.1359 +130.7652 +10.6715 +4.6874 +1.5245
-0.0000 +53.8745 +5.5600 -11.3588 +0.0306
-0.0000 -0.0000 +5.4043 +7.0561 -0.8214
+0.0000 +0.0000 +0.0000 +0.4678 -0.0060
+0.0000 +0.0000 +0.0000 -0.0000 +0.0176
Press return to continue.
Q_T :
+8.7615e-03 +0.0000e+00 +8.7615e-01 +2.1904e-01 +4.2931e-01
-2.1266e-02 +1.8562e-02 -2.7044e-01 -5.1309e-01 +8.1413e-01
+4.5779e-03 -1.9096e-02 +3.9854e-01 -8.2984e-01 -3.9005e-01
-6.7319e-01 +7.3872e-01 +1.9620e-02 +1.4100e-03 -2.7021e-02
-7.3910e-01 -6.7349e-01 +2.7660e-03 +1.0935e-02 +3.8595e-03
invR :
+8.7615e-03 -2.1266e-02 +4.5779e-03 -6.7319e-01 -7.3910e-01
-0.0000e+00 +1.8562e-02 -1.9096e-02 +7.3872e-01 -6.7349e-01
+0.0000e+00 +0.0000e+00 +1.8504e-01 -2.7909e+00 +7.7024e+00
-0.0000e+00 +0.0000e+00 -0.0000e+00 +2.1376e+00 +7.2929e-01
+0.0000e+00 +0.0000e+00 +0.0000e+00 +0.0000e+00 +5.6945e+01
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+1.00
-11.07
-0.89
-80.44
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +1.00y**2 -11.07x -0.89y -80.44 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
ri2qompdj4s1x1bj1aqlbq0m3elzso1
Mathc complexes/06w
0
82639
746222
2025-07-07T13:28:50Z
Xhungab
23827
news
746222
wikitext
text/x-wiki
[[Catégorie:Mathc complexes (livre)]]
[[Mathc complexes/069| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00a.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00a.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R3
#define CA C3
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R3*(C2*C2)] ={
1,0, 6,0,
2,0, 3,0,
3,0, 5,0 };
double ab[RA*((CA+Cb)*C2)]={
/* x**2 x**1 x**0 = y */
+1,0, +1,0, +1,0, +6,0,
+4,0, +2,0, +1,0, +3,0,
+9,0, +3,0, +1,0, +5,0
};
double **XY = ca_A_mZ(xy,i_mZ(R3,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c of the curve \n\n");
printf(" y = ax**2 + bx + c (x**0 = 1) \n\n");
printf(" that passes through the points. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n\n");
printf(" x**2 x**1 x**0 = y");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P2,C6);
printf(" The coefficients a, b, c, of the curve are : \n\n"
" %+.2fx**2 %+.2fx %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c of the curve
y = ax**2 + bx + c (x**0 = 1)
that passes through the points.
x y
+1 +6
+2 +3
+3 +5
Using the given XY, we obtain this matrix.
x**2 x**1 x**0 = y
+1.00 +1.00 +1.00 +6.00
+4.00 +2.00 +1.00 +3.00
+9.00 +3.00 +1.00 +5.00
Press return to continue.
Q :
+0.1010 +0.7184 +0.6882
+0.4041 +0.6025 -0.6882
+0.9091 -0.3476 +0.2294
R :
+9.8995 +3.6365 +1.4142
-0.0000 +0.8806 +0.9733
+0.0000 +0.0000 +0.2294
Q_T :
+1.0102e-01 +4.0406e-01 +9.0914e-01
+7.1841e-01 +6.0254e-01 -3.4762e-01
+6.8825e-01 -6.8825e-01 +2.2942e-01
invR :
+1.0102e-01 -4.1714e-01 +1.1471e+00
+0.0000e+00 +1.1355e+00 -4.8177e+00
+0.0000e+00 -0.0000e+00 +4.3589e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+2.50
-10.50
+14.00
The coefficients a, b, c, of the curve are :
+2.50x**2 -10.50x +14.00 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
6irnig0hziqmyf9dabszit0g4p6cof1
Mathc complexes/06x
0
82640
746225
2025-07-07T13:33:51Z
Xhungab
23827
news
746225
wikitext
text/x-wiki
[[Catégorie:Mathc complexes (livre)]]
[[Mathc complexes/069| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00b.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00b.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R3
#define CA C3
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R3*(C2*C2)] ={
1,0, -9,0,
2,0, 8,0,
3,0, -8,0 };
double ab[RA*((CA+Cb)*C2)]={
/* x**2 x**1 x**0 = y */
+1,0, +1,0, +1,0, -9,0,
+4,0, +2,0, +1,0, +8,0,
+9,0, +3,0, +1,0, -8,0,
};
double **XY = ca_A_mZ(xy,i_mZ(R3,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c of the curve \n\n");
printf(" y = ax**2 + bx + c (x**0 = 1) \n\n");
printf(" that passes through the points. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n\n");
printf(" x**2 x**1 x**0 = y");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P2,C6);
printf(" The coefficients a, b, c, of the curve are : \n\n"
" %+.2fx**2 %+.2fx %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c of the curve
y = ax**2 + bx + c (x**0 = 1)
that passes through the points.
x y
+1 -9
+2 +8
+3 -8
Using the given XY, we obtain this matrix.
x**2 x**1 x**0 = y
+1.00 +1.00 +1.00 -9.00
+4.00 +2.00 +1.00 +8.00
+9.00 +3.00 +1.00 -8.00
Press return to continue.
Q :
+0.1010 +0.7184 +0.6882
+0.4041 +0.6025 -0.6882
+0.9091 -0.3476 +0.2294
R :
+9.8995 +3.6365 +1.4142
-0.0000 +0.8806 +0.9733
+0.0000 +0.0000 +0.2294
Q_T :
+1.0102e-01 +4.0406e-01 +9.0914e-01
+7.1841e-01 +6.0254e-01 -3.4762e-01
+6.8825e-01 -6.8825e-01 +2.2942e-01
invR :
+1.0102e-01 -4.1714e-01 +1.1471e+00
+0.0000e+00 +1.1355e+00 -4.8177e+00
+0.0000e+00 -0.0000e+00 +4.3589e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
-16.50
+66.50
-59.00
The coefficients a, b, c, of the curve are :
-16.50x**2 +66.50x -59.00 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
m6ic6572q04lnnzmkw5ytcqwxrurf8e
Mathc complexes/06y
0
82641
746229
2025-07-07T13:59:51Z
Xhungab
23827
news
746229
wikitext
text/x-wiki
[[Catégorie:Mathc complexes (livre)]]
[[Mathc complexes/069| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00c.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00c.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R4
#define CA C4
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R4*(C2*C2)] ={
-5,0, -3,0,
-2,0, 0,0,
2,0, 3,0,
3,0, -2,0 };
double ab[RA*((CA+Cb)*C2)]={
/* x**3 x**2 x**1 x**0 y */
-125,0, +25,0, -5,0, +1,0, -3,0,
-8,0, +4,0, -2,0, +1,0, +0,0,
+8,0, +4,0, +2,0, +1,0, +3,0,
+27,0, +9,0, +3,0, +1,0, -2,0,
};
double **XY = ca_A_mZ(xy,i_mZ(R4,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d of the curve \n\n");
printf(" y = ax**3 + bx**2 + cx + d \n\n");
printf(" that passes through the points. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" x**3 x**2 x**1 x**0 y\n");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
stop();
clrscrn();
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P3,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.3fx**3 %+.3fx**2 %+.3fx %+.3f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d of the curve
y = ax**3 + bx**2 + cx + d
that passes through the points.
x y
-5 -3
-2 +0
+2 +3
+3 -2
Using the given XY, we obtain this matrix.
x**3 x**2 x**1 x**0 y
-125.00 +25.00 -5.00 +1.00 -3.00
-8.00 +4.00 -2.00 +1.00 +0.00
+8.00 +4.00 +2.00 +1.00 +3.00
+27.00 +9.00 +3.00 +1.00 -2.00
Press return to continue.
Q :
-0.9737 +0.2054 +0.0819 -0.0556
-0.0623 +0.1700 -0.9033 +0.3889
+0.0623 +0.3529 +0.4209 +0.8333
+0.2103 +0.8969 -0.0131 -0.3889
R :
+128.3822 -22.4486 +5.7485 -0.7633
+0.0000 +15.2990 +2.0292 +1.6252
-0.0000 +0.0000 +2.1995 -0.4136
-0.0000 +0.0000 -0.0000 +0.7778
Press return to continue.
Q_T :
-9.7365e-01 -6.2314e-02 +6.2314e-02 +2.1031e-01
+2.0543e-01 +1.7002e-01 +3.5289e-01 +8.9686e-01
+8.1914e-02 -9.0331e-01 +4.2088e-01 -1.3124e-02
-5.5556e-02 +3.8889e-01 +8.3333e-01 -3.8889e-01
invR :
+7.7892e-03 +1.1429e-02 -3.0902e-02 -3.2672e-02
+0.0000e+00 +6.5364e-02 -6.0304e-02 -1.6865e-01
+0.0000e+00 -0.0000e+00 +4.5466e-01 +2.4180e-01
-0.0000e+00 +0.0000e+00 -0.0000e+00 +1.2857e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
-0.139
-0.732
+1.307
+4.429
The coefficients a, b, c, d of the curve are :
-0.139x**3 -0.732x**2 +1.307x +4.429 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
5m2hrm4ofndu8xar6yee8mdcr4dfx5h
Mathc complexes/06z
0
82642
746230
2025-07-07T14:03:11Z
Xhungab
23827
news
746230
wikitext
text/x-wiki
[[Catégorie:Mathc complexes (livre)]]
[[Mathc complexes/069| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00c.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00d.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R4
#define CA C4
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R4*(C2*C2)] ={
-5,0, -8,0,
-2,0, 8,0,
2,0, -8,0,
5,0, 8,0};
double ab[RA*((CA+Cb)*C2)]={
/* x**3 x**2 x**1 x**0 y */
-125,0, +25,0, -5,0, +1,0, -8,0,
-8,0, +4,0, -2,0, +1,0, +8,0,
+8,0, +4,0, +2,0, +1,0, -8,0,
+125,0, +25,0, +5,0, +1,0, +8,0,
};
double **XY = ca_A_mZ(xy,i_mZ(R4,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d of the curve \n\n");
printf(" y = ax**3 + bx**2 + cx + d \n\n");
printf(" that passes through the points. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" x**3 x**2 x**1 x**0 y\n");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
stop();
clrscrn();
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P3,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.3fx**3 %+.3fx = 0\n\n"
,x[R1][C1],x[R3][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d of the curve
y = ax**3 + bx**2 + cx + d
that passes through the points.
x y
-5 -8
-2 +8
+2 -8
+5 +8
Using the given XY, we obtain this matrix.
x**3 x**2 x**1 x**0 y
-125.00 +25.00 -5.00 +1.00 -8.00
-8.00 +4.00 -2.00 +1.00 +8.00
+8.00 +4.00 +2.00 +1.00 -8.00
+125.00 +25.00 +5.00 +1.00 +8.00
Press return to continue.
Q :
-0.7057 +0.6982 +0.0452 -0.1117
-0.0452 +0.1117 -0.7057 +0.6982
+0.0452 +0.1117 +0.7057 +0.6982
+0.7057 +0.6982 -0.0452 -0.1117
R :
+177.1384 +0.0000 +7.2373 +0.0000
+0.0000 +35.8050 +0.0000 +1.6199
-0.0000 +0.0000 +2.3710 +0.0000
-0.0000 +0.0000 +0.0000 +1.1730
Press return to continue.
Q_T :
-7.0566e-01 -4.5162e-02 +4.5162e-02 +7.0566e-01
+6.9823e-01 +1.1172e-01 +1.1172e-01 +6.9823e-01
+4.5162e-02 -7.0566e-01 +7.0566e-01 -4.5162e-02
-1.1172e-01 +6.9823e-01 +6.9823e-01 -1.1172e-01
invR :
+5.6453e-03 +0.0000e+00 -1.7232e-02 +0.0000e+00
+0.0000e+00 +2.7929e-02 +0.0000e+00 -3.8569e-02
+0.0000e+00 -0.0000e+00 +4.2176e-01 -0.0000e+00
-0.0000e+00 +0.0000e+00 -0.0000e+00 +8.5250e-01
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+0.267
+0.000
-5.067
+0.000
The coefficients a, b, c, d of the curve are :
+0.267x**3 -5.067x = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
dr1m2wiybq8v31yl39farpg72d4y68g
746231
746230
2025-07-07T14:03:30Z
Xhungab
23827
746231
wikitext
text/x-wiki
[[Catégorie:Mathc complexes (livre)]]
[[Mathc complexes/069| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00d.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00d.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R4
#define CA C4
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R4*(C2*C2)] ={
-5,0, -8,0,
-2,0, 8,0,
2,0, -8,0,
5,0, 8,0};
double ab[RA*((CA+Cb)*C2)]={
/* x**3 x**2 x**1 x**0 y */
-125,0, +25,0, -5,0, +1,0, -8,0,
-8,0, +4,0, -2,0, +1,0, +8,0,
+8,0, +4,0, +2,0, +1,0, -8,0,
+125,0, +25,0, +5,0, +1,0, +8,0,
};
double **XY = ca_A_mZ(xy,i_mZ(R4,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d of the curve \n\n");
printf(" y = ax**3 + bx**2 + cx + d \n\n");
printf(" that passes through the points. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" x**3 x**2 x**1 x**0 y\n");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
stop();
clrscrn();
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P3,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.3fx**3 %+.3fx = 0\n\n"
,x[R1][C1],x[R3][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d of the curve
y = ax**3 + bx**2 + cx + d
that passes through the points.
x y
-5 -8
-2 +8
+2 -8
+5 +8
Using the given XY, we obtain this matrix.
x**3 x**2 x**1 x**0 y
-125.00 +25.00 -5.00 +1.00 -8.00
-8.00 +4.00 -2.00 +1.00 +8.00
+8.00 +4.00 +2.00 +1.00 -8.00
+125.00 +25.00 +5.00 +1.00 +8.00
Press return to continue.
Q :
-0.7057 +0.6982 +0.0452 -0.1117
-0.0452 +0.1117 -0.7057 +0.6982
+0.0452 +0.1117 +0.7057 +0.6982
+0.7057 +0.6982 -0.0452 -0.1117
R :
+177.1384 +0.0000 +7.2373 +0.0000
+0.0000 +35.8050 +0.0000 +1.6199
-0.0000 +0.0000 +2.3710 +0.0000
-0.0000 +0.0000 +0.0000 +1.1730
Press return to continue.
Q_T :
-7.0566e-01 -4.5162e-02 +4.5162e-02 +7.0566e-01
+6.9823e-01 +1.1172e-01 +1.1172e-01 +6.9823e-01
+4.5162e-02 -7.0566e-01 +7.0566e-01 -4.5162e-02
-1.1172e-01 +6.9823e-01 +6.9823e-01 -1.1172e-01
invR :
+5.6453e-03 +0.0000e+00 -1.7232e-02 +0.0000e+00
+0.0000e+00 +2.7929e-02 +0.0000e+00 -3.8569e-02
+0.0000e+00 -0.0000e+00 +4.2176e-01 -0.0000e+00
-0.0000e+00 +0.0000e+00 -0.0000e+00 +8.5250e-01
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+0.267
+0.000
-5.067
+0.000
The coefficients a, b, c, d of the curve are :
+0.267x**3 -5.067x = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
nr468z53u175mnvmg6vvfxnhtz8dlva
Mathc complexes/070
0
82643
746237
2025-07-07T14:15:39Z
Xhungab
23827
news
746237
wikitext
text/x-wiki
[[Catégorie:Mathc complexes (livre)]]
[[Mathc complexes/069| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00e.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00e.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R10*(C2*C2)] ={
1,0, -5,0,
2,0, 8,0,
3,0, -7,0,
4,0, 1,0,
5,0, -4,0 };
double ab[RA*((CA+Cb)*C2)]={
/* x**4 x**3 x**2 x**1 x**0 y */
+1,0, +1,0, +1,0, +1,0, +1,0, -5,0,
+16,0, +8,0, +4,0, +2,0, +1,0, +8,0,
+81,0, +27,0, +9,0, +3,0, +1,0, -7,0,
+256,0, +64,0, +16,0, +4,0, +1,0, +1,0,
+625,0, +125,0, +25,0, +5,0, +1,0, -4,0,
};
double **XY = ca_A_mZ(xy,i_mZ(R5,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c of the curve \n\n");
printf(" y = ax**4 + bx**3 + cx**2 + dx + e \n\n");
printf(" that passes through the points. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" x**4 x**3 x**2 x**1 x**0 y");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
stop();
clrscrn();
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P3,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.3fx**4 %+.3fx**3 %+.3fx**2 %+.3fx %+.3f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c of the curve
y = ax**4 + bx**3 + cx**2 + dx + e
that passes through the points.
x y
+1 -5
+2 +8
+3 -7
+4 +1
+5 -4
Using the given XY, we obtain this matrix.
x**4 x**3 x**2 x**1 x**0 y
+1.00 +1.00 +1.00 +1.00 +1.00 -5.00
+16.00 +8.00 +4.00 +2.00 +1.00 +8.00
+81.00 +27.00 +9.00 +3.00 +1.00 -7.00
+256.00 +64.00 +16.00 +4.00 +1.00 +1.00
+625.00 +125.00 +25.00 +5.00 +1.00 -4.00
Press return to continue.
Q :
+0.0015 +0.0485 +0.4216 +0.8487 +0.3156
+0.0235 +0.2856 +0.7083 -0.1335 -0.6312
+0.1190 +0.6174 +0.2365 -0.3877 +0.6312
+0.3762 +0.6420 -0.4915 +0.3242 -0.3156
+0.9185 -0.3504 +0.1519 -0.0805 +0.0631
R :
+680.4256 +142.3006 +30.1502 +6.5033 +1.4388
+0.0000 +16.2950 +8.2602 +3.2880 +1.2431
-0.0000 -0.0000 +1.3158 +1.3410 +1.0268
+0.0000 +0.0000 +0.0000 +0.3128 +0.5711
-0.0000 -0.0000 -0.0000 -0.0000 +0.0631
Press return to continue.
Q_T :
+1.4697e-03 +2.3515e-02 +1.1904e-01 +3.7624e-01 +9.1854e-01
+4.8534e-02 +2.8560e-01 +6.1737e-01 +6.4201e-01 -3.5037e-01
+4.2165e-01 +7.0827e-01 +2.3651e-01 -4.9150e-01 +1.5186e-01
+8.4868e-01 -1.3354e-01 -3.8774e-01 +3.2419e-01 -8.0475e-02
+3.1560e-01 -6.3119e-01 +6.3119e-01 -3.1560e-01 +6.3119e-02
invR :
+1.4697e-03 -1.2834e-02 +4.6895e-02 -9.6700e-02 +3.3138e-01
+0.0000e+00 +6.1368e-02 -3.8527e-01 +1.0067e+00 -4.0502e+00
+0.0000e+00 -0.0000e+00 +7.6002e-01 -3.2586e+00 +1.7121e+01
+0.0000e+00 +0.0000e+00 +0.0000e+00 +3.1973e+00 -2.8930e+01
+0.0000e+00 -0.0000e+00 +0.0000e+00 +0.0000e+00 +1.5843e+01
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
-3.625
+44.750
-191.875
+329.750
-184.000
The coefficients a, b, c, d of the curve are :
-3.625x**4 +44.750x**3 -191.875x**2 +329.750x -184.000 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
ab7visajtf7eo05ffglzhrpofoeussq
Mathc complexes/071
0
82644
746239
2025-07-07T14:18:40Z
Xhungab
23827
news
746239
wikitext
text/x-wiki
[[Catégorie:Mathc complexes (livre)]]
[[Mathc complexes/069| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00f.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00f.c */
/* ------------------------------------ */
#include "w_a.h"
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
int main(void)
{
double xy[R10*(C2*C2)] ={
1,0, -2,0,
2,0, -2,0,
3,0, 3,0,
4,0, -9,0,
5,0, 4,0 };
double ab[RA*((CA+Cb)*C2)]={
/* x**4 x**3 x**2 x**1 x**0 y */
+1,0, +1,0, +1,0, +1,0, +1,0, -2,0,
+16,0, +8,0, +4,0, +2,0, +1,0, -2,0,
+81,0, +27,0, +9,0, +3,0, +1,0, +3,0,
+256,0, +64,0, +16,0, +4,0, +1,0, -9,0,
+625,0, +125,0, +25,0, +5,0, +1,0, +4,0,
};
double **XY = ca_A_mZ(xy,i_mZ(R5,C2));
double **Ab = ca_A_mZ(ab,i_Abr_Ac_bc_mZ(RA,CA,Cb));
double **A = c_Ab_A_mZ(Ab,i_mZ(RA,CA));
double **b = c_Ab_b_mZ(Ab,i_mZ(RA,Cb));
double **Q = i_mZ(RA,CA);
double **R = i_mZ(CA,CA);
double **invR = i_mZ(CA,CA);
double **Q_T = i_mZ(CA,RA);
double **invR_Q_T = i_mZ(CA,RA);
double **x = i_mZ(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c of the curve \n\n");
printf(" y = ax**4 + bx**3 + cx**2 + dx + e \n\n");
printf(" that passes through the points. \n\n");
printf(" x y");
p_mRZ(XY,S5,P0,C6);
printf("\n");
printf(" Using the given XY, we obtain this matrix.\n");
printf(" x**4 x**3 x**2 x**1 x**0 y");
p_mRZ(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mZ(A,Q,R);
printf(" Q :");
p_mRZ(Q,S10,P4,C6);
printf(" R :");
p_mRZ(R,S10,P4,C6);
stop();
clrscrn();
transpose_mZ(Q,Q_T);
printf(" Q_T :");
pE_mRZ(Q_T,S12,P4,C6);
inv_mZ(R,invR);
printf(" invR :");
pE_mRZ(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mZ(invR,Q_T,invR_Q_T);
mul_mZ(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mRZ(x,S10,P3,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.3fx**4 %+.3fx**3 %+.3fx**2 %+.3fx %+.3f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mZ(XY);
f_mZ(A);
f_mZ(b);
f_mZ(Ab);
f_mZ(Q);
f_mZ(Q_T);
f_mZ(R);
f_mZ(invR);
f_mZ(invR_Q_T);
f_mZ(x);
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c of the curve
y = ax**4 + bx**3 + cx**2 + dx + e
that passes through the points.
x y
+1 -2
+2 -2
+3 +3
+4 -9
+5 +4
Using the given XY, we obtain this matrix.
x**4 x**3 x**2 x**1 x**0 y
+1.00 +1.00 +1.00 +1.00 +1.00 -2.00
+16.00 +8.00 +4.00 +2.00 +1.00 -2.00
+81.00 +27.00 +9.00 +3.00 +1.00 +3.00
+256.00 +64.00 +16.00 +4.00 +1.00 -9.00
+625.00 +125.00 +25.00 +5.00 +1.00 +4.00
Press return to continue.
Q :
+0.0015 +0.0485 +0.4216 +0.8487 +0.3156
+0.0235 +0.2856 +0.7083 -0.1335 -0.6312
+0.1190 +0.6174 +0.2365 -0.3877 +0.6312
+0.3762 +0.6420 -0.4915 +0.3242 -0.3156
+0.9185 -0.3504 +0.1519 -0.0805 +0.0631
R :
+680.4256 +142.3006 +30.1502 +6.5033 +1.4388
+0.0000 +16.2950 +8.2602 +3.2880 +1.2431
-0.0000 -0.0000 +1.3158 +1.3410 +1.0268
+0.0000 +0.0000 +0.0000 +0.3128 +0.5711
-0.0000 -0.0000 -0.0000 -0.0000 +0.0631
Press return to continue.
Q_T :
+1.4697e-03 +2.3515e-02 +1.1904e-01 +3.7624e-01 +9.1854e-01
+4.8534e-02 +2.8560e-01 +6.1737e-01 +6.4201e-01 -3.5037e-01
+4.2165e-01 +7.0827e-01 +2.3651e-01 -4.9150e-01 +1.5186e-01
+8.4868e-01 -1.3354e-01 -3.8774e-01 +3.2419e-01 -8.0475e-02
+3.1560e-01 -6.3119e-01 +6.3119e-01 -3.1560e-01 +6.3119e-02
invR :
+1.4697e-03 -1.2834e-02 +4.6895e-02 -9.6700e-02 +3.3138e-01
+0.0000e+00 +6.1368e-02 -3.8527e-01 +1.0067e+00 -4.0502e+00
+0.0000e+00 -0.0000e+00 +7.6002e-01 -3.2586e+00 +1.7121e+01
+0.0000e+00 +0.0000e+00 +0.0000e+00 +3.1973e+00 -2.8930e+01
+0.0000e+00 -0.0000e+00 +0.0000e+00 +0.0000e+00 +1.5843e+01
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+2.667
-30.333
+117.833
-181.167
+89.000
The coefficients a, b, c, d of the curve are :
+2.667x**4 -30.333x**3 +117.833x**2 -181.167x +89.000 = 0
</syntaxhighlight>
{{AutoCat}}
fcz1e3an4bm2ucloa4zb3q7q260m0ik
Mathc matrices/06m
0
82645
746305
2025-07-08T08:45:18Z
Xhungab
23827
news
746305
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00a.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00a.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[8] ={
1, 0,
2, 3,
3, 4,
4, 0 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y e = 0 */
+1, +0, +0, +0, +0, +1,
+1, +0, +1, +0, +1, +0,
+4, +9, +2, +3, +1, +0,
+9, +16, +3, +4, +1, +0,
+16, +0, +4, +0, +1, +0
};
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points.\n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y e = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Trouver les coefficients a, b, c, d, e du conique,
ax**2 + by**2 + cx + dy + e = 0
qui passe par ces quatre points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3]) (x[4],y[4])
En utilisant les quatre points nous obtenons la matrice.
(a)x**2 (b)y**2 (c)x (d)y (e) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Ce système à quatre lignes et cinq inconnus (a, b, c, d, e).
C'est un système homogène, il a donc une infinité de solution.
Pour trouver une solution j'ai choisi de poser que a = 1.
Nous avons donc cinq lignes et cinq inconnus.
1 0 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Il suffit maintenant de résoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 +0
+2 +3
+3 +4
+4 +0
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y e = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +0.00 +1.00 +0.00 +1.00 +0.00
+4.00 +9.00 +2.00 +3.00 +1.00 +0.00
+9.00 +16.00 +3.00 +4.00 +1.00 +0.00
+16.00 +0.00 +4.00 +0.00 +1.00 +0.00
Press return to continue.
Q :
+0.0531 -0.0323 -0.2668 +0.1748 +0.9457
+0.0531 -0.0323 +0.7999 -0.5069 +0.3152
+0.2123 +0.4447 +0.4686 +0.7332 -0.0000
+0.4777 +0.7296 -0.2636 -0.4124 +0.0000
+0.8492 -0.5175 -0.0022 +0.0694 -0.0788
R :
+18.8414 +9.5534 +5.3074 +2.5476 +1.5922
+0.0000 +15.6759 +0.9758 +4.2525 +0.6244
-0.0000 -0.0000 +0.9375 +0.3514 +1.0027
+0.0000 -0.0000 +0.0000 +0.5499 -0.1167
+0.0000 +0.0000 +0.0000 +0.0000 +0.2364
Press return to continue.
Q_T :
+5.3074e-02 +5.3074e-02 +2.1230e-01 +4.7767e-01 +8.4919e-01
-3.2345e-02 -3.2345e-02 +4.4475e-01 +7.2957e-01 -5.1753e-01
-2.6681e-01 +7.9987e-01 +4.6856e-01 -2.6357e-01 -2.2010e-03
+1.7476e-01 -5.0692e-01 +7.3320e-01 -4.1242e-01 +6.9449e-02
+9.4573e-01 +3.1524e-01 -8.6284e-15 +7.0729e-15 -7.8811e-02
invR :
+5.3074e-02 -3.2345e-02 -2.6681e-01 +1.7476e-01 +9.4573e-01
+0.0000e+00 +6.3792e-02 -6.6396e-02 -4.5089e-01 -1.0946e-01
+0.0000e+00 +0.0000e+00 +1.0667e+00 -6.8168e-01 -4.8600e+00
-0.0000e+00 -0.0000e+00 -0.0000e+00 +1.8185e+00 +8.9757e-01
-0.0000e+00 -0.0000e+00 -0.0000e+00 -0.0000e+00 +4.2295e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
-0.17
-5.00
+1.17
+4.00
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 -0.17y**2 -5.00x +1.17y +4.00 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
nwilccm1di0mb52rv4y1u4nc8dhv5ly
Mathc matrices/06n
0
82646
746306
2025-07-08T08:48:47Z
Xhungab
23827
news
746306
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00b.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00b.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[8] ={
1, 1,
2, 4,
3, 9,
4, 16 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y e = 0 */
+1, +0, +0, +0, +0, +1,
+1, +1, +1, +1, +1, +0,
+4, +16, +2, +4, +1, +0,
+9, +81, +3, +9, +1, +0,
+16, +256, +4, +16, +1, +0
};
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points.\n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y e = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n"
" %+.2fx**2 %+.2fy = 0\n\n"
,x[R1][C1],x[R4][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Trouver les coefficients a, b, c, d, e du conique,
ax**2 + by**2 + cx + dy + e = 0
qui passe par ces quatre points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3]) (x[4],y[4])
En utilisant les quatre points nous obtenons la matrice.
(a)x**2 (b)y**2 (c)x (d)y (e) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Ce système à quatre lignes et cinq inconnus (a, b, c, d, e).
C'est un système homogène, il a donc une infinité de solution.
Pour trouver une solution j'ai choisi de poser que a = 1.
Nous avons donc cinq lignes et cinq inconnus.
1 0 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Il suffit maintenant de résoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 +1
+2 +4
+3 +9
+4 +16
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y e = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +1.00 +1.00 +1.00 +1.00 +0.00
+4.00 +16.00 +2.00 +4.00 +1.00 +0.00
+9.00 +81.00 +3.00 +9.00 +1.00 +0.00
+16.00 +256.00 +4.00 +16.00 +1.00 +0.00
Press return to continue.
Q :
+0.0531 -0.1949 -0.6241 -0.7548 +0.0000
+0.0531 -0.1807 +0.6555 -0.4915 +0.5415
+0.2123 -0.5532 +0.3364 -0.1204 -0.7220
+0.4777 -0.6080 -0.2539 +0.4005 +0.4212
+0.8492 +0.5037 +0.0568 -0.1173 -0.0902
R :
+18.8414 +259.5343 +5.3074 +18.7884 +1.5922
+0.0000 +70.6822 -1.0960 +0.1949 -0.8381
-0.0000 -0.0000 +0.7936 +0.6241 +0.7948
+0.0000 +0.0000 +0.0000 +0.7548 -0.3287
+0.0000 +0.0000 +0.0000 +0.0000 +0.1504
Press return to continue.
Q_T :
+5.3074e-02 +5.3074e-02 +2.1230e-01 +4.7767e-01 +8.4919e-01
-1.9488e-01 -1.8073e-01 -5.5316e-01 -6.0796e-01 +5.0374e-01
-6.2405e-01 +6.5551e-01 +3.3642e-01 -2.5391e-01 +5.6750e-02
-7.5483e-01 -4.9155e-01 -1.2040e-01 +4.0047e-01 -1.1727e-01
+3.1923e-14 +5.4149e-01 -7.2199e-01 +4.2116e-01 -9.0249e-02
invR :
+5.3074e-02 -1.9488e-01 -6.2405e-01 -7.5483e-01 -0.0000e+00
+0.0000e+00 +1.4148e-02 +1.9537e-02 -1.9805e-02 -6.7686e-02
+0.0000e+00 -0.0000e+00 +1.2600e+00 -1.0417e+00 -8.9346e+00
-0.0000e+00 +0.0000e+00 -0.0000e+00 +1.3248e+00 +2.8955e+00
-0.0000e+00 +0.0000e+00 -0.0000e+00 -0.0000e+00 +6.6483e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
-0.00
-0.00
-1.00
+0.00
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 -1.00y = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
nbzhr0hgyq219q0enydd0c0c68zuq58
Mathc matrices/06o
0
82647
746307
2025-07-08T08:52:26Z
Xhungab
23827
news
746307
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00c.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00c.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[8] ={
1, -8,
2, 2,
3, 1,
4, 2 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y e = 0 */
+1, +0, +0, +0, +0, +1,
+1, +64, +1, -8, +1, +0,
+4, +4, +2, +2, +1, +0,
+9, +1, +3, +1, +1, +0,
+16, +4, +4, +2, +1, +0
};
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points.\n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y e = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Trouver les coefficients a, b, c, d, e du conique,
ax**2 + by**2 + cx + dy + e = 0
qui passe par ces quatre points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3]) (x[4],y[4])
En utilisant les quatre points nous obtenons la matrice.
(a)x**2 (b)y**2 (c)x (d)y (e) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Ce système à quatre lignes et cinq inconnus (a, b, c, d, e).
C'est un système homogène, il a donc une infinité de solution.
Pour trouver une solution j'ai choisi de poser que a = 1.
Nous avons donc cinq lignes et cinq inconnus.
1 0 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Il suffit maintenant de résoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 -8
+2 +2
+3 +1
+4 +2
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y e = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +64.00 +1.00 -8.00 +1.00 +0.00
+4.00 +4.00 +2.00 +2.00 +1.00 +0.00
+9.00 +1.00 +3.00 +1.00 +1.00 +0.00
+16.00 +4.00 +4.00 +2.00 +1.00 +0.00
Press return to continue.
Q :
+0.0531 -0.0068 -0.2458 +0.1164 +0.9608
+0.0531 +0.9973 -0.0277 -0.0429 +0.0022
+0.2123 +0.0357 +0.7522 +0.6136 +0.1066
+0.4777 -0.0452 +0.4432 -0.7365 +0.1759
+0.8492 -0.0454 -0.4203 +0.2563 -0.1858
R :
+18.8414 +8.1204 +5.3074 +2.1761 +1.5922
+0.0000 +63.7421 +0.7515 -8.0429 +0.9424
-0.0000 -0.0000 +1.1253 +1.3284 +0.7475
+0.0000 +0.0000 +0.0000 +1.3462 +0.0905
+0.0000 +0.0000 +0.0000 -0.0000 +0.0989
Press return to continue.
Q_T :
+5.3074e-02 +5.3074e-02 +2.1230e-01 +4.7767e-01 +8.4919e-01
-6.7614e-03 +9.9728e-01 +3.5707e-02 -4.5164e-02 -4.5430e-02
-2.4581e-01 -2.7670e-02 +7.5217e-01 +4.4320e-01 -4.2025e-01
+1.1638e-01 -4.2860e-02 +6.1361e-01 -7.3649e-01 +2.5628e-01
+9.6082e-01 +2.1987e-03 +1.0664e-01 +1.7589e-01 -1.8579e-01
invR :
+5.3074e-02 -6.7614e-03 -2.4581e-01 +1.1638e-01 +9.6082e-01
+0.0000e+00 +1.5688e-02 -1.0477e-02 +1.0407e-01 -1.6551e-01
+0.0000e+00 +0.0000e+00 +8.8867e-01 -8.7694e-01 -5.9111e+00
-0.0000e+00 -0.0000e+00 -0.0000e+00 +7.4285e-01 -6.7975e-01
-0.0000e+00 -0.0000e+00 -0.0000e+00 +0.0000e+00 +1.0107e+01
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
-0.14
-6.00
-0.57
+9.71
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 -0.14y**2 -6.00x -0.57y +9.71 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
8u5up5vqxf14tmymq5js9fqwvsnic9e
Mathc matrices/06p
0
82648
746308
2025-07-08T08:55:06Z
Xhungab
23827
news
746308
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00d.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00d.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[8] ={
1, 4,
2, 5,
3, -7,
4, 5 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y e = 0 */
+1, +0, +0, +0, +0, +1,
+1, +16, +1, +4, +1, +0,
+4, +25, +2, +5, +1, +0,
+9, +49, +3, -7, +1, +0,
+16, +25, +4, +5, +1, +0
};
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points.\n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y e = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Trouver les coefficients a, b, c, d, e du conique,
ax**2 + by**2 + cx + dy + e = 0
qui passe par ces quatre points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3]) (x[4],y[4])
En utilisant les quatre points nous obtenons la matrice.
(a)x**2 (b)y**2 (c)x (d)y (e) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Ce système à quatre lignes et cinq inconnus (a, b, c, d, e).
C'est un système homogène, il a donc une infinité de solution.
Pour trouver une solution j'ai choisi de poser que a = 1.
Nous avons donc cinq lignes et cinq inconnus.
1 0 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Il suffit maintenant de résoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 +4
+2 +5
+3 -7
+4 +5
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y e = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +16.00 +1.00 +4.00 +1.00 +0.00
+4.00 +25.00 +2.00 +5.00 +1.00 +0.00
+9.00 +49.00 +3.00 -7.00 +1.00 +0.00
+16.00 +25.00 +4.00 +5.00 +1.00 +0.00
Press return to continue.
Q :
+0.0531 -0.0740 -0.3021 +0.8975 -0.3081
+0.0531 +0.3652 +0.4419 +0.4171 +0.7033
+0.2123 +0.3903 +0.6371 +0.0179 -0.6296
+0.4777 +0.6791 -0.5380 -0.1419 +0.0335
+0.8492 -0.4977 +0.1346 -0.0069 +0.1139
R :
+18.8414 +50.7923 +5.3074 +2.1761 +1.5922
+0.0000 +36.4300 +1.1919 -3.8300 +0.9368
-0.0000 -0.0000 +0.6405 +9.3923 +0.6756
+0.0000 +0.0000 +0.0000 +2.7168 +0.2863
-0.0000 -0.0000 -0.0000 +0.0000 +0.2210
Press return to continue.
Q_T :
+5.3074e-02 +5.3074e-02 +2.1230e-01 +4.7767e-01 +8.4919e-01
-7.3999e-02 +3.6520e-01 +3.9025e-01 +6.7906e-01 -4.9773e-01
-3.0208e-01 +4.4185e-01 +6.3712e-01 -5.3802e-01 +1.3462e-01
+8.9751e-01 +4.1711e-01 +1.7931e-02 -1.4185e-01 -6.8557e-03
-3.0810e-01 +7.0327e-01 -6.2960e-01 +3.3489e-02 +1.1386e-01
invR :
+5.3074e-02 -7.3999e-02 -3.0208e-01 +8.9751e-01 -3.0810e-01
+0.0000e+00 +2.7450e-02 -5.1082e-02 +2.1530e-01 -2.3912e-01
+0.0000e+00 -0.0000e+00 +1.5613e+00 -5.3975e+00 +2.2203e+00
+0.0000e+00 -0.0000e+00 +0.0000e+00 +3.6808e-01 -4.7684e-01
-0.0000e+00 +0.0000e+00 -0.0000e+00 +0.0000e+00 +4.5243e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+0.28
-6.00
+0.48
-1.39
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +0.28y**2 -6.00x +0.48y -1.39 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
r9wltdbm2z6mfl7xvwht24qx22hjpnv
Mathc matrices/06q
0
82649
746309
2025-07-08T08:57:49Z
Xhungab
23827
news
746309
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00e.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00e.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[8] ={
1, 2,
2, -8,
3, -8,
4, -3 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y e = 0 */
+1, +0, +0, +0, +0, +1,
+1, +4, +1, +2, +1, +0,
+4, +64, +2, -8, +1, +0,
+9, +64, +3, -8, +1, +0,
+16, +9, +4, -3, +1, +0
};
double **XY = ca_A_mR(xy,i_mR(R4,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, e, of the curve \n\n");
printf(" ax**2 + by**2 + cx + dy + e = 0 \n\n");
printf(" that passes through these four points.\n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y e = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d, e, of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Trouver les coefficients a, b, c, d, e du conique,
ax**2 + by**2 + cx + dy + e = 0
qui passe par ces quatre points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3]) (x[4],y[4])
En utilisant les quatre points nous obtenons la matrice.
(a)x**2 (b)y**2 (c)x (d)y (e) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Ce système à quatre lignes et cinq inconnus (a, b, c, d, e).
C'est un système homogène, il a donc une infinité de solution.
Pour trouver une solution j'ai choisi de poser que a = 1.
Nous avons donc cinq lignes et cinq inconnus.
1 0 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
x[4]**2 y[4]**2 x[4] y[4] 1 0
Il suffit maintenant de résoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, e, of the curve
ax**2 + by**2 + cx + dy + e = 0
that passes through these four points.
x y
+1 +2
+2 -8
+3 -8
+4 -3
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y e = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+1.00 +4.00 +1.00 +2.00 +1.00 +0.00
+4.00 +64.00 +2.00 -8.00 +1.00 +0.00
+9.00 +64.00 +3.00 -8.00 +1.00 +0.00
+16.00 +9.00 +4.00 -3.00 +1.00 +0.00
Press return to continue.
Q :
+0.0531 -0.0369 -0.3184 +0.6481 +0.6888
+0.0531 +0.0166 +0.9276 +0.3589 +0.0879
+0.2123 +0.7087 +0.1022 -0.4485 +0.4909
+0.4777 +0.5240 -0.1642 +0.4552 -0.5129
+0.8492 -0.4707 +0.0287 -0.2069 +0.1172
R :
+18.8414 +52.0130 +5.3074 -7.9612 +1.5922
+0.0000 +74.7238 +1.1234 -8.4165 +0.7786
-0.0000 -0.0000 +0.7543 +2.2650 +0.8943
+0.0000 -0.0000 +0.0000 +1.2851 +0.1587
+0.0000 +0.0000 +0.0000 +0.0000 +0.1832
Press return to continue.
Q_T :
+5.3074e-02 +5.3074e-02 +2.1230e-01 +4.7767e-01 +8.4919e-01
-3.6944e-02 +1.6587e-02 +7.0871e-01 +5.2400e-01 -4.7065e-01
-3.1842e-01 +9.2757e-01 +1.0219e-01 -1.6420e-01 +2.8745e-02
+6.4808e-01 +3.5888e-01 -4.4855e-01 +4.5520e-01 -2.0685e-01
+6.8878e-01 +8.7929e-02 +4.9094e-01 -5.1292e-01 +1.1724e-01
invR :
+5.3074e-02 -3.6944e-02 -3.1842e-01 +6.4808e-01 +6.8878e-01
-0.0000e+00 +1.3383e-02 -1.9930e-02 +1.2278e-01 -6.5947e-02
+0.0000e+00 -0.0000e+00 +1.3257e+00 -2.3367e+00 -4.4478e+00
-0.0000e+00 +0.0000e+00 -0.0000e+00 +7.7818e-01 -6.7412e-01
-0.0000e+00 +0.0000e+00 -0.0000e+00 +0.0000e+00 +5.4589e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+0.04
-5.00
+0.04
+3.76
The coefficients a, b, c, d, e, of the curve are :
+1.00x**2 +0.04y**2 -5.00x +0.04y +3.76 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
9kzwr0rjpxzhbflazsp1uj9owv2uhc0
Mathc matrices/06r
0
82650
746316
2025-07-08T10:02:24Z
Xhungab
23827
news
746316
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00a.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00a.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[6] ={
1, -2,
2, -3,
3, 6 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y = 0 */
+1, +0, +0, +0, +0, +1,
+0, +1, +0, +0, +0, +1,
+1, +4, +1, -2, +1, +0,
+4, +9, +2, -3, +1, +0,
+9, +36, +3, +6, +1, +0,
};
double **XY = ca_A_mR(xy,i_mR(R3,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Calculons les coefficients a, b, c, d d'un cercle,
ax**2 + ay**2 + bx + cy + d = 0
Qui passe par ces trois points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3])
En utilisant ces trois points nous avons cette matrice.
(a)x**2 (a)y**2 (b)x (c)y (d) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Ce système a trois lignes et quatre inconnues.
Il est homogène, donc il a une infinité de solution.
Pour trouver une solution j'ai choisi que a = 1.
Nous obtenons cette matrice.
(a)x**2 (a)y**2
1 0 0 0 0 1
0 1 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Il suffit de resoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three XY.
x y
+1 -2
+2 -3
+3 +6
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +4.00 +1.00 -2.00 +1.00 +0.00
+4.00 +9.00 +2.00 -3.00 +1.00 +0.00
+9.00 +36.00 +3.00 +6.00 +1.00 +0.00
Press return to continue.
Q :
+0.1005 -0.4928 -0.5780 -0.3248 +0.5544
+0.0000 +0.1340 +0.0472 -0.9182 -0.3696
+0.1005 +0.0433 +0.7229 -0.2169 +0.6468
+0.4020 -0.7650 +0.3370 +0.0544 -0.3696
+0.9045 +0.3899 -0.1659 +0.0360 +0.0308
R :
+9.9499 +36.5834 +3.6181 +4.0202 +1.4071
-0.0000 +7.4603 -0.3168 +4.5480 -0.3317
-0.0000 -0.0000 +0.8993 -3.4522 +0.8940
+0.0000 +0.0000 -0.0000 +0.4863 -0.1264
+0.0000 +0.0000 +0.0000 +0.0000 +0.3080
Press return to continue.
Q_T :
+1.0050e-01 +0.0000e+00 +1.0050e-01 +4.0202e-01 +9.0453e-01
-4.9284e-01 +1.3404e-01 +4.3327e-02 -7.6499e-01 +3.8994e-01
-5.7800e-01 +4.7225e-02 +7.2290e-01 +3.3703e-01 -1.6589e-01
-3.2485e-01 -9.1825e-01 -2.1687e-01 +5.4449e-02 +3.5992e-02
+5.5444e-01 -3.6962e-01 +6.4684e-01 -3.6962e-01 +3.0802e-02
invR :
+1.0050e-01 -4.9284e-01 -5.7800e-01 -3.2485e-01 +5.5444e-01
+0.0000e+00 +1.3404e-01 +4.7225e-02 -9.1825e-01 -3.6962e-01
-0.0000e+00 +0.0000e+00 +1.1120e+00 +7.8932e+00 +1.2321e-02
-0.0000e+00 +0.0000e+00 -0.0000e+00 +2.0561e+00 +8.4398e-01
-0.0000e+00 +0.0000e+00 -0.0000e+00 -0.0000e+00 +3.2465e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+1.00
-10.40
-2.40
+0.60
The coefficients a, b, c, d of the curve are :
+1.00x**2 +1.00y**2 -10.40x -2.40y +0.60 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
pw1872mdckeeeq50uzmpm7dfyz38whf
746317
746316
2025-07-08T10:05:21Z
Xhungab
23827
746317
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26c| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00a.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00a.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[6] ={
1, -2,
2, -3,
3, 6 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y = 0 */
+1, +0, +0, +0, +0, +1,
+0, +1, +0, +0, +0, +1,
+1, +4, +1, -2, +1, +0,
+4, +9, +2, -3, +1, +0,
+9, +36, +3, +6, +1, +0,
};
double **XY = ca_A_mR(xy,i_mR(R3,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d, of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Calculons les coefficients a, b, c, d d'un cercle,
ax**2 + ay**2 + bx + cy + d = 0
Qui passe par ces trois points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3])
En utilisant ces trois points nous avons cette matrice.
(a)x**2 (a)y**2 (b)x (c)y (d) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Ce système a trois lignes et quatre inconnues.
Il est homogène, donc il a une infinité de solution.
Pour trouver une solution j'ai choisi que a = 1.
Nous obtenons cette matrice.
(a)x**2 (a)y**2
1 0 0 0 0 1
0 1 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Il suffit de resoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d, of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three XY.
x y
+1 -2
+2 -3
+3 +6
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +4.00 +1.00 -2.00 +1.00 +0.00
+4.00 +9.00 +2.00 -3.00 +1.00 +0.00
+9.00 +36.00 +3.00 +6.00 +1.00 +0.00
Press return to continue.
Q :
+0.1005 -0.4928 -0.5780 -0.3248 +0.5544
+0.0000 +0.1340 +0.0472 -0.9182 -0.3696
+0.1005 +0.0433 +0.7229 -0.2169 +0.6468
+0.4020 -0.7650 +0.3370 +0.0544 -0.3696
+0.9045 +0.3899 -0.1659 +0.0360 +0.0308
R :
+9.9499 +36.5834 +3.6181 +4.0202 +1.4071
-0.0000 +7.4603 -0.3168 +4.5480 -0.3317
-0.0000 -0.0000 +0.8993 -3.4522 +0.8940
+0.0000 +0.0000 -0.0000 +0.4863 -0.1264
+0.0000 +0.0000 +0.0000 +0.0000 +0.3080
Press return to continue.
Q_T :
+1.0050e-01 +0.0000e+00 +1.0050e-01 +4.0202e-01 +9.0453e-01
-4.9284e-01 +1.3404e-01 +4.3327e-02 -7.6499e-01 +3.8994e-01
-5.7800e-01 +4.7225e-02 +7.2290e-01 +3.3703e-01 -1.6589e-01
-3.2485e-01 -9.1825e-01 -2.1687e-01 +5.4449e-02 +3.5992e-02
+5.5444e-01 -3.6962e-01 +6.4684e-01 -3.6962e-01 +3.0802e-02
invR :
+1.0050e-01 -4.9284e-01 -5.7800e-01 -3.2485e-01 +5.5444e-01
+0.0000e+00 +1.3404e-01 +4.7225e-02 -9.1825e-01 -3.6962e-01
-0.0000e+00 +0.0000e+00 +1.1120e+00 +7.8932e+00 +1.2321e-02
-0.0000e+00 +0.0000e+00 -0.0000e+00 +2.0561e+00 +8.4398e-01
-0.0000e+00 +0.0000e+00 -0.0000e+00 -0.0000e+00 +3.2465e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+1.00
-10.40
-2.40
+0.60
The coefficients a, b, c, d of the curve are :
+1.00x**2 +1.00y**2 -10.40x -2.40y +0.60 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
aax02fp2zvjgodpi12msrayvfj02hzs
Mathc matrices/06s
0
82651
746318
2025-07-08T10:11:40Z
Xhungab
23827
news
746318
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26b| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00b.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00b.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[6] ={
1, -1,
2, -9,
3, -8 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y = 0 */
+1, +0, +0, +0, +0, +1,
+0, +1, +0, +0, +0, +1,
+1, +1, +1, -1, +1, +0,
+4, +81, +2, -9, +1, +0,
+9, +64, +3, -8, +1, +0,
};
double **XY = ca_A_mR(xy,i_mR(R3,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Calculons les coefficients a, b, c, d d'un cercle,
ax**2 + ay**2 + bx + cy + d = 0
Qui passe par ces trois points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3])
En utilisant ces trois points nous avons cette matrice.
(a)x**2 (a)y**2 (b)x (c)y (d) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Ce système a trois lignes et quatre inconnues.
Il est homogène, donc il a une infinité de solution.
Pour trouver une solution j'ai choisi que a = 1.
Nous obtenons cette matrice.
(a)x**2 (a)y**2
1 0 0 0 0 1
0 1 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Il suffit de resoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three XY.
x y
+1 -1
+2 -9
+3 -8
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +1.00 +1.00 -1.00 +1.00 +0.00
+4.00 +81.00 +2.00 -9.00 +1.00 +0.00
+9.00 +64.00 +3.00 -8.00 +1.00 +0.00
Press return to continue.
Q :
+0.1005 -0.1835 -0.3374 -0.6212 +0.6757
+0.0000 +0.0202 -0.0143 +0.7368 +0.6757
+0.1005 -0.1634 +0.9344 -0.1904 +0.2323
+0.4020 +0.8993 +0.0633 -0.1202 +0.1056
+0.9045 -0.3612 -0.0944 +0.1436 -0.1478
R :
+9.9499 +90.5539 +3.6181 -10.9549 +1.4071
+0.0000 +49.5882 +0.5518 -5.0413 +0.3748
-0.0000 -0.0000 +0.7776 -0.7482 +0.9032
-0.0000 -0.0000 -0.0000 +0.1234 -0.1670
-0.0000 -0.0000 -0.0000 +0.0000 +0.1900
Press return to continue.
Q_T :
+1.0050e-01 +0.0000e+00 +1.0050e-01 +4.0202e-01 +9.0453e-01
-1.8353e-01 +2.0166e-02 -1.6337e-01 +8.9933e-01 -3.6116e-01
-3.3742e-01 -1.4312e-02 +9.3436e-01 +6.3264e-02 -9.4444e-02
-6.2115e-01 +7.3679e-01 -1.9038e-01 -1.2020e-01 +1.4359e-01
+6.7567e-01 +6.7567e-01 +2.3226e-01 +1.0557e-01 -1.4780e-01
invR :
+1.0050e-01 -1.8353e-01 -3.3742e-01 -6.2115e-01 +6.7567e-01
-0.0000e+00 +2.0166e-02 -1.4312e-02 +7.3679e-01 +6.7567e-01
-0.0000e+00 +0.0000e+00 +1.2861e+00 +7.7945e+00 +7.3667e-01
-0.0000e+00 +0.0000e+00 -0.0000e+00 +8.1005e+00 +7.1180e+00
-0.0000e+00 +0.0000e+00 -0.0000e+00 -0.0000e+00 +5.2623e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+1.00
+1.44
+10.56
+7.11
The coefficients a, b, c, d of the curve are :
+1.00x**2 +1.00y**2 +1.44x +10.56y +7.11 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
1ce6fr2z7rx0bhr1em8twl9zy62foua
746319
746318
2025-07-08T10:12:27Z
Xhungab
23827
746319
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26c| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00b.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00b.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[6] ={
1, -1,
2, -9,
3, -8 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y = 0 */
+1, +0, +0, +0, +0, +1,
+0, +1, +0, +0, +0, +1,
+1, +1, +1, -1, +1, +0,
+4, +81, +2, -9, +1, +0,
+9, +64, +3, -8, +1, +0,
};
double **XY = ca_A_mR(xy,i_mR(R3,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Calculons les coefficients a, b, c, d d'un cercle,
ax**2 + ay**2 + bx + cy + d = 0
Qui passe par ces trois points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3])
En utilisant ces trois points nous avons cette matrice.
(a)x**2 (a)y**2 (b)x (c)y (d) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Ce système a trois lignes et quatre inconnues.
Il est homogène, donc il a une infinité de solution.
Pour trouver une solution j'ai choisi que a = 1.
Nous obtenons cette matrice.
(a)x**2 (a)y**2
1 0 0 0 0 1
0 1 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Il suffit de resoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three XY.
x y
+1 -1
+2 -9
+3 -8
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +1.00 +1.00 -1.00 +1.00 +0.00
+4.00 +81.00 +2.00 -9.00 +1.00 +0.00
+9.00 +64.00 +3.00 -8.00 +1.00 +0.00
Press return to continue.
Q :
+0.1005 -0.1835 -0.3374 -0.6212 +0.6757
+0.0000 +0.0202 -0.0143 +0.7368 +0.6757
+0.1005 -0.1634 +0.9344 -0.1904 +0.2323
+0.4020 +0.8993 +0.0633 -0.1202 +0.1056
+0.9045 -0.3612 -0.0944 +0.1436 -0.1478
R :
+9.9499 +90.5539 +3.6181 -10.9549 +1.4071
+0.0000 +49.5882 +0.5518 -5.0413 +0.3748
-0.0000 -0.0000 +0.7776 -0.7482 +0.9032
-0.0000 -0.0000 -0.0000 +0.1234 -0.1670
-0.0000 -0.0000 -0.0000 +0.0000 +0.1900
Press return to continue.
Q_T :
+1.0050e-01 +0.0000e+00 +1.0050e-01 +4.0202e-01 +9.0453e-01
-1.8353e-01 +2.0166e-02 -1.6337e-01 +8.9933e-01 -3.6116e-01
-3.3742e-01 -1.4312e-02 +9.3436e-01 +6.3264e-02 -9.4444e-02
-6.2115e-01 +7.3679e-01 -1.9038e-01 -1.2020e-01 +1.4359e-01
+6.7567e-01 +6.7567e-01 +2.3226e-01 +1.0557e-01 -1.4780e-01
invR :
+1.0050e-01 -1.8353e-01 -3.3742e-01 -6.2115e-01 +6.7567e-01
-0.0000e+00 +2.0166e-02 -1.4312e-02 +7.3679e-01 +6.7567e-01
-0.0000e+00 +0.0000e+00 +1.2861e+00 +7.7945e+00 +7.3667e-01
-0.0000e+00 +0.0000e+00 -0.0000e+00 +8.1005e+00 +7.1180e+00
-0.0000e+00 +0.0000e+00 -0.0000e+00 -0.0000e+00 +5.2623e+00
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+1.00
+1.44
+10.56
+7.11
The coefficients a, b, c, d of the curve are :
+1.00x**2 +1.00y**2 +1.44x +10.56y +7.11 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
19opi0wl69mvc69wzjoci9175294ia9
Mathc matrices/06t
0
82652
746320
2025-07-08T10:15:40Z
Xhungab
23827
news
746320
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26c| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00c.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00c.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[6] ={
1, 10,
2, 1,
3, -10 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y = 0 */
+1, +0, +0, +0, +0, +1,
+0, +1, +0, +0, +0, +1,
+1, +100, +1, +10, +1, +0,
+4, +1, +2, +1, +1, +0,
+9, +100, +3, -10, +1, +0,
};
double **XY = ca_A_mR(xy,i_mR(R3,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Calculons les coefficients a, b, c, d d'un cercle,
ax**2 + ay**2 + bx + cy + d = 0
Qui passe par ces trois points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3])
En utilisant ces trois points nous avons cette matrice.
(a)x**2 (a)y**2 (b)x (c)y (d) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Ce système a trois lignes et quatre inconnues.
Il est homogène, donc il a une infinité de solution.
Pour trouver une solution j'ai choisi que a = 1.
Nous obtenons cette matrice.
(a)x**2 (a)y**2
1 0 0 0 0 1
0 1 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Il suffit de resoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three XY.
x y
+1 +10
+2 +1
+3 -10
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+1.00 +100.00 +1.00 +10.00 +1.00 +0.00
+4.00 +1.00 +2.00 +1.00 +1.00 +0.00
+9.00 +100.00 +3.00 -10.00 +1.00 +0.00
Press return to continue.
Q :
+0.1005 -0.1023 -0.3709 +0.9175 -0.0082
+0.0000 +0.0101 -0.0043 -0.0095 -0.9999
+0.1005 +0.9068 +0.3402 +0.2277 +0.0055
+0.4020 -0.3993 +0.7909 +0.2310 -0.0096
+0.9045 +0.0881 -0.3481 -0.2299 +0.0046
R :
+9.9499 +100.9058 +3.6181 -7.6383 +1.4071
-0.0000 +99.0960 +0.3725 +7.7879 +0.5956
-0.0000 +0.0000 +0.8777 +7.6742 +0.7830
+0.0000 -0.0000 +0.0000 +4.8075 +0.2288
+0.0000 +0.0000 +0.0000 +0.0000 +0.0005
Press return to continue.
Q_T :
+1.0050e-01 +0.0000e+00 +1.0050e-01 +4.0202e-01 +9.0453e-01
-1.0234e-01 +1.0091e-02 +9.0678e-01 -3.9927e-01 +8.8069e-02
-3.7088e-01 -4.2823e-03 +3.4023e-01 +7.9089e-01 -3.4810e-01
+9.1750e-01 -9.5115e-03 +2.2771e-01 +2.3104e-01 -2.2993e-01
-8.1722e-03 -9.9989e-01 +5.5283e-03 -9.6144e-03 +4.5668e-03
invR :
+1.0050e-01 -1.0234e-01 -3.7088e-01 +9.1750e-01 -8.1722e-03
+0.0000e+00 +1.0091e-02 -4.2823e-03 -9.5115e-03 -9.9989e-01
+0.0000e+00 -0.0000e+00 +1.1393e+00 -1.8187e+00 -9.9009e+02
+0.0000e+00 -0.0000e+00 +0.0000e+00 +2.0801e-01 -9.9012e+01
-0.0000e+00 +0.0000e+00 -0.0000e+00 +0.0000e+00 +2.0802e+03
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+1.00
+996.00
+100.00
-2097.00
The coefficients a, b, c, d of the curve are :
+1.00x**2 +1.00y**2 +996.00x +100.00y -2097.00 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
1p53dx570oxi3uy8acc4jdg1pai7itz
Mathc matrices/06u
0
82653
746321
2025-07-08T10:18:38Z
Xhungab
23827
news
746321
wikitext
text/x-wiki
[[Catégorie:Mathc matrices (livre)]]
[[Mathc matrices/26c| '''Application''']]
Installer et compiler ces fichiers dans votre répertoire de travail.
{{Fichier|c00d.c|largeur=70%|info=|icon=Crystal128-source-c.svg}}
<syntaxhighlight lang="c">
/* ------------------------------------ */
/* Save as : c00d.c */
/* ------------------------------------ */
#include "v_a.h"
/* ------------------------------------ */
/* ------------------------------------ */
#define RA R5
#define CA C5
#define Cb C1
/* ------------------------------------ */
/* ------------------------------------ */
void fun(void)
{
double xy[6] ={
10, 10,
-5, 1,
7, -10 };
double ab[RA*(CA+Cb)]={
/* x**2 y**2 x y = 0 */
+1, +0, +0, +0, +0, +1,
+0, +1, +0, +0, +0, +1,
+100, +100, +10, +10, +1, +0,
+25, +1, -5, +1, +1, +0,
+49, +100, +7, -10, +1, +0,
};
double **XY = ca_A_mR(xy,i_mR(R3,C2));
double **Ab = ca_A_mR(ab,i_Abr_Ac_bc_mR(RA,CA,Cb));
double **A = c_Ab_A_mR(Ab,i_mR(RA,CA));
double **b = c_Ab_b_mR(Ab,i_mR(RA,Cb));
double **Q = i_mR(RA,CA);
double **R = i_mR(CA,CA);
double **invR = i_mR(CA,CA);
double **Q_T = i_mR(CA,RA);
double **invR_Q_T = i_mR(CA,RA);
double **x = i_mR(CA,Cb); // x = invR * Q_T * b
clrscrn();
printf("\n");
printf(" Find the coefficients a, b, c, d of a circle \n\n");
printf(" ax**2 + ay**2 + bx + cy + d = 0 \n\n");
printf(" that passes through these three XY. \n\n");
printf(" x y");
p_mR(XY,S5,P0,C6);
printf(" Using the given points, we obtain this matrix.\n");
printf(" (a = 1. This is my choice)\n\n");
printf(" Ab :\n");
printf(" x**2 y**2 x y = 0 ");
p_mR(Ab,S7,P2,C6);
stop();
clrscrn();
QR_mR(A,Q,R);
printf(" Q :");
p_mR(Q,S10,P4,C6);
printf(" R :");
p_mR(R,S10,P4,C6);
stop();
clrscrn();
transpose_mR(Q,Q_T);
printf(" Q_T :");
pE_mR(Q_T,S12,P4,C6);
inv_mR(R,invR);
printf(" invR :");
pE_mR(invR,S12,P4,C6);
stop();
clrscrn();
printf(" Solving this system yields a unique\n"
" least squares solution, namely \n\n");
mul_mR(invR,Q_T,invR_Q_T);
mul_mR(invR_Q_T,b,x);
printf(" x = invR * Q_T * b :");
p_mR(x,S10,P2,C6);
printf(" The coefficients a, b, c, d of the curve are : \n\n"
" %+.2fx**2 %+.2fy**2 %+.2fx %+.2fy %+.2f = 0\n\n"
,x[R1][C1],x[R2][C1],x[R3][C1],x[R4][C1],x[R5][C1]);
stop();
f_mR(XY);
f_mR(A);
f_mR(b);
f_mR(Ab);
f_mR(Q);
f_mR(Q_T);
f_mR(R);
f_mR(invR);
f_mR(invR_Q_T);
f_mR(x);
}
/* ------------------------------------ */
int main(void)
{
fun();
return 0;
}
/* ------------------------------------ */
/* ------------------------------------ */
</syntaxhighlight>
Calculons les coefficients a, b, c, d d'un cercle,
ax**2 + ay**2 + bx + cy + d = 0
Qui passe par ces trois points.
(x[1],y[1]) (x[2],y[2]) (x[3],y[3])
En utilisant ces trois points nous avons cette matrice.
(a)x**2 (a)y**2 (b)x (c)y (d) = 0
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Ce système a trois lignes et quatre inconnues.
Il est homogène, donc il a une infinité de solution.
Pour trouver une solution j'ai choisi que a = 1.
Nous obtenons cette matrice.
(a)x**2 (a)y**2
1 0 0 0 0 1
0 1 0 0 0 1
x[1]**2 y[1]**2 x[1] y[1] 1 0
x[2]**2 y[2]**2 x[2] y[2] 1 0
x[3]**2 y[3]**2 x[3] y[3] 1 0
Il suffit de resoudre le système.
'''Exemple de sortie écran :'''
<syntaxhighlight lang="c">
Find the coefficients a, b, c, d of a circle
ax**2 + ay**2 + bx + cy + d = 0
that passes through these three XY.
x y
+10 +10
-5 +1
+7 -10
Using the given points, we obtain this matrix.
(a = 1. This is my choice)
Ab :
x**2 y**2 x y = 0
+1.00 +0.00 +0.00 +0.00 +0.00 +1.00
+0.00 +1.00 +0.00 +0.00 +0.00 +1.00
+100.00 +100.00 +10.00 +10.00 +1.00 +0.00
+25.00 +1.00 -5.00 +1.00 +1.00 +0.00
+49.00 +100.00 +7.00 -10.00 +1.00 +0.00
Press return to continue.
Q :
+0.0088 -0.0213 +0.0046 -0.6732 -0.7391
+0.0000 +0.0186 -0.0191 +0.7387 -0.6735
+0.8761 -0.2704 +0.3985 +0.0196 +0.0028
+0.2190 -0.5131 -0.8298 +0.0014 +0.0109
+0.4293 +0.8141 -0.3901 -0.0270 +0.0039
R :
+114.1359 +130.7652 +10.6715 +4.6874 +1.5245
-0.0000 +53.8745 +5.5600 -11.3588 +0.0306
-0.0000 -0.0000 +5.4043 +7.0561 -0.8214
-0.0000 -0.0000 -0.0000 +0.4678 -0.0060
-0.0000 +0.0000 +0.0000 +0.0000 +0.0176
Press return to continue.
Q_T :
+8.7615e-03 +0.0000e+00 +8.7615e-01 +2.1904e-01 +4.2931e-01
-2.1266e-02 +1.8562e-02 -2.7044e-01 -5.1309e-01 +8.1413e-01
+4.5779e-03 -1.9096e-02 +3.9854e-01 -8.2984e-01 -3.9005e-01
-6.7319e-01 +7.3872e-01 +1.9620e-02 +1.4100e-03 -2.7021e-02
-7.3910e-01 -6.7349e-01 +2.7660e-03 +1.0935e-02 +3.8595e-03
invR :
+8.7615e-03 -2.1266e-02 +4.5779e-03 -6.7319e-01 -7.3910e-01
-0.0000e+00 +1.8562e-02 -1.9096e-02 +7.3872e-01 -6.7349e-01
-0.0000e+00 +0.0000e+00 +1.8504e-01 -2.7909e+00 +7.7024e+00
-0.0000e+00 +0.0000e+00 -0.0000e+00 +2.1376e+00 +7.2929e-01
-0.0000e+00 +0.0000e+00 -0.0000e+00 +0.0000e+00 +5.6945e+01
Press return to continue.
Solving this system yields a unique
least squares solution, namely
x = invR * Q_T * b :
+1.00
+1.00
-11.07
-0.89
-80.44
The coefficients a, b, c, d of the curve are :
+1.00x**2 +1.00y**2 -11.07x -0.89y -80.44 = 0
Press return to continue.
</syntaxhighlight>
{{AutoCat}}
meagao96cyudwxake9whv7l8h6by5k6