Modul 1.2: API og språk, Intro. WebGL

I denne sammenheng omtales teknologi som kombinasjon av programmeringsspråk og 3D API. Under er noen aktuelle teknologier for utvikling av 3D datamaskingrafikk:

  • C++ og OpenGL
  • Java og OpenGL (JOGL).
  • C++ og Direct3D
  • C# og Monogame (tidligere XNA)
  • Android, Java og Open GL ES
  • Javascript, WebGL, og OpenGL ES
  • Javascript og Three.js

Det finnes også andre varianter som for eksempel Microsofts Windows Presentation Foundation som også tilbyr et 3D API. Dette er imidlertid ikke så omfattende som de nevnte og er tiltenkt enklere animasjoner og utvikling av 3D brukergrensesnittelementer.

C++ og OpenGL

Bruk av C++ sammen med OpenGL er kanskje den mest utbredte måten å anvende OpenGL på. Det finnes C++ kompilatorer for de fleste operativsystemplattformer. Det samme gjelder OpenGL APIet. En variant er å bruke C++ og Qt-biblioteket fra The Qt Company sammen med OpenGL.

Java OpenGL (JOGL)

Dette lar oss bruke Java til å programmere mot OpenGL APIet. JOGL er en referanseimplementasjon av JSR 231 (Java Bindings for OpenGL). Dette er ikke det samme som Java3D som opererer på et høyere abstraksjonsnivå.

C++ og Direct3D

Vi kan selvfølgelig kode direkte mot DirectX APIet vha. unmanaged C++. Dette krever at vi har installert DirectX SDKen. Microsoft Visual Studio er et egnet verktøy for denne type utvikling.

C# og Monogame

MonoGame.net (www.monogame.net) er et åpen kildekode-prosjekt. Monogame er et kryssplattformverktøy som lar oss skrive C#-kode som deretter kan kompileres til aktuell plattform. Monogame støtter per i dag (2026) følgende plattformer: Windows, macOS, Linux, Android, iOS, iPadOS, PlayStation 4, PlayStation 5, Xbox One og Nintendo Switch.

Avhengig av plattform som det kompileres til brukes OpenGL, OpenGL ES eller DirectX i bunnen.

Android og Open GL ES

Android støtter OpenGL ES som et rent Java eller Kotlin-API. Dersom man behersker Android/Java/Kotlin og samtidig er kjent med WebGL/Javascript vil overgangen til 3D-grafikk på Android-plattformen være enkel.

Introduksjon til WebGL, Javascript og Open GL ES

WebGL

Ved hjelp Javascript og WebGL-APIet kan man utvikle applikasjoner som kan generere og vise 3D-grafikk direkte i nettleseren uten bruk av spesielle plugins e.l. WebGL er basert på OpenGL ES (Khronos Group, OpenGL ES) og brukes sammen med HTML5 og Javascript.

Figuren under viser sammenhengen.

All tegning gjøres på et et HTML5 «canvas» element som tilsvarer et rektangulært område i nettleseren. HTML5 canvas-elementet ble opprinnelig tatt med i HTML-standarden for å kunne tegne 2D grafikk men brukes også til 3D.

Canvas

I 2009 etablerte Khronos-gruppen (www.khronos.org) en arbeidsgruppe for WebGL som nå består av flere nettleserprodusenter som Apple, Google, Mozilla og Opera.

Khronos-gruppen ble etablert i år 2000 og er en «non-profit» organisasjon som utvikler og vedlikeholder standarder for diverse APIer, deriblant OpenGL, OpenGL ES, WebGL, OpenCL m.m.

WebGL versjon 1.0 ble «frosset» i mars 2011 og støtte for WebGL er nå blitt standard i de fleste store nettlesere som Chrome, Firefox, Edge, Vivaldi, Safari m.fl.

Med WebGL har man tilgang til maskinvareakselerert interaktiv 3D-grafikk direkte i nettleseren – dvs. nettleseren utnytter evt. grafikkort i datamaskinen.

