WebGL-varjostimien käyttö WebAssembly -sovelluksessa

WebAssembly palaa nopeasti numeroiden murskaamiseen, pelimoottoreihin ja moniin muihin asioihin, mutta mikään ei voi täysin verrata GPU: lla toimivien shaderien äärimmäiseen rinnakkaistamiseen.

Tämä on erityisen syytä, jos haluat käsitellä kuvankäsittelyä. Yleensä tämä tapahtuu Webissä WebGL: n kautta, mutta miten pääsette sen sovellusliittymiin, kun käytät WebAssemblyä?

Asettaa

Tutkimme hyvin lyhyesti esimerkkihanketta, jonka jälkeen tarkastelemme kuinka kuva voidaan ladata tekstuurina. Sitten, erillisessä yhteydessä, käytämme kuvan reunatunnistimen GLSL-varjostinta.

Kaikki koodi on täällä repo-tilassa, jos haluat hypätä suoraan siihen. Huomaa, että sinun on palveltava tiedostoja palvelimen kautta, jotta WebAssembly toimii.

Edellytyksenä oletan, että WebAssembly-projekti on jo määritetty. Jos ei, voit tutustua tähän artikkeliin miten se tehdään, tai vain haarrua yllä linkitetty repo.

Alla olevan koodin demoimiseksi käytän html-perustiedostoa, joka on tarkoitettu vain kuvan lataamiseen, imageData-tiedoston hakemiseen ja WebAssembly-koodiin siirtämiseen ccallArrays-toiminnolla.

HTML-tiedosto esikatselun syöttökuvan kanssa

C ++ -koodin suhteen on olemassa emscripten.cpp-tiedosto, joka hallitsee ja reitittää menetelmäkutsuja Context.cpp-tiedostoon luotuihin konteksti-ilmentymiin. Context.cpp-tiedosto on rakennettu seuraavasti:

kokoelma

WebGL perustuu OpenGL ES (Embedded Systems) -spesifikaatioon, joka on OpenGL: n osajoukko, ja seuraa sitä. Kun käännät, emscripten kartoittaa koodimme WebGL-sovellusliittymään.

Voimme kohdistaa pari erilaista versiota. OpenGL ES 2 karttaa WebGL 1: een, kun taas OpenGL ES 3 karttaa WebGL 2: een. Oletusarvoisesti sinun pitäisi kohdistaa WebGL 2: een, koska se sisältää joitain ilmaisia ​​optimointeja ja parannuksia.

Tätä varten meidän on lisättävä lippu USE_WEBGL2 = 1 kompilaatioon.

Jos aiot käyttää joitain OpenGL ES -ominaisuuksia, joita ei ole WebGL-eritelmässä, voit käyttää FULL_ES2 = 1 ja / tai FULL_ES3 = 1 lippuja.

Jotta pystymme käsittelemään suuria kuvioita / kuvia, voimme lisätä myös lipun ALLLOW_MEMORY_GROWTH = 1. Tämä poistaa WebAssembly-ohjelman muistirajan joidenkin optimointien kustannuksella.

Jos tiedät etukäteen, kuinka paljon muistia tarvitset, voit sen sijaan käyttää TOTAL_MEMORY = X -lippua, missä X on muistin koko.

Joten päädymme jotain tällaista:

emcc -o ./dist/appWASM.js ./dev/cpp/emscripten.cpp -O3 -s ALLOW_MEMORY_GROWTH = 1 -s USE_WEBGL2 = 1 -s FULL_ES3 = 1 -s WASM = 1 -s NO_EXIT_RUNTIME = 1-std = c ++ 1z

Lopuksi tarvitsemme seuraavaa tuontia koodissamme:

#sisältää 
# sisällytä 
# sisällytä 
# sisällytä 
ulkoinen "C" {
   #include "html5.h" // emscripten-moduuli
}

täytäntöönpano

Jos sinulla on aikaisempaa kokemusta WebGL: stä tai OpenGL: stä, tämä bitti saattaa tuntua tutulta.

Kun kirjoitat OpenGL: ää, sovellusliittymä ei toimi, ennen kuin olet luonut kontekstin. Tämä tehdään yleensä käyttöympäristökohtaisilla sovellusliittymillä. Verkkoa ei kuitenkaan ole sidottu käyttöympäristöön, ja voimme sen sijaan käyttää OpenGL ES: ään integroitua sovellusliittymää.

Suurin osa jalkatyöstä voidaan kuitenkin helpommin toteuttaa käyttämällä emscriptenin sovellusliittymiä html5.h-tiedostossa. Olemme kiinnostuneita toiminnoista:

  • emscripten_webgl_create_context - Tämä pikaistaa kontekstin annetulle kankaalle ja määritteille
  • emscripten_webgl_destroy_context - Tätä tarvitaan muistin puhdistamiseen kontekstitapahtumien tuhoamisen yhteydessä
  • emscripten_webgl_make_context_current - Tämä määrittää ja vaihtaa minkä kontekstin WebGL tuottaa

Luo konteksti

Aloittaaksesi käyttöönotto, sinun on ensin luotava kankaalelementit JavaScript-koodiin. Sitten, kun käytät emscripten_webgl_create_context-toimintoa, siirrät kankaan tunnuksen ensimmäiseksi parametriksi, jolloin mahdolliset kokoonpanot ovat toiset. Emscripten_webgl_make_context_current-toimintoa käytetään uuden kontekstin asettamiseen nykyisenä käytetyksi.

