
Lab · Add-on Blender · En cours
Jihanki Generator
Des distributeurs automatiques japonais générés de toutes pièces, de l’enseigne lumineuse aux étiquettes des canettes. Un seul mesh par machine, et une feuille de route de cinquante étapes vers une image qu’on puisse confondre avec une photo.
- Logiciel
- Blender 4.2+
- Version
- 0.4.0
- Régénération
- < 100 ms
- Feuille de route
- 3 / 50
- Lecture
- 11 min
Un jihanki se reconnaît de loin : un caisson peint, une enseigne lumineuse, une vitrine rétroéclairée où les canettes s’alignent au-dessus de leurs prix, bleus pour le froid, rouges pour le chaud, et les boutons qui s’allument 売切 quand c’est épuisé.
Jihanki Generator est une extension Blender qui en fabrique autant qu’on veut, chacun différent. Le but est écrit dans la feuille de route : des machines qu’on puisse confondre avec une photo. Le projet est en cours. Cette page suit le devlog jusqu’à la v0.4.0, et le dernier chapitre dit ce qui vient.
Log 01 / 06
Un seul mesh 自販機 · Jihanki
Caisson, enseigne, vitrine, canettes, trappe, monnayeur : tout sort du même script, dans le même objet.
Chaque distributeur est un seul mesh construit avec bmesh : des boîtes pour le caisson et les détails, des profils en révolution (lathe) pour les canettes et les bouteilles PET. Il a tout ce qu’on attend d’une machine japonaise : l’enseigne lumineuse, la vitrine rétroéclairée et ses rails, la trappe de sortie, le panneau de paiement avec son écran, les fentes à pièces et à billets et le lecteur de carte IC.

Les couleurs ne sont pas dans les matériaux. Chaque face porte la sienne dans un attribut de coin, jhk_color, au-dessus de 1 pour les parties lumineuses. Cinq matériaux partagés le lisent : Paint, Plastic, Metal, Glass et Emit. On peut donc générer autant de machines qu’on veut sans multiplier les matériaux.






La topologie, sans chanfreins ni vitres : un seul mesh.
Fig. 02La même machine et la même caméra, passe après passe.
- 1
- Mesh par machine, enseigne et canettes comprises.
- 5
- Matériaux partagés par toutes les machines de la scène.
- < 100 ms
- Pour régénérer une machine après un réglage, chanfreins compris.
Les réglages vivent sur l’objet : taille, rangées, colonnes, rangées de bouteilles, couleur du caisson et de l’enseigne, part de boissons chaudes et d’épuisés. Chaque changement régénère la machine. Le seed tire de nouveaux produits, et « Randomize Style » un nouveau style complet. Deux boutons préparent le rendu : Cycles sur le meilleur GPU disponible, et une scène de nuit avec sol mouillé, lune, réverbère et caméra.


Log 02 / 06
Cinquante étapes 計画 · Keikaku
Le but : une machine qu’on puisse confondre avec une photo. Le chemin : cinquante étapes, dans l’ordre.
La feuille de route part d’un diagnostic du gros plan sur la vitrine. Quatre choses y trahissent la 3D : les arêtes sont vives, les étiquettes sont des aplats de couleur, la lumière est uniforme, et tout est neuf et identique.
Elle liste cinquante features en neuf phases, chacune avec son issue GitHub au même numéro. Les phases suivent les dépendances : les UV débloquent les textures, les textures débloquent les étiquettes et les stickers, et ainsi de suite. Un parcours conseillé traite d’abord les quatre défauts du diagnostic, du meilleur rapport entre gain visuel et effort au moins bon.
- 01
Chanfreins · fait
Un petit chantier sans dépendance, à l’effet immédiat : chaque arête attrape un liseré de lumière.
- 02
Étiquettes · en cours
Les produits occupent la moitié de la façade. Il faut les UV, le générateur de textures et la police japonaise : les deux premiers sont faits.
- 03
Lumière · à venir
De nuit, la vitrine est la source principale. Des points chauds et une décroissance à la place de l’aplat émissif.
- 04
Usure · à venir
Une machine neuve et propre ne ressemble à aucune machine réelle. Un curseur d’âge, l’usure des arêtes et la crasse qui monte du sol.
| Phase | Pistes | Par exemple | État |
|---|---|---|---|
| Fondations | 1–6 | UV, chanfreins, textures, police japonaise | 3 sur 6 |
| Caisson | 7–12 | porte avec joint et charnières, persiennes, câble | — |
| Façade | 13–18 | boutons en relief, afficheurs 7 segments | — |
| Produits | 19–25 | canettes détaillées, maquettes 見本 | — |
| Matériaux | 26–31 | peau d’orange, inox brossé, verre physique | — |
| Usure | 32–40 | rouille, crasse, stickers arrachés, tube qui clignote | — |
| Typologies | 41–43 | Showa rétro, glaces, marques fictives | — |
| Contexte | 44–48 | poubelles de recyclage, météo, caméra « photo » | — |
| Pipeline | 49–50 | export temps réel, banc de test face à des photos | — |
Avant de toucher au réalisme, j’ai ajouté des images qui montrent comment la machine est construite, avec la méthode de Torii Generator : le même plan rendu deux fois, les vrais matériaux d’un côté, de l’autre l’argile sombre aux arêtes tracées par Freestyle, fondus sur une bande de 30 % de la largeur. Les vitres sont retirées de la passe filaire : Freestyle ignore la transparence et cacherait les produits.

