{
	"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": "./*.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": "./*.png",
	"tags": []
}