Vha. WebGL kan man lage fullverdige 3D spill som kjører direkte i nettleseren. Distribusjonsmodellen blir dermed svært enkel, all kode ligger på en webserver og lastes ned til nettleseren på samme måte som vanlige web-sider. Det er heller ingen «royalties» involvert – WebGL er fritt tilgjengelig.

Siden WebGL er basert på OpenGL ES, som igjen er basert på OpenGL, vil utviklere som tidligere har brukt noen av OpenGL-APIene enkelt kunne ta i bruk WebGL.

WebGL 1.0 eller WebGL 2.0?

WebGL 1.0 er basert på OpenGL ES 2.0 og benytter shaderspråket GLSL ES versjon 1.0. WebGL 2.0 ble standardisert i 2017 og er basert på OpenGL ES versjon 3.0. WebGL 2.0 støtter derfor, i tillegg til GLSL ES 1.0, også shaderspråket GLSL ES 3.0.

Dokumentasjon av WebGL APIene ligger her:

WebGL 1.0 APIet gir tilgang til OpenGL ES 2.0 funksjonalitet mens WebGL 2.0 APIet også gir tilgang til OpenGL ES 3.0 spesifikk funksjonalitet. WebGL 1.0 programmer vil dermed også fungere med WebGL 2.0.

Kodeeksempler knyttet til disse notatene bruker webgl 2.0.

Komme i gang med WebGL

Det er enkelt å komme i gang med WebGL – man trenger i prinsippet kun en teksteditor til å skrive Javascript-kode og en nettleser der man kan teste koden.

Javascript-kode kan debugges direkte i nettleseren (bruk f.eks. F12 på Windows eller Option+Commannd+i på Mac).

Javascript

Javascript ble opprinnelig utviklet av Netscape for å kunne tilføre dynamiske elementer på websider og lastes ned fra webserveren. Javascriptkoden, som gjerne er integrert i html-koden, kjøres lokalt av nettleseren.

Tradisjonelt har Javascript-kode kjørt interpreterende. I nyere versjoner av nettlesere JIT-kompileres gjerne koden før den kjøres. Dette for å øke ytelsen. Javascript støtter også (en form for) objektorientering.

Javascript har en Java-liknende syntaks men er ellers svært forskjellig fra programmeringsspråket Java.

Kort introduksjon til Three.js

