inear

Alla experiment

Skapa, testa och förstå3D, WebGL och spelutveckling

Breaking Waves: en brytande våg i WebGL

Sommaren 2011 byggde Einar Öberg en brytande våg i WebGL för att lära sig shaders. Här är tekniken bakom vattnet och hur du gör vågor i webbläsaren i dag.

Senast uppdaterad 20 oktober 2026

Två vänner skrattar åt en brytande havsvåg i 3D på en bärbar dator

Sommaren 2011 rullade en våg in i webbläsaren. Breaking Waves var ett WebGL-experiment som visade en brytande våg med skum och stänk. Projektet byggdes i three.js av den svenska utvecklaren Einar Öberg. Han använde projektet för att lära sig shaders på djupet.

Original
Einar Öbergs eget experiment
År
2011, blogginlägget publicerades i augusti 2011
Teknik
WebGL, three.js, vertex och fragment shaders, modell från 3D Studio Max
Status i dag
Inte längre tillgänglig online på den gamla adressen

Den här sidan beskriver experimentet och tekniken bakom det, men redaktionen har inte byggt originalet. Beskrivningen bygger på Öbergs eget blogginlägg och på arkiverade versioner av sidan.

Vad var Breaking Waves?

Experimentet visade en enda stor våg som bröt mot betraktaren, med ett lugnare hav bakom. Enligt Öberg var målet att förstå shaders bättre genom att helt enkelt pröva sig fram. Han skrev att det kanske var nybörjarnivå för erfarna grafikprogrammerare, men att samma frågor dyker upp gång på gång inom området.

Vatten var ett återkommande tema för honom. I ett tidigare experiment, Waterfall, byggde han ett vattenfall i Flash med Away3D och brus.

Så fungerade det

Det fanns ingen fysisk simulering bakom. Det var en fast 3D-modell som fick liv av två shaders. Den första flyttade hörnen och den andra rörde texturerna. Det är ett vanligt sätt att få vatten att se levande ut utan att räkna på strömningar.

Modellen och färgerna

Vågen och ett enkelt plan för havet längre bort modellerades i 3D Studio Max. Öberg målade vågen med hörnfärger som shadern använde för att efterlikna hur ljus lyser igenom tunt vatten. En riktig ljusspridning under ytan hade varit mer korrekt men den enkla lösningen räckte. Ovanpå lades en normalkarta med upprepat brus för krusningar och en skumtextur av vitt Perlinbrus.

Vertex shader: hörnen gungar med en sinusFragment shader: texturen glider framåtVågens topp har texturen vänd åt andra hållet
Förenklad illustration av principen. Redaktionens egen bild, inte en skärmdump från originalet.

Rörelse i vertex shadern

Själva nätet var statiskt. Gungningen kom från vertex shadern, där varje hörns höjd ändrades med en sinusfunktion av hörnets position plus ett tidsvärde. Sinus ger ett värde mellan minus ett och ett. Multiplicerat med en faktor flyttar värdet hörnen upp och ned. En rörelse i sidled gav mindre krusningar.

Rörelse i fragment shadern

Vågen själv flyttade sig aldrig framåt. Det var texturkoordinaterna som sköts fram med samma tidsvärde. Vid den brytande toppen skulle vattnet röra sig åt motsatt håll, så där vreds texturen ett halvt varv. Skarven syntes bara om man tittade över vågkanten.

Djupbuffert, stänk och skum

Öberg fick problem med flimmer där ytor låg nära varandra, så kallad z-fighting. Lösningen var att placera kamerans närmaste plan så långt bort som möjligt, eftersom djupbufferten har mest precision nära kameran. Dimma och stänk ritades som sprites, det vill säga platta bilder som alltid är vända mot kameran. De återanvändes ur en pool i stället för att skapas på nytt.

Går det att göra likadant i 2026?

Ja. Verktygen är bättre i dag. Det klassiska sättet att beskriva hav i realtid är att lägga ihop flera vågor. Mark Finch beskrev 2004 i GPU Gems hur summor av sinusvågor och Gerstnervågor ger vassare toppar och bredare dalar. three.js har ett färdigt exempel med hav att utgå från.

En brytande våg som rullar över är svårare att återskapa. Den kräver antingen en modellerad form som i Breaking Waves eller en riktig vätskesimulering, som fortfarande är tung att köra i en webbläsare. Någon källkod till originalet har inte hittats på Öbergs GitHub.

Bygg något liknande

1. Ytan

Skapa ett plan med många segment så att det finns hörn att flytta.

2. Vågorna

Lägg ihop tre eller fyra sinusvågor med olika riktning och våglängd i vertex shadern.

3. Krusningarna

Låt två normalkartor glida åt olika håll i fragment shadern för att skapa små krusningar.

4. Skummet

Blanda in en vit brustextur där vågen är som högst och ställ in kamerans närmaste plan med omsorg.

Vill du veta mer om brus kan du läsa Ridged Perlin noise. Grunderna i 3D finns i A brief introduction to 3D.

Vad du kan lära dig av experimentet

Breaking Waves visar hur mycket som kan fuskas fram med enkla medel. En statisk modell, en sinusvåg och en glidande textur räckte för att lura ögat. Ingen fysikmotor behövdes för att vågen skulle kännas levande.

Lärdomen om djupbufferten är lika nyttig. Flimmer mellan ytor beror ofta på kamerans inställningar och inte på modellen, något många nybörjare upptäcker först efter lång tids felsökning.

Vanliga frågor om Breaking Waves

Vem skapade Breaking Waves?

Den svenska utvecklaren Einar Öberg gjorde experimentet och beskrev det på sin blogg i augusti 2011.

Var vågen en riktig vattensimulering?

Nej. Vågen var en modell från 3D Studio Max som animerades med shaders. Hörnens rörelse styrdes av en sinusfunktion medan texturen gled framåt.

Hur skapar man vågor i three.js i dag?

Det vanligaste är att lägga ihop flera sinusvågor eller Gerstnervågor i en vertex shader. Små krusningar läggs till med normalkartor som rör sig.

Vad orsakade flimret på ytorna?

Båda ytorna fick samma värde i djupbufferten. Problemet löstes genom att flytta kamerans närmaste plan längre bort.

Källor

Uppgifterna kontrollerades 20 oktober 2026.