WebGPU · three.js TSL

Flower Field

1,8 miljoen planten over een procedureel landschap, met een zwevende kubus die zijn eigen begroeiing draagt. Geen enkele positie staat op de CPU. De GPU bepaalt waar alles komt.

Flower Field
Open de live scene

Een avondproject. Ik wilde weten wat een browser met WebGPU echt kan zodra je de GPU niet langer afgemaakte geometrie voert, maar hem laat bepalen wat er bestaat.

Één height-functie, gedeeld door alles

Dezelfde TSL-functie voedt het terreinmesh én elke afzonderlijke plant. Er is nergens een noise-implementatie op de CPU, dus de grond en wat erop groeit kunnen niet uit elkaar lopen. De bergen zitten in diezelfde functie als radiale mask over het vlak. Geen apart mesh, en het maakt de harde horizonlijn meteen onzichtbaar.

Geïndexeerde geometry, en een veld in chunks

Geometry samenvoegen eist non-indexed input, en daardoor groeide één bloemkop van 42 naar 240 vertices. Bij 70.000 instances is dat 14 miljoen extra vertex-invocaties per frame, elk met een fractal noise erin: 13 tegen 47 fps, bij exact hetzelfde beeld. Het veld in 12×12 meshes knippen zodat de camera het grootste deel kan weggooien bracht het op 73.

Werk per instance hoort in een compute pass

De terreinhoogte is een 7-octaaf fractal noise die bij de instance hoort, maar een vertex shader kent geen fase per instance, dus draaide hij hem opnieuw voor elk van de tien vertices van een spriet. Eén compute pass schrijft nu positie, grootte, kleur en hoek eenmalig in een storage buffer, en een tweede ververst alleen de windhoek per frame. De vertexshader van de begroeiing ging van 3 noise-aanroepen en 339 regels naar nul en 129.

Gemeten op Apple Silicon, 1456×826, pixelRatio 2: 99 fps tegen een vsync-plafond van 100, dus de echte winst is groter dan het getal laat zien.

Begonnen bij deze referentie. Dezelfde lage zon, dezelfde zwevende kubus, maar waar die gemodelleerd is, wordt elke bloem hier tijdens het draaien door een shader geplaatst.

WebGPU · three.js r185 · TSL · compute shaders