# Glas, folie, platen en spiegels: de notities

*Materials notes. Keywords: glass, breaking glass, shards, rescue foil /
space blanket (gold and silver), hammered copper and aluminium plates,
mirror, two facing mirrors, reflected sunlight spot, lantern at night,
environment map, CubeCamera, three.js r147, picture quality.*

Opgeschreven op 7 oktober 2026, na uw vraag: "stel dat ik de
kennismakingsmissie in een andere chat wil doen met u, kunt u daar dan de
spiegel en folie enzo vinden door de demo? Zo niet, maak het dan zo dat u
de informatie hierover daar vinden kunt."

Een nieuw gesprek weet niets van een vorig gesprek; het weet alleen wat in
de bestanden staat. Daarom staat hier op één plek: wat er is, waar de code
staat, hoe elk materiaal gemaakt is, waar u op moet letten, en wat u ervan
vond. Alles zit in één bestand, `materials-demo.html` in deze map. Het zijn
nog geen onderdelen van de Architect (open punt E36 in
`editors/prefab-architect/open-points.txt`).

## Voor Claude in een nieuw gesprek

three.js: r147, definitief sinds 10 oktober 2026 (een korte overstap naar
r128 op 9 oktober is teruggedraaid). Alles in deze notities geldt voor r147.

1. Dit bestand komt uit `prefab-contractor.zip`; om de materialen te
   begrijpen is meer niet nodig.
2. Lees eerst dit bestand, daarna de code in `materials-demo.html`. De
   koppen in de code (`// --- glas ---`, `// --- folie ---` enzovoort)
   volgen de hoofdstukken hieronder; de kop van het bestand bevat de
   wensen en het oordeel van de maker.
3. De demo draaien vraagt three.js **r147** in `prefab-contractor/lib/`
   (`three.min.js` en `OrbitControls.js`), naast `editors`, `game` en
   `demos`. Die map zit niet in de zip; de maker heeft hem zelf. Zonder die
   map: `npm pack three@0.147.0`, en uit het pakket
   `package/build/three.min.js` en
   `package/examples/js/controls/OrbitControls.js` naar `lib/`. Precies
   r147: de omwegen onder *Valkuilen* zijn voor die versie.
4. De demo laadt ook een paar bestanden van de Architect
   (`../editors/prefab-architect/js/`: core, util, i18n, style, scene, en
   de onderdelen lantern, tree_linden en bench).
5. Voor proeven en schermafdrukken heeft de demo een haakje,
   `window.__materiaalDemo`: `breek(x, y)`, `heel()`, `kijk(naam)` (alles,
   glas, folie, platen, spiegel), `opt` (de vinkjes), `zetLicht()`, `rek`,
   `platen`, `hdr`, `scherven()`, `extra()` en `vlek()`.

## Wat de maker vroeg en vond

- Waarom (concept-binnenbrink-6, nr 8, 4 oktober): "Onder andere daarvoor
  [de kas van de bloemist] wil ik weten of u glas/spiegels/EHBO-folie/
  ruimtevaartfolie kunt maken, maar die heb ik ook nodig voor de grote
  verhaallijn. Dus glimmen, weerspiegelen, lichtweerkaatsing."
- De vraag (concept-binnenbrink-7, 5 oktober): "Kunt u een aparte demo
  maken van: + Glas, en als mogelijk het breken ervan; + Goud-Zilver folie
  en hoe dat er van verschillende hoeken eruit ziet; + Spiegels, een
  spiegel is prima en komt niet vaak voor, en één en maximaal twee
  tegenover elkaar is meer dan genoeg (gewoon een spiegel is al genoeg,
  maar dubbel is beter in sommige situaties)". En bij vraag 7: "Ja graag,
  plus hoe het met licht werkt, al begreep ik dat licht een apart
  onderdeel zal worden naast die oppervlakken."