Three.js (http://threejs.org/) er et Javascript bibliotek som gjør livet enklere for WebGL-utviklere. Three skjuler flere av de kompliserende elementene som man må forholde seg til i "ren" WebGL, som f.eks. shadere.

Ved hjelp av noen få linjer med kode kan man f.eks. vise en roterende, teksturert og belyst kube. Tilsvarende i ren WebGL krever mange titalls, om ikke hundrevis av kodelinjer.

Før vi sier mer om html5 og canvas går vi rett på et eksempel. Åpner man følgende html-fil i nettleseren vil man se en grønn kube:

Fila index.html

Fila demo1.js

Html-fila, index.html, refererer javascriptfila, demo1.js. I main-funksjonen til demo1.js opprettes et canvas-objekt, dvs. flaten som det skal tegnes på. Dette legges til det DOM treet som en del av html-dokumentet. Deretter opprettes et renderer-objekt som brukes til å tegne/gjengi (rendre) hele scenen.

Videre opprettes et Three scene-objekt. Three bruker en såkalt scenegraf slik at alle elementer, som figurer/objekter, kamera og lyskilder legges til en hierarkisk organisert scenegraf.

Deretter opprettes et kameraobjekt. Her brukes et perspektivkamera. Hva de ulike parametrene betyr vil bli forklart senere. Som standard står kameraet i origo og peker "innover", mot negativ z. Kameraet flyttes litt "utover" slik at det står i [0,0,3] og peker mot origo. Siden kuben er tegnet om origo vil den fanges opp av kameraet og vises på skjermen. Kameraets kan også settes til å peke i andre retninger. Mer om dette senere.

Man kan enkelt legge til flere kuber e.l. i sceneobjektet som også vil bli tegnet når man kaller render() på renderer-objektet.

Selve kuben genereres vha. et geometriobjekt og et materialobjekt. Disse indikerer henholdsvis "geometrien" (form og størrelse) og materialtype, dvs. en kombinasjon av farge, belysning og eventuell tekstur. Her brukes kun farge slik at kuben fremstår som grønn på skjermen.

WebGL: 2 og 3D grafikk direkte i nettleseren

Vi fortsetter her med "ren" WebGL, dvs. uten bruk av Three.js.

HTML5 Canvas-elementet

I Three-eksemplet over brukte vi <canvas> elementet i html-fila. Ved hjelp av <canvas> og Javascript kan man, som vist, generere og vise/gjengi («rendre») både 2D og 3D-grafikk direkte i nettleseren.

Canvas og 2D

Eksemplet under viser hvordan man tegner 2D grafikk på canvas-elementet. HTML-fila ser slik ut:

tegnrektangel/index.html

I .html fila defineres et <script> av type "module". Her importeres en funksjon, main(), fra fila helloRektangel.js. Funksjonen kalles så for å starte programmet. Funksjonen ligger i en egen Javascript-fil, og ser slik ut:

tegnrektangel/tegnRektangel.js

Her opprettes først et nytt div-element, som vi kaller "divWrapper". Derettes opprettes et canvas element som "pakkes inn" i et div-elementet. Canvas-elementet er det vi kan tegne både 2D og 3D grafikk på. Det nye div-elementet legges så inn i document.body slik at det blir en del av html-dokumentet. Deretter settes størrelse på canvaset til 960x480. Vi gir divWrapper id'en "my2DCanvas". Deretter hentes en referanse, gl ("graphics library"), til en 2D kontekst. Vha. denne kan man tegne enkel 2D grafikk vha. fillRect() o.l.

Som vi skal se i neste avsnitt følger vi omtrent samme oppskrift når vi skal tegne 3D grafikk vha. WebGL. Den viktigste forskjellen er at kontekst-objektet hentes ut på følgende måte:

   let gl = canvasElem.getContext('webgl2');

Den andre store forskjellen er måten man bruker gl-objektet til å kalle på WebGL/OpenGL ES-metoder/funksjoner for å få generert 3D grafikk. Dette gjøres på en helt annen måte enn hva man gjør i forhold til 2D grafikk.

Canvas og 3D

I denne gjennomgangen skal vi se hvordan man bruker WebGL-kontekst til å tegne på. I det første eksemplet, helloCanvas, setter vi kun bakgrunnsfargen.

hellocanvas/index.html

I .html fila defineres et <script> av type "module". Her importeres en funksjon, helloCanvas(), fra fila helloCanvas.js. Funksjonen kalles så for å starte programmet. Funksjonen helloCanvas() ser slik ut:

hellocanvas/helloCanvas.js

Ved hjelp av WebGLCanvas-klassen opprettes et WebGLCanvas-objekt. WebGLCanvas-klassen setter gl lik:

   this.gl = canvasElem.getContext('webgl2');

I helloCanvas() kan vi så hente ut gl fra canvas-objektet. Videre vil gl bli brukt til å utføre WebGL/OpenGL ES kall for å få generert 3D-grafikk på canvaset. I eksemplet settes kun bakgrunnsfargen til canvaset vha. gl.clearColor(…) og gl.clear(…).

Legg merke til at koden importerer WebGLCanvas fra WebGLCanvas.js. Denne ser slik ut:

WebGL & 3D grafikk

Hvordan fremstiller man 3D objekter på en 2D skjerm? Dette er i korte trekk hva 3D datamaskingrafikk handler om. 3D grafikk kan fremstilles på flere måter vha. WebGL.

Modeller kan tegnes ”manuelt” vha. vertekser og primitiver i et tenkt 3D rom. Foreløpig kan vi tenke på en verteks som et punkt i rommet definert av en x,y og z-verdi mens et primitiv kan være et punkt, en linje eller en trekant som er definert av henholdsvis en, to eller tre vertekser.

Det er også mulig å laste inn modeller fremstilt i andre verktøy, som for eksempel 3D Studio Max, Blender o.l. Slike modeller er på tilsvarende måte konstruert av et (stort) antall vertekser og primitiver som behandles på samme måte som ”manuelle” modeller. Deretter vil verteksene gå gjennom flere transformasjoner, via shaderne og GPUen, før de til slutt ender opp som piksler på skjermen. I første omgang vil «manuelle modeller», dvs. der vi selv definerer vertekser og primitiver, gjennomgås.

Tegne på canvaset – bruk av shader

Shadere må brukes uansett hva vi skal tegne i WebGL. Hva er så en shader? For å kunne svar på dette må man se på hele prosessen, fra vi definerer vertekser til f.eks. en trekant i WebGL/Javascript-programmet til pikslene som utgjør denne trekanten ender opp i skjermbufret og til slutt på canvaset/skjermen.

Hele prosessen omtales som en «graphics/rendering pipeline», se figuren under:

Forenklet graphics/rendering pipeline

Til venstre ser vi Javascript/WebGL-programmet der man spesifiserer verteksene til modellen som skal tegnes.

Dersom vi ser på eksemplet med en enkel trekant består den av tre vertekser. Hver verteks vil bli sent til verteksshaderfunksjonen. Dette er et eget program/funksjon som mottar verteksene og ev. gjør diverse transformasjoner på disse. Legg merke til at det som skjer fra og med verteksshaderen og utover utføres av GPUen. Transformert verteks "videresendes" fra verteksshaderfunksjonen (og sendes videre i pipelinen) ved at gl_Position settes lik (ev. transformert) verteks (aVertexPosition).

Verteksene sammenstilles (slik at de utgjør en trekant) og deretter vil det bli beregnet hvilke piksler/fragmenter (foreløpig sier vi at et fragment tilsvarer en piksel – egentlig er et fragment en «kandidat» til å bli en piksel i skjermbufret, ikke alle fragmenter ender opp som piksler i skjermbufret) som skal til for å få tegnet denne trekanten.

For hver piksel/fragment som inngår i trekanten vil fragmentshaderen bli kjørt. Denne kan manipulere hver enkelt fragment. Fragmentshaderen må gi fragColor en fargeverdi (RGBA). Denne ender så opp i skjermbufret som til slutt vises på skjermen.

Foreløpig er denne gjennomgangen noe forenklet – det er flere steg som inngår, spesielt mellom fragmentshader og framebuffer, men disse er ikke tatt med her. Her er kun det de viktigste stegene vist.

Tegne et punkt

Vi starter så enkelt som overhodet mulig - det vil si å tegne et enkelt punkt på skjermen. Det nye er her bruk av shadere. Shaderkoden er lagt i html-fila vha. script-tagger av type "x-shader/x-vertex" eller "x-shader/x-fragment". Disse gis også en id.

Shaderfunksjonene er skrevet i shaderprogrammeringsspråket OpenGL ES Shading Language (Khronos Group, GLSL ES, 2009). Dette er en variant av OpenGL GLSL. Dette språket har sin egen syntaks, datatyper, nøkkelord som attributes og uniforms osv. Vi kommer tilbake til detaljene etter hvert. Vi starter med .html fila:

hellopoint/index.html

hellopoint/hellopoint.js

I html-fila ligger kildekoden til verteks- og fragment-shaderne som programmet bruker for å tegne punktet. Begge shaderfunksjonene består av en main() funksjon. Legg merke til at vi bruker OGSL ES versjon 3, derfor "#version 300 es" i starten av shaderfunksjonene (dette må ligge helt fremst i koden).

Javascript-koden bruker WebGL-APIet for å tegne et enkelt punkt på canvaset. Punktet består av EN verteks. Etter hvert vil vi tegne mer kompliserte modeller som består av mange vertekser og primitiver. Vi vil da også bruke såkalte vertex buffers for å lagre verteksene i GPUens minne. I eksemplet over er det kun EN verteks og det er derfor ikke nødvendig å bruke vertex buffers.

I main() opprettes et WebGLCanvas-objekt, som inneholder gl-objektet. Dette representerer WebGL-APIet og brukes, som vi ser, "overalt" i programmet til å utføre WebGL-kall.

Fra main() kalles initBaseShaders(). Denne funksjonen leser vertex- og fragmentshaderne fra html-fila. Ved hjelp av klassen WebGLShaders kompileres shaderkoden. Funksjonen returnerer et objekt som blant annet inneholder det kompilerte shaderProgram-objektet fra WebGLShaders-objektet.

Deretter kalles draw(), som starter med å renske skjermen/canvaset. Deretter aktiveres nevnte shaderProgram. Ved hjelp av funksjonene connectPositionAttribute() aktiveres aVertexPosition i verteksshaderen. Legg merke til at aVertexPosition er et "in"-parameter av type vec3 (vektor bestående av 3 elementer, slik [x, y, z]). vec3 er en GLSL-datatype. "in" er også et nøkkelord i GLSL versjon 3 (i eldre versjoner brukte man her "attribute" i stedet for "in").

Funksjonen connectPositionAttribute() kaller på GLSL-funksjonen gl.vertexAttrib3f(…) som levererer EN verteksposisjon, [0,0,0], til vertekshaderen. Vertekshaderen mottar denne via parametret aVertexPosition. I shaderen er denne av type vec3. Vi ser at denne utvides til en vec4 når vi setter gl_Position. Det siste elementet (w) settes her lik 1 (kommer tilbake til hvorfor dette gjøres).

Størrelsen på punktet bestemmes av gl_PointSize som her hardkodes til 50. Denne kunne alternativt blitt sendt inn til shaderen vha. gl.vertexAttrib1f(aVertexPointSize, 50.0). I så falle må man legge til et nytt "in"-parameter i verteksshaderen, aVertexPointSize, og sette gl_PointSize = aVertexPointSize.

I siste linje kalles drawArrays(gl.POINTS, 0, 1). Dette trigger verteksshaderen som setter gl_Position = aVertexPosition. Variabelen gl_Position er en spesiell GLSL-variabel og verteksshaderen har som hovedoppgave å gi denne variabelen en verdi. Som regel vil man gjøre diverse transformasjoner (matrisemultiplikasjoner) på aVertexPosition og tilordne resultatet til gl_Position. I eksemplet over utføres ingen transformasjon, gl_Position settes bare lik vec4(aVertexPosition, 1.0).

Variabelen gl_PointSize er også spesiell og brukes kun i tilfeller der man tegner punkter. Her settes gl_PointSize lik 50 og bestemmer hvor stort punktet blir. Som nevnt kunne man alternativt sendt inn punktstørrelsen som et parameter til verteksshaderen.

Etter at verteksshaderen har kjørt for alle vertekser (her kun en) vil det automatisk beregnes hvilke piksler som trengs for å tegne punktet. For hver piksel som inngår i punktet kjøres fragmentshaderen. Tenk foreløpig på et fragment som det samme som en piksel. Hovedoppgaven til fragmentshaderen er å returnere fargen til fragmentet. I eksemplet over returneres en «hardkodet» farge, som betyr at alle piksler får samme farge. Det er også mulig å knytte en farge til verteksene slik at fragmentene får farge basert på verteksfargene. Man kan også sende inn en enkelt farge til fragmentshaderen vha. et såkalt uniform-parameter. Mer om dette etter hvert.

Legg meke til at fragmentshaderen har en spesiell variabel, fragColor, som er en "out"-variabel og må settes til en fargeverdi. Dette er en vec4 (RGBA) og verdiene må være mellom 0.0 og 1.0. I eksemplet over settes fragColor lik vec4(1.0, 0.4, 1.0, 1.0), som betyr at fargen blir lilla.

Kjører man koden over i en standard nettleser vil det gi (omtrent) følgende resultat:

Med andre ord; et stort punkt i origo.

Tegne en trekant

En trekant består av tre vertekser og man bruker typisk et verteksarray og et verteksbuffer til dette. Følgende fem steg inngår for å få sendt et verteksarray til shaderen:

  • Opprett et bufferobjekt
    • gl.createBuffer()
  • Binde bufret – sette gjeldende buffer
    • gl.bindBuffer()
  • Skrive data/verteksarray til bufferet
    • gl.bufferData()
  • Kopler shaderparameter til bufferobjektet
    • gl.vertexAttribPointer()
  • «Enable» tilordninga (over):
    • gl.enableVertexAttribArray()

Følgende kodeeksempel viser dette:

hellotrianglesimple/index.html

Vi ser at denne refererer helloTrianglesimple() som ligger i helloTrianglesimple.js:

Resultate blir som vist under.

En enkel trekant bestående av tre vertekser
En enkel trekant bestående av tre vertekser

I funksjonen initBuffers() opprettes et VertexBuffer-objekt vha. et Float32Array-objekt, som igjen inneholder de tre verteksposisjonene. Bufret må «aktiveres» ved at det bindes til gl.ARRAY_BUFFER før data fra positions-arrayet kopieres til bufret vha. gl.bufferData(…).

I draw() koples aVertexPosition til data i position-bufret vha. vertexAttribPointer(…) før bufret til slutt «enables». Funksjonen vertexAttribPointer() forteller WebGL at data (posisjoner eller farger) skal hentes fra bufret som sist ble bundet vha. bindBuffer(…).

Funksjonsprototypen til vertexAtttribPointer() ser slik ut:

  gl.vertexAttribPointer(
    location,
    numComponents,
    typeOfData,
    normalizeFlag,
    strideToNextPieceOfData,
    offsetIntoBuffer);

Første parameter indikerer hvilken parameter i verteksshaderen som skal motta data, andre parameter angir antall float-verdier per verteks (for posisjon er denne 3 og for farger er den typisk 4). Det tredje parametret angir datatype som brukes (her gl.FLOAT). Nest siste parameter, strideToNextPieceOfData, er aktuell når man knytter andre verdier, som f.eks. farge eller normalvektor, til verteksene. Det siste parametret angir hvor i bufret første verteks ligger (her indeks 0).

Deretter sendes det inn en fargeverdi, uFragmentColor. Her indikerer prefikset "u" at dette er en såkalt «uniform» parameter. I dette tilfellet betyr det at alle fragmenter får samme farge. I fragmentshaderen ser vi at gl_FragColor settes lik uFragmentColor.

I draw() funksjonen renskes skjermen og drawArrays() kalles med gl.TRIANGLES som første parameter. Det andre parametret antyder hvor i bufret første verteks skal hentes fra (her indeks 0) mens det siste parametret indikerer hvor mange vertekser som skal tegnes. Det første parametret, gl.TRIANGLES, antyder hvordan verteksene skal sammenstilles. For hver verteks i som ligger i verteksbufret vil verteksshaderen kjøre. Aktuell verteks vil komme som innparameter via aVertexPosition. Videre ser vi at verteksshaderen setter gl_Position lik aVertexPosition.

Mer om bindBuffer()

Anta at vi har definert følgende vertekstabell:

  let positions = [-50.0, 50.0, 0.0,
                 -50.0,-50.0, 0.0,
                  50.0,-50.0, 0.0,
                  50.0, 50.0, 0.0];

  var myBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, myBuffer);   

