Utforska kraften i WebGL 2.0 Geometry Shaders. LÀr dig generera och transformera primitiver i realtid med praktiska exempel, frÄn punktsprites till exploderande meshar.
Frigör grafikpipelinen: En djupdykning i WebGL Geometry Shaders
Inom realtids 3D-grafik söker utvecklare stÀndigt efter mer kontroll över renderingsprocessen. Under mÄnga Är var den standardiserade grafikpipelinen en relativt fast vÀg: vertexar in, pixlar ut. Införandet av programmerbara shaders revolutionerade detta, men under lÄng tid förblev geometrins grundlÀggande struktur oförÀnderlig mellan vertex- och fragmentstegen. WebGL 2.0, baserat pÄ OpenGL ES 3.0, Àndrade detta genom att introducera ett kraftfullt, valfritt steg: Geometry Shader.
Geometry Shaders (GS) ger utvecklare en oövertrÀffad förmÄga att manipulera geometri direkt pÄ GPU:n. De kan skapa nya primitiver, förstöra befintliga, eller Àndra deras typ helt och hÄllet. FörestÀll dig att omvandla en enda punkt till en hel fyrhörning, extrudera fenor frÄn en triangel, eller rendera alla sex sidor av en cubemap i ett enda rit-anrop. Detta Àr kraften en Geometry Shader tillför dina webblÀsarbaserade 3D-applikationer.
Denna omfattande guide tar dig med pÄ en djupdykning i WebGL Geometry Shaders. Vi kommer att utforska var de passar in i pipelinen, deras kÀrnkoncept, praktisk implementering, kraftfulla anvÀndningsfall och kritiska prestandaövervÀganden för en global utvecklarpublik.
Den moderna grafikpipelinen: Var Geometry Shaders passar in
För att förstÄ den unika rollen som Geometry Shaders har, lÄt oss först Äterbesöka den moderna programmerbara grafikpipelinen som den existerar i WebGL 2.0:
- Vertex Shader: Detta Àr det första programmerbara steget. Den körs en gÄng för varje vertex i din indata. Dess primÀra uppgift Àr att bearbeta vertexattribut (som position, normaler och texturkoordinater) och transformera vertexpositionen frÄn modellrymd till clip space genom att mata ut variabeln `gl_Position`. Den kan inte skapa eller förstöra vertexar; dess input-till-output-förhÄllande Àr alltid 1:1.
- (Tessellation Shaders - Ej tillgÀngliga i WebGL 2.0)
- Geometry Shader (Valfri): Detta Àr vÄrt fokus. GS körs efter Vertex Shader. Till skillnad frÄn sin föregÄngare opererar den pÄ en komplett primitiv (en punkt, linje eller triangel) Ät gÄngen, tillsammans med dess angrÀnsande vertexar om sÄ begÀrs. Dess superkraft Àr dess förmÄga att Àndra mÀngden och typen av geometri. Den kan mata ut noll, en eller mÄnga primitiver för varje inmatad primitiv.
- Transform Feedback (Valfri): Ett speciellt lÀge som lÄter dig fÄnga utdata frÄn Vertex eller Geometry Shader tillbaka till en buffert för senare anvÀndning, och kringgÄr resten av pipelinen. Det anvÀnds ofta för GPU-baserade partikelsimuleringar.
- Rasterisering: Ett steg med fast funktion (icke-programmerbart). Det tar de primitiver som matats ut av Geometry Shader (eller Vertex Shader om GS saknas) och rÀknar ut vilka skÀrmpixlar som tÀcks av dem. DÀrefter genererar det fragment (potentiella pixlar) för dessa tÀckta omrÄden.
- Fragment Shader: Detta Àr det sista programmerbara steget. Den körs en gÄng för varje fragment som genererats av rasteriseraren. Dess huvudsakliga uppgift Àr att bestÀmma den slutgiltiga fÀrgen pÄ pixeln, vilket den gör genom att mata ut till en variabel som `gl_FragColor` eller en anvÀndardefinierad `out`-variabel. Det Àr hÀr belysning, texturering och andra per-pixel-effekter berÀknas.
- Per-Sample Operations: Det sista steget med fast funktion dÀr djup-testning, stencil-testning och blending sker innan den slutgiltiga pixelfÀrgen skrivs till framebuffer.
Geometry Shaderns strategiska position mellan vertexbearbetning och rasterisering Àr det som gör den sÄ kraftfull. Den har tillgÄng till alla vertexar i en primitiv, vilket gör att den kan utföra berÀkningar som Àr omöjliga i en Vertex Shader, som bara ser en vertex Ät gÄngen.
GrundlÀggande koncept för Geometry Shaders
För att bemÀstra Geometry Shaders mÄste du förstÄ deras unika syntax och exekveringsmodell. De skiljer sig fundamentalt frÄn vertex och fragment shaders.
GLSL-version
Geometry Shaders Àr en funktion i WebGL 2.0, vilket innebÀr att din GLSL-kod mÄste börja med versionsdirektivet för OpenGL ES 3.0:
#version 300 es
Input- och output-primitiver
Den mest avgörande delen av en GS Àr att definiera dess input- och output-primitivtyper med hjÀlp av `layout`-kvalificerare. Detta talar om för GPU:n hur den ska tolka de inkommande vertexarna och vilken typ av primitiver du avser att bygga.
- Input-layouter:
points: Tar emot enskilda punkter.lines: Tar emot linjesegment med 2 vertexar.triangles: Tar emot trianglar med 3 vertexar.lines_adjacency: Tar emot en linje med dess tvÄ angrÀnsande vertexar (totalt 4).triangles_adjacency: Tar emot en triangel med dess tre angrÀnsande vertexar (totalt 6). AngrÀnsande information Àr anvÀndbar för effekter som att generera siluettkonturer.
- Output-layouter:
points: Matar ut enskilda punkter.line_strip: Matar ut en sammanhÀngande serie linjer.triangle_strip: Matar ut en sammanhÀngande serie trianglar, vilket ofta Àr mer effektivt Àn att mata ut enskilda trianglar.
Du mÄste ocksÄ specificera det maximala antalet vertexar som shadern kommer att mata ut för en enskild inmatad primitiv med hjÀlp av `max_vertices`. Detta Àr en hÄrd grÀns som GPU:n anvÀnder för resursallokering. Att överskrida denna grÀns vid körtid Àr inte tillÄtet.
En typisk GS-deklaration ser ut sÄ hÀr:
layout (triangles) in;
layout (triangle_strip, max_vertices = 4) out;
Denna shader tar trianglar som indata och lovar att mata ut en triangle strip med högst 4 vertexar för varje inmatad triangel.
Exekveringsmodell och inbyggda funktioner
En Geometry Shaders `main()`-funktion anropas en gÄng per inmatad primitiv, inte per vertex.
- Indata: Indata frÄn Vertex Shadern anlÀnder som en array. Den inbyggda variabeln `gl_in` Àr en array av strukturer som innehÄller utdata frÄn vertex shadern (som `gl_Position`) för varje vertex i den inmatade primitiven. Du kommer Ät den som `gl_in[0].gl_Position`, `gl_in[1].gl_Position`, etc.
- Generera utdata: Du returnerar inte bara ett vÀrde. IstÀllet bygger du nya primitiver vertex för vertex med hjÀlp av tvÄ nyckelfunktioner:
EmitVertex(): Denna funktion tar de nuvarande vÀrdena för alla dina `out`-variabler (inklusive `gl_Position`) och lÀgger till dem som en ny vertex i den aktuella output-primitiv-stripen.EndPrimitive(): Denna funktion signalerar att du Àr klar med att konstruera den aktuella output-primitiven (t.ex. en punkt, en linje i en strip eller en triangel i en strip). Efter att ha anropat denna kan du börja emittera vertexar för en ny primitiv.
Flödet Àr enkelt: stÀll in dina output-variabler, anropa `EmitVertex()`, upprepa för alla vertexar i den nya primitiven, och anropa sedan `EndPrimitive()`.
Att konfigurera en Geometry Shader i JavaScript
Att integrera en Geometry Shader i din WebGL 2.0-applikation innebÀr nÄgra extra steg i din shader-kompilerings- och lÀnkningsprocess. Processen Àr mycket lik den för att konfigurera vertex och fragment shaders.
- HÀmta en WebGL 2.0-kontext: Se till att du begÀr en `"webgl2"`-kontext frÄn ditt canvas-element. Om detta misslyckas stöder webblÀsaren inte WebGL 2.0.
- Skapa shadern: AnvÀnd `gl.createShader()`, men denna gÄng skickar du `gl.GEOMETRY_SHADER` som typ.
const geometryShader = gl.createShader(gl.GEOMETRY_SHADER); - Ange kÀlla och kompilera: Precis som med andra shaders, anvÀnd `gl.shaderSource()` och `gl.compileShader()`.
gl.shaderSource(geometryShader, geometryShaderSource);
gl.compileShader(geometryShader);Kontrollera för kompileringsfel med `gl.getShaderParameter(shader, gl.COMPILE_STATUS)`. - Bifoga och lÀnka: Bifoga den kompilerade geometry shadern till ditt shader-program tillsammans med vertex och fragment shaders innan lÀnkning.
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, geometryShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
Kontrollera för lÀnkningsfel med `gl.getProgramParameter(program, gl.LINK_STATUS)`.
Det var allt! Resten av din WebGL-kod för att konfigurera buffertar, attribut och uniforms, samt det slutgiltiga rit-anropet (`gl.drawArrays` eller `gl.drawElements`) förblir densamma. GPU:n anropar automatiskt geometry shadern om den Àr en del av det lÀnkade programmet.
Praktiskt exempel 1: Genomströmnings-shadern
"Hello world" för Geometry Shaders Àr genomströmnings-shadern (pass-through). Den tar en primitiv som indata och matar ut exakt samma primitiv utan nÄgra Àndringar. Detta Àr ett utmÀrkt sÀtt att verifiera att din konfiguration fungerar korrekt och att förstÄ det grundlÀggande dataflödet.
Vertex Shader
Vertex shadern Àr minimal. Den transformerar helt enkelt vertexen och skickar dess position vidare.
#version 300 es
layout (location=0) in vec3 a_position;
uniform mat4 u_modelViewProjection;
void main() {
gl_Position = u_modelViewProjection * vec4(a_position, 1.0);
}
Geometry Shader
HĂ€r tar vi in en triangel och emitterar samma triangel.
#version 300 es
// Denna shader tar trianglar som indata
layout (triangles) in;
// Den kommer att mata ut en triangle strip med högst 3 vertexar
layout (triangle_strip, max_vertices = 3) out;
void main() {
// Indata 'gl_in' Àr en array. För en triangel har den 3 element.
// gl_in[0] innehÄller utdata frÄn vertex shadern för den första vertexen.
// Vi loopar helt enkelt igenom indata-vertexarna och emitterar dem.
for (int i = 0; i < gl_in.length(); i++) {
// Kopiera positionen frÄn indata-vertexen till utdata
gl_Position = gl_in[i].gl_Position;
// Emittera vertexen
EmitVertex();
}
// Vi Àr klara med denna primitiv (en enskild triangel)
EndPrimitive();
}
Fragment Shader
Fragment shadern matar bara ut en enfÀrgad fÀrg.
#version 300 es
precision mediump float;
out vec4 outColor;
void main() {
outColor = vec4(0.2, 0.6, 1.0, 1.0); // En fin blÄ fÀrg
}
NÀr du kör detta kommer du att se din ursprungliga geometri renderad exakt som den skulle vara utan Geometry Shadern. Detta bekrÀftar att data flödar korrekt genom det nya steget.
Praktiskt exempel 2: Primitivgenerering - FrÄn punkter till fyrhörningar
Detta Àr ett av de vanligaste och mest kraftfulla anvÀndningsomrÄdena för en Geometry Shader: förstÀrkning (amplification). Vi kommer att ta en enskild punkt som indata och generera en fyrhörning (quad) frÄn den. Detta Àr grunden för GPU-baserade partikelsystem dÀr varje partikel Àr en kamera-vÀnd billboard.
LÄt oss anta att vÄr indata Àr en uppsÀttning punkter ritade med `gl.drawArrays(gl.POINTS, ...)`.
Vertex Shader
Vertex shadern Àr fortfarande enkel. Den berÀknar punktens position i clip space. Vi skickar ocksÄ vidare den ursprungliga positionen i vÀrldsrymden, vilket kan vara anvÀndbart.
#version 300 es
layout (location=0) in vec3 a_position;
uniform mat4 u_modelView;
uniform mat4 u_projection;
out vec3 v_worldPosition;
void main() {
v_worldPosition = a_position;
gl_Position = u_projection * u_modelView * vec4(a_position, 1.0);
}
Geometry Shader
Det Àr hÀr magin sker. Vi tar en enskild punkt och bygger en fyrhörning runt den.
#version 300 es
// Denna shader tar punkter som indata
layout (points) in;
// Den kommer att mata ut en triangle strip med 4 vertexar för att bilda en fyrhörning
layout (triangle_strip, max_vertices = 4) out;
// Uniforms för att kontrollera fyrhörningens storlek och orientering
uniform mat4 u_projection; // För att transformera vÄra förskjutningar till clip space
uniform float u_size;
// Vi kan ocksÄ skicka data till fragment shadern
out vec2 v_uv;
void main() {
// Punktens inmatade position (mitten av vÄr fyrhörning)
vec4 centerPosition = gl_in[0].gl_Position;
// Definiera de fyra hörnen av fyrhörningen i skÀrmrymden
// Vi skapar dem genom att addera förskjutningar till mittpositionen.
// 'w'-komponenten anvÀnds för att göra förskjutningarna pixelstora.
float halfSize = u_size * 0.5;
vec4 offsets[4];
offsets[0] = vec4(-halfSize, -halfSize, 0.0, 0.0);
offsets[1] = vec4( halfSize, -halfSize, 0.0, 0.0);
offsets[2] = vec4(-halfSize, halfSize, 0.0, 0.0);
offsets[3] = vec4( halfSize, halfSize, 0.0, 0.0);
// Definiera UV-koordinaterna för texturering
vec2 uvs[4];
uvs[0] = vec2(0.0, 0.0);
uvs[1] = vec2(1.0, 0.0);
uvs[2] = vec2(0.0, 1.0);
uvs[3] = vec2(1.0, 1.0);
// För att göra fyrhörningen alltid vÀnd mot kameran (billboarding), skulle vi
// vanligtvis hÀmta kamerans höger- och upp-vektorer frÄn view-matrisen
// och anvÀnda dem för att konstruera förskjutningarna i vÀrldsrymden före projektion.
// För enkelhetens skull skapar vi hÀr en skÀrm-justerad fyrhörning.
// Emittera fyrhörningens fyra vertexar
gl_Position = centerPosition + offsets[0];
v_uv = uvs[0];
EmitVertex();
gl_Position = centerPosition + offsets[1];
v_uv = uvs[1];
EmitVertex();
gl_Position = centerPosition + offsets[2];
v_uv = uvs[2];
EmitVertex();
gl_Position = centerPosition + offsets[3];
v_uv = uvs[3];
EmitVertex();
// Avsluta primitiven (fyrhörningen)
EndPrimitive();
}
Fragment Shader
Fragment shadern kan nu anvÀnda UV-koordinaterna som genererats av GS för att applicera en textur.
#version 300 es
precision mediump float;
in vec2 v_uv;
uniform sampler2D u_texture;
out vec4 outColor;
void main() {
outColor = texture(u_texture, v_uv);
}
Med denna konfiguration kan du rita tusentals partiklar genom att bara skicka en buffert med 3D-punkter till GPU:n. Geometry Shadern hanterar den komplexa uppgiften att expandera varje punkt till en texturerad fyrhörning, vilket avsevÀrt minskar mÀngden data du behöver ladda upp frÄn CPU:n.
Praktiskt exempel 3: Primitivtransformation - Exploderande meshar
Geometry Shaders Àr inte bara till för att skapa ny geometri; de Àr ocksÄ utmÀrkta för att modifiera befintliga primitiver. En klassisk effekt Àr den "exploderande meshen", dÀr varje triangel i en modell trycks utÄt frÄn mitten.
Vertex Shader
Vertex shadern Àr Äterigen mycket enkel. Vi behöver bara skicka vidare vertexpositionen och normalen till Geometry Shadern.
#version 300 es
layout (location=0) in vec3 a_position;
layout (location=1) in vec3 a_normal;
// Vi behöver inga uniforms hÀr eftersom GS kommer att göra transformationen
out vec3 v_position;
out vec3 v_normal;
void main() {
// Skicka attributen direkt till Geometry Shadern
v_position = a_position;
v_normal = a_normal;
gl_Position = vec4(a_position, 1.0); // TemporÀrt, GS kommer att skriva över
}
Geometry Shader
HÀr bearbetar vi en hel triangel pÄ en gÄng. Vi berÀknar dess geometriska normal och trycker sedan ut dess vertexar lÀngs den normalen.
#version 300 es
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
uniform mat4 u_modelViewProjection;
uniform float u_explodeAmount;
in vec3 v_position[]; // Indata Àr nu en array
in vec3 v_normal[];
out vec3 f_normal; // Skicka normalen till fragment shadern för belysning
void main() {
// HÀmta positionerna för de tre vertexarna i den inmatade triangeln
vec3 p0 = v_position[0];
vec3 p1 = v_position[1];
vec3 p2 = v_position[2];
// BerÀkna ytans normal (anvÀnder inte vertexnormaler)
vec3 v01 = p1 - p0;
vec3 v02 = p2 - p0;
vec3 faceNormal = normalize(cross(v01, v02));
// --- Emittera första vertex ---
// Flytta den lÀngs normalen med explode-mÀngden
vec4 newPos0 = u_modelViewProjection * vec4(p0 + faceNormal * u_explodeAmount, 1.0);
gl_Position = newPos0;
f_normal = v_normal[0]; // AnvÀnd ursprunglig vertexnormal för mjuk belysning
EmitVertex();
// --- Emittera andra vertex ---
vec4 newPos1 = u_modelViewProjection * vec4(p1 + faceNormal * u_explodeAmount, 1.0);
gl_Position = newPos1;
f_normal = v_normal[1];
EmitVertex();
// --- Emittera tredje vertex ---
vec4 newPos2 = u_modelViewProjection * vec4(p2 + faceNormal * u_explodeAmount, 1.0);
gl_Position = newPos2;
f_normal = v_normal[2];
EmitVertex();
EndPrimitive();
}
Genom att kontrollera `u_explodeAmount`-uniformen i din JavaScript-kod (till exempel med en slider eller baserat pÄ tid) kan du skapa en dynamisk och visuellt imponerande effekt dÀr modellens ytor flyger isÀr frÄn varandra. Detta demonstrerar GS:s förmÄga att utföra berÀkningar pÄ en hel primitiv för att pÄverka dess slutgiltiga form.
Avancerade anvÀndningsfall och tekniker
Utöver dessa grundlÀggande exempel möjliggör Geometry Shaders en rad avancerade renderingstekniker.
- Procedurell geometri: Generera grÀs, pÀls eller fenor i realtid. För varje inmatad triangel pÄ en terrÀngmodell kan du generera flera tunna, höga fyrhörningar för att simulera grÀsstrÄn.
- Visualisering av normaler och tangenter: Ett fantastiskt felsökningsverktyg. För varje vertex kan du emittera ett litet linjesegment orienterat lÀngs dess normal, tangent eller bitangentvektor, vilket hjÀlper dig att visualisera modellens ytegenskaper.
- Lagerindelad rendering med `gl_Layer`: Detta Àr en mycket effektiv teknik. Den inbyggda output-variabeln `gl_Layer` lÄter dig styra till vilket lager i en framebuffer-array eller vilken sida av en cubemap som output-primitiven ska renderas. Ett utmÀrkt anvÀndningsfall Àr att rendera rundstrÄlande skuggkartor för punktljus. Du kan binda en cubemap till framebuffern och, i ett enda rit-anrop, iterera igenom alla 6 sidor i Geometry Shadern, stÀlla in `gl_Layer` frÄn 0 till 5 och projicera geometrin pÄ rÀtt kubsida. Detta undviker 6 separata rit-anrop frÄn CPU:n.
Prestandavarningen: Hantera varsamt
Med stor makt följer stort ansvar. Geometry Shaders Àr notoriskt svÄra för GPU-hÄrdvara att optimera och kan lÀtt bli en prestandaflaskhals om de anvÀnds felaktigt.
Varför kan de vara lÄngsamma?
- Bryter parallellism: GPU:er uppnÄr sin hastighet genom massiv parallellism. Vertex shaders Àr mycket parallella eftersom varje vertex bearbetas oberoende. En Geometry Shader bearbetar dock primitiver sekventiellt inom sin lilla grupp, och utdatastorleken Àr variabel. Denna oförutsÀgbarhet stör GPU:ns högt optimerade arbetsflöde.
- Minnesbandbredd och cache-ineffektivitet: Indata till en GS Àr utdata frÄn hela vertex shading-steget för en primitiv. Utdata frÄn GS matas sedan till rasteriseraren. Detta mellanliggande steg kan överbelasta GPU:ns cache, sÀrskilt om GS förstÀrker geometrin avsevÀrt ("förstÀrkningsfaktorn").
- Overhead i drivrutiner: PÄ viss hÄrdvara, sÀrskilt mobila GPU:er som Àr vanliga mÄl för WebGL, kan anvÀndningen av en Geometry Shader tvinga drivrutinen till en lÄngsammare, mindre optimerad vÀg.
NÀr ska man anvÀnda en Geometry Shader?
Trots varningarna finns det scenarier dÀr en GS Àr rÀtt verktyg för jobbet:
- LÄg förstÀrkningsfaktor: NÀr antalet output-vertexar inte Àr drastiskt större Àn antalet input-vertexar (t.ex. att generera en enskild fyrhörning frÄn en punkt, eller att explodera en triangel till en annan triangel).
- CPU-bundna applikationer: Om din flaskhals Àr CPU:n som skickar för mÄnga rit-anrop eller för mycket data, kan en GS avlasta det arbetet till GPU:n. Lagerindelad rendering Àr ett perfekt exempel pÄ detta.
- Algoritmer som krÀver primitiv-angrÀnsning: För effekter som behöver kÀnna till en triangels grannar kan GS med angrÀnsande primitiver vara effektivare Àn komplexa flerstegstekniker eller förberÀkning av data pÄ CPU:n.
Alternativ till Geometry Shaders
ĂvervĂ€g alltid alternativ innan du vĂ€ljer en Geometry Shader, sĂ€rskilt om prestanda Ă€r kritisk:
- Instanced Rendering: För att rendera ett massivt antal identiska objekt (som partiklar eller grÀsstrÄn) Àr instancing nÀstan alltid snabbare. Du tillhandahÄller en enda mesh och en buffert med instansdata (position, rotation, fÀrg), och GPU:n ritar alla instanser i ett enda, högt optimerat anrop.
- Vertex Shader-trick: Du kan uppnÄ viss geometriförstÀrkning i en vertex shader. Genom att anvÀnda `gl_VertexID` och `gl_InstanceID` och en liten uppslagstabell (t.ex. en uniform array) kan du lÄta en vertex shader berÀkna hörnförskjutningarna för en fyrhörning inom ett enda rit-anrop med `gl.POINTS` som indata. Detta Àr ofta snabbare för enkel sprite-generering.
- Compute Shaders: (Inte i WebGL 2.0, men relevant för kontexten) I native API:er som OpenGL, Vulkan och DirectX Àr Compute Shaders det moderna, mer flexibla och ofta mer högpresterande sÀttet att utföra allmÀnna GPU-berÀkningar, inklusive procedurell geometrigenerering till en buffert.
Slutsats: Ett kraftfullt och nyanserat verktyg
WebGL Geometry Shaders Àr ett betydande tillskott till webbgrafikens verktygslÄda. De bryter det stela 1:1 input/output-paradigmet hos vertex shaders, och ger utvecklare kraften att skapa, modifiera och gallra geometriska primitiver dynamiskt pÄ GPU:n. FrÄn att generera partikelsprites och procedurella detaljer till att möjliggöra högeffektiva renderingstekniker som single-pass cubemap-rendering, Àr deras potential enorm.
Denna kraft mÄste dock anvÀndas med en förstÄelse för dess prestandakonsekvenser. De Àr inte en universallösning för alla geometrirelaterade uppgifter. Profilera alltid din applikation och övervÀg alternativ som instancing, som kan vara bÀttre lÀmpade för högvolymsförstÀrkning.
Genom att förstÄ grunderna, experimentera med praktiska tillÀmpningar och vara medveten om prestanda kan du effektivt integrera Geometry Shaders i dina WebGL 2.0-projekt och tÀnja pÄ grÀnserna för vad som Àr möjligt inom realtids 3D-grafik pÄ webben för en global publik.