Sommaire

  1. Introduction
  2. Historique
  3. Technique
    1. Principe et initialisation
    2. Émission des rayons
    3. Intersections des rayons avec la grille
    4. Construction du rendu
    5. Texturation
  4. Index des moteurs de raycasting et des jeux
  5. Comparaison des moteurs d'id Software
  6. Références

A version in English of this page is available here.

Je tiens à remercier Lode Vandevenne pour sa série d'articles sur le Raycasting et les contributeurs anonymes de Wikipédia.

1. Introduction

Le raycasting (lancer de rayons) est une technique de rendu dans les jeux vidéo pour, à partir d'une carte en 2D, afficher en temps réel un environnement qui à l'apparence d'une 3D rudimentaire [1]. Le principal avantage de cette technique est d'être rapide et abordable en termes de calcul temps réel pour les ordinateurs du début des années 90. Cette technique est également appelée parfois « 2.5D » pour la différencier des premiers moteurs de rendu en vraie 3D, comme le moteur de Quake, mais cette appellation recouvre également d'autres techniques de représentation d'un environnement 3D en 2D comme les projections axonométriques dont la perspective isométrique.

Il ne faut pas confondre raycasting et raytracing. Le raytracing est une autre technique de rendu qui est beaucoup plus coûteuse en temps de calcul et simule le parcours des rayons de lumière de différentes sources pour éclairer la scène en essayant d'atteindre une qualité photoréaliste. Depuis la fin 2019, la puissance des cartes graphiques est telle que certains proposent du raytracing en temps réel.

Notons enfin que si le raycasting est rapide sur les résolutions de l'écran de l'époque, 320x200, 640x400 ou 640x480, ses performances sont très mauvaises sur nos grandes résolutions modernes et il vaut mieux passer par des techniques plus modernes comme les API 3D OpenGL, Vulkan ou DirectX.

2. Historique

Le jeu qui a popularisé cette technique est Wolfenstein 3D, d'id Software, sorti le 5 mai 1992. Le raycasting était une technique bien adaptée à la puissance des machines de l'époque, codée avec brio par John Carmack, et mis au service d'un jeu avec un gameplay simple et rapide, dans une direction artistique faisant la part belle à la violence (pour l'époque) et aux décorations nazies, les ennemis du jeu.

Le moteur qui implémente le raycasting dans Wolfenstein 3D propose néanmoins des fonctionnalités très rudimentaires : le niveau est stocké dans un tableau, une grille. La valeur d'une case indique s'il la case est pleine ou creuse. Tous les murs ont la même hauteur, ont une longueur qui est toujours un multiple d'une valeur de base, ils se croisent toujours perpendiculairement, le sol et le plafond ne sont pas texturés, la lumière est statique. Le moteur offre néanmoins des murs texturés, des portes, des sprites pour les ennemis, les objets et les lumières statiques.

Avant Wolfenstein 3D, John Carmack avait développé en 1991 le moteur de Hovertank 3D pour son précédent employeur, Softdisk. Celui-ci fut amélioré pour Catacomb 3-D, sorti en 1991. Un autre développeur, Blue Sky Productions était lui en train de programmer Ultima Underworld: The Stygian Abyss, un autre jeu utilisant le raycasting comme principe mais doté d'un moteur beaucoup plus avancé.

id Software a vendu le moteur de Wolfenstein 3D à différentes équipes de développement, qui en retour l'ont utilisé dans différents jeux, parfois en le changeant profondément pour lui rajouter des capacités.

Avec la sortie du jeu suivant d'id Software, Doom, en 1993, le moteur de Wolfenstein 3D prend un sérieux coup de vieux. Le moteur de Doom, connu sous le nom de id Tech 1 permet d'afficher des murs non perpendiculaires, de longueurs et de hauteurs variées, avec des sols et des plafonds texturés avec des hauteurs différentes et des effets de lumière dynamique.

Mais c'est encore le jeu suivant d'id Software, Quake, sorti en 1996, qui rangera le raycasting au rang des technologies obsolètes. Le moteur de Quake, nommé plus tard id Tech 2, ne part plus d'un plan 2D pour en construire une vision 3D mais bel et bien d'un environnement 3D. Les derniers feux du raycasting brilleront avec les jeux Dark Forces, Duke Nukem 3D, Shadow Warrior et Blood, ces trois derniers utilisant le Build engine comme moteur, développé par Ken Silverman.

A la fin des années dix, on constate un retour d'engouement pour les jeux de cette période avec le développement du rétrogaming. De nouveaux jeux sortent également qui utilisent de vieux moteurs ou en construisent de nouveaux émulant le rendu d'époque pour offrir aux joueurs d'aujourd'hui un retour vers le passé du FPS avec des titres comme GUN GODZ, Project Warlock ou Ion Fury.

