TLDR: Mathsworld, un outil web pour faire du raytracing avec des scènes décrite en lisp.
Salut 'nal,
J'ai encore commis un code
improbable. En gros, c'est un truc qui prend en entrée une scène écrite sous forme de S-Expression et qui génère un shader WebGL raytraçant la scène.
Pourquoi ? Parce que ça m'amusait. Et que je voulais apprendre des trucs.
Tout à commencé avec mon envie de comprendre comment on générait des images avec des maths (et un ordinateur). J'ai d'abord fait un raycaster façon Wolfenstein 3D. Ça m'a amené à découvrir et jouer avec les SDF. C'était cool, mais je voulais aller plus loin et passer à la 3D (et oui, bien que le rendu donne une impression de 3D, le moteur est purement 2D). J'ai acheté le livre Computer Graphics From Scratch (contenu disponible gratuitement sur le site de l'auteur ici) de Gabriel Gambetta et commencé mon raytracer en rust. Grace au livre, j'ai pu rapidement et facilement faire mes premiers rendus. J'étais content. Mais c'était lent (rendu CPU), et je n'étais pas satisfait du format d'entrée des scènes (YAML).
C'est vers cette période là que Joalland< a posté son lien Painting a Landscape with Maths. Et ça m'a frustré. Parce que les concepts mathématiques sous-jacents sont étonnament simples (accessibles à un niveau Bac, peut-être moins), mais personne n'a envie de d'apprendre à faire de fat shaders GLSL pour pouvoir jouer avec des maths et faire de jolis paysages. Je le sais, parce que moi même j'avais envie de faire de jolis paysages avec des maths sans apprendre à faire de fat shaders GLSL. (Joalland<, tu te demandais comment il avait fait. C'est un fat shader. Ces autres créations sont visibles là. Ce sont à chaque fois de fat shaders).
Ça m'a aidé à redéfinir le scope du projet :
- faire un truc genre shadertoy pour simplifier la distribution afin de le rendre plus accessible qu'un utilitaire CLI en rust
- avoir un format de scène facile à utiliser pour les humains ET l'ordinateur, possiblité de scripting, voir de génération procédurale de la scène.
- utiliser le GPU pour avoir des performances décentes.
- posséder un fort syndrome de NIH
Comme format de scène, j'ai choisi les s-expr. C'est plutôt simple à utiliser par un humain, c'est plutot simple à manipuler par l'ordinateur, et ça ouvre des possibilités de scripting for intéressantes pour méta-générer les scènes.
Notre scène de référence:
(scene
(camera
(vector3 0 0 0)
(vector3 1 1 1))
(union (list
(sphere
(vector3 0 -5001 0)
5000
(material
(color 1 1 0)
1000))
(sphere
(vector3 0 -1 3)
1
(material
(color 1 0 0)
500))
(sphere
(vector3 2 0 4)
1
(material
(color 0 0 1)
500))
(sphere
(vector3 -2 0 4)
1
(material
(color 0 1 0)
10))))
(list
(ambiant_light
0.2)
(omni_directional_light
0.6
(vector3 2 1 0))
(directional_light
0.2
(vector3 1 4 4))))
Un petit truc simple où on déclare 4 sphères colorés et quelques lumières.
À terme, je compte utiliser parenscript qui m'a été recommandé suite à mon dernier journal pour profiter de toute la puissance de Common Lisp et pouvoir décrire des scènes de guedin facilement. Mais pour l'instant, afin de "gagner" du temps, j'ai écrit mon propre parseur. J'avoue, j'avais vraiment envie de découvrir les parseur LL. C'est magique ces trucs, ils méritent leurs propre journal.
Une fois que j'ai chargé la scène en mémoire, je la transforme en shader GLSL que je dessine dans un context WebGL du navigateur. Là, j'ai fait sale. Je suis allé sur Using shaders to apply color in WebGL, copié collé le code comme un sagouin et supprimé comme un sauvage les trucs qui semblaient superflus. Vint la parti rigolote où je réimplémenta mon raytracer rust sous forme de fat shader GLSL capable de faire un rendu de la scène donnée plus haut hardcodé dans le shader. Une fois en possession de ce shader de référence, je m'en suis servi pour faire un template dans lequel j'insère la scène voulu par l'utilisateur, je file ça au GPU, et ka-boom, on a des pixels colorés :
Tu peux jouer avec là.
Comme tu peux le constater, l'UI et l'UX sont inexistantes. Tu as une textarea dans lequel tu peux éditer la scène, le canvas dans lequel le rendu est fait, et une textarea qui contient le shader généré et que tu peux ignorer. Le shader est regénéré à la volé lors de l'édition, les erreurs sont remontés dans la console.
Pour l'instant, la syntaxe supporté est la suivante : une expression parenthésée composée d'un identifier et suivi de 0 ou plusieurs arguments pouvant être un nombre ou une expression parenthésée.
S ::= SEXPR $
SEXPR ::= ( identifier ARGS )
ARGS ::= ARG ARGS
ARGS ::= ε
ARG ::= number
ARG ::= SEXPR
Il n'y a aucun retour en cas d'erreur, hormis un message cryptique dans la console du navigateur.
Le format de description de scène est le suivant:
scene: Une scène avec une caméra, des lumières et des objets. Doit être la racine de la S-Expr
- camera: La caméra
- root: Le nœud principal. Peut-être de type `union` ou `sphere`
- lights: Une liste de lumières. Peuvent-être de type `ambiant_light`, `omni_directional_light` ou `directional_light`
camera:
- position: (x, y, z) La position de la caméra dans la scène
- view_port: (hauteur, largeur, distance) Les réglages du view port (en trèèèèèès gros, les réglages de zoom)
union: Une union d'objets
- nodes: Une liste de nœuds à unir. Peuvent être de type `union` ou `sphere`
sphere: Une sphere
- position: (x, y, z) La position de la sphère dans la scène
- radius: Le rayon de la sphère
- material: Le matériau de la sphère
material:
- color: (rouge, vert, bleu) La couleur RGB du matériau. Chaque composante est est défini dans l'intervalle [0, 1]
- specular: l'intensité de l'effet de brillance
ambiant_light: une lumière ambiante
- intensity: intensité de la lumière
omni_directional_light: une lumière omnidirectionnelle (une amoule)
- intensity: intensité de la lumière
- position: (x, y, z
directional_light: une lumière directionnelle (le soleil)
- intensity: intensité de la lumière
- direction: (x, y, z) La direction normalisée de la lumière
À terme, je compte ajouter :
- plus de formes
- possibilité de définir ses propres formes
- matériaux procéduraux
- le plein support de common lisp pour écrire la scène
- réécriture du backend du générateur de shader GLSL parce que là c'est vraiment caca :D
- possibilité de faire des animations
- une interface plus dans le style de shader toys, avec possibilité d'enregistrer et partager ses créations
# Wolfenstein 3D
Posté par Dr BG . Évalué à 5.
C'est plus du ray casting que tracing non ?
[^] # Re: Wolfenstein 3D
Posté par jtremesay (site web personnel) . Évalué à 2.
tutafé, je m'a trompé
[^] # Re: Wolfenstein 3D
Posté par gUI (Mastodon) . Évalué à 3.
du coup j'ai corrigé, merci à tous les deux.
En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.
# détail synthaxique
Posté par steph1978 . Évalué à 2. Dernière modification le 17 mars 2023 à 22:53.
Je ne suis pas très calé en dialectes lisp mais à la lecture du source (qui, si on omet les parenthèses est plutôt claire, parce qu'indenté :troll:), pourquoi a-t'on :
(union (list (items...)))
et pas(union (items...))
? Autrement dit pourquoi une "list" d'une liste et pas simplement une liste.[^] # Re: détail synthaxique
Posté par jtremesay (site web personnel) . Évalué à 2.
C'est comme ça qu'ils font en lisp, je n'ai pas cherché ầ étre innovant sur ce point là.
[^] # Re: détail synthaxique
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 0.
Grosso-modo, c'est
(commande arguments)
et ici, la commandeunion
attend une liste, pas des éléments de listes. Ça tombe bien, la commandelist
transforme ses arguments en une seule liste (et si l'argument était déjà une liste tant mieux rien ne change).Je pense que ton incompréhension vient du fait que tu penses que les parenthèses sont des délimiteurs de listes. Si tu transposes dans d'autres langages,
(union ma-liste)
correspond à l'appel de fonctionunion(ma-liste)
où ta variablema-liste
va contenir une liste mais ce n'est aucunement un appel à une fonction multi-valeursunion(élément-1, élément-2, …, élément-N)
Par contre,list
est une telle fonction.“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: détail synthaxique
Posté par steph1978 . Évalué à 4.
Si c'est bien mon point : pourquoi
union
n'a pas le comportement de faire l'union de tous ses arguments au lieu d'attendre quelist
fasse le travail de rassembler tous ses arguments sous forme de liste et de lui passer une liste. Silist
sait travailler sur un nombre non fixe d'arguments, j'imagine que c'est donc possible en LISP. Pourquoi ne pas le faire pourunion
? Cela éviterai une imbrication supplémentaire et rendrait le code plus clair.[^] # Re: détail synthaxique
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 0.
Ce comportement est justement possible dans tous les langages, avec un certain coût : c'est le principe des fonctions variadiques
C'est justement plus clair ainsi (en tout cas pour moi) : la commande accepte "un argument" qui représente une liste.
Ta demande est d'accepter plusieurs arguments, sachant que le langage a choisi d'être flexible… et sans nécessiter de contorsions ni avoir d'effets de bords… Je te laisse transposer dans le langage de ton choix (TypeScript, Java, Eiffel, Pascal, Rust, whatever) pour voir (pourquoi aucun langage dérivé n'a changé cet aspect.)
Au pire, il faut voir
list()
comme indiquant/forçant le type/typage, tout comme tu auraisdouble foo
en C.“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: détail synthaxique
Posté par steph1978 . Évalué à 4.
C'est pas super clair comme explication.
ça vient d'où ça ?
Mais puisque Lisp le permet et l'utilise dans
list
.C'est supporté par la plupart des langages.
Dérivé de quoi ? de Lisp ? Mais "TypeScript, Java, Eiffel, Pascal, Rust, whatever" ne sont pas dérivés de Lisp. Et Lisp le permet et l'utilise dans
list
.En effet tous les goûts sont dans la nature.
Un exemple en openscad qui sert à faire des modèles 3D :
C'est lisible IMHO. Ils n'ont pas fait
union de list de trucs
ils ont faitunion de trucs
.Au final, je préfère l'explication de l'auteur : "[…], je n'ai pas cherché ầ étre innovant sur ce point là".
[^] # Re: détail synthaxique
Posté par dzecniv . Évalué à 1.
On va prendre l'exemple de CL:
union
fait l'union de 2 listes, et en retourne 1:On manipule des listes d'objets.
Dans notre exemple on a des objets sphères au lieu des entiers, et surtout on a 1 seule liste donnée en argument et pas 2, ce qui nous fout le doute. Je suppose qu'elle est retournée telle quelle.
[^] # Re: détail synthaxique
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à -1.
Oui, ça se comprend qu'une/un seule/seul liste/ensemble donnée en argument foute le doute quand on fait une réunion ensembliste. Ici le mot utilisé dans un sens un peu différent.
Mais steph1978< semble bloquer sur le mot
list
et les parenthèses ; d'où ma remarque“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: détail synthaxique
Posté par jtremesay (site web personnel) . Évalué à 2.
Tutafé. Ici, c’est une opération ensembliste sur les volumes passés en paramètre. À terme, il y aura aussi l’intersection et la différence. Avant, j’utilisai des opérateurs binaires. L’intention était plus claire mais la scène plus chiante à écrire :
[^] # Re: détail synthaxique
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à -1.
Une invitation à transposer dans un autre ne signifie pas que ce langage est dérivé de Lisp --"
Oui successeurs/descendants de Lisp n'ont pas changé cet aspect. Et l'auteur n'a pas jugé utile non plus d'innover sur cet aspect.
union de trucs =
(union trucs)
ici, et trucs =(list …)
ici.“It is seldom that liberty of any kind is lost all at once.” ― David Hume
# CADQuery
Posté par YBoy360 (site web personnel) . Évalué à 2.
Très intéressant comme approche, il y a quelques temps j'avais essayé de faire un CAD via des DSLs (Langage Spécifique), avec une pré-visualisation dans Intellij (pour l'auto-completion du code).
C'était tiptop comme projet, mais vu la gueule des designers lors de la présentation, je suis passé à autre chose …
Tu connais CADQuery ?
ça devrait t'intéressé …
[^] # Re: CADQuery
Posté par jtremesay (site web personnel) . Évalué à 3.
Je connaissais OpenSCAD de nom, jamais joué avec.
[^] # Re: CADQuery
Posté par YBoy360 (site web personnel) . Évalué à 1.
Je disais CADQuery, pas OpenSCAD :)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.