Obligement - L'Amiga au maximum

Dimanche 14 septembre 2025 - 01:05  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in English


Réseaux sociaux

Suivez-nous sur X




Liste des jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z,
ALL


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Dossier : Histoire du développement de PixelMachine
(Article écrit par SuperJer et extrait de www.superjer.com - février 2007)


Créer un moteur de rendu 3D de lancer de rayons à partir de zéro en un week-end

J'ai toujours aimé l'idée du lancer de rayons pour restituer des images 3D avec une précision incroyable. Samedi 17 février 2017 au soir (étant un grand féru d'informatique), j'ai décidé d'essayer d'en écrire un de toutes pièces, juste pour le plaisir. Par "de toutes pièces", j'entends que j'ai commencé avec ce code C++ dans un fichier texte :

#include <stdlib.h>
#include <stdio.h>

int main()
{
	return 0;
}

Je n'ai utilisé aucune bibliothèque graphique ni aucun code extérieur autre que les éléments C/C++ standard de Visual Studio Express 2005. J'ai décidé de générer des fichiers BMP Windows car le format est d'une simplicité déconcertante.

Petit rappel sur le lancer de rayons

Le lancer de rayons fonctionne un peu comme un appareil photo réel, mais à l'envers. Avec un appareil photo (ou vos yeux, d'ailleurs), les rayons lumineux de l'environnement pénètrent dans l'objectif et frappent le film, la puce numérique et les cellules oculaires. Un phénomène magique se produit là où la lumière frappe et où l'on obtient une image !

Avec le lancer de rayons, on part de chaque point de notre "film" ou image, on projette un rayon depuis l'objectif de l'appareil photo et on observe ce qu'il touche. Ce qu'il touche détermine la couleur et la luminosité à cet endroit du film. Bien sûr, par "film", j'entends image numérique, et par "point", pixel.

J'ai décidé que tout mon programme serait centré sur une fonction, appelée raytrace(). L'idée est la suivante : on donne à raytrace() un point de départ et une direction, et elle suit ce rayon jusqu'à ce qu'il entre en collision avec un élément de mon environnement virtuel. Elle renvoie la couleur de l'objet touché.

Lors de la génération d'une image 3D, raytrace() ne trouvera la couleur que pour un seul pixel de l'image résultante. En exécutant le lancer de rayons une fois pour chaque pixel, nous pouvons obtenir la scène entière ! Le lancer de rayons est un peu lent, car, par exemple, pour une image d'un mégapixel, il faudrait l'exécuter un million de fois.

Je me suis donc mis à la programmation !

...et voici un journal visuel de mes progrès...

Samedi 17 février 2007

PixelMachine

Première image rendue vers 22h. Pour simplifier, j'ai choisi une matrice 16x16x16 de blocs colorés ou invisibles. J'ai initialisé le monde avec un damier de blocs bleus unis sur le mur du fond. L'objectif était de rendre la scène et de rechercher un motif en damier pour vérifier son bon fonctionnement.

Les pixels roses sont ceux dont le rendu a échoué à cause d'un bogue. Au lieu de s'éteindre lorsque la fonction raytrace() rencontre un problème, elle renvoie simplement du rose.

PixelMachine

J'ai déplacé la caméra pour voir si cela changeait la perspective. Et oui ! Un bon signe que le rendu était vraiment en 3D.

PixelMachine

J'ai ensuite placé la caméra dans les blocs. Comme vous pouvez le voir, ils sont partiellement transparents. Je pense qu'ils étaient solides à environ 15%. Le rose à droite correspond à l'endroit où les rayons ont quitté le monde 16x16x16 et ont subi une sorte de défaillance.

PixelMachine

J'ai supprimé le damier et placé des blocs de couleurs aléatoires à travers l'espace. J'ai oublié d'utiliser le mode binaire lors de l'écriture du fichier BMP, ce qui a entraîné une distorsion.

PixelMachine

Correction du problème du mode binaire.

PixelMachine

J'ai rendu les blocs moins transparents. Bon, maintenant il y a trop de blocs.

PixelMachine

...j'ai donc configuré la génération aléatoire pour créer moins de blocs.

PixelMachine

Au lieu de placer des blocs de manière totalement aléatoire, j'ai décidé de placer une couche de sol remplie de blocs, puis d'empiler des blocs aléatoirement par-dessus. L'objectif était de créer un environnement de type paysage urbain.

J'ai également fait en sorte que lorsque le rayon frappe les différentes faces des cubes, sa luminosité varie légèrement.

PixelMachine

Ici, j'ai augmenté la taille du monde de 16x16x16 à 64x64x64.

PixelMachine

Je m'assure que la transparence fonctionne toujours. Le plus intéressant, c'est que cela n'a pas vraiment ralenti le rendu. Seulement d'environ 5% !

PixelMachine

J'ai ajouté une source lumineuse. Plus elle est éloignée, moins la lumière atteint chaque pixel, d'un facteur égal à l'inverse de la distance au carré. Comme dans la vraie vie.

PixelMachine

On y arrive ! Lorsqu'un rayon de l'écran touche un bloc solide, le code calcule un nouveau rayon depuis le point d'impact jusqu'à la source lumineuse. Si ce rayon ne touche pas d'objet, on obtient de la lumière ! Sinon, on obtient de l'ombre. Le résultat est un éclairage et des ombres parfaits au pixel près.

J'ai ajouté des tonnes de blocs aléatoires au "ciel" pour rendre l'effet plus spectaculaire.

PixelMachine

J'ai ajouté une deuxième source de lumière et pris une autre photo.

PixelMachine

J'en avais assez de ce fond noir, alors j'ai essayé d'ajouter un ciel bleu. La pente à laquelle un rayon s'échappe du monde détermine la luminosité qu'il renvoie. Je l'ai inversé, donc le ciel est à l'envers. :/

