Journal
Sections
Réglages de lecture
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
Code et termes techniques en anglais
Désactivé : la voix de la page lit tout, sans pause entre deux voix.
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
D’autres voix : Microsoft Edge propose des voix naturelles en ligne, et Windows (Paramètres › Heure et langue › Voix), macOS et Android en installent d’autres.
Avancement
Section intitulée « Avancement »- three 0.186.0,
@types/three0.186.0, Vite 8.3.0, TypeScript 7.0.2, Playwright 1.63.0 et glTF Transform 4.5.0 épinglés dans lepackage.jsonet le fichier de verrouillage propres au cours -
check.sh: la vérification des types, les scripts Node.js, chaque page de leçon sondée dans Chromium headless, le build de production, comparés àexpected/ - CI sur Ubuntu, Windows et macOS
- Leçon 1 : scène, caméra, moteur de rendu
- Leçon 2 : géométries, matériaux, lumières et ombres
- Leçon 3 : couleur, tone mapping et environnements HDR
- Leçon 4 : modèles glTF et animations
- Leçon 5 : interaction,
Raycasteret contrôles de caméra - Leçon 6 : TSL et matériaux à nœuds
- Leçon 7 : post-traitement avec
RenderPipeline - Leçon 8 : la performance, mesurée
- Leçon 9 : React Three Fiber et drei
- Leçon 10 : physique avec Rapier en WebAssembly
- Leçon 11 : WebXR, émulé
- Leçon 12 : tests et CI
- Leçon 13 : projet, le manche de guitare 3D de GuitarAlchemist
- Leçon 14 : labo, Guitar Alchemist en 3D, quinze expériences mesurées
- Cours terminé
- Démos en direct : chaque page de leçon et cinq scènes complètes, publiées sous le site avec un panneau Try it
three.js r186 et les bibliothèques autour sont le logiciel de quelqu’un d’autre ; ce tableau retient ce que les leçons y ont trouvé, pas ce qu’elles ont trouvé dans les pages du cours. L’essentiel vient du passage à WebGPURenderer, assez récent pour que ce soit souvent l’habitude WebGL qui ait tort. Rien n’a été signalé en amont, sauf là où la colonne État nomme un ticket existant.
| Attendu | Ce qui se passe | Où | Mesure | État |
|---|---|---|---|---|
WebGPURenderer compte les appels de dessin de la scène, comme WebGLRenderer |
Avec un tone mapping, ou un espace colorimétrique de sortie différent de l’espace de travail, il rend dans une cible puis la dessine dans une passe de sortie | three.js r186 WebGPURenderer |
2 appels de dessin et 13 triangles pour un cube, contre 1 sur WebGL ; les deux objets info n’ont d’ailleurs pas la même forme |
Reproduit 2026-09-16 |
@types/three décrit toJSON() tel que le runtime l’écrit |
En 0.186.0, Object3DJSON déclare children comme des chaînes et n’a ni geometries ni materials ; le runtime écrit des objets et les deux tableaux |
@types/three 0.186.0 |
Les erreurs du compilateur sont conservées dans errors/l01_tojson_types.ts |
Reproduit. Aucun ticket amont cherché, aucun ouvert 2026-09-16 |
PCFSoftShadowMap fonctionne sur WebGPURenderer |
Retiré en r186, et il avertit. R3F le pose pour tout shadows vrai et récolte le même avertissement à chaque rendu de <Canvas> |
three.js r186, @react-three/fiber 9.7.0 | LunarLanderEngine.ts et MinimalThreeInstrument.tsx de GA l’associent encore à WebGPURenderer |
Reproduit 2026-09-16 · 2026-09-16 · Pas encore dans GA, qui épingle three 0.180.0, où il fonctionne toujours ; 2026-09-24 |
| Une lumière ponctuelle et un spot convertissent les lumens en candelas de la même façon | SpotLight.power utilise π au lieu de 4π et ne varie pas avec l’angle du cône |
three.js SpotLight.power |
800 lm donnent 63,66 cd sur une ponctuelle et 254,65 sur un spot | Reproduit 2026-09-16 |
| Construire un environnement équirectangulaire coûte à peu près une passe | Le PMREM coûte 17 rendus : 1 dans le premier niveau, puis 2 pour chacun des 8 autres, même pour une simple texture équirectangulaire | three.js PMREMGenerator |
17 rendus pour un environnement de 256 sur 128 | Reproduit, par conception 2026-09-16 |
| Le bundler embarque les décodeurs Draco que le navigateur charge | Depuis r185, DRACOLoader les résout avec import.meta.url : Vite les copie donc tous les cinq |
three.js r185+ DRACOLoader, Vite |
1,31 Mo embarqués là où un navigateur en charge deux ; le build avertit aussi que three.webgpu dépasse 500 ko |
Reproduit, comportement changé depuis r184 2026-09-16 |
| La compression garde tous les sommets de la géométrie | Draco et meshopt suppriment tous deux les deux sommets polaires que SphereGeometry crée et qu’aucun triangle n’utilise ; le weld() de Draco fusionne en plus les coins du corps non indexé |
Draco, meshopt, glTF Transform 4.5.0 | Les comptes de sommets avant et après, dans expected/ |
Reproduit, défendable 2026-09-16 |
Le <Text> de drei se dessine sur WebGPURenderer comme sur WebGL |
Il dessine un quad vide : troika-three-text injecte son shader par onBeforeCompile, que le renderer à nœuds n’appelle jamais |
drei 10.7.8 | Quad vide | Reproduit. Reste ouvert : l’existence d’un troika-three-text prêt pour WebGPU 2026-09-16 |
setColorAt colore les instances quoi qu’ait fait le premier rendu |
Un InstancedMesh l’ignore si son premier rendu n’avait pas de couleurs d’instance |
three.js InstancedMesh |
Les marqueurs du manche sont restés blancs jusqu’à ce que l’attribut soit alloué d’avance | Reproduit, contourné 2026-09-16 |
WebXR sur WebGPURenderer se comporte comme sur WebGLRenderer |
Il lui faut XRGPUBinding et la fonctionnalité webgpu ; son message d’erreur nomme un VRButtonGPU qui n’existe pas ; WebGLXRFallback échange les renderers mais les contrôleurs pris au premier cessent de recevoir des événements |
three.js r186, WebXR sur WebGPU | Seul le WebGLRenderer classique a dessiné les deux yeux sous IWER |
Reproduit 2026-09-16 |
InstancedMesh fonctionne avec des morph targets |
Il échoue avec une seule instance, morphTargetInfluences n’étant jamais créé, et autrement avec plusieurs, dans UniformArrayNode.updateBuffer. Il lui faut aussi geometry.morphTargetsRelative = true, sans le dire |
three.js r186 | Deux échecs distincts, un par nombre d’instances | Reproduit 2026-09-17 |
BatchedMesh regroupe en peu d’appels de dessin sur tous les backends |
Sur WebGPU, il dessine un appel par objet | three.js r186 BatchedMesh, backend WebGPU |
44 appels pour un manche contre 3 sur WebGL 2, qui utilise WEBGL_multi_draw |
Reproduit 2026-09-17 |
renderer.info.memory.texturesSize compte les textures compressées |
Il compte 0 octet pour les textures KTX2, et compte les cibles de rendu à la place | three.js r186 renderer.info.memory |
0 octet pour le jeu de bois compressé, ce qui a empêché de finir l’expérience 6 | Reproduit. Reste ouvert : mesurer la mémoire GPU KTX2 avec un outil GPU 2026-09-17 |
powerPreference choisit un GPU |
Chromium sous Windows l’ignore et avertit ; les autres OS non | Chromium 153 | check.sh filtre la ligne |
Connu en amont, ticket Chromium 369219127 2026-09-16 |
L’addon RapierPhysics de three.js suit une version courante de Rapier |
Celui de r186 charge encore 0.17.3 depuis skypack.dev, alors que le cours utilise 0.20.0 | three.js r186 examples/jsm/physics/RapierPhysics |
Deux versions mineures d’écart | Reproduit, le cours n’utilise pas l’addon 2026-09-16 |
| Le build WebGPU de R3F n’embarque que le point d’entrée WebGPU | Il importe three à côté de three/webgpu, donc le cœur WebGL est embarqué aussi |
@react-three/fiber 9.7.0 | 565 ko gzip contre 210 ko pour la même scène sans React | Reproduit. Reste ouvert : aliaser three vers three/webgpu 2026-09-17 |
Expériences
Section intitulée « Expériences »La leçon 14 a mené quinze expériences dans le labo 3D Guitar Alchemist. Chaque hypothèse et ses prédictions chiffrées ont été versées le 16 septembre, la veille des mesures : c’est ce qui permet à une ligne ci-dessous de dire réfutée. Sur les 61 prédictions, 19 étaient fausses en tout ou partie et 2 n’ont pas pu être mesurées. Mesuré sur une RTX 5080 sous Chromium 153, temps GPU par requêtes d’horodatage WebGPU. Le détail est dans la leçon 14.
| Question | Hypothèse | Résultat | Verdict |
|---|---|---|---|
| Quelle façon de dessiner 16 manches de guitare coûte le moins : celle de GA, instanciée, batchée ou fusionnée ? | Les appels de dessin dictent le temps CPU ; le GPU bouge à peine de l’une à l’autre | La version batchée sur WebGPU a tiré 44 appels, et non les 4 ou moins prédits. Les 16 manches de GA coûtent 2,9 ms de CPU, et non les 10 ms ou plus prédites | Majoritairement réfutée |
| Réécrire les sommets d’une corde vibrante sur le CPU contre le faire en TSL, jusqu’à 600 cordes | À 600 cordes, la réécriture CPU coûte davantage, et le coût GPU est comparable | CPU 1,8 ms contre TSL 0,36 ms, un facteur 5, comme prédit. Mais TSL a coûté 1,7 fois le temps GPU de la version CPU | Confirmée côté CPU, réfutée côté GPU |
Désigner une note parmi 10 000 : Raycaster contre une passe d’identifiants sur GPU |
La sélection GPU l’emporte à 10 000 objets | Raycaster 0,465 ms contre 3,395 ms pour la sélection GPU |
Réfutée |
| Transposer 49 152 bracelets de classes de hauteurs, sur CPU contre TSL | Le CPU paie la reconstruction de 49 152 matrices par image : 4 ms ou plus | CPU 2,455 ms, en dessous de la prédiction. TSL 0,29 ms, comme prédit | Confirmée pour TSL, réfutée pour le CPU |
| Un nuage de points OPTIC-K jusqu’à 1 000 000 de points, et si le calcul TSL exige WebGPU | Les points restent bon marché jusqu’au million ; le compute shader ne tourne pas sur WebGL 2 | Les points ont tenu. Le compute shader a bien tourné sur WebGL 2, par transform feedback, à 2,8 ms GPU | Confirmée pour les points, réfutée pour le backend |
| Ce que coûtent les textures de bois non compressées, et ce que KTX2 économise | RGBA8 coûte environ 22 Mo ; KTX2 en BC7 en coûte environ le quart | 21,33 Mo confirmés. Le chiffre KTX2 n’a pas pu être lu : renderer.info compte 0 pour les textures compressées |
Moitié confirmée, moitié non mesurable |
Une grille de matériaux PBR : combien de programmes de shader, et ce qu’ajoute MeshPhysicalMaterial |
Un programme par type de matériau, partagé sur la grille ; le matériau physique coûte plus de temps GPU | 16 programmes dans les deux cas, comme prédit. Le matériau physique a coûté 1,46 fois le temps GPU | Confirmée |
| Ce que coûte une shadow map dynamique face à une cuite | Une shadow map dynamique double à peu près les appels de dessin | 13 appels contre 8, un facteur 1,6. La cuisson a pris 398 ms, comme prédit | Confirmée en gros |
| Ce que coûte le bloom en 1080p | Entre 0,5 et 1,5 ms GPU | 0,135 ms, quatre fois sous la prédiction la plus basse | Réfutée |
| Un mur de 1 000 guitares avec LOD et frustum culling | Le LOD divise les triangles par environ 10 | Par 11,2 | Confirmée |
| Si le CCD de Rapier empêche un médiator cinématique de traverser une corde | Le CCD empêche la traversée | Le médiator a traversé à 30 comme à 60 pas par seconde, CCD activé ou non | Réfutée |
| Si un manche est à l’échelle 1:1 en WebXR, sous l’émulation IWER | Du sillet à la douzième case, on mesure 0,324 m | 0,324 m | Confirmée |
| De combien il faut programmer une note à l’avance pour que WebAudio et l’image s’accordent | L’avance naïve vaut à peu près la somme des latences, 50 ms, à une image près | 31,8 ms, soit 18 ms de moins que la somme | Réfutée |
| Ce que coûte React Three Fiber face à three.js nu, pour la même scène | Peu cher par image, cher au démarrage (+30 à 150 ms) et dans le bundle (+50 à 90 ko) | Peu cher par image, comme prédit. Première image +430 ms et bundle +195 ko, tous deux très au-dessus | Confirmée par image, réfutée sur les deux coûts |
| Si un ratio de pixels adaptatif se stabilise sur un GPU faible | Le temps GPU est proportionnel au nombre de pixels, et le régulateur se stabilise en moins de 60 images | Il s’est stabilisé à 0,655 en moins de 60 images | Confirmée |
2026-09-16 — Versions
Section intitulée « 2026-09-16 — Versions »npm view threedonne 0.186.0, publiée le 8 septembre 2026, et@types/three0.186.0. Le cours les épingle exactement danscode/threejs/package.json, avec Vite 8.3.0, TypeScript 7.0.2, Playwright 1.63.0 et son Chromium 153, glTF Transform 4.5.0,draco3dgltf1.5.7,meshoptimizer1.2.0 etpngjs7.0.0, sur Node.js 24.21.0 en CI.- La release r186 correspond à la version npm
0.186.0. Les leçons citent le code source au tagr186de mrdoob/three.js. require('three/package.json')échoue : la tableexportsdu paquet ne le liste pas. La vérification de version danscheck.shlit donc le fichier avecfs.- Les pages de documentation sont à
https://threejs.org/docs/pages/<Class>.htmlethttps://threejs.org/manual/pages/<page>.html; les leçons pointent vers ces URL, toutes vérifiées.
2026-09-16 — Mesurer des pages dans un navigateur headless
Section intitulée « 2026-09-16 — Mesurer des pages dans un navigateur headless »- Chaque page de leçon rapporte ce qu’a fait le moteur de rendu à travers une promesse
window.probe, etscripts/probe.mjsdémarre Vite, ouvre la page avec Playwright en 800 par 450 pixels avec un pixel ratio de 1, et affiche le rapport. Les pages animées avancent par pas fixes quand elles sont sondées, si bien que la troisième image est la même sur toutes les machines. - Ma première sonde restait bloquée sur la page
WebGLRenderer: la page remplaçaitwindow.probeaprès quepage.evaluateavait commencé à attendre l’ancienne promesse. Les pages résolvent désormais une promesse créée une seule fois, danssrc/probe.ts, etprobe.mjsabandonne au bout de 30 secondes. - Les couleurs sont lues dans la capture d’écran, aux positions que la page indique.
- Le mode headless par défaut de Playwright, sous Windows, donne WebGPU sur la RTX 5080 de l’auteur (« nvidia, blackwell »).
chromium-headless-shell, la version allégée, n’a pas d’adaptateur WebGPU :WebGPURendererse replie sur WebGL 2, avec SwiftShader.check.shexécute la leçon 1 dans les deux.
2026-09-16 — Ce avec quoi les runners de CI dessinent
Section intitulée « 2026-09-16 — Ce avec quoi les runners de CI dessinent »- La première exécution de la CI, 35114590607, a échoué sur les trois OS, pour trois raisons : le
coordinateSystemrapporté par la page dépend du backend ; quelques couleurs diffèrent d’une unité par rapport au GPU de l’auteur ; et sous macOS, l’avertissement de Vite, sur stderr, arrivait avant ses lignes sur stdout. Les sondes filtrent maintenant les lignes qui dépendent du backend,compare.mjsaccepte un écart de 2 par canal dans les couleurs, et le stderr du build est affiché après son stdout. - L’exécution 35115118564 est passée. Ses logs montrent avec quoi dessine chaque runner.
ubuntu-24.04etwindows-2025-vs2026n’ont pas de GPU : WebGPU n’est pas disponible, etWebGPURenderercommeforceWebGLtournent en WebGL 2 sur SwiftShader (Subzero).macos-26-arm64a WebGPU, sur un adaptateur « apple », et WebGL 2 sur « ANGLE Metal Renderer: Apple Paravirtual device » ; son headless shell utilise SwiftShader (LLVM 10.0.0). - Deux couleurs diffèrent d’une unité sur les runners : ACES transforme le linéaire 0.18 en 127 sur le GPU de l’auteur et en 128 en CI, et Neutral transforme 0.05 en 33 et en 34.
- Une CI au vert prouve donc que les scènes se construisent, se chargent, et produisent les mêmes compteurs et les mêmes couleurs, à 2 près, sur un rastériseur logiciel et sur le GPU d’Apple. Elle ne prouve rien sur WebGPU sous Windows ou Linux.
2026-09-16 — Surprises
Section intitulée « 2026-09-16 — Surprises »WebGPURenderercompte un draw call de plus que la scène n’en a. Avec du tone mapping, ou avec un espace colorimétrique de sortie différent de l’espace de travail, il rend la scène dans une cible et dessine le résultat dans une passe de sortie : 2 draw calls et 13 triangles pour un cube.WebGLRendererconvertit dans chaque shader et rapporte 1. Les objetsinfodes deux moteurs de rendu n’ont pas non plus la même forme.@types/threedécrit maltoJSON(). Dans 0.186.0,Object3DJSONdéclarechildrencomme des chaînes et n’a nigeometriesnimaterials, alors que l’exécution écrit des objets et les deux tableaux. La leçon 1 conserve les erreurs du compilateur danserrors/l01_tojson_types.ts. Je n’ai pas cherché d’issue existante dans three-types/three-ts-types, et je n’en ai pas ouvert.- La boîte de la caméra d’ombre n’est lue qu’une fois par
WebGPURenderer.ShadowNodeappelleupdateProjectionMatrix()quand il construit l’ombre ; un changement ultérieur demande votre propre appel. PCFSoftShadowMapa disparu deWebGPURendererdans r186 et produit un avertissement.LunarLanderEngine.tsetMinimalThreeInstrument.tsxde GA l’utilisent encore avecWebGPURenderer.- Une lumière ponctuelle de 800 lumens fait 63.66 candelas, et un spot de 800 lumens 254.65 :
SpotLight.powerutilise π, pas 4π, et ne change pas avec l’angle du cône. - La PMREM coûte 17 rendus pour un environnement de 256 par 128 : 1 dans le premier niveau, puis 2 pour chacun des 8 autres. Elle a lieu même quand
scene.environmentest une simple texture équirectangulaire. - Depuis r185,
DRACOLoadertrouve ses décodeurs avecimport.meta.url, ce que r184 ne faisait pas. Vite copie alors les cinq fichiers de décodeur dans le build, 1.31 MB, alors qu’un navigateur en charge deux. Le build avertit aussi quethree.webgpudépasse 500 kB. - Draco et meshopt éliminent des sommets. Tous deux éliminent les deux sommets de pôle que
SphereGeometrycrée et qu’aucun triangle n’utilise ; leweld()de Draco fusionne en plus les coins du corps non indexé. - Le
meshopt()de glTF Transform affiche des lignesprune: Removed types... Accessorsur stdout ; elles figurent telles quelles dansexpected/.
2026-09-16 — GuitarAlchemist/ga
Section intitulée « 2026-09-16 — GuitarAlchemist/ga »Le cours lit GA au commit 05c8eda, qui utilise three 0.180. 139 fichiers sous des répertoires src importent three ; 33 d’entre eux appellent requestAnimationFrame, 3 setAnimationLoop ; 29 créent un WebGLRenderer et 8 un WebGPURenderer ; 15 utilisent Clock, déprécié dans r183. Rien de ce qui suit n’a été signalé à GA.
ThreeFretboard.tsxs’attend à ce queWebGPURendererlève une exception quand WebGPU manque, et affiche « Using WebGPU renderer » sur toutes les machines, puisque le moteur de rendu se replie plutôt sur WebGL 2 (leçon 1). Ses ombres ne sont activées que pour unWebGLRenderer, donc le chemin de repli n’en a aucune (leçon 2).ThreeFretboard.tsxdemandesamples: 8; la spécification WebGPU autorise 1 ou 4 échantillons pour une texture. Ce que fait r180 avec 8 sur chaque backend est à vérifier.- Sa lumière ponctuelle de 0.6 cd avec
distance: 20donne environ 5 % de la lumière principale là où elle est le plus proche du manche (leçon 2), et une seule texture de canvas sert de carte de couleur, de rugosité, de normales et de relief. - Sa texture de bois n’a pas de
colorSpace, elle s’affiche donc plus claire que peinte, et son environnement est un dégradé sur 8 bits dont le moteur de rendu construit quand même la PMREM (leçon 3). L’écart de couleur du bois à l’écran est à vérifier dans un navigateur. Guitar3D.tsxconstruit un environnement noir avecfromScenesur une scène qui ne contient qu’une lumière ambiante, et annonce la prise en charge de KTX2 et de meshopt sans régler aucun des deux décodeurs (leçons 3 et 4).Ocean.tsxcharge les décodeurs de Draco depuis gstatic.com ;DemerzelFaceOverlay.tsxmasque les erreurs KTX2 en remplaçantconsole.errorpendant un chargement ;HandAnimationTest.tsxmet à jour unAnimationMixerqui n’a aucune action (leçon 4).Experiments/ThreeJS-BSP-LoaderimporteWebGPURendererdepuis un chemin supprimé dans r167 ; le visualiseur d’embeddings du tableau de bord retire un écouteur de redimensionnement avec un nouveaubind, ce qui ne retire rien.ThreeHeadstock.tsxajoute un écouteur de clic au même canvas à chaque exécution de son effet et ne le retire jamais ;TonalOrbit.tsxetMinimalThreeInstrument.tsxlancent un rayon à chaque événement de souris, le second avec un nouveauRaycasterà chaque fois ;InteractionHandler.ts, dans Prime Radiant, le fait comme la leçon ; 4 des 25 fichiers qui utilisentOrbitControlsne les libèrent jamais (leçon 5).- Le ciel GLSL de
Sunburst3D.tsx, copié dansImmersiveMusicalWorld.tsx, ne peut pas tourner surWebGPURenderer;FresnelGlowTSL.tsfige l’intensité et la couleur de sa couronne dans le shader sous forme de constantes ;MoebiusPassTSL.tsest unShaderPassGLSL malgré son nom (leçon 6). Ocean.tsxetLunarLanderEngine.tsutilisentPostProcessing, renomméRenderPipelinedans r183, etOcean.tsxne libère jamais le pipeline ni ses nœuds ;ForceRadiant.tsxtourne surWebGPURenderersans les effets de bloom et deShaderPassde son chemin WebGL, une « tâche de suivi » ; les 10 fichiers qui utilisentEffectComposers’en servent correctement pour WebGL (leçon 7).ThreeFretboard.tsxcrée une géométrie et un matériau par frette et par repère, reconstruit sa scène chaque fois que sa proppositionsest un nouveau tableau, y compris le[]par défaut, et ne libère pas ses 29 sprites ;NodeInstancer.tsinstancie bien ses nœuds, avec le culling désactivé ; GA n’utilise niBatchedMeshniTHREE.LOD(leçon 8).
2026-09-16 — Leçons 5 à 8 : pointeurs, shaders, effets, comptes
Section intitulée « 2026-09-16 — Leçons 5 à 8 : pointeurs, shaders, effets, comptes »- De vrais événements de pointeur dans une page headless.
scripts/probe.mjseffectue maintenant les déplacements et les glisser-déposer qu’une page liste, avec la souris de Playwright, et demande à la page ce qu’elle a vu après chacun. Chromium les transforme en événementspointermove,pointerdownetpointerup, queOrbitControlstraite comme ceux d’un utilisateur : un glisser de 120 pixels a tourné la caméra de 96°. - Une caméra hors de la scène a une matrice périmée dans un script. La première version de
l05-raycaster.tsprojetait un point à un NDC de −3.6 : le rendu met à jour la matrice monde de la caméra, et un script Node.js ne fait pas de rendu.camera.updateMatrixWorld()l’a corrigé. - Une feuille de style partagée fait partie du hash de chaque chunk. Ajouter les règles de mise en page de la leçon 5 à
page.cssa changé le nom de tous les chunks du build, y compris le04-gltf-CCDSPzCq.jsque cite la leçon 4. La leçon 5 a reçu sa propre feuille de style, etcheck.shconstruit d’abord les pages des leçons 1 à 4 seules, avec un filtrePAGES, avant de tout construire : avec les pages des leçons 5 à 8, Rolldown nomme le chunk WebGPU partagéthree.tslau lieu dethree.webgpu. PostProcessings’appelleRenderPipelinedepuis r183, etpipeline.dispose()libère un seul matériau. Les 11 render targets du bloom et celui de la passe de scène restent jusqu’à ce que les nœuds eux-mêmes soient libérés.DirectRenderPipeline.renderprend la scène et la caméra, contrairement àRenderPipeline.render(), donc TypeScript le refuse dans une variableRenderPipeline; et il dessine un fond uni sous la forme d’une sphère de 1 984 triangles.renderer.info.renders’additionne entre les appels àrender()d’une même image d’animation. Une page qui rendait 60 images dans une boucle pour les chronométrer rapportait 610 061 draw calls ; la leçon 8 lit d’abord les comptes.
2026-09-16 — Les runners de CI et les leçons 5 à 8
Section intitulée « 2026-09-16 — Les runners de CI et les leçons 5 à 8 »- L’exécution 35123228046 a réussi sous macOS (WebGPU sur l’adaptateur « apple ») et échoué sous Linux et Windows, où
WebGPURendererse replie sur WebGL 2 avec SwiftShader. Quatre différences, toutes réelles :- Les chaînes de bloom comptent un programme de plus sur WebGL 2 : 14 au lieu de 13, 16 au lieu de 15.
- Avec FXAA, le pixel du halo vaut 135, 176, 197 sur SwiftShader, là où le GPU de l’auteur donne 132, 174, 195 sur les deux backends : 3 de plus que ce qu’accepte
compare.mjs. - Un
BatchedMeshest dessiné avecWEBGL_multi_drawet compté comme 2 draw calls, pas 10 001. - Le fichier de shader WebGPU contient du GLSL, puisque la page tournait sur WebGL 2.
- Les pages de la leçon 8 à 800 000 triangles dépassaient le délai : le résultat de la sonde arrivait, mais la capture d’écran de Playwright attendait plus de 30 secondes derrière les 60 images chronométrées encore en file dans le rasteriseur logiciel.
- La correction :
check.shcompare une sonde avecexpected/<name>.webgl.txtquand ce fichier existe et que la page a tourné sur WebGL 2. Les cinq fichiers viennent dechromium-headless-shellsur la machine de l’auteur, dont le SwiftShader a donné les mêmes valeurs que les runners. La ligne GLSL est comparée, et la ligne WGSL seulement affichée. La leçon 8 chronomètre 20 images, et ses sondes attendent jusqu’à 240 secondes (PROBE_TIMEOUT). L’exécution 35124756498 a réussi sur les trois OS.
2026-09-16 — Leçons 9 à 13 : versions
Section intitulée « 2026-09-16 — Leçons 9 à 13 : versions »- React 19.3.0 est sorti le 9 septembre 2026, mais
@react-three/fiber9.7.0 déclarereact>=19 <19.3: le cours épingle React 19.2.8, avec drei 10.7.8. R3F 10 et drei 11 sont en alpha.@react-three/test-renderer9.1.1 et Vitest 5.0.1 testent les composants. - Rapier 0.20.0 a été publié le 8 août 2026. L’addon
RapierPhysicsde three.js r186 charge encore la 0.17.3 depuis skypack.dev, et@react-three/rapier2.2.0 épingle la 0.19.2. Le cours utilise à la fois@dimforge/rapier3d-compatet@dimforge/rapier3d-deterministic-compat; leurs classes ont des membres privés, donc TypeScript refuse le type d’un module pour l’autre sans cast. - IWER 2.4.0 émule un Quest 3 ; pixelmatch 7.2.0 compare les captures d’écran.
2026-09-16 — Leçons 9 à 13 : ce qu’ont trouvé les pages
Section intitulée « 2026-09-16 — Leçons 9 à 13 : ce qu’ont trouvé les pages »- R3F 9.7.0 émet un avertissement sur
WebGPURenderersans qu’on lui demande rien. Leshadowsde<Canvas>vautfalsepar défaut, et R3F règlePCFSoftShadowMappour tout booléen ;WebGPURendererle réinitialise avec un avertissement, une fois par rendu de<Canvas>. Son store crée aussi unTHREE.Clockdéprécié. - Le
<Text>de drei dessine un quad vide surWebGPURenderer: troika-three-text injecte son shader viaonBeforeCompile, que le moteur de rendu à nœuds n’appelle jamais. - Les comptes de rendus dépendent de la machine. Dans
chromium-headless-shell, 10 mises à jour d’état espacées d’une image ont donné 6 rendus au lieu de 11, et un déplacement de souris en 5 étapes moins de rendus que sur le GPU : React regroupe les mises à jour et le navigateur fusionne les événements de pointeur quand les images sont lentes. Les pages utilisentflushSync, et ne rapportent aucun compte après les actions de pointeur. - Chromium sous Windows avertit que
powerPreferenceest ignoré chaque fois que R3F le passe ; les autres OS, non.check.shfiltre la ligne. - Les builds déterministe et standard de Rapier ont donné le même hash d’état sous Windows x64, dans Node.js et dans le navigateur. Avec un pas variable entre 1/144 et 1/30 s, 16 médiators sur 24 ont atterri ailleurs et un est passé à travers un sol de 20 cm. Une balle rapide a traversé un mur dynamique mince sans CCD, mais pas un mur fixe.
- IWER a besoin de
forceInstalldans Chromium, qui a déjà unnavigator.xr, et destereoEnabled, sinon le viewport de l’œil droit fait 0 pixel de large. - La première sonde WebXR est restée bloquée dix minutes en gardant le verrou GPU partagé : le backend WebGL 2 levait une exception à chaque image XR, la page attendait des images indéfiniment, et l’exécution a été arrêtée à la main. La cause est le
XRWebGLLayer.framebufferd’IWER,null, utilisé comme clé deWeakMapdansWebGLState.drawBuffers. La page intercepte maintenant les erreurs de rendu et borne ses attentes, etprobe.mjss’arrête de lui-même après deux fois son délai plus 30 secondes. - Dans r186,
WebGPURenderern’entre dans une session WebXR sur WebGPU qu’avecXRGPUBindinget la fonctionnalitéwebgpu; son message d’erreur mentionne unVRButtonGPUqui n’existe pas.WebGLXRFallbackchange de moteur de rendu, mais les contrôleurs que la page a pris au premier moteur de rendu cessent de recevoir des événements. Seul leWebGLRendererclassique a rendu les deux yeux avec IWER. - Un
InstancedMeshignoresetColorAtsi son premier rendu n’avait pas de couleurs d’instance : les marqueurs du portage s’affichaient en blanc jusqu’à ce que l’attribut soit alloué dès le départ. @react-three/test-renderera besoin deIS_REACT_ACT_ENVIRONMENT, sinon React avertit à chaque mise à jour et R3F libère plus tard ; son build CommonJS chargethree.cjsà côté du build en module ES, sans séparer les classes qu’utilise la scène.- Pixels : la même page deux fois a donné des octets identiques ; WebGPU face à ANGLE sur le même GPU différait de 294 pixels, face à SwiftShader de 10 330, aucun au-dessus du seuil par défaut de pixelmatch une fois l’anticrénelage écarté.
- Une image importée par une leçon doit exister avant la page : la leçon 11 a un jour importé une capture d’écran pas encore convertie, et le build Astro de l’arbre de travail partagé a échoué pour les autres sessions qui y travaillaient jusqu’à ce que l’image soit ajoutée.
2026-09-16 — Les leçons 9 à 13 sur les runners de CI
Section intitulée « 2026-09-16 — Les leçons 9 à 13 sur les runners de CI »- L’exécution 35175914560 a réussi sur les trois OS du premier coup. Le build standard de Rapier a donné le hash d’état
68235a2bdba192f1sous Linux x64 et macOS arm64 aussi, dans Node.js et dans le navigateur, le même que le build déterministe : pour cette scène, et pas comme une garantie. - macOS a exécuté les pages sur WebGPU avec l’adaptateur « apple » ; Linux et Windows sur le WebGL 2 de SwiftShader, où le
ThreeFretboardde GA a produit ses 253 lignes d’erreur WebGL et où R3F a averti 13 fois à propos dePCFSoftShadowMapau lieu de 14. - Avant le push, la vérification locale a comparé la page de repli de la leçon 11 à sa sortie WebGL 2 sur le GPU de l’auteur : la page finit sur WebGL 2 après avoir démarré sur WebGPU.
check.shn’utilise maintenant la sortie WebGL 2 que quand la page a tourné sur WebGL 2 dès le départ.
2026-09-16 — GuitarAlchemist/ga, leçons 9 à 13
Section intitulée « 2026-09-16 — GuitarAlchemist/ga, leçons 9 à 13 »Rien de ce qui suit n’a été signalé à GA.
- Les composants React de GA utilisent R3F 8, drei 9 et React 18, avec
WebGLRendererdans les 6 fichiers qui ont un<Canvas>;ThreeFretboard.tsxn’utilise pas R3F. L’explorateur BSP met à jour un état React avec une copie de la position de la caméra à chaque image, etGuitarAlchemistLogo3D.tsxpasse un nouvel objet dansargs, ce qui reconstruit sa géométrie à chaque rendu (leçon 9). - Aucune bibliothèque de physique : Cheese Avalanche est écrit à la main avec un pas variable plafonné à 1/30 s ; le lunar lander utilise un pas fixe de 1/120 s avec un accumulateur (leçon 10).
- Aucun code WebXR (leçon 11).
- 22 fichiers de specs Playwright, dont les 7 hors de la suite du tableau de bord, 6 d’entre eux sur des pages 3D, ne sont exécutés par aucun workflow de CI ; des attentes de 3 secondes pour WebGPU ; un
toBeTruthy()sur le buffer d’une capture d’écran ; une vérification de canvas noir qui appellegetContext('2d')sur un canvas WebGL et ne peut jamais réussir ; 13 fichiers de résultats de tests commités, issus d’une exécution en échec (leçon 12). ThreeFretboard.tsx, reproduit et mesuré : 69 draw calls et 32 textures pour un manche vide ; 10 rendus de son parent avec les mêmes notes reconstruisent la scène 10 fois et font fuir 29 textures de sprites à chaque fois ; un pixel ratio qui va jusqu’à 6 ;samples: 8, qui ne dessine rien sur le WebGL 2 de SwiftShader ; des cordes deux fois trop épaisses, puisqu’un tirant sert de rayon ; et une proponPositionClickjamais appelée. Le portage dessine le même manche en 6 draw calls avec 4 textures, et sélectionne des notes (leçon 13).
2026-09-17 — Démos en direct
Section intitulée « 2026-09-17 — Démos en direct »- Les pages sont construites par une seconde configuration Vite,
vite.demos.config.ts, avec la base/learn/threejs-demos/, danspublic/threejs-demos, que le site copie tel quel. Les sources des pages des leçons sont inchangées, puisque les leçons 4, 8 et 13 citent leur build ; un plugin de cette configuration ajoute le panneau Try it aux seules pages construites. Une page ne se pilote pas de l’extérieur : chaque contrôle règle donc un paramètre d’URL et recharge la page. Le panneau est lil-gui 0.17, la copie que three.js livre dansthree/addons/libs: aucune nouvelle dépendance. - Le build pèse 9.4 Mo en 75 fichiers. Les builds standard et déterministe de Rapier, chacun avec son WebAssembly intégré en base64, font 2.9 Mo chacun ;
three.tsl0.69 Mo.check.shle reconstruit et le compare à la copie commitée (diff -r, hors modèles et HDR régénérés). - Les cadres sont dans un
<details>fermé, avecloading="lazy": rien ne se charge tant qu’un lecteur n’en déplie pas un. - La guitare est modélisée en code : aucune guitare glTF sous licence connue n’était disponible. 47 405 triangles en 32 draw calls ; de l’instanciation pour les frettes, les repères, les plots, les pontets, les boutons et les mécaniques.
- Le
body.handlede Rapier est un flottant qui regroupe un index et une génération :handle % 8n’est pas un index de palette ; chaque médiator garde maintenant la couleur de son numéro de lancer. Et les couleurs réglées avecsetColorAtn’atteignaient le GPU que dans une image desetAnimationLoop: une sonde qui appelaitrender()elle-même voyait des médiators blancs. En r186,InstanceNodeles envoie dans sa mise à jour par image. - La page VR utilise le
WebGLRendererclassique, comme la leçon 11 l’a trouvé nécessaire avec IWER. Pendant une session, son écouteurresizene doit rien faire :WebGLRendereravertit « Can’t change size while VR device is presenting ». - Sur une page à taille de téléphone (émulation Pixel 7 de Playwright), les cadres mesuraient d’abord 150 pixels de haut : les styles du site écrasent l’attribut
heightde l’iframe, donc la hauteur est maintenant un style en ligne,min(420px, 70vh). Le panneau démarre replié dans un cadre étroit. Avec un CPU ralenti 4 fois mais le GPU de bureau de l’auteur, les cinq scènes tenaient 60 images par seconde en WebGPU et avec?webgl; sur le GPU d’un vrai téléphone de milieu de gamme, c’est à vérifier. - La première sonde d’une page qui importe une nouvelle dépendance a échoué avec « Execution context was destroyed » : Vite a optimisé la dépendance et rechargé la page. Le second essai est passé.
2026-09-17 — Leçon 14, le labo 3D Guitar Alchemist
Section intitulée « 2026-09-17 — Leçon 14, le labo 3D Guitar Alchemist »- Quinze expériences dans
code/threejs/ga-lab, chacune avec une hypothèse et des prédictions chiffrées committées le 16 septembre avant son exécution, mesurées le 17 septembre sur une RTX 5080 dans Chromium 153 avec les requêtes d’horodatage WebGPU. Sur 61 prédictions, 19 étaient fausses en tout ou partie et 2 n’ont pas pu être mesurées ; elles restent dans la leçon. - Les images sont rendues en boucle serrée sans
requestAnimationFrame, vsync désactivé, et la page fait avancer elle-mêmerenderer._nodes.nodeFrame: sans cela, les shadow maps et le bloom ne se mettent pas à jour.onSubmittedWorkDone()n’a jamais résolu sous 3.5 ms environ : les temps GPU viennent des requêtes d’horodatage. - Constats r186 :
InstancedMeshavec morph targets échoue avec une instance (pas demorphTargetInfluences) et avec plusieurs quand on les définit ;BatchedMeshfait un appel par objet sur WebGPU ; le compute TSL tourne sur WebGL 2 ;renderer.info.memory.texturesSizecompte 0 pour les textures compressées. - L’index OPTIC-K de GA contient 313 047 vecteurs de 124 dimensions (OPTK v4), pas 112. Le labo le lit en lecture seule ; la CI utilise un substitut synthétique.
- Le CCD de Rapier n’arrête pas un médiator cinématique : il traverse la corde à 30 et 60 pas par seconde dans tous les cas.
- Le build R3F embarquait
threeà côté dethree/webgpu: 565 ko gzip contre 210 pour la même scène sans React. - Les textures de bois ComfyUI sont en attente : l’expérience 4 du labo ComfyUI GA n’avait pas encore de sortie.
- La CI (
threejs-ga-lab.yml) compare les compteursrenderer.infosur WebGL 2 sur trois OS, jamais les temps. Une capture Playwright a bloqué 300 s une fois sur macOS : le lanceur garde désormais le résultat quand une capture échoue.
2026-09-24 — Correctifs amont
Section intitulée « 2026-09-24 — Correctifs amont »- La ligne
PCFSoftShadowMapne s’applique pas encore à GA. Le retrait et son avertissement sont réels en r186 (Renderer.jsetShadowNode.jsdu paquetthree@0.186.0), mais GA épingle three 0.180.0 et @react-three/fiber 8.18.0. En 0.180,ShadowNodeassocie encorePCFSoftShadowMapà son filtre doux : pas d’avertissement, et les ombres douces fonctionnent. - Les deux associations de GA avec
WebGPURenderer, dansLunarLanderEngine.tsetMinimalThreeInstrument.tsx, commenceront à avertir quand GA passera à r186 ou plus. Les changer maintenant ferait perdre les ombres douces : #733 les laisse pour cette mise à jour et le dit. Lu dans les sources du paquet, pas exécuté (à vérifier).
À vérifier
Section intitulée « À vérifier »- La mémoire GPU des KTX2 avec un outil GPU.
- Le premier rendu de R3F sur un build de production.
- Un alias de
threeversthree/webgpu. - Le manche à taille réelle et le décalage de tête de 25 mm d’IWER sur un casque.
- La latence audio au microphone.
powerPreferencesur un portable à deux GPU.- La démo VR avec un vrai casque : l’entrée dans la session, les rayons des manettes, la note et l’impulsion haptique.
- Si les médiators lancés sans CCD traversent les frettes de la démo physique.
- Les cinq scènes complètes sur un téléphone : fréquence d’images, et si la shadow map de 2048 × 2048 et le bloom du studio tiennent.
- Si une scène modélisée en centimètres a besoin de lumières 10 000 fois plus fortes pour avoir le même aspect qu’en mètres (leçon 2).
- Les textures KTX2 : chargement, formats GPU choisis sur chaque OS, mémoire.
- Si
ThreeHeadstock.tsxappelleonTuningPegClickune fois par écouteur périmé après les nouvelles exécutions de son effet (leçon 5). - Si
ThreeFretboard.tsxest reconstruit à chaque rendu de ses parents dans les pages de GA (leçon 8). - Le temps GPU, pas seulement le temps CPU, avec le
timestamp-queryde WebGPU (leçon 8). - Draco face à meshopt sur un gros modèle et sur de nombreux petits, sur un vrai réseau (leçon 4, exercice 3).
- WebGPU sur une machine Linux ou Windows dotée d’un GPU, autre que celle de l’auteur.
XRGPUBinding: quels navigateurs et quels casques le proposent, etWebGPURendererdans une vraie session WebXR (leçon 11).- Le backend WebGL 2 et
WebGLXRFallbacksur un vrai casque, dont le framebuffer de couche n’est pasnull(leçon 11). - La fréquence d’images, le rendu fovéal et le confort de la page WebXR sur un Quest (leçon 11).
- S’il existe un troika-three-text compatible avec WebGPU pour le
<Text>de drei (leçon 9). - La fréquence à laquelle les parents de GA rendent
ThreeFretboardet l’explorateur BSP en pratique, avec le profileur de React (leçons 9 et 13). samples: 8sur le WebGL 2 de vrais GPU, selon leurMAX_SAMPLES(leçon 13).- Pourquoi Rapier a arrêté une balle rapide sur un mur fixe mince sans CCD (leçon 10).