{
	"version": "https://jsonfeed.org/version/1.1",
	"language": "fr",
	"title": "Expérimentations - PolariTOON",
	"description": "Fil d'expérimentations",
	"favicon": "../favicon.ico",
	"home_page_url": "./",
	"user_comment": "",
	"items": [
		{
			"title": "Serpents et échelles",
			"summary": "Génération procédurale de musique spectrale, inspirée du jeu de société éponyme",
			"date_published": "2024-02-10",
			"authors": [
				{
					"name": "PolariTOON"
				}
			],
			"external_url": "./snakes-and-ladders",
			"id": "snakes-and-ladders",
			"image": "./snakes-and-ladders/*.svg",
			"content_html": "<h2>Introduction</h2>\n<p>En musique spectrale, on est amené à procéder à l'analyse harmonique d'un son, c'est-à-dire décomposer son timbre en une somme de signaux élémentaires, appelés harmoniques, généralement pour mieux pouvoir le reconstituer. Ici le processus est moins ambitieux&nbsp;: on se contente de parcourir une à une les premières harmoniques d'un nombre limité de notes, jouées au piano, sans chercher à les superposer. Mais pas n'importe comment.</p>\n<h2>Règles du jeu</h2>\n<p>L'algorithme à suivre est directement inspiré des règles du jeu de société <cite>Serpents et échelles</cite>, mais avec quelques différences.</p>\n<p>Le tout se joue sur un plateau rectangulaire dont chaque case correspond à une note. Sur chaque rangée sont disposées une fréquence fondamentale et ses harmoniques, arrondies au demi-ton le plus proche. Les rangées sont elle-mêmes à un demi-ton d'intervalle.</p>\n<p>Chaque pion devient ici un doigt d'un pianiste, mais il pourrait tout à fait s'agir d'un instrument différent pour chaque pion. Lorsque qu'un doigt se rend sur une case, la note spécifiée est jouée.</p>\n<p>Initialement, au lieu d'être tous placés sur la même case, les doigts sont répartis totalement aléatoirement sur la grille.</p>\n<p>À chaque tour, le déplacement d'un doigt donné est le résultat de deux lancers de dé. Le premier, qui a autant de faces que la largeur du plateau, donne son déplacement horizontal. Contrairement au jeu de société, on ne change pas de rangée lorsqu'on arrive à une extrémité, on fait juste demi-tour. Le seul moyen de passer d'une rangée à une autre est d'emprunter un des serpents ou échelles qui relient les cases correspondant aux mêmes notes. C'est le second dé qui détermine sur laquelle de ces cases on se rend. À noter que serpents et échelles sont ici confondus. Ils peuvent être empruntés dans n'importe quel sens.</p>\n<p>Le but du jeu est d'atteindre la case correspondant à la note la plus grave. Une fois qu'un doigt l'a atteinte, on laisse toutefois les autres doigts finir leur propre tour.</p>\n<p>Pour certaines tailles de plateau, il existe cependant des configurations initiales qui ne permettent pas de terminer la partie.</p>\n<h2>Interprétation</h2>\n<p>Les doigts ont donc exactement le même nombre de tours et donc de notes à jouer. Cependant, il ne jouent pas l'un après l'autre, mais indépendamment, sans aucune interruption, en commençant tout de même exactement en même temps, ce qui forme un accord duquel émergent des arpèges. L'interprétation d'une note reprend certains concepts de musique spectrale, déjà explorés dans la pièce <cite>Bien à toi</cite> de José Manuel López López&nbsp;: moins d'importance est donnée aux harmoniques lointaines de leurs fondamentales, en réduisant leur durée et leur intensité. Mais ici elles ne sont pas pour autant plus fréquentes. Puisque les durées varient, la note la plus grave n'est donc pas nécessairement la dernière à être entendue. Aussi, puisque les doigts sont indépendants, des notes appartenant à des spectres différents peuvent très bien être entendues en même temps.</p>\n<h2>Rendus</h2>\n<figure>\n\t<figcaption>Rendu d'une partie à 4 joueurs sur un plateau de taille 6×6 avec la case A2 pour objectif</figcaption>\n\t<audio controls=\"\" src=\"./recording-a2-6-6-4-1.ogg\" preload=\"none\"></audio>\n</figure>\n<figure>\n\t<figcaption>Rendu d'une partie à 12 joueurs sur un plateau de taille 24×24 avec la case A0 pour objectif</figcaption>\n\t<audio controls=\"\" src=\"./recording-a0-24-24-12-1.ogg\" preload=\"none\"></audio>\n</figure>\n<figure>\n\t<figcaption>Rendu d'une partie à 8 joueurs sur un plateau de taille 12×40 avec la case A0 pour objectif</figcaption>\n\t<audio controls=\"\" src=\"./recording-a0-40-12-8-1.ogg\" preload=\"none\"></audio>\n</figure>\n<p>Une démonstration en direct est aussi disponible <a href=\"./snakes-and-ladders\">ici</a>. Le synthétiseur utilise la <em>sound font</em> <em>Fluid R3</em> <a href=\"//web.archive.org/web/20011225000000/http://www.jazzybee.com/fluid/\">publiée</a> par Frank Wen sous la licence <em>MIT</em> et <a href=\"//blog.gleitzman.com/post/63283830260/midijs-playing-audio-in-the-browser-with\">pré-rendue</a> par Benjamin Gleitzman sous la licence <em>Creative Commons Attribution 3.0 United States</em>.</p>\n",
			"banner_image": "./snakes-and-ladders/*.png",
			"tags": []
		},
		{
			"title": "Monitoring des traductions de Super Bear Adventure",
			"summary": "Outil utilisé par la communauté pour évaluer la qualité des traductions de Super Bear Adventure",
			"image": "./translations-monitor/*.svg",
			"date_published": "2023-09-05",
			"authors": [
				{
					"name": "PolariTOON"
				}
			],
			"external_url": "//github.com/PolariTOON/translations-monitor",
			"id": "translations-monitor",
			"content_html": "<p>Ce projet a commencé en août 2022. La refonte de l'ultime niveau du jeu <em>Super Bear Adventure</em>, la ruche, était en développement et la nouvelle fin du jeu qui se profilait imposait une réécriture complète du scénario. Cela nécessitait de passer en revue et de mettre à jour l'intégralité des traductions existantes, soit plus de 600 lignes de textes pour chacun des 14 langages pris en charge. Les traductions étant hébergées dans Google Sheet, il n'était pas évident de suivre les évolutions de la version originale (l'anglais) et d'identifier ce sur quoi travailler, d'autant que le niveau de maintenance des différents langages était déjà assez disparate.</p>\n<p>Pour procéder à cet effort de manière méthodique et éviter de perdre en qualité au niveau des traductions, j'ai donc créé ce script assez simple exportant les traductions pour les versionner avec <em>Git</em> et pour générer des rapports d'avancement pour chaque langage. Le versionnage permettait d'identifier les modifications substantielles apportées à la version anglaise, tandis que les rapports fréquents permettaient de déterminer certaines lacunes historiques et certains besoin propres à chaque traduction vis à vis de la version anglaise.</p>\n<p>Voici une description de la signification de chaque section dans les rapports, à destination des traducteurs&nbsp;:</p>\n<dl>\n\t<dt><q>extra</q></dt>\n\t<dd>\n\t\t<p>La ligne n'existe pas dans le fichier anglais, donc le jeu n'utilise pas cette ligne.</p>\n\t\t<p>Elle peut avoir été supprimée du fichier anglais ou vous pouvez vous être trompé sur son nom.</p>\n\t\t<p>Corrigez le nom de la ligne ou supprimez-la.</p>\n\t</dd>\n\t<dt><q>extra, but optional</q></dt>\n\t<dd>\n\t\t<p>La ligne existe dans le fichier anglais, et vous avez fait une traduction pour cette ligne dans votre fichier.</p>\n\t\t<p>Vous pouvez la retirer si la version anglaise a du sens dans votre langage.</p>\n\t\t<p>Cela apparaîtra dans <q>missing, but optional</q> la prochaine fois.</p>\n\t</dd>\n\t<dt><q>missing</q></dt>\n\t<dd>\n\t\t<p>La ligne existe dans le fichier anglais mais pas dans votre langage.</p>\n\t\t<p>Il peut s'agir d'une nouvelle ligne.</p>\n\t\t<p>Ajoutez la ligne à votre fichier et traduisez-la.</p>\n\t</dd>\n\t<dt><q>missing, but optional</q></dt>\n\t<dd>\n\t\t<p>La ligne existe dans le fichier anglais, et vous n'avez pas fait de traduction pour cette ligne.</p>\n\t\t<p>Mais vous pouvez en ajouter une si la version anglaise n'a pas de sens dans votre langage.</p>\n\t\t<p>Cela apparaîtra dans <q>extra, but optional</q> la prochaine fois.</p>\n\t</dd>\n\t<dt><q>multiple</q></dt>\n\t<dd>\n\t\t<p>Plusieurs lignes ont le même nom dans votre fichier, donc le jeu utilise la première.</p>\n\t\t<p>Le développeur duplique parfois des lignes de manière à vous dire que la ligne anglaise a beaucoup changé.</p>\n\t\t<p>Supprimez la ligne qui est obsolète.</p>\n\t</dd>\n\t<dt><q>transparent</q></dt>\n\t<dd>\n\t\t<p>Ce sont exactement les mêmes mots que dans le fichier anglais.</p>\n\t\t<p>Cela peut être normal si la ligne se traduit par elle-même, ou alors elle peut encore être en anglais et vous avez oublié de la traduire.</p>\n\t\t<p>Traduisez la ligne si besoin ou ignorez-la si c'est délibéré.</p>\n\t</dd>\n</dl>\n<p>À noter que les rapports ne sont pas exhaustifs. L'outil ne détecte pas si les traductions sont dépassées ou si les lignes sont encore écrites en anglais sans pour autant être identiques à la version originale. Cependant, même en étant aussi rudimentaires, de telles informations ont été cruciales et ont permis à la communauté de réaliser des traductions complètes pour la plupart des langages pris en charge par le jeu en quelques mois seulement.</p>\n<p>Avec l'accord du développeur du jeu, le code source est désormais disponible sur <a href=\"//github.com/PolariTOON/translations-monitor\">GitHub</a>.</p>\n",
			"banner_image": "./translations-monitor/*.png",
			"tags": []
		},
		{
			"title": "Monitoring des speedruns de Super Bear Adventure",
			"summary": "Outil utilisé par la communauté pour visualiser la chronologie des speedruns de Super Bear Adventure",
			"image": "./speedruns-monitor/*.svg",
			"date_published": "2023-09-05",
			"authors": [
				{
					"name": "PolariTOON"
				}
			],
			"external_url": "//github.com/PolariTOON/speedruns-monitor",
			"id": "speedruns-monitor",
			"content_html": "<p>Ce projet a commencé en juin 2021. La dernière mise à jour du jeu <em>Super Bear Adventure</em>, remplaçant le moteur physique du joueur par un nouveau, était à peine sortie qu'il apparaissait qu'elle apportait son lot de <em>breaking changes</em> en ce qui concerne les <em>speedruns</em>. En particulier, les changements relatifs au quad rendait impossible de rivaliser avec les records du monde dans deux sous-catégories des niveaux individuels. Il apparaîtra plus tard qu'un boss du jeu était aussi concerné, avec une incidence encore plus importante sur de nombreuses catégories l'incluant.</p>\n<p>Pour la première fois dans l'histoire des niveaux individuels de ce jeu, qui sont bien plus populaires que les catégories plus longues comme l'<q>Any%</q>, il était nécessaire de scinder des catégories en deux, en fonction de la version du jeu utilisée, afin de permettre à la compétition de pouvoir reprendre. Or, jusqu'à peu, cette information n'avait jamais été demandée aux joueurs soumettant leurs <em>speedruns</em>, et ne pouvait pas systématiquement être identifiée avec certitude rien qu'à partir des vidéos, lorsqu'elles étaient toujours disponibles.</p>\n<p>Pour correctement séparer les runs et éviter de rencontrer ce genre de difficultés dans le futur en cas de nouveau <em>breaking change</em> (ce qui arrivera plus d'une fois par la suite), j'ai donc étendu la tâche à l'intégralité des <em>speedruns</em> déjà soumis, que j'ai visionnés un par un et étiquetés lorsqu'il était possible d'être formel, en me basant sur les dates de soumission des <em>speedruns</em>, les dates des vidéos et différents indices à l'écran, après avoir déterminé au mieux les dates de sorties de chaque mise à jour, de chaque costume et de chaque changement majeur de l'interface graphique.</p>\n<p>Cependant, pour la majorité des <em>speedruns</em>, seule une fourchette de versions plus ou moins large pouvait être attribuée. J'ai donc créé ce script exportant les données des <em>speedruns</em> déjà connues, afin d'estimer les données manquantes en les interpolant ou extrapolant. Après quelques années, tous les speedruns valides ont été étiquetés.</p>\n<p>Le code source est désormais disponible sur <a href=\"//github.com/PolariTOON/speedruns-monitor\">GitHub</a>.</p>\n",
			"banner_image": "./speedruns-monitor/*.png",
			"tags": []
		},
		{
			"title": "Visionneuse Spotify",
			"summary": "Visualisation des titres favoris dans Spotify",
			"image": "./spotify-viewer/*.svg",
			"date_published": "2023-06-13",
			"authors": [
				{
					"name": "PolariTOON"
				}
			],
			"external_url": "//github.com/PolariTOON/spotify-viewer",
			"id": "spotify-viewer",
			"content_html": "<p>Le nom n'est pas très original, comme il s'agit seulement d'une petite preuve de concept d'une application se connectant à un tiers (<em>Spotify</em> en l'occurrence) grâce à une autorisation <a href=\"//datatracker.ietf.org/doc/html/rfc7636\"><em>OAuth 2.0</em></a>. Elle permet à l'utilisateur de consulter et de modifier sa liste de musiques favorites via des appels à l'<em>API</em>. Pour éviter de complexifier, le jeton d'accès n'est ni stocké ni rafraîchi, ce qui a l'inconvénient de demander une autorisation à chaque ouverture de la page, ce qui bien sûr ne serait pas approprié pour un usage réel. Le code source est disponible sur <a href=\"//github.com/PolariTOON/spotify-viewer\">GitHub</a>.</p>\n",
			"banner_image": "./spotify-viewer/*.png",
			"tags": []
		},
		{
			"title": "Bruitage d'images vectorielles",
			"summary": "Un court pipeline de filtres générant une image du soleil",
			"date_published": "2021-03-16",
			"authors": [
				{
					"name": "PolariTOON"
				}
			],
			"external_url": "./vector-graphics-noising",
			"id": "vector-graphics-noising",
			"image": "./vector-graphics-noising/*.svg",
			"content_html": "<h2>Introduction</h2>\n<p>Hier est paru <a href=\"//css-tricks.com/creating-patterns-with-svg-filters/\">un article sur <em>CSS Tricks</em></a> par Bence Szabó, traitant de la création de motifs à base de filtres <em>SVG</em>. Recourir à dégradés <em>CSS</em> pour générer des motifs ne m'est pas inconnu, mais je n'ai jamais pris le temps de me pencher réellement sur les techniques équivalentes en <em>SVG</em>. Or le champ des possibles qu'offre <em>SVG</em> me semble bien plus large, notamment grâce à la possibilité de générer du bruit. J'ai donc voulu essayer, et tel un enfant faisant son premier dessin, j'ai représenté... le soleil.</p>\n<h2>Ciel étoilé</h2>\n<p>J'ai repris quasiment telle quelle l'idée du fond étoilé généré pseudo-aléatoirement avec du bruit via l'élément <code>&lt;feTurbulence /&gt;</code>. J'ai juste ajusté les différents poids de la matrice pour avoir des étoiles de couleurs variées, quoiqu'essentiellement jaunes, au lieu d'être systématiquement blanches.</p>\n<figure>\n\t<figcaption>Code <em>SVG</em> et rendu d'un bruit de fond</figcaption>\n\t<pre><code>&lt;filter id=\"sky\"&gt;&#x0A;&#x09;&lt;feTurbulence type=\"fractalNoise\" baseFrequency=\".25\" /&gt;&#x0A;&#x09;&lt;feColorMatrix values=\"32 0 0 0 -24&#x0A;&#x09;                       24 0 0 0 -18&#x0A;&#x09;                       16 0 0 0 -12&#x0A;&#x09;                       0 0 0 0 1\" /&gt;&#x0A;&lt;/filter&gt;&#x0A;&lt;rect width=\"100%\" height=\"100%\" filter=\"url(#sky)\" /&gt;&#x0A;</code></pre>\n\t<picture><img width=\"640\" height=\"360\" alt=\"\" src=\"./background.svg\" loading=\"lazy\" /></picture>\n</figure>\n<p>Au passage, en voulant indenter la chose, j'ai découvert à mes dépens que <em>Webkit</em> n'apprécie pas les espaces en début et fin de la valeur de l'attribut <code>values=\"\"</code> de l'élément <code>&lt;feColorMatrix /&gt;</code>. La solution de contournement est vite trouvée...</p>\n<h2>Soleil</h2>\n<p>Un simple disque jaune pour dépeindre le soleil aurait fait tâche sur ce fond étoilé. Pour réprésenter son rayonnement et éviter d'avoir un contour trop net, j'ai ajouté un dégradé externe variant du jaune au rouge en partant du bord.</p>\n<figure>\n\t<figcaption>Code <em>SVG</em> et rendu d'un disque jaune rayonnant un dégradé jaune-rouge</figcaption>\n\t<pre><code>&lt;radialGradient id=\"gradient\" fr=\"33.3%\" r=\"50%\"&gt;&#x0A;&#x09;&lt;stop offset=\"3.125%\" stop-color=\"#ff0f\" /&gt;&#x0A;&#x09;&lt;stop offset=\"6.25%\" stop-color=\"#fc0c\" /&gt;&#x0A;&#x09;&lt;stop offset=\"12.5%\" stop-color=\"#f909\" /&gt;&#x0A;&#x09;&lt;stop offset=\"25%\" stop-color=\"#f606\" /&gt;&#x0A;&#x09;&lt;stop offset=\"50%\" stop-color=\"#f303\" /&gt;&#x0A;&#x09;&lt;stop offset=\"100%\" stop-color=\"#f000\" /&gt;&#x0A;&lt;/radialGradient&gt;&#x0A;&lt;circle cx=\"50%\" cy=\"50%\" r=\"96\" fill=\"url(#gradient)\" /&gt;&#x0A;</code></pre>\n\t<picture><img width=\"640\" height=\"360\" alt=\"\" src=\"./disk.svg\" loading=\"lazy\" /></picture>\n</figure>\n<p>Mieux. Un peu trop parfait en fait. Comment faire pour avoir une forme un peu moins ronde&nbsp;? Il se trouve que l'élément <code>&lt;feDisplacementMap/&gt;</code> permet précisément de créer un effet de distortion bidirectionnelle sur une image. Et en lui donnant du bruit en entrée, on peut obtenir une sorte de blob à la forme assez irrégulière&nbsp;! N'en abusons pas, tout de même.</p>\n<figure>\n\t<figcaption>Code <em>SVG</em> et rendu d'un blob</figcaption>\n\t<pre><code>&lt;filter id=\"sun\"&gt;&#x0A;&#x09;&lt;feTurbulence type=\"fractalNoise\" baseFrequency=\".125\" numOctaves=\"2\" /&gt;&#x0A;&#x09;&lt;feDisplacementMap xChannelSelector=\"R\" yChannelSelector=\"G\" in=\"SourceGraphic\" scale=\"16\" /&gt;&#x0A;&lt;/filter&gt;&#x0A;&lt;circle cx=\"50%\" cy=\"50%\" r=\"96\" filter=\"url(#sun)\" /&gt;&#x0A;</code></pre>\n\t<picture><img width=\"640\" height=\"360\" alt=\"\" src=\"./blob.svg\" loading=\"lazy\" /></picture>\n</figure>\n<p>Pas mal&nbsp;! On croirait voir les éruptions solaires ionisant la couronne du soleil&nbsp;! Il ne manque plus que la touche finale&nbsp;: animer le tout. Mais pour garder une animation régulière avec une échelle de bruit assez uniforme, la technique consacrée, bien qu'assez simple à mettre en place, semble vraiment relever de la magie. On peut en voir <a href=\"https://codepen.io/mullany/pen/BWKePz\">une démonstration sur <em>Codepen</em></a> par Michael Mullany. Au lieu d'animer la fréquence, l'astuce est de passer le bruit à travers un filtre de couleurs qui opère une rotation progressive sur la teinte selon le cercle chromatique. Cela permet d'exploiter le bruit généré sur le troisième canal de couleur, au lieu de se limiter à deux comme c'était le cas précédemment, mais aussi à l'animation de boucler.</p>\n<figure>\n\t<figcaption>Code <em>SVG</em> et rendu d'un blob animé</figcaption>\n\t<pre><code>&lt;filter id=\"sun\"&gt;&#x0A;&#x09;&lt;feTurbulence type=\"fractalNoise\" baseFrequency=\".125\" numOctaves=\"2\" /&gt;&#x0A;&#x09;&lt;feColorMatrix type=\"hueRotate\"&gt;&#x0A;&#x09;&#x09;&lt;animate attributeName=\"values\" from=\"0\" to=\"360\" dur=\"4s\" repeatCount=\"indefinite\" /&gt;&#x0A;&#x09;&lt;/feColorMatrix&gt;&#x0A;&#x09;&lt;feDisplacementMap xChannelSelector=\"R\" yChannelSelector=\"G\" in=\"SourceGraphic\" scale=\"16\" /&gt;&#x0A;&lt;/filter&gt;&#x0A;&lt;circle cx=\"50%\" cy=\"50%\" r=\"96\" filter=\"url(#sun)\" /&gt;&#x0A;</code></pre>\n\t<picture><img width=\"640\" height=\"360\" alt=\"\" src=\"./rotation.svg\" loading=\"lazy\" /></picture>\n</figure>\n<p>Je reste étonné du si peu de code nécessaire pour parvenir à un tel effet. Assemblons tout ça et jetons un œil au résultat final sans plus attendre&nbsp;!</p>\n<h2>Rendu</h2>\n<figure>\n\t<figcaption>Rendu du soleil devant un ciel étoilé (<a href=\"./sun-over-a-starry-sky.svg\">voir le code source</a>)</figcaption>\n\t<picture><img width=\"640\" height=\"360\" alt=\"\" src=\"./sun-over-a-starry-sky.svg\" loading=\"lazy\" /></picture>\n</figure>\n<p>Une démonstration est aussi disponible <a href=\"./vector-graphics-noising\">ici</a>.</p>\n",
			"banner_image": "./vector-graphics-noising/*.png",
			"tags": []
		},
		{
			"title": "Lancer de rayons",
			"summary": "Deux fragment shaders affichant des sphères et des tétrahèdres",
			"date_published": "2020-02-28",
			"authors": [
				{
					"name": "PolariTOON"
				}
			],
			"external_url": "./ray-tracing",
			"id": "ray-tracing",
			"image": "./ray-tracing/*.svg",
			"content_html": "<h2>Introduction</h2>\n<p>Dans le cadre d'un projet, j'ai eu l'occasion de découvrir <em>GLSL</em> (pour <em>OpenGL Shading Language</em>), et de programmer mes premiers <em>shaders</em>, mon expérience avec le graphisme par ordinateur se résumant jusque là à de la 2D avec <em>SVG</em> et l'élément <em>HTML</em> <code>&lt;canvas&gt;&lt;/canvas&gt;</code> (ou des technologies équivalentes). L'objectif ici est d'implémenter du lancer de rayon, de la 3D donc. Heureusement, des environnements comme <a href=\"//members.loria.fr/Bruno.Levy/GEOGRAM/geoshade.html\"><em>Geoshade</em></a> ou <a href=\"//www.shadertoy.com/\"><em>Shadertoy</em></a> permettent d'itérer et d'obtenir un retour assez rapidement lors du développement de <em>shaders</em>, ou devrais-je dire, de <em>fragment shaders</em> pour être correct, ces outils ne couvrant pas les <em>vertex shaders</em>, une autre étape majeur du <em>pipeline</em> de rendu d'<em>OpenGL</em>. Le challenge est donc de parvenir à calculer un rendu complet dans un unique <em>shader</em> en faisant abstraction du reste du <em>pipeline</em>.</p>\n<p>Les deux <em>shaders</em> présentés ici réalisent donc du lancer de rayons, que je présume physiquement simpliste et plutôt sous-optimal étant données les structures de données utilisées et leurs performances, sur deux types de surfaces, respectivement des sphères et des triangles assemblés en tétrahèdres. Ils n'en restent pas moins essentiellement identiques ; seuls la nature des objets, l'algorithme d'intersection et le calcul d'orientation des surfaces varient entre eux. Pour bénéficier des dernières fonctionnalités de <em>GLSL</em>, les <em>shaders</em> ont été développés et testés sur <em>Shadertoy</em>.</p>\n<h2>Macros</h2>\n<p>Les programmes macro-définissent trois constantes : <code>SPHERE_COUNT</code> / <code>TETRAHEDRON_COUNT</code>, <code>RING_COUNT</code> et <code>REFLECTION_COUNT</code>. Celles-ci représentent respectivement :</p>\n<ul>\n\t<li>\n\t\t<p>le nombre de sphères / tétrahèdres par anneau ;</p>\n\t</li>\n\t<li>\n\t\t<p>le nombre d'anneaux ;</p>\n\t</li>\n\t<li>\n\t\t<p>le nombre de réflexions.</p>\n\t</li>\n</ul>\n<p>Voici quelques ensembles de valeurs qui donnent des résultats intéressants :</p>\n<dl>\n\t<dt><code>{SPHERE_COUNT: 27, RING_COUNT: 1, REFLECTION_COUNT: 0}</code></dt>\n\t<dt><code>{TETRAHEDRON_COUNT: 9, RING_COUNT: 1, REFLECTION_COUNT: 0}</code></dt>\n\t<dd>\n\t\t<p>Une simple chaîne de sphères mates / tétrahèdres mats tournoyant autour d'une source de lumière</p>\n\t</dd>\n\t<dt><code>{SPHERE_COUNT: 27, RING_COUNT: 1, REFLECTION_COUNT: 2}</code></dt>\n\t<dt><code>{TETRAHEDRON_COUNT: 9, RING_COUNT: 1, REFLECTION_COUNT: 2}</code></dt>\n\t<dd>\n\t\t<p>La même scène, mais avec des surfaces polies</p>\n\t</dd>\n\t<dt><code>{SPHERE_COUNT: 9, RING_COUNT: 2, REFLECTION_COUNT: 0}</code></dt>\n\t<dt><code>{TETRAHEDRON_COUNT: 3, RING_COUNT: 2, REFLECTION_COUNT: 0}</code></dt>\n\t<dd>\n\t\t<p>Deux couches de sphères mates / tétrahèdres mats tournoyant autour d'une source de lumière</p>\n\t</dd>\n\t<dt><code>{SPHERE_COUNT: 9, RING_COUNT: 2, REFLECTION_COUNT: 2}</code></dt>\n\t<dt><code>{TETRAHEDRON_COUNT: 3, RING_COUNT: 2, REFLECTION_COUNT: 2}</code></dt>\n\t<dd>\n\t\t<p>La même scène, mais avec des surfaces polies</p>\n\t</dd>\n</dl>\n<p>Les rendus ci-dessous ont été obtenus avec le dernier ensemble de paramètres. Contrairement aux sphères qui ne sont composées que d'une face, les tétrahèdres en ont quatre, ce qui a clairement une incidence sur les performances du second <em>shader</em> ; c'est pourquoi il convient de réduire le nombre de tétrahèdres.</p>\n<h2>Bruitage du fond</h2>\n<p>Le fond à été bruité pseudo-aléatoirement pour éviter de le confondre avec les ombres qui sont quant à elles noires en absence de réflexions.</p>\n<h2>Perspective</h2>\n<p>Au lieu d'utiliser une projection orthographique, la scène est rendue en perspective. Pour cela, il est nécessaire de définir la focale de la caméra dont l'objectif est le canevas. Dans le code, il s'agit de la variable <code>perspective</code>. Celle-ci peut être ajustée pour modifier l'impression de profondeur. Il faut noter que dans la démarche de lancer de rayons, le rayon part bien de l'objectif et non du foyer pour éviter d'afficher les objets se situant entre les deux.</p>\n<h2>Lumière</h2>\n<p>La source de lumière est ponctuelle et au centre de la scène. L'intensité lumineuse de cette source est calculée de manière à être en tout point inversement proportionnelle au carré de la distance parcourue depuis la source (y compris après une réflexion). La puissance maximale de la source de lumière est stockée dans la variable <code>power</code> et peut être ajustée. Il faut noter que les surfaces sont orientées. Si la source se situe à l'intérieur d'un objet, alors la lumière le traverse mais ne s'y réfléchit pas : il cachera les objets se situant derrière, mais sans faire d'ombre à toute la scène.</p>\n<h2>Rendus</h2>\n<figure>\n\t<figcaption>Rendu d'un lancer de rayons sur des sphères (<a href=\"./spheres.glsl\">voir le code source</a>)</figcaption>\n\t<video controls=\"\" width=\"640\" height=\"360\" poster=\"./spheres.png\" src=\"./spheres.webm\" preload=\"none\" loop=\"\"></video>\n</figure>\n<figure>\n\t<figcaption>Rendu d'un lancer de rayons sur des tétrahèdres (<a href=\"./tetrahedrons.glsl\">voir le code source</a>)</figcaption>\n\t<video controls=\"\" width=\"640\" height=\"360\" poster=\"./tetrahedrons.png\" src=\"./tetrahedrons.webm\" preload=\"none\" loop=\"\"></video>\n</figure>\n<p>Une démonstration en direct est aussi disponible <a href=\"./ray-tracing\">ici</a>. À savoir que les calculs sont potentiellement intensifs et des problèmes de rendu sont attendus sur les appareils avec une représentation des nombres à virgule flottante de moyenne / faible précision.</p>\n",
			"banner_image": "./ray-tracing/*.png",
			"tags": []
		}
	]
}