Etter dette vil myBuffer være gjeldende buffer. Dette betyr at påfølgende bufferoperasjoner vil utføres vha. dette, inntil det "avbindes" eller at et annet buffer settes som gjeldende vha. et nytt bindBuffer() kall.

Det første parametret til bindBuffer() kan ha følgende to verdier:

  • gl.ARRAY_BUFFER
  • gl.ELEMENT_ARRAY_BUFFER.

Dersom bufret inneholder verteksdata brukes ARRAY_BUFFER mens ELEMENT_ARRAY_BUFFER brukes dersom det inneholder indeksdata.

En figur/modell kan bestå av mange trekanter der flere av trekantene kan dele samme verteks. I stedet for å lagre verteksdata flere ganger (i et verteksbuffer) lagres kun indeksen, i et indeksbuffer (mer om dette senere).

Deretter brukes typisk bufferData() for å fylle bufret med verteksdata:

   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

Etter dette bør man kople fra bufret vha.:

   gl.bindBuffer(gl.ARRAY_BUFFER, null);

Før man kan kalle på drawArrays() må man kople shaderparametre med til aktuelle bufferobjekter.

Kople shaderparametre til bufferobjekter

Etter at bufferobjektet er opprettet må dette koples til shaderparametre / attributter. Eksempler på shaderattributt er a_Position definert i verteksshaderen. Et verteksshaderattributt er assosiert med et, og kun et, bufferobjekt som vist i figuren under.