3. Technique

3.1 Principe et initialisation

La plupart des jeux en raycasting sont divisés en épisodes qui sont eux-mêmes divisés en niveaux (level) ou cartes (map) dans lesquels le joueur évolue. Un niveau est représenté par une grille dans Wolfenstein ou par un ensemble de secteurs, eux-mêmes constitués de murs dans Doom ou Build. Nous traiterons ici du cas le plus ancien, celui de la grille.

Tout d’abord, on part d’un environnement 2D. En termes mathématiques, on a un plan affine euclidien avec une base orthonormée : les deux axes de coordonnées sont orthogonaux et possèdent une norme égale à un. Chaque point dans ce plan est défini par deux coordonnées cartésiennes : son abscisse, notée x, et son ordonnée, notée y, les deux sont notées (x, y). Dans ce plan, on a deux éléments centraux :

Chaque case de la grille peut avoir comme valeur 0, elle est alors vide, ou 1, elle est alors pleine. Une case pleine a quatre murs, un sur chaque face, qui bloquent la visibilité et les déplacements du joueur.

On veut obtenir une image qui représente en pseudo-3D, ou 2.5D, le monde décrit en 2D. C’est cette image que verra le joueur. Pour cela, on attribue à notre joueur :

Nous pouvons conclure en reprenant nos deux éléments avec toutes leurs propriétés :

3.2 Émission des rayons

Nous allons donc créer une image qui représente en 2.5D le monde en 2D. Cet affichage sera affiché à l’écran, qui possède une largeur et une hauteur : les vieilles résolutions de l’époque étaient par exemple 320x200, 640x400 ou 640x480. Nous prenons 640x480.

Nous allons donc émettre 640 rayons, un par colonne. Pour calculer les coordonnées de chaque rayon, nous additionnons au vecteur direction une fraction du vecteur caméra. La valeur de la fraction va de -1 à +1 et augmente linéairement avec l’indice de la colonne que nous dessinons. La formule est la suivante : ( indice de la colonne / largeur de l’écran ) * 2 - 1. La division va de 0 à 1, nous multiplions par 2 pour aller de 0 à 2 et faisons -1 pour décrire de -1 à +1.

Nous avons donc 640 rayons avec leurs coordonnées à lancer. Nous devons maintenant savoir quand et où ils rencontrent un obstacle, une case avec une valeur différente de 0.

3.3 Intersections des rayons avec la grille

Nous allons nous concentrer sur un seul rayon, sachant que ce que nous allons expliquer vaut pour tous. Un rayon est émis à partir de la position du joueur et va croiser la route de plusieurs cases, c’est-à-dire franchir plusieurs fois des axes qui correspondent à une valeur entière : par exemple pour les abscisses, x = 1, x = 2, x = 3, et pour les ordonnées y = 1, y =2 ,y =3, etc., nous parlerons pour les désigner d’axes X et Y.

La question centrale est comment testée chaque case traversée par le rayon, qui est une demi-droite, dont l’origine est le joueur et la direction et le sens sont donnés par le vecteur rayon. Le premier croisement avec chaque axe se fait à une distance initiale diX et diY qu’il faut calculer. Mais ensuite, la distance entre chaque intersection pour un axe donnée est constante. C’est la clé de cet algorithme qui s’apparente à un « analyseur différentiel numérique » (ADN) ou « digital differential analyzer » (DDA) en anglais.

On ne fait que des additions en avançant sur la demi-droite décrite par le rayon en testant à chaque fois la prochaine case que nous rencontrons : soit elle est égale à 0 et on avance, soit elle est différente de 0 et on s’arrête car on a touché un mur. Pour chaque intersection, suivant le dernier axe traversé on est en mesure de savoir si c’est un mur horizontal ou vertical (par rapport au plan 2D) que l’on a touché et on est en mesure de savoir si c’est le mur du haut ou du bas pour les horizontaux et si c’est le mur de gauche ou de droite pour les verticaux en regardant le sens du rayon. Par exemple, si ray.x est négatif et que l’on touche un mur vertical, cela ne peut être que le mur de droite de la case.

Il faut ensuite calculer la distance orthogonale entre ce point et le segment caméra. Notre algorithme le donne directement. Nous avons donc les coordonnées de la case touchée, lequel de ces quatre murs nous avons touché, et la distance orthogonale. On peut à présent construire notre représentation en pseudo-3D.

3.4 Construction du rendu

XXX

3.5 Texturation