- Over de folie (6 oktober): "De eerste versies van het folie vond ik
  zeer goed, omdat die folie zo vreselijk reflecteerd. Uw laatste versie
  is een soort ingetimmerde koperplaat. ... Beide versies hebben we nodig!
  De laatste als ingetimmerde koper en aluminiumplaat." En: "Die EHBO/NASA
  Folie reflecteerd als een spiegel, van plastic, als een plastic zak, het
  weerspiegelt en ook weer niet, alleen het licht weerspiegelt, dat kan
  heel fel, net als een spiegel."
- Het oordeel (concept-binnenbrink-8, 7 oktober): "De folie heb ik
  bekeken. De 'lap aan het rek' is overtuigend genoeg voor nu. Ik moet
  even uitvinden hoe we dit verder perfectioneren, want inderdaad veel
  weerspiegeling." En: de demo's in een map `demos`, "Dan kunnen we die
  later gebruiken voor Making-of".

Dus: de folie is goed genoeg voor nu; de platen zijn de tweede versie en
blijven bewust bestaan; een spiegel komt weinig voor, hoogstens twee
tegenover elkaar.

## Het tafereel

Een binnenplaats van 17 × 7,5 m met tegels, een bakstenen muur op z = −4
met een witte band en twee donkere ramen, een rode bal, een geel blok en
een blauwe zuil (kleur om te weerspiegelen), en uit de Architect een linde,
een bank en een lantaarn. De 3D-opzet is die van de Architect
(`PREFAB.scene.make`): geen tone mapping, lineaire uitvoer en de gewone
lichteenheden van r147 (`physicallyCorrectLights` uit). Alle getallen
hieronder zijn op die instellingen afgestemd; wie dat verandert, moet de
materialen opnieuw afstemmen.

## De omgevingsfoto: nodig voor glas, folie en platen

Glas, folie en platen spiegelen niet de echte wereld, maar een foto van de
omgeving rondom: een kubusfoto (`WebGLCubeRenderTarget` van 256 pixels,
gemaakt met een `CubeCamera`, bereik 0,1 tot 120 m).

- Twee foto's: `omgevingCam` bij de folie (0; 1,4; 0,6) en `platenCam`
  vlak voor de muur bij de platen (7,4; 1,7; −3,0).
- HalfFloat als de computer het kan (`HDR`: WebGL2 met
  `EXT_color_buffer_float` of `EXT_color_buffer_half_float`), anders gewone
  bytes. Met HalfFloat blijft de zon op de foto veel feller dan wit, en dat
  geeft de felle flitsen in de folie. Zonder wordt de zon gewoon wit en zijn
  de flitsen zwakker.
- Wanneer: direct na het gewone beeld (dan zijn de schaduwen er al), en
  alleen als er iets verandert: de zon verzet, dag of nacht, de tweede
  spiegel aan of uit (`omgevingVernieuwen`). Niet elk beeld, dus goedkoop.
- Wat er niet op staat (`maakFoto()`): het glas en de scherven (zie
  *Valkuilen*, 1), bij de foto van de folie de folie zelf (die zou zichzelf
  zien) en de platen (die hangen ver weg). Bij de foto van de platen staat
  de folie er wel op. Een spiegel staat op de foto als een lichte vlek in de
  kleur van de horizon.

## De lucht