Figuren viser hvordan tre ulike shaderattributt, a_Position, a_Color og a_Normal er koplet til hvert sitt bufferobjekt.

Vi kopler shaderparametre til bufferobjektet slik:

  • Binde til aktuelt bufferobjekt.
  • Setter et shaderattributtet til å peke på bufret, dvs. aktiverte buffer.
  • Til slutt må attributtet gjøres aktivt («enable»).

Som allerede vist over aktiveres aktuelt buffer slik:

   gl.bindBuffer(gl.ARRAY_BUFFER, myBuffer);

Ved hjelp av WebGL-funksjonen vertexAttribPointer() koples shaderattributtet (f.eks. a_Position) til verteksbufret:

   let floatsPerVertex = 3;
   let posAttrib = gl.getAttribLocation(gl.program, 'a_Position');
   gl.vertexAttribPointer(posAttrib, floatsPerVertex, gl.FLOAT, false, 0, 0);

Funksjonen gl.getAttribLocation() returnerer shaderparametrets posisjon (i form av en integer) i shaderen.

Parametrene til vertexAttribPointer(index, size, type, norm, stride, offset) har følgende betydning:

  • Index (her posAttrib) er en indeks som refererer til attributtet a_Position definert i verteksshaderen. Dette får sin verdi vha. gl.getAttribLocation() som vist.
  • Size (her floatsPerVertex = 3) indikerer antall floats per verteks.
  • Type (her FLOAT) indikerer datatypen til verdiene i bufret.
  • Norm (her false). Vi bruker stort sett alltid false her.
  • Stride (her 0). Denne indikerer at verteksverdiene følger direkte etter hverandre. Dersom bufret også inneholder fargeverdier i tillegg til koordinatverdier vil dette parametret indikere «avstanden» mellom koordinatverdiene til hver enkelt verteks.
  • Offset (her 0). Indikerer startindeksen til hvor i bufret data skal hentes fra.

