Fibonacci Flowers - wizualizacja generatywnych kwiatów w Three.js

Fibonacci Flowers to aplikacja 3D napisana w czystym JavaScript przy użyciu Three.js, która generuje proceduralne kwiaty inspirowane matematycznymi wzorcami natury. Projekt wyróżnia się zaawansowanym podejściem do renderowania - customowe shadery GLSL, autorska implementacja bloom bez zewnętrznych bibliotek post-processingu oraz precyzyjna kontrola nad geometrią płatków generowaną proceduralnie.

Matematyczne podstawy: Dlaczego Fibonacci?

Sekwencja Fibonacciego to ciąg liczb, w którym każda liczba jest sumą dwóch poprzednich: [mathb]0,1,1,2,3,5,8,13,21,34,55,89,144,…[/mathb] Z ciągiem tym wiąże się złota proporcja, która pojawia się powszechnie w naturze - od układu liści na łodygach (filotaksja), przez spirale w słonecznikach, po kształty muszli.

Złoty kąt w generowaniu kwiatów

Sercem algorytmu jest złoty kąt (Golden Angle), wyprowadzony ze złotej proporcji:
const GA = Math.PI * (3 - Math.sqrt(5)); // ≈ 2.39996 rad ≈ 137.5°
Każdy płatek jest pozycjonowany według wzoru:
for(let i = 0; i < n; i++){
    const t = i / n;
    const ang = i * GA;
    const r = Math.sqrt(t) * R;
    const x = r * Math.cos(ang);
    const z = r * Math.sin(ang);
    const y = dome * Math.pow(Math.max(0, 1 - r/R), dP);
    // ...
}
Ten wzór zapewnia, że płatki układają się w naturalne spirale, minimalizując nakładanie się - dokładnie tak, jak w prawdziwych kwiatach (np. słonecznikach, gdzie nasiona układają się w spirale Fibonacciego). Fibonacci Flowers

Architektura renderowania

Geometria proceduralna płatków

Kluczowa funkcja mkPetal() generuje geometrię BufferGeometry z trzema atrybutami:
  • position - pozycje wierzchołków
  • uv - współrzędne tekstury
  • pa - parametry anizotropowe używane w shaderze do obliczeń oświetleniowych
function mkPetal(len, wid, sharp, bend, curl, ridge, crease, SU, SV){
  const pos = [], pa = [], uva = [], idx = [];
  for(let iu = 0; iu <= SU; iu++){
    const u = iu / SU;
    const w = Math.pow(Math.sin(u * Math.PI), sharp) * wid;
    const ly = Math.sin(u * Math.PI * .65) * bend
             - Math.pow(Math.max(0, u - .62) / .38, 2.8) * curl;
    for(let iv = 0; iv <= SV; iv++){
      const v = (iv / SV) * 2 - 1;
      pos.push(
        v * w,
        ly + (1 - v*v*v*v) * ridge * Math.sin(u * Math.PI * .9) - Math.abs(v) * w * crease,
        u * len
      );
      uva.push((v + 1) * .5, u);
      pa.push(u, Math.abs(v));
    }
  }
  // ... generowanie indeksów
}
Geometria jest parametryzowana przez 9 zmiennych, co pozwala na tworzenie różnych kształtów - od ostrych płatków anemonów po grube, mięsiste liście sukulentów. Fibonacci Flowers

Custom shadery GLSL

Aplikacja używa własnych shaderów zamiast standardowych materiałów Three.js: Vertex Shader (FVS):
attribute vec2 pa;
uniform float pt;
varying vec2 vPA;
varying vec3 vN, vVP;
varying float vPT;

void main(){
  vPA = pa; vPT = pt;
  vec4 mv = modelViewMatrix * vec4(position, 1.);
  vN = normalize(normalMatrix * normal);
  vVP = -mv.xyz;
  gl_Position = projectionMatrix * mv;
}
Fragment Shader (FFS): implementuje zaawansowany model oświetlenia z:
  • Diffuse lighting z dwoma źródłami światła
  • Specular highlights (Blinn-Phong)
  • Fresnel effect dla krawędzi
  • Subsurface scattering symulowany przez wzmocnienie czerwieni przy krawędziach
  • Color grading z korekcją luminancji
uniform vec3 cA, cB, cE;
varying vec2 vPA;
varying vec3 vN, vVP;
varying float vPT;

void main(){
  float u = vPA.x, ev = vPA.y, ft = 1. - vPT;
  vec3 rc = mix(cA, cB, ft * .44);
  vec3 tc = mix(cA, cB, min(1., ft * .5 + .54));
  vec3 base = mix(rc, tc, pow(u, .60));
  base = mix(base, cE, pow(ev, 2.2) * .42);
  // ... oświetlenie
}