Une texture est une image qui va être appliqué à un objet de l'environnement, on dit dans ce cas que l'objet est texturé. Un calcul est effectué pour savoir quel pixel de la texture dessiner sur un pixel représentant l'objet à l'écran. En anglais on parle de texture mapping.

Les premiers jeux utilisant le raycasting n'avait pas d'objets texturés (Hovertank 3D, 1991). Puis il y eu des jeux avec les murs texturés (Catacomb 3D, 1991). Et enfin des jeux avec également les sols et les plafonds texturés (Blake Stone: Aliens of Gold, 1993).

Un raycaster dessine les textures colonnes par colonnes, il est donc plus efficace de les stocker ainsi en mémoire, c'est-à-dire faire un tableau de tableaux, ces derniers représentent les colonnes.

Une technique simple pour simuler l'effet de la lumière est de dessiner les textures de façon plus claire, pour simuler une forte luminosité, ou plus sombre, pour l'obscurité. Deux techniques existent : avoir en mémoire plusieurs versions de la texture, le calcul est donc déjà fait, ou calculer à la volée la couleur définitive à partir d'une seule texture de départ. Pour obscurcir, il suffit de diminuer les valeurs RGB de chaque pixel et pour illuminer, il faut augmenter ces mêmes valeurs.

4. Index des moteurs de raycasting et des jeux

Ceci est un aperçu rapide des principaux moteurs et jeux. Pour une liste plus complète, vous pouvez consulter mon index.

Année Autres moteurs Wolfenstein Engine (Carmack) Doom Engine (Carmack) Build Engine (Silverman)
1991 Hovertank 3-D
Catacomb 3-D
1992 Ultima Underworld: The Stygian Abyss Wolfenstein 3D
Spear of Destiny
1993 Ultima Underworld II: Labyrinth of Worlds
The Terminator: Rampage
Blake Stone: Aliens of Gold Doom
1994 The Elder Scrolls: Arena Rise of the Triad: Dark War
Blake Stone: Planet Strike!
Operation Body Count
Corridor 7: Alien Invasion
Super Noah's Ark 3-D
Doom II: Hell on Earth
Heretic
Legend of the Seven Paladins 3D
(Euijeok: Im Kkeokjeong)
1995 Dark Forces (Jedi) The Ultimate DOOM
Hexen: Beyond Heretic
William Shatner's TekWar
Witchaven
1996 Final DOOM
Chex Quest
Chex Quest 2
Strife
Duke Nukem 3D
Witchaven II: Blood Vengeance
Powerslave/Exhumed
1997 Outlaws (Jedi) Blood
Redneck Rampagne
Shadow Warrior
1998 NAM
Extreme Paintbrawl
1999 WW2
2018 Ion Maiden
2019 Hedon

5. Comparaison des moteurs d'id Software

Game name Hovertank 3D Catacomb 3D Wolfenstein 3D ShadowCaster
(Raven Engine)
Doom
(id Tech 1)
Genre Science Fiction Fantasy War Fantasy Science Fiction
Mobygames Hovertank Catacomb Wolfenstein3D ShadowCaster Doom
Wikipedia WikiHovertank WikiCatacomb WikiWolfenstein WikiShadowCaster WikiDoom
Source Code
Release Date April, 1991 November, 1991May 5, 1992 1993 December 10, 1993
Release Source June, 2014 June, 2014 July 21, 1995 December 23, 1997
Programmer John Carmack John Carmack John Carmack John Carmack John Carmack
Editing tool TEd? TEd TEd TEd DoomEd
Display
Resolution 320x200 320x200 320x200 320x200 320x200
Colors 16-color EGA 16-color EGA 256-color VGA 256-color VGA 256-color VGA
Levels
Level form grid? grid grid grid sectors
Rendering wall span? wall span? raycasting raycasting raycasting
Automap no no no yes yes
Hub no yes no yes no
Things
Scalable sprites yes yes yes yes yes
Multiangle spritesno yes yes yes yes
Polygonal things no no no no no
Walls
Orthogonal walls yes yes yes yes yes
Non-ortho. walls no no no no yes
Free length no no no no yes
Doors no Disappearing wallsyes yes yes
Transparent walls no no no no yes
Breaking glass no no no no no
3rd Dimension
!= heights/levels no no no yes yes
!= heights/walls no no no no yes
Sloped floors/ceilno no no yes no
Elevators no no no no yes
Texture-mapping
Textured walls no yes yes yes yes
Textured floors/ceno no no yes yes
Animated textures no no no yes yes
Skybox no no no yes yes
Lighting

6. Références

[1] : Lode's Computer Graphics Tutorial