Plus important encore, j'ai rendu la couche de blocs la plus basse réfléchissante. Lorsqu'un rayon en touche un, il rebondit et continue à chercher un autre objet à percuter. C'était assez simple à faire.

De plus, ces pixels d'erreur "roses" sont de retour, sauf qu'ils sont maintenant noirs. Ils étaient probablement là depuis le début, cachés dans l'obscurité... :O

PixelMachine

Un très bon exemple d'ombrage parfait au pixel près. Si un rayon atteint un bloc coloré et saute directement vers la source lumineuse sans autre collision, on obtient un pixel éclairé. C'est aussi simple que ça !

Le "terrain" est généré grâce à un algorithme assez basique qui consiste à choisir des hauteurs complètement aléatoires pour quelques points d'origine, puis à les lisser entre eux.

J'ai enfin corrigé les pixels d'erreur. Lorsqu'un rayon sortait du monde dans l'espace "-x" ou "-y", mon code n'avait aucun sens et provoquait une boucle infinie. Enfin, infinie, sauf pour mon instruction d'urgence if(k>1000) break;.

PixelMachine

Un aperçu du bord du monde. On peut également voir le relief se refléter dans la couche de blocs la plus basse.

PixelMachine

J'ai encore agrandi le monde. Et puis je me suis endormi. Il était environ 5h du matin. :D

Dimanche 18 février 2007

PixelMachine

Ajout d'une imprécision intentionnelle lorsqu'un rayon rebondit sur une surface réfléchissante. Cette diffusion donne à la surface un aspect texturé, comme des vagues d'eau très réfléchissantes.

PixelMachine

J'ai ajouté des ombres aux surfaces réfléchissantes. C'était très simple : il m'a suffi de calculer l'éclairage, même lorsque le rayon rebondit. Il s'agissait simplement de réorganiser quelques lignes de code.

PixelMachine

J'ai créé une zone carrée au centre du monde, plane et haute de 30 blocs, et je les ai tous rendus réfléchissants. Je voulais tester la réflexion sur d'autres surfaces que des surfaces planes.

PixelMachine

Waouh, regardez ça !

Aussi génial que cela puisse paraître, le lancer de rayons n'était pas si compliqué ! La sphère est simplement un point central et un rayon. Chaque fois que le code envoie un rayon dans la scène, il calcule la distance jusqu'au premier point d'intersection avec la sphère. Il suffit de résoudre les racines de l'équation du second degré. Il y a toujours 0, 1 ou 2 points où un rayon frappera une sphère, soit en le manquant, soit en le heurtant tangentiellement, soit en créant des blessures d'entrée et de sortie. Une fois le point d'impact sur la sphère défini, la normale à la surface n'est plus que le vecteur rayon, ce qui permet de rebondir facilement et d'obtenir un nouveau rayon ! Le reste est déjà géré par l'ancien code de réflexion.

L'éclairage du point d'impact sur la sphère est également géré par l'ancien code de test de lumière.

PixelMachine

J'ai ajouté 100 sphères supplémentaires, mais la plupart sont très haut dans le ciel (on les voit dans les reflets).

Toutes ces sphères ralentissent à peine le rendu, de seulement 10 à 15% ! Grâce aux cadres de délimitation, nous pouvons éviter de tester la plupart des sphères la plupart du temps. Comme la plupart des rayons ne se réfléchissent pas, et que ceux qui le font ont peu de chances de le faire à nouveau, j'ai autorisé jusqu'à 100 rebonds par rayon envoyé dans la scène, sans trop de souci.

PixelMachine

J'ai limité la hauteur maximale des sphères placées au hasard pour essayer de voir comment elles intersectent le terrain et tout ça.

PixelMachine

J'ai légèrement tourné la caméra vers la gauche (en fait, j'ai déplacé la cible juste en dessous de la sphère d'origine).

PixelMachine

J'ai décidé de rendre les couleurs du terrain moins aléatoires et plus jaunes. J'ai perdu la génération aléatoire pour cette photo, sinon j'en aurais créé une en haute résolution. :(

PixelMachine

Regardant vers le bas d'une sphère haute dans le ciel.

Il est possible qu'un rayon rebondisse indéfiniment entre les sphères, mais c'est peu probable. Il faut abandonner après un certain nombre de rebonds.

PixelMachine

C'est la première photo que j'ai utilisée pour mon fond d'écran. Rien de vraiment nouveau ici, mais j'ai raté la génération du terrain. Après cette photo, j'ai pris des pilules mystérieuses pour mon mal de dents et je ne me souviens plus de ce qui s'est passé, mais c'était la fin de la programmation pour la journée !

Réflexions finales

Le titre provisoire de ma nouvelle application est "PixelMachine"... parce que ça sonne un peu bizarre. Le tout a nécessité environ 800 nouvelles lignes de code. Je n'ai recyclé que 20 lignes de CollideRaySphere(), une fonction de mon moteur de jeu insaisissable sur lequel je travaille depuis toujours. En fait, c'était un excellent test pour voir si CollideRaySphere() fonctionne (et C'est le cas !).

Ensuite, je testerai CollideRayCylinder(), CollideRayTriangle(), CollideTriangleTriangle() et plus encore...

Concernant les futurs effets graphiques de PixelMachine, j'ajouterai un éclairage spéculaire, un effet bloom surbrillant, une plage dynamique élevée, une profondeur de champ, un brouillard/lumière volumétrique, ce genre de choses...

Si tout se passe bien, j'intégrerai toutes ces fonctionnalités dans mon moteur de jeu et j'ajouterai un bouton "prendre une capture d'écran en lancer de rayons" ou quelque chose de génial comme ça.


[Retour en haut] / [Retour aux articles]