Autorski Bloom bez post-processing library

Zamiast korzystać z EffectComposer z three/examples/jsm/postprocessing, aplikacja implementuje własną klasę Bloom:
class Bloom {
  constructor(renderer, w, h){
    this.strength = 1.40;
    this.threshold = .45;
    this.radius = 2.;
    this.passes = 3;
    // ... render targets
  }

  render(sc, cam){
    // 1. Render sceny do _sRT
    // 2. Threshold do _pRT
    // 3. Wielokrotny blur (Gaussian) w _pRT i _qRT
    // 4. Composite: scene + bloom * strength
  }
}
Fibonacci Flowers Kluczowe elementy:
  • Separate render targets dla sceny, threshold i blur
  • Gaussian blur z 9-tapowym filtrem (wagi: 0.054, 0.0918, 0.1216, 0.1945, 0.1762...)
  • Tonemapping w kompozycji: <span class="kod">col = col / (col + 1.)</span> + gamma correction <span class="kod">pow(col, 0.4545)</span>
To rozwiązanie eliminuje zależność od zewnętrznych modułów post-processingu, które często wymagają importowania wielu plików z three/examples/jsm.

Animacja kwitnienia

System animacji kwitnienia to jeden z najbardziej zaawansowanych elementów aplikacji.

Struktura referencji płatków

let petalRefs = [];
// Każdy entry: { tiltNode, finalRot, petalT }
// petalT = 0 (najbardziej wewnętrzny) … 1 (najbardziej zewnętrzny)

Funkcja opóźnienia (stagger)

function petalDelay(petalT) {
  return P.bloomOrder === 'inside → outside'
    ? petalT * P.animStagger
    : (1 - petalT) * P.animStagger;
}
To pozwala na wybór kierunku kwitnienia - od środka na zewnątrz lub odwrotnie.

Easing functions

const easeOutBack = t => {
  const c = 1.70158, p = c + 1;
  return 1 + p * (t - 1)**3 + c * (t - 1)**2;
};

const easeInOutCubic = t =>
  t < .5 ? 4 * t * t * t : 1 - (-2 * t + 2)**3 / 2;
  • Scale używa <span class="kod">easeOutBack</span> - daje efekt "sprężystego" rozkwitu,
  • Rotation używa <span class="kod">easeInOutCubic</span> - płynny, symetryczny ruch

Presety kwiatów

Aplikacja zawiera 9 starannie dostrojonych presetów. Każdy preset definiuje 18 parametrów. Fibonacci Flowers

Optymalizacje

Brak InstancedMesh

W przeciwieństwie do wielu projektów generatywnych, aplikacja nie używa InstancedMesh. Każdy płatek to osobny THREE.Mesh z własną geometrią. To podejście:
  • Zaleta: pełna kontrola nad każdym płatkiem (indywidualne animacje, shadery)
  • Koszt: większe zużycie pamięci przy dużej liczbie płatków

Tło gwiaździste

Tło generowane jest jako punkty na sferze:
const sp = [];
for(let i = 0; i < 2400; i++){
  const th = Math.random() * Math.PI * 2;
  const ph = Math.acos(2 * Math.random() - 1);
  const r = 62 + Math.random() * 28;
  sp.push(r * Math.sin(ph) * Math.cos(th), ...);
}
Użycie THREE.Points z PointsMaterial zapewnia wydajne renderowanie tysięcy gwiazd.

Kontrola jakości segmentacji

Parametry segsU i segsV pozwalają na balans między jakością a wydajnością:
const SU = Math.max(3, Math.round(P.segsU));
const SV = Math.max(3, Math.round(P.segsV));
Domyślnie 22×12 segmentów na płatek, co daje płynne kształty przy rozsądnym koszcie renderowania.

Interakcja

Orbitowanie kamery

canvas.addEventListener('mousedown', e => {
  drag = true;
  mx = e.clientX; my = e.clientY;
  P.autoRotate = false;
});

window.addEventListener('mousemove', e => {
  if(!drag) return;
  orb.th -= (e.clientX - mx) * .005;
  orb.ph = clamp(orb.ph + (e.clientY - my) * .004, .05, Math.PI * .48);
  syncCam();
});
Kamera używa współrzędnych sferycznych (th, ph, dist) dla płynnego orbitowania.

Skróty klawiszowe

  • H - pokaż/ukryj GUI
  • B - uruchom animację kwitnienia
  • 1-9 - wybierz preset
Projekt demonstruje, że nawet skomplikowane efekty wizualne można zaimplementować w czystym JavaScript z Three.js, zachowując pełną kontrolę nad każdym aspektem renderowania.

Demo: Fibonacci Flowers

Fibonacci Flowers