Deretter må «attributtpekeren», posAttrib, gjøres aktiv, slik:

gl.enableVertexAttribArray(posAttrib);

Verteksshaderen vil nå hente posisjonsdata fra assosiert verteksbuffer som vist i figuren under:

I dette tilfellet vil shaderen hente tre verdier i slengen pga. at verdien på andre parameter til vertexAttribPointer() er satt lik 3.

Se tilgjengelige kodeeksempler.

5 kommentarer:

  1. Nyttig guide. Hvis du er ute etter egnede mobilapplikasjonsutviklere, kan Cleveroad hjelpe deg med dette.

    SvarSlett
  2. Hei, du har denne setningen: "Funksjonen helloPoint() oppretter et WebGLCanvas-objekt og henter, ved. hjelp av dette, gl-objektet. Dette brukes til å utføre WebGL-kall." Tror du mener Funksjonen main() i helloPoint modulen. Det finnes ingen funksjon helloPoint() i hele git prosjektet.

    SvarSlett

  3. "I neste steg vil verteks- og fragmentshaderne lastes og kompileres i initShaders(…) funksjonen. Legg merke til bruk av klassen WebGLShader." - initBaseShaders(...)

    SvarSlett
  4. Takk for tilbakemelding. Teksten er justert.

    SvarSlett
  5. i eksempelkoden til threejs kan man istedetfor den lokale threejs bruke https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js dersom man vil enkelt teste koden.

    SvarSlett