Le breakdown façon VFX aligne six passes en bandes : topologie, argile, normales en espace caméra, attribut jhk_color, matériaux en studio, éclairage de nuit.
Un script rend les passes dans Blender, en headless ; un second les assemble avec le Python du système, et appelle ffmpeg pour les GIF du README. Les images de cette page viennent de ces scripts, ou de rendus faits de la même façon avec l’add-on.

Log 03 / 06
Des arêtes qui se voient 面取り · Mentori
Sur le gros plan, aucune arête n’attrapait la lumière : le caisson avait l’air découpé au laser.
Surprise en ouvrant le code : la machine portait déjà un modificateur Bevel de 3 mm, à deux segments. Il fallait comprendre pourquoi il ne se voyait pas.
La mesure tient en deux chiffres. Au coin du toit, le sommet le plus proche du coin théorique est à 0,87 mm avec « Clamp Overlap », et à 2,20 mm sans : le modificateur ne chanfreinait qu’environ 1,2 mm. Ce clamp ne travaille pas pièce par pièce. Blender calcule une seule limite pour tout le mesh, et sur un distributeur ce sont les pièces les plus fines qui décident : 3 mm pour une étiquette de prix, 2 mm pour le rebord sous le bouchon des bouteilles.
- 3 mm
- Le chanfrein demandé au modificateur.
- 1,2 mm
- Le chanfrein obtenu, réduit par la pièce la plus fine.
- 1
- Seule limite pour tout le mesh.
Le builder écrit maintenant le rayon de chaque arête vive dans son bevel weight, et le modificateur passe en mode poids, avec une largeur de 1 cm : un poids de 0,4 donne 4 mm. Le clamp est coupé, et c’est le builder qui garantit qu’aucun chanfrein ne déborde. Une pièce prend 4 mm, le rayon de la tôle, ou 30 % de sa plus petite dimension si elle est plus fine. La largeur du modificateur devient une échelle globale : la doubler double tous les rayons.
| Pièce | Rayon |
|---|---|
| Caisson, toit, porte, rails | 4 mm |
| Serrure | 3,9 mm |
| Boutons, plaques du paiement | 2,7 mm |
| Étagères | 1,8 mm |
| Étiquettes de prix | 0,9 mm |
| Bords des canettes et bouteilles | 0,6 mm × l’échelle |

v0.1.1v0.2.0Un vrai chanfrein crée un nouveau problème. La porte était faite de boîtes côte à côte : deux montants, un linteau, quatre boîtes autour de la trappe. Avec 4 mm de chaque côté, chaque jointure devient une rainure de 8 mm, là où la vraie porte est d’un seul tenant.
Une nouvelle primitive construit une plaque percée comme un seul mesh fermé. Elle découpe la plaque en grille à partir des coordonnées de son contour et de ses trous. Chaque case pleine reçoit une face avant et une face arrière, plus un mur sur chaque côté qui borde un trou ou l’extérieur. Les arêtes de la grille entre deux faces coplanaires ont un angle nul : ni chanfrein, ni ligne d’ombrage. C’est la grille qu’on voit dans la moitié filaire de l’ouverture.