Een bol van 90 m om alles heen met een eigen kleurverloop (boven, horizon,
onder; overdag blauw, 's nachts bijna zwart), die ook op de foto komt. De
zon staat erin als schijf:
`kleur += (1; 0,95; 0,85) × zonKracht × (s^3000 × 40 + s^90 × 0,4)`, met s
de cosinus van de hoek tot de zon: een kleine schijf, veertig keer feller
dan wit, met een zachte krans. 's Nachts is zonKracht 0. Mist staat uit.

## Glas en breken

**De ruit**: 1,2 × 1,6 m, 12 mm dik, in een houten kozijn op twee voeten.
`MeshPhysicalMaterial`: transmission 1, thickness 0,04, ior 1,5, roughness
0,03, attenuationColor #cfeedd over 0,35 m (licht groen getint), clearcoat
1 met clearcoatRoughness 0,03, specularIntensity 1, envMap de
omgevingsfoto (× 1,2), dubbelzijdig.

**Breken**, `breek(x, y)`, met x en y in het vlak van de ruit (klikken op
de ruit, of de knop):

- 10 tot 13 stralen vanuit het breekpunt, en ringen op ongeveer 0,07-0,11,
  0,2-0,26, 0,4-0,5 en 0,72-0,84 m, plus een laatste ver buiten de ruit;
  elk hoekpunt een beetje onregelmatig. De hoekpunten worden gedeeld,
  zodat de barsten op elkaar aansluiten.
- Elk stuk wordt binnen de ruit geknipt (Sutherland-Hodgman, `knip`);
  stukjes kleiner dan 4 cm² vallen weg. Een scherf is 1 cm dik
  (`ExtrudeGeometry`), van hetzelfde glas.
- Vallen: hoe verder van het breekpunt, hoe later een scherf vertrekt
  (0,06 s per meter), alsof de barst loopt. Hij vliegt naar buiten vanaf
  het breekpunt, weg van wie gooide, een beetje omhoog, en tolt.
  Zwaartekracht 9,81. Op de grond stuitert hij (een kwart terug omhoog,
  opzij de helft), en onder 0,6 m/s blijft hij plat liggen, in een
  willekeurige draai.
- Een paar blijven in het kozijn: stukken uit de buitenste twee ringen,
  binnen 12 cm van de rand, elk met 45% kans.
- `heel()` ruimt de scherven op en zet een nieuwe ruit.
- Breken is toeval: elke keer anders.

## De folie: de lap aan het rek

Een stalen rek (twee palen, een stang op 2,2 m) met een lap van
1,7 × 1,3 m die aan de stang hangt. Goud aan de ene kant, zilver aan de
andere: twee meshes op dezelfde vorm, goud met `FrontSide`, zilver met
`BackSide`.

- Vorm: `PlaneGeometry` met 96 × 74 vakjes, dus vlakjes van bijna 2 cm.
- Kreukels (`kreukZ`): 110 korte, steile vouwen, smalle ruggen die aan
  weerszijden over 1,5 tot 6 cm aflopen, 16 tot 76 cm lang, 3 tot 10 mm
  hoog of diep, en naar hun uiteinden verlopend. Daaronder drie lagen
  zachte golving (`ruis`: 16, 6 en 2,5 mm). Elk hoekpunt schuift een beetje
  opzij (tot 0,3 vakje, niet aan de rand), anders ziet u een raster, en
  krijgt ±1,5 mm fijn gekreukel. Aan de stang is de lap strakker; vanaf
  12 cm eronder is hij helemaal gekreukeld.
- Vast toeval (`maakToeval(20261006)`): de folie heeft elke keer dezelfde
  kreukels.
- Materiaal: `MeshPhysicalMaterial`, metalness 1, roughness 0,2 (iets
  wazig, zodat u er geen beeld in ziet), clearcoat 0,7 met
  clearcoatRoughness 0,04 (de gladde laklaag, het plastic: die geeft de
  felle flitsen), envMap de omgevingsfoto (× 1,1), en **flatShading**: elk
  vlakje een eigen spiegeltje. Goud #ffc94f, zilver #f3f5f7.
- Bewegen: het rek draait (0,35 rad/s, vinkje Draaien), en de wind
  (`wapper`) laat de lap golven, onderaan het meest (twee golven, 6 en
  2,5 cm). Elk beeld worden de hoogtes opnieuw gezet; door flatShading
  rekent three.js de vlakjes zelf.
- Op de binnenplaats toont de zilveren kant veel groen (het gras eromheen);
  in een dorpsstraat worden dat gevels en lucht.

## De platen: gedreven koper en aluminium

De zachte deuken en de wazige glans van de folie van 5 oktober, sinds
6 oktober als twee platen aan de muur, links en rechts van het raam (knop
Platen), elk in een houten lijst.

- 1,0 × 1,4 m, 50 × 70 vakjes. Deuken (`deuken`): drie lagen golving (28,
  12 en 5 mm) plus vouwen (18 mm), alles × 0,7; de buitenste 7 cm lopen
  vlak uit naar de lijst.
- Gladde normalen (`computeVertexNormals`), geen vlakjes: daardoor zacht.
- `MeshStandardMaterial`, metalness 1, envMap de foto bij de muur (× 1,3):
  koper #d98a5f met roughness 0,3; aluminium #d4d9dd met roughness 0,34 en
  een ander stuk van de deuken (`verschuif` 2,3).
- De lijst: 8 cm breed, 12 cm diep.

## De spiegel

Een echte spiegel: hij tekent de wereld nog een keer, vanuit het
spiegelbeeld van de camera, in een eigen beeld, en toont dat (`Spiegel(b,
h)`). Eigen code in de geest van `Reflector` uit de voorbeelden van
three.js, met diepte voor twee spiegels tegenover elkaar.

- Spiegelglas van 1,1 × 1,9 m op een houten standaard (lijst van 7 cm,
  achterkant, twee voeten), met een heel licht blauwgrijze tint (#e7ecef).
- Elk beeld, net voordat de spiegel getekend wordt (`onBeforeRender`):
  een camera op de spiegelplek van de echte camera, het nabije vlak schuin
  langs de spiegel (zodat wat achter de spiegel staat niet meedoet), het
  beeld in een render target, en de spiegel toont dat met een projectie.
- Twee spiegels tegenover elkaar (vinkje Tweede spiegel; 3,4 m verder,
  teruggedraaid): de ene tekent de andere, tot `MAX_DIEPTE` = 3 keer diep,
  daarna donker. Per diepte een eigen beeld (512, 384 en 256 pixels).
- Van achteren gezien: donker. Tijdens een spiegelbeeld worden de
  schaduwen niet opnieuw berekend (die van dit beeld blijven gelden).
- Kosten: elke spiegel in beeld betekent de hele wereld nog een keer
  tekenen; met twee tegenover elkaar tot zes keer per beeld (de teller
  *Extra tekeningen per beeld* links in de demo). Een spiegel die niet in
  beeld is, kost niets. Dus een of twee tegelijk, geen tientallen; dat
  wilde de maker ook.

## Zonlicht via de spiegel: de lichtvlek

three.js rekent niet uit waar teruggekaatst licht landt; de demo doet het
zelf (`zetVlek()`). De richting van de zon wordt gespiegeld in de spiegel,
en waar die straal de muur (z = −4) of de grond raakt, komt een lichtvlek:
een plat vlak met een zachte ronde vlek (een verloop op een canvas),
optellend licht (`AdditiveBlending`), en hoe schuiner, hoe langer
uitgerekt (tot drie keer). Geen vlek als de zon op de achterkant schijnt,
's nachts, of met het vinkje uit. De vlek wordt alleen opnieuw berekend als
de zon verzet wordt (de spiegel staat stil); een spiegel die beweegt, moet
het elk beeld doen. Hetzelfde idee als de lichtvlek van de koplampen
(`editors/prefab-architect/js/vehicles/lights.js`). In de uitleg van de
demo: "Goed genoeg voor een signaal met spiegels in een verhaal."

## Licht: zon, lucht, nacht en lantaarn

- De zon: het zonlicht van `PREFAB.scene.make` (DirectionalLight met
  schaduwen), op 30 m gezet met de schuifjes Zon (richting, 0 tot 360°) en
  Hoogte (5 tot 80°). Overdag 0,95 en #fff2dc; 's nachts 0,04 en blauwig
  #8fa3ff (de maan). Schaduwcamera ±12 m.
- De lucht: het halfrondlicht van de Architect, overdag 0,75, 's nachts
  0,1.
- De nacht: de lucht donker, geen zonneschijf, en de lantaarn aan: een
  echt `PointLight` (#ffc98a, sterkte 3,2, bereik 14 m, verval 2) bovenin.
  In het spel krijgen de meeste lampen alleen een gloed en een lichtvlek,
  en zijn er maar een paar echte lichten, zoals deze lantaarn.
- Knop *Hoe licht werkt*: acht korte punten in drie talen (de zon, de
  lucht, lampen, glimmen, spiegelen, weerkaatsen, doorlaten, straks), ook
  bruikbaar voor de Making-of.

## Valkuilen: om te onthouden

1. **Glas op de kubusfoto (three.js r147).** Staat er doorzichtig glas
   (transmission) in beeld bij een kubusfoto, dan tekent three.js na de pas
   voor dat glas de rest van de foto in de eerste zijde van de kubus; de
   andere vijf blijven leeg en de folie wordt vlekkerig zwart. Daarom
   altijd het glas (de ruit en de scherven) van de foto weghalen. Dat geldt
   straks ook voor een kasruit als onderdeel.
2. **Glas in de spiegel.** Om dezelfde reden staan de ruit, het kozijn en
   de scherven niet in het spiegelbeeld: r147 heeft één beeld voor de pas
   van het glas, en een ruit in het spiegelbeeld zou dat overschrijven voor
   de echte ruit. Zichtbaar: in de spiegel ontbreekt de ruit.
3. **Een spiegel in een ander beeld.** Wordt de spiegel getekend terwijl
   three.js al in een ander beeld tekent (de kubusfoto, de pas van het
   glas), dan tekent hij niet zelf opnieuw, maar toont hij zijn vorige
   beeld, of op de foto een lichte vlek.
4. **De foto na het beeld**, en alleen bij een verandering (zie boven).
5. **HalfFloat** kan niet op elke computer; de demo valt dan terug op
   gewone bytes, met zwakkere flitsen.
6. **De lantaarn gloeit niet.** Het glas van de lantaarn (`lantern.js`) is
   een gewoon kleurtje. De lantaarn verlicht 's nachts de omgeving wel (het
   PointLight), maar zijn eigen glas licht niet op; te zien op de
   schermafdruk van de maker van 7 oktober. De regel
   `PREFAB.vehicleLights.zet(lantaarnDeel, ...)` in de demo doet niets:
   `lights.js` wordt niet geladen, en de lantaarn heeft geen lampglas om te
   schakelen. Nog niet opgelost.
7. **De lichtvlek kent alleen deze binnenplaats**: de muur op z = −4 en de
   grond. Elders: een straal door de wereld schieten (`THREE.Raycaster`)
   in plaats van die twee vlakken.
8. **Samenbakken.** Het 3D-aanzicht van het Kadaster voegt alle blokjes van
   een gebouw met hetzelfde materiaal samen (`js/bake.js`). Een spiegel
   (met zijn `onBeforeRender`) en de folie (die beweegt) verliezen dan hun
   werking. Als onderdelen moeten ze los blijven, zoals nu al een vorm met
   een plaatje (`mat.map`).

## Beeldkwaliteit: de aantekening reflectie

Wens van de maker (9 oktober 2026): spiegelende ramen moeten uit kunnen als
de instellingen omlaag gaan; de spiegel nooit, want dan is het geen spiegel.
Daarom heeft elk spiegelend materiaal een aantekening,
`material.userData.reflectie`:

- `'optioneel'`: mag uit bij een lagere beeldkwaliteit. Nu: de ruit (en dus
  de scherven, die hetzelfde glas zijn). Op Laag blijft het glas half
  doorzichtig en breekt het gewoon; alleen de spiegeling van de omgeving
  gaat eraf.
- `'altijd'`: blijft altijd aan. Nu: de spiegel, de folie en de platen (hun
  glans is wat ze zijn).

In de demo: Beeldkwaliteit, Hoog of Laag (`zetKwaliteit`). Op Laag tekent
de spiegel ook maar een keer diep in plaats van drie keer: hij blijft
spiegelen, maar kost minder. Eerlijk over de winst: de spiegeling van glas
komt van de omgevingsfoto, die alleen opnieuw genomen wordt als er iets
verandert; dat is goedkoop. De echte spiegel is duur (de hele wereld nog een
keer per beeld), en daar zit dus de grootste winst van Laag. In het spel
wordt dit een instelling van de speler; de aantekening hoort straks bij de
onderdelen zelf (E36).

## Van demo naar onderdelen (open punt E36)

Het plan: kasruit, spiegel, folie, koperplaat en aluminiumplaat worden
onderdelen van de Architect; licht wordt een eigen soort onderdeel ernaast
(lampen, lantaarns, de zon door een raam). Wat daarvoor nodig is, en nog
niet gedaan:

- Een gedeeld stukje voor de omgevingsfoto: wie maakt hem, waar, en
  wanneer opnieuw. De Architect heeft nu geen omgevingsfoto;
  `PREFAB.scene.make` heeft alleen een zon, een halfrondlicht, gras en een
  raster.
- De spiegel met gedeelde toestand (de diepte, "foto bezig", wat niet in
  de spiegel mag); in de demo zijn dat losse variabelen.
- Een uitzondering in `bake.js` (*Valkuilen*, 8).
- De aantekening `reflectie` (`'optioneel'` of `'altijd'`) op elk spiegelend
  materiaal, en een instelling Beeldkwaliteit in het spel.
- Een haakje dat de folie elk beeld laat wapperen, zoals
  `PREFAB.vehicleLights.animeer` de zwaailichten laat knipperen. Zonder
  haakje hangt de folie stil, wat ook kan.
- Namen in drie talen (`js/i18n.js`), de zes staten (`PREFAB.condition`) en
  de proeven (`test/run.js` bouwt elk onderdeel in alle staten en
  stijlen).

## Wat een verhaal ermee kan

- Een ruit laten breken waar en wanneer het verhaal wil (`breek(x, y)`), en
  later weer heel maken.
- Een lichtsignaal met een spiegel: de vlek valt waar de zon hem heen
  kaatst; wie de spiegel draait, verplaatst de vlek.
- Folie die in de zon fel flitst, en 's nachts bijna donker is (dan schijnt
  alleen de lantaarn erop).
- Een of twee spiegels in een kamer; twee tegenover elkaar geven de gang
  van spiegels.
- Koper- en aluminiumplaten als versiering of als bord.

## Waar verder

- `materials-demo.html` (deze map): de code, met in de kop de wensen en het
  oordeel van de maker.
- `editors/prefab-architect/open-points.txt`: punt E36.
- `editors/prefab-architect/README.md`: de zesde ronde (*Materiaaldemo*)
  en de zevende (de map demos).
- De werkbank van 6 en 7 oktober (de zips die de maker bewaart):
  `scratchpad/r6/materials-demo.html.bak1` en `.r6b` (eerdere versies van
  6 oktober, met de folie van zachte deuken), `scratchpad/r6/matshot*.py`
  (de schermafdrukken van de demo maken), de afbeeldingen
  `scratchpad/r6/mat_*.png` tot en met `mat7_*.png` (alle tussenstappen),
  en `work/out/renders-round6/materials-demo*.png`.
- De brieven van de maker (niet in de zip): concept-binnenbrink-6.txt
  (nr 8), concept-binnenbrink-7.txt (de vraag) en concept-binnenbrink-8.txt
  (het oordeel).