Seuraavaksi sekä vertex shader (koordinaattien määrittämiseksi) että fragment fragment shader (kunkin pikselin värin laskemiseksi) kootaan, ja ohjelma rakennetaan.

Lopuksi varjostimet kiinnitetään ohjelmaan, joka sitten yhdistetään ja validoidaan.

Vaikka se kuulostaa paljon, tämän koodi on seuraava:

Shader-kokoaminen tehdään CompileShader-helper-toiminnossa, joka suorittaa kokoamisen ja tulostaa kaikki virheet:

Luo varjostin

Tämän esimerkin varjokoodi on minimaalinen, ja se vain kartoittaa jokaisen pikselin itseensä kuvan näyttämiseksi tekstuurina:

Voit käyttää kankaan kontekstia JavaScript-muodossa C ++ -koodin lisäksi, mutta sen on oltava samantyyppistä, 'webgl2'. Useiden kontekstityyppien määritteleminen ei tee mitään vain käytettäessä JavaScriptiä, jos teet sen ennen webgl2-kontekstin luomista WebAssemblyssä, se aiheuttaa virheen, kun koodin suorittaminen pääsee sinne.

Tekstuurin lataaminen

Ensimmäinen tehtävä shaderin asettamisessa on soittaa emscripten_webgl_make_context_currentfunction -toimintoon varmistaaksemme, että käytämme edelleen oikeaa asiayhteyttä, ja glUseProgramto varmistaa, että käytämme oikeaa ohjelmaa.

Seuraavaksi saamme GLSL-muuttujien indeksit (samanlaisia ​​kuin osoittimen saaminen )glGetAttribLocation ja glGetUniformLocation -toimintojen kautta, joten voimme osoittaa omat arvot näihin paikkoihin. Tätä varten käytetty toiminto riippuu arvotyypistä.

Esimerkiksi kokonaisluku, kuten pintarakenteen sijainti, tarvitsee glUniform1i, kun taas kelluu tarvitsee glUniform1f. Tämä on hyvä resurssi nähdäksesi mitä toimintoa sinun tulee käyttää.

Seuraavaksi saamme tekstuuriobjektin glGenTextures-sovelluksen kautta, määritämme sen aktiiviseksi tekstuuriksi ja lataamme imageData-puskurin. Kärki- ja indeksipuskurit sitotaan sitten tekstuurin rajojen asettamiseksi kankaan täyttämiseksi.

Lopuksi tyhjennämme olemassa olevan sisällön, määrittelemme jäljellä olevat muuttujamme tiedoilla ja vedämme kankaalle.

Tekstuuria ladataan

Tunnista reunat shaderilla

Jos haluat lisätä toisen kontekstin, jossa reunatunnistus tehdään, lataamme eri fragmenttivarjostimen (joka käyttää Sobel-suodatinta) ja sitomme leveyden ja korkeuden lisämuuttujina koodiin.

Voit valita eri fragmenttivarjostimien välillä eri konteksteihin lisäämällä vain if-else-lauseen rakentajaan, kuten näin:

Ja ladataksesi leveys- ja korkeusmuuttujat, lisäämme seuraavat ajofunktioon:

Jos törmäät virheen kanssa, joka on samanlainen kuin ERROR: GL_INVALID_OPERATION: glUniform1i: väärä yhdenmukainen funktio tyypille, annetulle muuttujalle on yhteensopimaton osoitusfunktio.

Yksi asia, jota on kiinnitettävä huomiota lähettäessäsi imageData, on käyttää oikeaa kasaan, allekirjoittamatonta kokonaislukua (Uint8Array -tyyppinen taulukko). Voit oppia lisää niistä täällä, mutta jos käytät ccallArray-toimintoa, aseta heapIn-asetukseksi HEAPU8, kuten yllä.

Jos tyyppi ei ole oikea, tekstuuri latautuu edelleen, mutta näet outoja renderointeja, kuten nämä:

johtopäätös

Olemme käyneet läpi mini-"Hello World!" - tyyliprojektin osoittaaksesi kuinka ladata tekstuurit ja soveltaa niihin GLSL-varjostimia WebAssembly -sovelluksessa. Täydellinen koodi isännöidään täällä GitHubissa lisätietoja varten.

Todelliseen projektiin kannattaa ehkä lisätä vielä joitain virheiden käsittelyjä. Poistin sen täällä selvyyden vuoksi.

Voi olla myös tehokkaampaa (yllä olevassa esimerkissä) jakaa tietoja, kuten imageData-rakenne, kontekstien välillä. Voit lukea lisää tästä ja lisää täältä.

Jos haluat lisätietoja, voit tarkistaa tämän linkin yleisten virheiden varalta tai etsiä joitain esittelyprojekteja emscriptenin glbook-kansiosta GitHubissa.

Jos haluat nähdä WebGL: n käytettävän WebAssembly-projektissa, voit tutustua Web-pohjaisen syvän oppimisen viitekehyksen jsNet dev-haaraan, jossa työskentelen siirtämään raskaampia laskelmia varjostimiin seuraavien viikkojen aikana (tuki WebGL-laskennalle) Shaderit OpenGL ES 3.1: n kautta eivät tule tarpeeksi pian ).

Päivittää

Voit nähdä, miltä varjostimien avulla käytettävä GPU-laskenta näyttäisi WebAssemblyltä, tarkistamalla GPGPU: n, pienen kirjaston, jossa työskentelen, repo sekä JavaScript- että WebAssembly-versioilla.