v0.1.1v0.2.0En vérifiant les poids, le compteur affichait zéro arête chanfreinée. Une face créée par bm.faces.new() a une normale nulle tant qu’on ne la calcule pas, et l’angle des arêtes se lit sur cette normale : tous les angles valaient 0. Le même bug touchait depuis la v0.1 la fonction qui marque les arêtes vives. Les normales durcies du modificateur le masquaient au rendu ; la passe filaire, qui coupe le modificateur, le montrait.
f = self.bm.faces.new(verts)
f.normal_update() # une face neuve a une normale nulle, et l’angle des arêtes la lit
Fig. 09 · v0.1.1
Fig. 10 · v0.2.0La passe filaire, dans les mêmes conditions. En v0.1.1, aucune arête n’était marquée vive : les montants de la porte s’ombraient comme des tubes.
Log 04 / 06
Des UV pour les textures 展開 · Tenkai
Le mesh n’avait aucune UV. Pas de texture, pas de décal, pas de masque d’usure : presque toute la feuille de route en dépend.
Les UV naissent avec chaque face, comme sa couleur, sur deux couches. La première est métrique : une unité vaut un mètre, sur toutes les pièces. Une texture de rouille ou de crasse aura la même densité sur le caisson et sur un bouton. Les boîtes sont projetées selon leur normale, vues de l’extérieur pour qu’aucune texture ne soit en miroir.
La seconde va de 0 à 1, seulement sur les pièces qui porteront un visuel : l’étiquette des produits, les étiquettes de prix, le panneau publicitaire et l’enseigne. Un attribut de face dit lequel.

Les produits sont déroulés : u fait le tour, en mètres, v suit le profil plutôt que la hauteur, pour ne pas s’étirer sur l’épaule des bouteilles. La couture passe derrière, face au fond de la vitrine, et l’avant de l’étiquette tombe au milieu.
« Sans étirement » se vérifie arête par arête : la longueur de chaque arête dans les UV, divisée par sa longueur en 3D, doit valoir 1.
| Surfaces | Rapport UV / 3D |
|---|---|
| Boîtes, plaques, cylindres | 1,000 |
| Cônes, à moins de 45° de l’avant | jusqu’à 1,18 |
| Anneau plat sous le bouchon, vers l’avant | 1,27 |
| Cônes et anneau, vers la couture | jusqu’à 3,3 |
Les cônes se cisaillent : les deux anneaux d’une bande n’ont pas le même rayon, donc pas le même u pour un même angle. Le décalage grandit avec l’angle, d’où un u qui part de l’avant, là où l’on regarde les produits. Pour l’éviter tout à fait, il faudrait une couture à chaque anneau ; une buée ou une poussière cachera mieux un léger biais qu’une couture.
rapport = √(1 + (angle × Δr / L)²)
// L : longueur de la bande, Δr : écart entre ses deux rayons
// pire cas : l’anneau plat, Δr = LLog 05 / 06
Le générateur de textures 印刷 · Insatsu
Les étiquettes, les prix, l’affiche et l’enseigne étaient des aplats de couleur. Ils reçoivent des images, dessinées pour chaque seed.
Il fallait d’abord une méthode qui marche en headless, puisque toutes les images de la doc sont rendues ainsi. J’ai testé cinq pistes dans Blender 4.2 et 4.5 ; voici les quatre principales.
| Méthode | 4.2 sans interface | 4.5 sans interface | Verdict |
|---|---|---|---|
| blf dans un GPUOffScreen | non | non | pas de dessin GPU sans interface |
| blf dans un ImBuf | n’existe pas | oui | pixels inaccessibles : un fichier par texte |
| Rendu d’objets texte | oui | oui | un rendu par atlas, trop lent |
| Contours des glyphes, remplis en numpy | oui | oui | retenue |
La méthode retenue confie au moteur de polices de Blender ce qu’il sait faire, lire la police et donner la forme des lettres, et fait le reste en numpy. Un objet texte, qui n’a même pas besoin d’être dans la scène, est converti en courbes, échantillonnées à 8 points par segment. Le remplissage parcourt l’image ligne par ligne : chaque contour croisé ajoute +1 ou −1 selon son sens, et une somme cumulée donne, pour chaque pixel, le nombre de tours des contours autour de lui. 4 × 4 échantillons par pixel lissent les bords. Tout reste en mémoire, sans GPU ni fichier temporaire.

