Introduksjon
Denne bloggen benyttes som støtte i faget "Datamaskingrafikk" ved Uit, IVT campus Narvik. Bloggen omhandler grunnleggende OpenGL ES og WebGL. All eksempelkode er derfor basert på Javascript.
Fagområdet omfatter både to- og tredimensjonal grafikk i form av bilder og animasjoner. I vår sammenheng er vi interessert i den tekniske prosessen. Det vil si: hvordan fremstiller man 3D-grafikk ved hjelp av datamaskinen og tilhørende programvare?
Fagområdet kan deles inn i flere underemner, som for eksempel geometri (hvordan representere 3D-modeller), animasjon (bevegelse over tid) og rendring (2D-fremvisning/gjengivelse av 3D-modeller). Også bildebehandling kan ses på som et underemne av fagområdet (New World Encyclopedia, Computer Graphics).
I denne sammenheng er det geometri, animasjon og rendring som står i fokus. Bildebehandling er ikke tema i disse notatene.
Anvendelser
Det finnes mange og varierte anvendelser av datamaskingrafikk, og man kan stort sett plassere disse i en av følgende fire hovedkategorier (Angel et al., 2015):
Visning av informasjon – visualisering
Kart og geografiske informasjonssystemer (GIS), statistikk
CAD (Computer Aided Design) for arkitekter og ingeniører
Verktøy for å utvikle integrerte kretser
Simuleringer og animasjoner
Simuleringer: sanntidsfremstilling, f.eks. fly- og bilsimulatorer
Datamaskinspill
Opplæringsprogramvare
Animasjoner
Film- og TV-industrien
Reklame
Fotorealistiske bilder, med lys- og skyggelegging
VR – Virtual Reality, og AR – Augmented Reality
Brukergrensesnitt
Windows, Linux, macOS osv. Alle er basert på samme prinsipp – vinduer, menyer, ikoner m.m., dvs. grafiske elementer på skjermen.
3D-datamaskingrafikk bruker geometriske data til å rendre (fremstille) 2D-bilder. Spørsmålet er dermed: Hvordan fremstiller man 3D-modeller/objekter[1] på en 2D-flate (skjermen)?
Før vi går videre med dette, er det en del andre begreper og bakgrunnsteori som må på plass.
Vi starter med å se på hva et «grafikksystem» er, litt om grafikkortet og Graphics Processing Unit (GPU),
samt en introduksjon til begrepene scene, virtuelt kamera, shadere og graphics pipeline – og litt om ulike 3D-APIer.
Grafikksystem
Et grunnleggende datamaskingrafikksystem består i prinsippet av følgende deler (Angel et al., 2015):
Grafikksystemet i dagens datamaskiner er stort sett rasterbasert. Rastergrafikk innebærer at bildet er sammensatt av bildepunkter ordnet i rader og kolonner. Et bildepunkt, eller picture element, kalles piksel (pixel).
Skjermbufferet (framebuffer) er et minneområde som inneholder pikseldata tilsvarende et komplett bilde (en frame) klart til visning på skjermen.
Skjermbufferet er essensielt, og presisjonen avgjør detaljgraden i bildene som fremvises. Presisjonen er definert av antall bits per piksel. Én bit per piksel gir tilgang på to farger, 8 bits gir 256 farger. 24 bits dybde (8 bits per fargekomponent) er i dag vanlig og kalles gjerne true color eller RGB-systemer.
Såkalte HDR-systemer (High Dynamic Range) bruker 10 eller flere bits per fargekomponent ut mot skjermen, og typisk 16-bits flyttall internt i rendringen. Tidligere ble fargekomponentene lagret som heltall (integer), men i dag lagres de gjerne som flyttall (float) i skjermbufferet, slik at også HDR støttes.
Framebufferet inneholder mer informasjon enn bare fargen til hver piksel. Her ligger også informasjon om dybde m.m. Oppgaven til grafikksystemet er i prinsippet å omgjøre grafiske primitiver (gitt fra et program) til piksler i skjermbufferet. Denne prosessen kalles rasterisering eller scan conversion. Et grafisk primitiv kan være et punkt, en linje eller et polygon (mangekant). Et polygon er en flate beskrevet av et sett rette linjer – en trekant er dermed en type polygon.
Noen polygoner og tilhørende vertekser
Rasterisering gjør at vi kan gå fra linjer og trådmodeller (wireframes) til fylte polygoner. Primitiver er definert av et sett vertekser. En verteks (vertex) er i prinsippet et punkt i det tredimensjonale rom. Figuren under viser en kule oppbygd av polygoner (trekanter).
En kule oppbygd av trekanter
Fra et program kan man for eksempel angi tre punkter i rommet for å tegne en trekant. I tillegg angis for eksempel farge og/eller en teksturkoordinat for hver verteks. Ved hjelp av rasterisering vil nødvendige piksler genereres, med korrekt farge, slik at trekanten kan vises på skjermen.
Rasterisering er imidlertid ikke den eneste måten å lage et bilde av en 3D-scene på.
Den andre hovedtilnærmingen kalles ray tracing. Der rasterisering går «fra geometri til piksler»
går ray tracing motsatt vei: den tar for seg én piksel om gangen og sporer en stråle ut i scenen for å finne ut hva som er
synlig akkurat der. Rasterisering er rask, og det er denne teknikken dagens grafikkort og APIer i hovedsak er bygd rundt.
Ray tracing gir på sin side mer fysisk korrekt lys, skygge og speiling, men koster vesentlig mer regnekraft.
Disse notatene vil i hovedsak fokusere på rasterisering, men vi vil også se litt på ray tracing.
Mer om skjermbufferet
Skjermbufferet er som regel satt sammen av flere delbuffere:
Fargebuffer
Z-buffer (dybdebuffer)
Stensilbuffer
Fargebufferet inneholder fargeverdiene, typisk RGB (evt. RGBA), til alle pikslene. Z-bufferet, eller dybdebufferet, inneholder en dybdeverdi for hver piksel. Denne brukes for å avgjøre om en piksel ligger foran eller bak andre piksler. Stensilbufferet er ikke like sentralt som farge- og z-bufferet, men det kan for eksempel brukes til å lage skygger og speilinger. I praksis ligger ofte dybde- og stensilverdiene i samme buffer (f.eks. 24 bits dybde + 8 bits stensil).
Grafikkortet og grafikkprosessoren
I eldre datamaskiner ble gjerne grafikkprosesseringen utført av CPUen, samtidig som skjermbufferet var en del av standard minne.
Ingen spesiell støtte for grafikk
Dagens datamaskiner har typisk hovedkort eller prosessorbrikke med integrert grafikkprosessor – en Graphics Processing Unit (GPU).
Denne kan for eksempel ha en dedikert del av standard minne som den jobber mot (med assosiert skjermbuffer).
Poenget med grafikkprosessoren er å avlaste CPUen ved at den tar seg av beregningskrevende grafikkoperasjoner.
GPUer ble opprinnelig utviklet for å avlaste CPUen for grafikkoperasjoner. En GPU er egentlig en spesialisert mikroprosessor som i dag også brukes
i forbindelse med HPC (high performance computing). HPC omfatter teknikker og metoder for mange og/eller tunge beregninger.
HPC-metoder brukes i mange sammenhenger, f.eks. i forbindelse med utvinning av kryptovaluta, maskinlæring og kunstig intelligens,
værsimuleringer m.m. (Store Norske Leksikon, 2026). Det finnes ulike produsenter av GPUer som f.eks. Nvidia, AMD, Intel m.fl.
GPU-arkitekturen varierer mellom de ulike produsentene og det er derfor viktig at det finnes felles programmeringsgrensesnitt mot disse.
Eksempler er OpenGL, DirectX og Vulkan. Mer om dette senere i notatet.
Integrert løsning, GPU på hovedkortet
En GPU er, som en CPU, en mikroprosessor. Den er spesielt god til å utføre de matematiske og geometriske beregningene som trengs for å fremstille 3D-grafikk på en 2D-skjerm, fordi den kjører svært mange enkle beregninger parallelt.
Alternativt kan man legge til ekstern maskinvare i form av dedikerte grafikkort (skjermkort) med integrert grafikkprosessor. Slike kort kobles direkte til maskinens hovedkort. Skjermbufferet er i så fall et minneområde assosiert med grafikkortet, og vil typisk inneholde piksler tilsvarende et komplett bilde (en frame) for visning på skjermen.
Eksternt grafikkort
Mer om grafikkortet
Dagens grafikkort (kalles også grafikkakselerator) har gjerne direkte støtte for 3D-APIer som OpenGL, Direct3D, Vulkan og Metal. 3D-grafikk fremstilles programmatisk ved hjelp av disse APIene. Via APIet sendes kommandoer til kortets driver, som igjen sørger for at kommandoene utføres.
Grafikkprosessoren utfører kommandoene og genererer rastergrafikk som legges i skjermbufferet. Skjermbufferet oppdateres dermed via GPUen. Denne arkitekturen avlaster CPUen betraktelig. NVIDIA, AMD, Intel og Apple er eksempler på produsenter av GPUer og grafikkort.
Figuren under viser et eksempel på et grafikkort med GPU, kjøleribbe, minnebrikker og diverse tilkoplinger og kontakter. Kortet kobles typisk til hovedkortet via PCI Express (PCIe).
Eksempel på et grafikkort (med hjelp fra ChatGPT)
Forestill deg en bedrift med en ingeniøravdeling og en designavdeling. Dersom ingeniørene konstruerer en bru – dvs. gjør nødvendige beregninger, finner nødvendige materialer og lager en skisse – vil industridesignerne (satt litt på spissen) sørge for at brua ser bra ut innenfor ingeniørenes rammer. Forenklet oppsummert: beskrivelsen av brua sendes til designerne, som så bestemmer hvordan brua skal se ut. Merk at dette antakelig er en grov forenkling som mange designere vil protestere mot.
Fra vertekser til piksler
Resultatet er at beskrivelsen av brua ender opp som en pen bru.
Dette kan sammenlignes med virkemåten til et grafikkort. Brukerapplikasjoner, som for eksempel et 3D-tegneverktøy eller en WebGL-applikasjon, sender informasjon om bildet til grafikkortet. Grafikkortet bestemmer så hvordan skjermpikslene skal fylles for å skape ønsket bilde. Denne informasjonen sendes videre til skjermen.
Det er krevende å fremstille et 3D-bilde ut fra en geometrisk beskrivelse. Første steg består i å opprette en trådmodell (wireframe) bestående av for eksempel mange trekanter. Deretter utføres rasterisering, som innebærer å beregne hvilke piksler som trengs for å fylle trekantene. Så får hver piksel en farge. Fargen kan også, eller i tillegg, bestemmes av teksturer og lys-/skyggedata. I forbindelse med spill skjer denne prosessen typisk 60 ganger per sekund eller oftere. Dersom CPUen skulle utført alle disse beregningene, ville den fort blitt overbelastet.
Grafikkortet består av fire hovedkomponenter:
En kontakt for tilknytning til hovedkortet, i dag så godt som alltid PCI Express (PCIe). Eldre maskiner brukte PCI eller AGP (Advanced Graphics Port).
En egen prosessor – GPU – som bestemmer hvordan hver piksel skal se ut
Minne som holder på alle pikslene som skal fremvises, inkludert skjermbufferet
En eller flere skjermkontakter, typisk HDMI, DisplayPort eller USB-C
Hva kjennetegner et godt grafikkort?
Ytelsen til et grafikkort måles gjerne i form av antall komplette bilder per sekund det klarer å generere. På engelsk betegnes dette som frame rate og måles i fps – frames per second. Det er imidlertid flere faktorer som innvirker på fps:
Hvor raskt det kan bygge opp en trådmodell – måles i antall trekanter eller vertekser per sekund
Hvor raskt trådmodeller fylles med piksler – måles i antall piksler GPUen kan prosessere per sekund
Hvor mye og hvor rask minnebåndbredde kortet har – teksturer og buffere må leses og skrives kontinuerlig
3D-illusjonen
Teknikker for å vise 3D-grafikk med dybde på en 2D-flate, for eksempel et papirark, har blitt grundig studert og utviklet av kunstnere gjennom de siste århundrene. Teknikkene er basert på fenomener som vi observerer i vår fysiske verden. Ser man langs en rett jernbanelinje, vil kantene på linja se ut til å møtes i et «forsvinningspunkt» (vanishing point) langt der fremme. Objekter vil oppfattes som mindre jo lenger unna de er. Et hus som står veldig langt unna betrakteren vil virke lite, mens et tre som står nært vil oppfattes som stort.
«Vanishing point» og dybde
Objekter dekker til hverandre, enten helt eller delvis, avhengig av gjennomsiktighet, plassering og dybdeplassering.
Ugjennomsiktige objekter
Poenget er at dybdeinformasjon er viktig når grafikksystemet gjør om 3D-objekter til 2D-skjermgrafikk. Bruk av lys, skyggelegging og materialer kan være med på å fremheve 3D-illusjonen.
Modellrepresentasjon
Modeller kan for eksempel utformes ved hjelp av geometrisk modellering – dvs. ved å angi punkter og grafiske primitiver i en virtuell 3D-verden. Et primitiv er i denne sammenheng en enkel «figur» som en linje, en trekant eller andre typer polygoner.
En modell er igjen oppbygd av et sett med primitiver. Det viser seg at en effektiv måte å bygge opp 3D-modeller på er ved hjelp av et sett trekanter. Kuler kan for eksempel genereres utelukkende av trekanter. Jo mindre trekantene er, jo glattere vil kuleflaten fremstå. Kombineres dette med lyssetting og tekstur, er det mulig å oppnå fotorealistiske kuler med gjenskinn og skyggeeffekter. En tekstur er kort fortalt et 2D-bilde som knyttes til / legges på primitiver.
Et mesh er en samling polygoner som til sammen utgjør en 3D-modell. Jo flere polygoner (trekanter), jo mer detaljert modell. Dette betyr også høyere prosesseringskrav. Videre er det ønskelig å kunne manipulere modellene, dvs. rotere, flytte eller skalere dem. En annen viktig del av 3D-grafikken er å kunne bestemme synsvinkel – dvs. fra hvilken kant modellen skal betraktes – og hvordan modellen skal projiseres på skjermen (perspektiv eller ortografisk projeksjon – mer om dette senere).
Scene
Når man tar bilder i den virkelige verden, stilles gjerne modellene og objektene opp i en scene før vi «knipser» bildet med fotoapparatet. Vi bruker kameraet til å fange opp og generere 2D-bilder av vår fysiske 3D-verden. Tilsvarende skjer når man programmerer 3D-scener med for eksempel WebGL. Etter at modellene er laget, posisjonerer man disse i en tenkt 3D-verden. Deretter plasseres eventuelle lyskilder og et «kamera» i den tenkte verdenen. Kameraet posisjoneres slik at det fanger opp den delen av scenen som ønskes vist på skjermen. Det er viktig å skille mellom objektene og bildet av objektene. Legg merke til at objekter, lyskilder og kamera plasseres uavhengig av hverandre.
Scene og kamera
Objekter spesifiseres ved hjelp av geometriske primitiver. Geometriske primitiver spesifiseres igjen ved hjelp av vertekser. Vertekser kan, i tillegg til å inneholde en posisjon, også inneholde informasjon om farge, tekstur og normalvektor (brukes i forbindelse med lys – mer om dette senere). Man kan dermed beskrive modeller ved hjelp av et sett vertekser. En trekant spesifiseres av tre vertekser. En kule består av et stort antall trekanter.
Det virtuelle kameraet
Kameramodellen som brukes i moderne 3D-grafikksystemer, simulerer i prinsippet optiske systemer.
Figuren viser hvordan objektet (huset) i scenen projiseres på et bildeplan. Trekker man projeksjonslinjer fra objektet til kameralinsen, finner vi «bildet» av modellen på bildeplanet.
Det virtuelle kameraet
Objekter som er langt fra kameraet, vil fremstå som mindre på bildeplanet enn objekter som er nærmere kameraet.
Fordeler med denne kameramodellen er blant annet at vi skiller mellom objekter, betrakter (kameraet) og lyskilder. Alle disse spesifiseres og plasseres uavhengig av hverandre. Dette gjør det mulig å utforme bildet av objektene ved hjelp av geometriske beregninger i form av vektor- og matriseoperasjoner.
Figuren viser essensen av 3D-datamaskingrafikk. Modellene beskrives og plasseres i en tenkt 3D-verden. Deretter plasseres kamera og lyskilder uavhengig av modellene. Kameraets plassering og synsvidde bestemmer bildet av «scenen», som så omgjøres til et 2D-bilde ved hjelp av projeksjon.
3D-API
3D-APIer gir som regel programmereren følgende muligheter:
spesifisere primitiver og objekter
spesifisere betrakteren (eller kameraet)
spesifisere lyskilder
spesifisere materialegenskaper (bl.a. farge)
Både OpenGL, OpenGL ES og Direct3D er eksempler på 3D-APIer som tilbyr dette. Andre eksempler er Metal og Vulkan.
Programmereren ser grafikksystemet gjennom APIet. På den ene siden av APIet er vårt program, på den andre implementasjonen av funksjonene i APIet – dette kan være i form av programvare og/eller maskinvare.
Grafikk-API (Angel et al., 2015)
Dette fungerer ved at objektene (og verteksene) prosesseres i den rekkefølgen de genereres av applikasjonen. Omforming av vertekser til piksler i skjermbufferet utføres i en såkalt graphics pipeline, som vil bli nærmere forklart etter hvert.
3D-grafikkteknologier
Det finnes en rekke lavnivås 3D-APIer, men de mest utbredte er følgende:
Direct3D (Microsoft)
OpenGL (Khronos Group)
OpenGL ES (Khronos Group)
Vulkan (Khronos Group)
Metal (Apple)
Følgende Javascript 3D-APIer bygger på nevnte 3D-APIer:
WebGL - JavaScript-API over OpenGL ES (WebGL, 2026)
WebGPU - JavaScript-API over Vulkan, Metal og Direct3D 12 (WebGPU, 2026).
Både WebGL og WebGPU er JavaScript-APIer over henholdsvis OpenGL ES og Vulkan, Metal, Direct3D 12 (det er imidlertid også mulig å bruke WebGPU fra andre språk, som kan kjøre utenfor nettleseren). WebGPU er ikke et tema i disse notatene.
WebGL vil derimot bli mye omtalt etter hvert siden dette er gjennomgangstemaet i disse notatene.
Det kan også nevnes at Googles Android har et tilsvarende API mot OpenGL ES.
I tillegg finnes flere høyerenivås APIer, som OGRE, jMonkeyEngine og MonoGame (etterfølgeren til Microsofts XNA, se monogame.net). Java3D er et Java-API for 3D-grafikk som kjører over OpenGL eller Direct3D, men det er ikke lenger under aktiv utvikling.
For nettleseren finnes det også høynivås APIer, som Three.js (threejs.org) og Babylon.js (babylonjs.com).
Begge disse er basert på WebGL (og etter hvert også WebGPU).
Direct3D
Direct3D er en del av Microsofts DirectX-plattform. DirectX var i utgangspunktet en samling APIer for utvikling av spill,
og omfattet APIer som DirectDraw (2D), DirectMusic, DirectPlay m.fl.
Direct3D er i utgangspunktet kun tilgjengelig på Windows-plattformene, i tillegg til Xbox-konsollene.
APIet er optimalisert for 3D-grafikk til datamaskinspill. Direct3D bruker tilgjengelig maskinvarestøtte, dvs. grafikkort dersom det er tilgjengelig.
Dette betyr at den beregningskrevende koden kjører på grafikkprosessoren (GPU) – i stedet for på maskinens CPU.
Ved hjelp av Direct3D kan man utvikle både 2D- og 3D-grafikkapplikasjoner.
Direct3D gir programmereren et API mot grafikkortets drivere. Figuren under viser en oversikt over Direct3D sin plassering i forhold til applikasjon,
grafikkortdrivere og maskinvare.
Direct3D, lagdeling
Direct3D er et lavnivås API som i prinsippet gir tilgang til alle grafikkortets funksjoner. Fra og med Direct3D 12 (2015) er APIet «eksplisitt»: programmereren har mer direkte kontroll over minne og kommandokøer, på samme måte som i Vulkan og Metal.
OpenGL
OpenGL (opengl.org) er et 3D-API for utvikling av portable 2D- og 3D-applikasjoner. OpenGL ble introdusert i 1992 av Silicon Graphics Inc. (SGI) og er svært utbredt. Det brukes i forbindelse med Computer Aided Design (CAD), virtuell virkelighet, medisinske applikasjoner, ulike typer simuleringer og datamaskinspill.
Det finnes støtte for OpenGL på de fleste operativsystemplattformer, inkludert de fleste UNIX-varianter, alle aktuelle Windows-varianter og Linux. Apple har imidlertid markert OpenGL som utdatert (deprecated) i macOS fra og med versjon 10.14 (2018) og anbefaler Metal i stedet.
Det finnes språkbindinger til blant annet C, C++, Java, Python, C#, Fortran og Ada. OpenGL er leverandøruavhengig og ble i utgangspunktet administrert av OpenGL Architecture Review Board, med bred støtte fra industrien. I 2006 ble administrasjonen av OpenGL flyttet til Khronos Group (Khronos Group, About). Det kan nevnes at Microsoft var med her inntil 2003.
OpenGL bruker tilgjengelig maskinvare/grafikkort og GPU dersom dette er tilgjengelig på maskinen som kjører OpenGL-applikasjonen.
Fordelen med OpenGL er den brede støtten, den åpne arkitekturen, samt at det finnes støtte for APIet på de fleste plattformer. OpenGL er et rent grafikkbibliotek og ikke spesielt rettet mot spillutvikling (selv om det er mye brukt til dette). Siste versjon er OpenGL 4.6 fra 2017.
OpenGL ES og WebGL
En variant av OpenGL, OpenGL ES (Khronos Group, OpenGL ES), der ES står for Embedded Systems, brukes av plattformer som Android, Apple/iOS og Sony PlayStation. I tillegg ble det ved innføringen av HTML5-standarden også mulig å lage 3D-applikasjoner via WebGL, som også er basert på OpenGL ES. Programmering mot WebGL-APIet gjøres ved hjelp av JavaScript.
OpenGL ES er et subsett av standard OpenGL og finnes i flere versjoner. Figuren under viser sammenhengen mellom disse. Her ser vi at OpenGL ES 1.1 og OpenGL ES 2.0 er inkompatible versjoner. Versjon 1.1 var et subsett av OpenGL 1.5. Forskjellen på disse versjonene og de etterfølgende er at det fra og med OpenGL 2.0 / GL ES 2.0 ble innført programmerbare shadere. Dette gjør at programmereren har mye mer kontroll på hvordan det endelige 3D-bildet fremstår på skjermen. Det gjøres ved å lage såkalte «shaderprogrammer» som utføres av GPUen. Tidligere var ikke dette mulig. OpenGL ES 2.0 er et subsett av OpenGL 2.0, mens OpenGL ES 3.0 er et subsett av OpenGL 3.3.
Sammenhengen mellom OpenGL og OpenGL ES
WebGL finnes i to versjoner: WebGL 1.0 (basert på OpenGL ES 2.0) og WebGL 2.0 (basert på OpenGL ES 3.0).
WebGL 2 har vært støttet i alle store nettlesere siden 2022 og gir blant annet vertex array objects, instansiert tegning,
3D-teksturer og en nyere versjon av shaderspråket.
Vulkan
Vulkan er et lavnivås 3D-API som ble introdusert i 2016 av Khronos Group. Dette er en åpen standard som støttes av flere grafikkortprodusenter, inkludert AMD, Intel og NVIDIA.
Vulkan er designet for å gi utviklere mer direkte kontroll over GPUen og redusere overhead sammenlignet med OpenGL og Direct3D.
Vulkan støtter flere OS-plattformer, inkludert Windows, Linux og Android. Khronos Group anser i dag Vulkan som OpenGLs etterfølger.
Til tross for dette vil sannsynligvis OpenGL/ES fortsatt støttes av maskinvareprodusenter i mange år fremover.
OpenGL/ES og Vulkan bruker helt ulike programmeringsmodeller, men deler allikevel mange grunnleggende konsepter som shaderprogrammering,
transformasjoner, graphics pipeline og bruk av vertekser og teksturer.
Man kan derfor trygt fortsatt bruke OpenGL/ES i undervisningssammenheng for å lære grunnleggende 3D-konsepter.
Metal
Metal er, på samme måte som Vulkan, også et lavnivås 3D-API, men det er utviklet av Apple og er kun tilgjengelig på Apple-plattformer som macOS, iOS og iPadOS.
Dette er en proprietær løsning som er tett integrert med Apples maskinvare og operativsystemer.
Metal gir utviklere tilgang til grafikk- og databehandlingsfunksjoner på Apple-maskinvare, og er optimalisert for ytelse og effektivitet.
WebGPU
WebGPU er et relativt nytt 3D-API som er under utvikling av W3C. På samme måte som med WebGL-applikasjoner kjøres WebGPU-applikasjoner direkte i nettlesere.
De vanligste nettlesere støtter i dag WebGPU. WebGPU er designet for å gi bedre ytelse og effektivitet sammenlignet med WebGL.
På samme måte som WebGL programmeres WebGPU-applikasjoner ved hjelp av JavaScript, men WebGPU har et eget shaderspråk - WGSL (WebGPU Shading Language).
WebGPU er basert på underliggende grafikk-APIer som Vulkan, Metal og Direct3D 12.
Shadere og graphics pipeline
I disse notatene fokuserer vi på OpenGL ES, WebGL og tilhørende shaderprogrammering. WebGPU, Vulkan og/eller Metal omtales derfor ikke mer enn det som allerede er nevnt.
Videre skal vi konsentrere oss om shaderbegrepet og det vi kaller «graphics pipeline» i sammenheng med OpenGL og OpenGL ES.
OpenGL og OpenGL ES definerer en prosess for å konvertere vertekser, teksturer m.m. til et bilde som kan vises på skjermen. Prosessen er beskrevet som en graphics/rendering pipeline. Vi kan sammenligne dette med et slags «transportbånd» der verteks- og pikseloperasjoner utføres i ulike steg. Dette er vist i figuren under:
Forenklet rendering-/graphics pipeline
En shader er et program som kjøres av GPUen, og som regel består et shaderprogram av flere delprogrammer/funksjoner. Shaderspråket knyttet til OpenGL ES kalles GLSL ES og har en C-lignende syntaks.
I klientprogrammet, f.eks. WebGL/JavaScript, spesifiseres punkter/vertekser. Verteksene definerer igjen primitiver/trekanter som utgjør modellen(e) som skal tegnes på skjermen.
Primitiv og modell
Hver trekant spesifiseres ved hjelp av tre vertekser. Hver verteks har en x-, y- og z-verdi.
Verteksene legges typisk i et array/buffer og leveres til GPUen. Første steg på veien er verteksshaderen. Dette er, som nevnt, et lite program som kjører for hver enkelt verteks i modellen.
Verteksshaderen manipulerer (transformerer) verteksen i henhold til kriterier gitt av klientprogrammet. Dette kan f.eks. bety at alle vertekser flyttes litt langs x-aksen, som igjen betyr at hele modellen flyttes litt langs x-aksen. En annen mulighet er at modellen skal roteres noen grader om z-aksen – alle verteksene må da roteres om z. Hvor mye modellen skal flyttes eller roteres, bestemmes fra klientprogrammet ved å sende en transformasjonsmatrise til shaderen.
Deretter vil verteksene settes sammen til primitiver («primitive assembly»), slik at de er klare til å bli fylt med farger (piksler). Dette skjer i neste steg – her beregnes hvilke fragmenter (tenk på et fragment som en piksel) som inngår i hvert primitiv (trekant).
For hvert fragment kjøres deretter fragmentshaderen. Dette er også et lite program som kjøres av GPUen. Siden denne kjører for hvert fragment, kan man som programmerer påvirke hvert enkelt fragment (piksel) før det vises på skjermen. Resultatet fra fragmentshaderen må være en farge for aktuell piksel, som til slutt ender opp i framebufferet og på skjermen.
Figuren under illustrerer dette:
Enda mer forenklet rendering-/graphics pipeline
Når man bruker OpenGL ES 2.0 eller nyere, må man bruke verteks- og fragmentshadere for i det hele tatt å få noe frem på skjermen.
NB! En verteks er ikke det samme som et punkt på skjermen. En verteks er et (virtuelt) punkt i 3D-rommet som brukes til å definere primitiver (f.eks. trekanter). Hittil har vi sagt at primitiver enten er linjer eller trekanter (evt. mangekanter/polygoner, som støttes av OpenGL, men ikke av OpenGL ES).
I WebGL/OpenGL ES finnes det også et punktprimitiv. Dette betyr at det er mulig å tegne en enkelt verteks som et punkt. Man kan også bestemme størrelsen på punktet. Mer om dette etter hvert.
GLSL / GLSL ES
Shaderprogrammene, dvs. verteksshader og fragmentshader, er i praksis to programmer/funksjoner som kjøres av GPUen. Verteksshaderen kjører for hver verteks, mens fragmentshaderen kjører for hvert fragment (piksel).
Disse programmeres ved hjelp av et eget shaderspråk som kalles OpenGL Shading Language (GLSL). Syntaksen til GLSL er C-lignende, og språket er dermed relativt enkelt å lære dersom man har programmeringsbakgrunn. OpenGL ES har sin variant, som heter GLSL ES (Khronos Group, GLSL ES).
Vi kommer tilbake til shadere etter hvert, siden dette er en fundamental del av OpenGL ES og dermed også WebGL.
Ray tracing – et alternativ til rasterisering
"Graphics pipeline" beskrevet over er basert på rasterisering: modellen sendes gjennom pipelinen ett primitiv om gangen,
og for hvert primitiv (trekant) beregnes hvilke fragmenter (piksler) det dekker. Pipelinen «vet» dermed lite om scenen som helhet – når
fragmentshaderen kjører for én piksel, har den i utgangspunktet ingen kunnskap om de andre objektene i scenen.
Nettopp derfor er effekter som speilinger, gjennomsiktighet og skygger krevende å få til med rasterisering.
Dette er effekter som i så fall må løses med egne teknikker, som f.eks. shadow maps, environment maps o.l.
Ray tracing snur problemstillingen på hodet. I stedet for å spørre «hvilke piksler dekker denne trekanten?»,
spør man «hva ser jeg gjennom denne pikselen?». For hver piksel i bildet sendes en stråle (en ray) fra kameraet,
gjennom pikselen på bildeplanet og videre ut i scenen. Deretter beregnes hvilket objekt strålen først treffer,
og fargen til pikselen bestemmes ut fra materialet, lyset og geometrien i akkurat det treffpunktet.
Metoden er en direkte etterligning av hvordan lys oppfører seg i den fysiske verden – men baklengs. I virkeligheten går lyset fra lyskilden, spretter rundt i scenen og ender opp i øyet vårt. De aller fleste lysstrålene treffer aldri øyet, så det ville vært svært lite effektivt å simulere dem alle. Derfor sporer man i stedet strålene fra kameraet og ut i scenen, slik at man kun regner på det lyset som faktisk bidrar til bildet.
Rasterisering eller ray tracing?
Kort oppsummert:
Rasterisering er raskt og velegnet til sanntid (spill, simulatorer, VR).
Realistisk lys og speiling må tilnærmes med ulike teknikker.
Ray tracing gir tilnærmet fysisk korrekt lys, skygge, speiling og brytning nesten «gratis» som en konsekvens
av selve metoden, men er langt mer beregningskrevende.
Historisk har derfor ray tracing hørt hjemme i offline rendring, der man kan bruke sekunder eller timer på ett enkelt bilde:
film, animasjon, arkitekturvisualisering og reklame. Verktøy som Blender (Cycles), Pixars RenderMan og V-Ray er eksempler på dette.
Rasterisering har på sin side vært enerådende i sanntidsgrafikk.
Dette skillet er i ferd med å viskes ut. Fra og med NVIDIAs RTX-serie (2018) har grafikkortene fått egne
maskinvareenheter (RT-kjerner) som akselererer det mest kostbare steget i ray tracing,
nemlig å finne ut hvilket objekt en stråle treffer. Tilsvarende støtte finnes i dag også fra AMD, Intel og Apple.
På API-siden er dette eksponert gjennom blant annet DirectX Raytracing (DXR) og Vulkan Ray Tracing.
WebGL har ingen innebygd støtte for ray tracing, men er, som beskrevet over, basert på verteks- og fragmentshadere og rasterisering.
Ray tracing er per i dag (august 2026) heller ikke en del av WebGPU-standarden.
Siden OpenGL ES og WebGL er basert på rasterisering, vil vi i disse notatene fokusere på dette.
OpenGL, Direct3D eller Vulkan/Metal?
Her er det selvfølgelig delte meninger. Fordelen med OpenGL / OpenGL ES er åpenheten og støtten for mange plattformer. Ulempen er at det kan oppleves som relativt tungt å komme i gang med APIet. OpenGL er tiltenkt alle typer 3D-applikasjoner, og ikke spesielt utviklet med tanke på datamaskinspill.
Direct3D (en del av DirectX) ble i utgangspunktet utviklet med tanke på spill. Direct3D kan programmeres fra C++ og fra .NET-språk som C#. En av ulempene er at DirectX er et proprietært Microsoft-API som bare kjører på Windows og Xbox.
Vulkan, Direct3D 12 og Metal er alle lavnivå-APIer som gir høyere ytelse, men som har en høyere læringsterskel og krever betydelig mer kode enn OpenGL/ES.
Metal er på sin side proprietært og kun tilgjengelig på Apple-plattformer.
[1] Her brukes begrepene objekt og modell om hverandre, men i samme betydning – en 3D-modell beskrevet av et sett 3D-punkter/koordinater.