Le premier atlas écrivait « ΛOBΛNE » : des A sans barre, et de petites marques accrochées aux N et aux A. Inter est une police variable, dont les lettres sont faites de traits qui se chevauchent : la barre du A recouvre ses jambes. Remplie avec la règle pair-impair (en haut), chaque zone recouverte deux fois devenait un trou.
La règle non nulle (en bas) règle le problème, à condition de connaître le sens de chaque contour. C’est pour ça que les contours viennent des courbes de Blender, qui le gardent, plutôt que de son maillage.
Un piège attendait la police japonaise. Blender embarque Noto Sans CJK en woff2, 11 Mo compressés, et chaque nouveau caractère y coûte environ 100 ms, comme si toute la police était décompressée à chaque glyphe. Une police non compressée sort une chaîne entière en 2 à 4 ms. Cette étape écrit donc en Inter, la police latine de Blender ; le japonais viendra avec un sous-ensemble non compressé de Noto Sans JP.
- 100 ms
- Par nouveau caractère japonais, dans la police woff2 de Blender.
- 5 ms
- Par nouveau caractère latin, en Inter, puis en cache.
- 2–4 ms
- Pour une chaîne entière, dans une police non compressée.
Les matériaux sont partagés par toutes les machines : un atlas par machine les aurait obligées à avoir chacune les leurs. Le seed du catalogue, une propriété de la scène, décide donc d’un catalogue commun : 48 produits tirés de 12 boissons, avec leur marque, leur motif, leur contenance et leur prix. Les marques sont huit noms inventés. Chaque machine pioche dans ce catalogue avec son propre seed.
| Atlas | Cases | Case | Contenu |
|---|---|---|---|
| Étiquettes | 48 | 512 × 256 | centre face à la vitrine, motifs raccordés à la couture |
| Prix | 22 | 256 × 64 | de ¥100 à ¥200, en bleu (froid) et en rouge (chaud) |
| Affiches | 8 | 512 × 512 | une par marque |
| Enseignes | 8 | 1024 × 128 | une par marque |

Chaque face qui porte un visuel enregistre sa case. Dans les matériaux, un groupe de nœuds partagé en tire la colonne et la rangée, place les UV 0–1 dans la case avec un pixel et demi de marge, lit le bon atlas et multiplie la couleur de la face. Les étiquettes reçoivent un blanc pur, qui laisse passer l’image telle quelle ; les affiches et les enseignes sont dessinées en gris, et la couleur d’accent de la machine les teinte.

v0.3.0v0.4.0- 2 Mo
- Le .blend avec ses quatre atlas packés.
- 0
- Pixel d’écart, rouvert dans un Blender sans l’add-on.
- 2 s
- Pour changer de catalogue sur toute la scène.
Log 06 / 06
La suite 続き · Tsuzuki
Trois pistes sur cinquante. Les étiquettes sont à mi-chemin.
La prochaine étape est la police japonaise et le générateur de textes : des noms en katakana, des prix, les mentions qu’on lit sur toutes les canettes (500ml, 無糖, 新発売). Viendront ensuite les étiquettes elles-mêmes, avec marque, nom, illustration, code-barres et contenance. Après elles, la lumière, puis l’âge.
- 01
#5 Police japonaise
Noto Sans JP en sous-ensemble non compressé, et des textes tirés par seed.
- 02
#22 Étiquettes
Marque, nom, illustration (thé, grain de café, fruit), code-barres, contenance.
- 03
#30 Lumière
Diffuseur avec points chauds, LED visibles en gros plan, température de couleur, unités physiques.
- 04
#32 Âge
Un curseur, du neuf au Showa abandonné, qui pilotera l’usure des arêtes, la crasse et la rouille.
Au-delà, la suite dépend de l’usage : la façade pour les gros plans (boutons en relief, afficheurs 7 segments, trappe à volet), le contexte pour les plans larges (poubelles de recyclage, rangées de machines sous un auvent, pluie sur la vitrine), et un export temps réel pour Unity ou Unreal. La dernière piste est un banc de test : une scène de lookdev à côté de vraies photos, pour mesurer le réalisme au lieu de le deviner.
- Projet
- Personnel, en cours
- Compatibilité
- Blender 4.2+ · Cycles · EEVEE
- Version
- 0.4.0, 30 octobre 2026
- Licence
- Propriétaire : revente et modification interdites
- Python
- bmesh
- numpy
- Nœuds de shader
- Freestyle
- Rendu headless
