Syvällinen ammattilaisopas WebGL-tekstuuriresurssien käytön ymmärtämiseen ja hallintaan. Opi kuinka shaderit tarkastelevat ja lukevat GPU-dataa perusteista edistyneisiin tekniikoihin.
GPU:n tehon vapauttaminen verkossa: Syväsukellus WebGL-tekstuuriresurssien käyttöön
Nykyaikainen web on visuaalisesti rikas maisema, jossa interaktiiviset 3D-mallit, henkeäsalpaavat datavisualisoinnit ja immersiiviset pelit toimivat sujuvasti selaimissamme. Tämän vallankumouksen ytimessä on WebGL, tehokas JavaScript-API, joka tarjoaa suoran, matalan tason rajapinnan grafiikkaprosessoriin (GPU). Vaikka WebGL avaa mahdollisuuksien maailman, sen hallitseminen vaatii syvällistä ymmärrystä siitä, miten CPU ja GPU kommunikoivat ja jakavat resursseja. Yksi perustavanlaatuisimmista ja kriittisimmistä näistä resursseista on tekstuuri.
Natiiveja grafiikka-API:eja, kuten DirectX, Vulkan tai Metal, käyttäville kehittäjille termi "Shader Resource View" (SRV) on tuttu käsite. SRV on pohjimmiltaan abstraktio, joka määrittelee, miten shader voi lukea resurssista, kuten tekstuurista. Vaikka WebGL:ssä ei ole nimenomaisesti "Shader Resource View" -nimistä API-objektia, sen taustalla oleva konsepti on täysin keskeinen sen toiminnalle. Tämä artikkeli selvittää, miten WebGL-tekstuureja luodaan, hallitaan ja lopulta käytetään shadereiden toimesta, tarjoten sinulle mentaalimallin, joka on linjassa tämän modernin grafiikkaparadigman kanssa.
Matkaamme perusteista, mitä tekstuuri todella edustaa, tarvittavan JavaScript- ja GLSL (OpenGL Shading Language) -koodin kautta edistyneisiin tekniikoihin, jotka nostavat reaaliaikaiset grafiikkasovelluksesi uudelle tasolle. Tämä on kattava oppaasi tekstuurien shader-resurssinäkymän WebGL-vastaavuuteen.
Grafiikkaputki: Missä tekstuurit heräävät eloon
Ennen kuin voimme käsitellä tekstuureja, meidän on ymmärrettävä niiden rooli. GPU:n päätehtävä grafiikassa on suorittaa sarja vaiheita, jotka tunnetaan renderöintiputkena. Yksinkertaistettuna tämä putki ottaa verteksidataa (3D-mallin pisteitä) ja muuntaa sen lopullisiksi värillisiksi pikseleiksi, jotka näet näytölläsi.
Kaksi keskeistä ohjelmoitavaa vaihetta WebGL-putkessa ovat:
- Verteksivarjostin: Tämä ohjelma suoritetaan kerran jokaiselle geometriasi verteksille. Sen päätehtävä on laskea kunkin verteksin lopullinen sijainti näytöllä. Se voi myös välittää dataa, kuten tekstuurikoordinaatteja, eteenpäin putkessa.
- Fragmenttivarjostin (tai pikselivarjostin): Kun GPU on määrittänyt, mitkä näytön pikselit kolmio peittää (prosessi nimeltä rasterointi), fragmenttivarjostin suoritetaan kerran jokaiselle näistä pikseleistä (tai fragmenteista). Sen päätehtävä on laskea kyseisen pikselin lopullinen väri.
Tässä kohtaa tekstuurit astuvat näyttämölle. Fragmenttivarjostin on yleisin paikka käyttää eli "samplata" tekstuuria pikselin värin, kiiltävyyden, karheuden tai minkä tahansa muun pintaominaisuuden määrittämiseksi. Tekstuuri toimii massiivisena hakutaulukkona fragmenttivarjostimelle, joka suoritetaan rinnakkain huimilla nopeuksilla GPU:lla.
Mitä on tekstuuri? Enemmän kuin vain kuva
Arkikielessä "tekstuuri" tarkoittaa esineen pinnan tuntua. Tietokonegrafiikassa termi on tarkempi: tekstuuri on jäsennelty tietojoukko, joka on tallennettu GPU:n muistiin ja johon shaderit voivat päästä tehokkaasti käsiksi. Vaikka tämä data on useimmiten kuvadataa (pikselien värejä, jotka tunnetaan myös texeleinä), on kriittinen virhe rajoittaa ajattelua vain siihen.
Tekstuuri voi tallentaa lähes mitä tahansa numeerista dataa, mitä voit kuvitella:
- Albedo/diffuusiokartat: Yleisin käyttötarkoitus, määrittää pinnan perusvärin.
- Normaalikartat: Tallentavat vektoridataa, joka jäljittelee monimutkaisia pinnan yksityiskohtia ja valaistusta, saaden matalan polygonitason mallin näyttämään uskomattoman yksityiskohtaiselta.
- Korkeuskartat: Tallentavat yksikanavaista harmaasävydataa luodakseen siirtymä- tai parallaksiefektejä.
- PBR-kartat: Fysikaalisesti perustuvassa renderöinnissä (Physically Based Rendering) erilliset tekstuurit tallentavat usein metallisuuden, karheuden ja ambient occlusion -arvoja.
- Hakutaulukot (LUT): Käytetään värimäärittelyyn ja jälkikäsittelyefekteihin.
- Mielivaltainen data GPGPU:lle: Yleiskäyttöisessä GPU-ohjelmoinnissa (General-Purpose GPU programming) tekstuureja voidaan käyttää 2D-taulukkoina paikkojen, nopeuksien tai simulaatiodatan tallentamiseen fysiikkaa tai tieteellistä laskentaa varten.
Tämän monipuolisuuden ymmärtäminen on ensimmäinen askel GPU:n todellisen tehon vapauttamiseen.
Silta: Tekstuurien luominen ja konfigurointi WebGL API:lla
CPU (joka suorittaa JavaScript-koodiasi) ja GPU ovat erillisiä yksiköitä omilla muisteillaan. Jotta voit käyttää tekstuuria, sinun on orkestroitava sarja vaiheita WebGL API:n avulla luodaksesi resurssin GPU:lle ja ladataksesi datasi sinne. WebGL on tilakone, mikä tarkoittaa, että asetat ensin aktiivisen tilan, ja sen jälkeen seuraavat komennot toimivat kyseisessä tilassa.
Vaihe 1: Luo tekstuurin kahva (Handle)
Ensin sinun on pyydettävä WebGL:ää luomaan tyhjä tekstuuri-objekti. Tämä ei vielä varaa muistia GPU:lta; se palauttaa vain kahvan tai tunnisteen, jota käytät viittaamaan tähän tekstuuriin tulevaisuudessa.
// Hae WebGL-renderöintikonteksti canvas-elementistä
const canvas = document.getElementById('myCanvas');
const gl = canvas.getContext('webgl2');
// Luo tekstuuri-objekti
const myTexture = gl.createTexture();
Vaihe 2: Sido tekstuuri (Bind)
Jotta voit työskennellä juuri luodun tekstuurin kanssa, sinun on sidottava se tiettyyn kohteeseen WebGL-tilakoneessa. Standardille 2D-kuvalle kohde on `gl.TEXTURE_2D`. Sitominen tekee tekstuuristasi "aktiivisen" kaikille myöhemmille tekstuuritoiminnoille kyseisessä kohteessa.
// Sido tekstuuri TEXTURE_2D-kohteeseen
gl.bindTexture(gl.TEXTURE_2D, myTexture);
Vaihe 3: Lataa tekstuuridata
Tässä vaiheessa siirrät datasi CPU:lta (esim. `HTMLImageElement`, `ArrayBuffer` tai `HTMLVideoElement` -elementistä) sidottuun tekstuuriin liittyvään GPU-muistiin. Pääasiallinen funktio tähän on `gl.texImage2D`.
Katsotaan yleistä esimerkkiä kuvan lataamisesta ``-tagista:
const image = new Image();
image.src = 'path/to/my-image.jpg';
image.onload = () => {
// Kun kuva on ladattu, voimme ladata sen GPU:lle
// Sido tekstuuri uudelleen varmuuden vuoksi, jos jokin toinen tekstuuri on sidottu muualla
gl.bindTexture(gl.TEXTURE_2D, myTexture);
const level = 0; // Mipmap-taso
const internalFormat = gl.RGBA; // GPU:lle tallennettava formaatti
const srcFormat = gl.RGBA; // Lähdedatan formaatti
const srcType = gl.UNSIGNED_BYTE; // Lähdedatan tyyppi
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
srcFormat, srcType, image);
// ... jatka tekstuurin konfiguroinnilla
};
`texImage2D`:n parametrit antavat sinulle hienojakoisen hallinnan siitä, miten data tulkitaan ja tallennetaan, mikä on ratkaisevan tärkeää edistyneille datatekstuureille.
Vaihe 4: Määritä samplerin tila
Datan lataaminen ei riitä. Meidän on myös kerrottava GPU:lle, miten siitä luetaan tai "samplataan". Mitä pitäisi tapahtua, jos shader pyytää pistettä kahden texelin välistä? Entä jos se pyytää koordinaattia standardin `[0.0, 1.0]` -alueen ulkopuolelta? Tämä konfiguraatio on samplerin ydin.
WebGL 1:ssä ja 2:ssa samplerin tila on osa itse tekstuuri-objektia. Määrität sen käyttämällä `gl.texParameteri`.
Suodatus: Suurennuksen ja piennennyksen käsittely
Kun tekstuuri renderöidään suurempana kuin sen alkuperäinen resoluutio (suurennus) tai pienempänä (pienennys), GPU tarvitsee säännön palautettavalle värille.
gl.TEXTURE_MAG_FILTER: Suurennukselle.gl.TEXTURE_MIN_FILTER: Pienennykselle.
Kaksi pääasiallista tilaa ovat:
gl.NEAREST: Tunnetaan myös nimellä point sampling. Se nappaa yksinkertaisesti lähimmän texelin pyydettyyn koordinaattiin. Tämä johtaa lohkomaiseen, pikselöityyn ulkonäköön, mikä voi olla toivottavaa retrotyylisessä taiteessa, mutta ei usein ole sitä, mitä halutaan realistisessa renderöinnissä.gl.LINEAR: Tunnetaan myös nimellä bilineaarinen suodatus. Se ottaa neljä lähintä texeliä pyydettyyn koordinaattiin ja palauttaa painotetun keskiarvon perustuen koordinaatin läheisyyteen kuhunkin. Tämä tuottaa pehmeämmän, mutta hieman sumeamman tuloksen.
// Terävään, pikselöityyn ilmeeseen kun zoomataan sisään
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// Pehmeään, sekoitettuun ilmeeseen
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
Kierrätys (Wrapping): Rajojen ulkopuolisten koordinaattien käsittely
TEXTURE_WRAP_S (vaakasuora, tai U) ja TEXTURE_WRAP_T (pystysuora, tai V) -parametrit määrittelevät käyttäytymisen koordinaateille, jotka ovat [0.0, 1.0]-alueen ulkopuolella.
gl.REPEAT: Tekstuuri toistaa itseään.gl.CLAMP_TO_EDGE: Koordinaatti rajoitetaan, ja reunatekseliä toistetaan.gl.MIRRORED_REPEAT: Tekstuuri toistuu, mutta joka toinen toisto on peilattu.
// Toista tekstuuria vaaka- ja pystysuunnassa
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
Mipmappaus: Avain laatuun ja suorituskykyyn
Kun teksturoitu objekti on kaukana, yksi pikseli näytöllä saattaa kattaa suuren alueen tekstuurista. Jos käytämme standardisuodatusta, GPU:n on valittava yksi tai neljä texeliä sadoista, mikä johtaa välkkyviin artefakteihin ja aliasointiin. Lisäksi korkearesoluutioisen tekstuuridatan noutaminen kaukaiselle objektille on muistikaistan haaskausta.
Ratkaisu on mipmappaus. Mipmap on ennalta laskettu sarja alkuperäisen tekstuurin pienennettyjä versioita. Renderöinnin aikana GPU voi valita sopivimman mip-tason objektin etäisyyden perusteella, mikä parantaa merkittävästi sekä visuaalista laatua että suorituskykyä.
Voit luoda nämä mip-tasot helposti yhdellä komennolla perustekstuurisi lataamisen jälkeen:
gl.generateMipmap(gl.TEXTURE_2D);
Jotta voit käyttää mipmappeja, sinun on asetettava pienennyssuodatin yhteen mipmappeja tukevista tiloista:
gl.LINEAR_MIPMAP_NEAREST: Valitsee lähimmän mip-tason ja soveltaa sitten lineaarista suodatusta kyseisellä tasolla.gl.LINEAR_MIPMAP_LINEAR: Valitsee kaksi lähintä mip-tasoa, suorittaa lineaariset suodatukset molemmissa ja interpoloi sitten lineaarisesti tulosten välillä. Tätä kutsutaan trilineaariseksi suodatukseksi ja se tarjoaa korkeimman laadun.
// Ota käyttöön korkealaatuinen trilineaarinen suodatus
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
Tekstuurien käyttö GLSL:ssä: Shaderin näkökulma
Kun tekstuurimme on konfiguroitu ja sijaitsee GPU:n muistissa, meidän on tarjottava shaderillemme tapa päästä siihen käsiksi. Tässä kohtaa käsitteellinen "Shader Resource View" todella astuu kuvaan.
Uniform Sampler
GLSL-fragmenttivarjostimessasi ilmoitat erityyppisen `uniform`-muuttujan edustamaan tekstuuria:
#version 300 es
precision mediump float;
// Uniform sampler, joka edustaa tekstuuriresurssinäkymäämme
uniform sampler2D u_myTexture;
// Syötteenä tekstuurikoordinaatit verteksivarjostimelta
in vec2 v_texCoord;
// Tämän fragmentin ulostuloväri
out vec4 outColor;
void main() {
// Lue (sample) tekstuurista annetuilla koordinaateilla
outColor = texture(u_myTexture, v_texCoord);
}
On elintärkeää ymmärtää, mikä `sampler2D` on. Se ei ole itse tekstuuridataa. Se on läpinäkymätön kahva, joka edustaa kahden asian yhdistelmää: viittausta tekstuuridataan ja sille määritettyä samplerin tilaa (suodatus, kierrätys).
JavaScriptin ja GLSL:n yhdistäminen: Tekstuuriyksiköt
Miten siis yhdistämme `myTexture`-objektin JavaScriptissämme `u_myTexture`-uniformiin shaderissamme? Tämä tehdään välikäden kautta, jota kutsutaan tekstuuriyksiköksi.
GPU:lla on rajallinen määrä tekstuuriyksiköitä (rajan voi tarkistaa komennolla `gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)`), jotka ovat kuin paikkoja, joihin tekstuuri voidaan asettaa. Kaiken yhdistämisprosessi ennen piirtokutsua on kolmivaiheinen tanssi:
- Aktivoi tekstuuriyksikkö: Valitset, mitä yksikköä haluat käyttää. Ne on numeroitu alkaen nollasta.
- Sido tekstuurisi: Sidot tekstuuri-objektisi tällä hetkellä aktiiviseen yksikköön.
- Kerro shaderille: Päivität `sampler2D`-uniformin valitsemasi tekstuuriyksikön kokonaislukuindeksillä.
Tässä on täydellinen JavaScript-koodi renderöintiluuppia varten:
// Hae uniform-muuttujan sijainti shader-ohjelmassa
const textureUniformLocation = gl.getUniformLocation(myShaderProgram, "u_myTexture");
// --- Renderöintiluupissasi ---
function draw() {
const textureUnitIndex = 0; // Käytetään tekstuuriyksikköä 0
// 1. Aktivoi tekstuuriyksikkö
gl.activeTexture(gl.TEXTURE0 + textureUnitIndex);
// 2. Sido tekstuuri tähän yksikköön
gl.bindTexture(gl.TEXTURE_2D, myTexture);
// 3. Kerro shaderin samplerille, että sen tulee käyttää tätä tekstuuriyksikköä
gl.uniform1i(textureUniformLocation, textureUnitIndex);
// Nyt voimme piirtää geometrian
gl.drawArrays(gl.TRIANGLES, 0, numVertices);
}
Tämä järjestys luo linkin oikein: shaderin `u_myTexture`-uniform osoittaa nyt tekstuuriyksikköön 0, joka tällä hetkellä sisältää `myTexture`-tekstuurin kaikkine konfiguroituine datan ja samplerin asetuksineen. GLSL:n `texture()`-funktio tietää nyt tarkalleen, mistä resurssista lukea.
Edistyneet tekstuurien käyttötavat
Kun perusteet on käsitelty, voimme tutustua tehokkaampiin tekniikoihin, jotka ovat yleisiä modernissa grafiikassa.
Moniteksturointi
Usein yksi pinta tarvitsee useita tekstuurikarttoja. PBR:ää varten saatat tarvita värikartan, normaalikartan ja karheus/metallisuuskartan. Tämä saavutetaan käyttämällä useita tekstuuriyksiköitä samanaikaisesti.
GLSL-fragmenttivarjostin:
uniform sampler2D u_albedoMap;
uniform sampler2D u_normalMap;
uniform sampler2D u_roughnessMap;
in vec2 v_texCoord;
void main() {
vec3 albedo = texture(u_albedoMap, v_texCoord).rgb;
vec3 normal = texture(u_normalMap, v_texCoord).rgb;
float roughness = texture(u_roughnessMap, v_texCoord).r;
// ... suorita monimutkaisia valaistuslaskelmia näillä arvoilla ...
}
JavaScript-asetukset:
// Sido albedo-kartta tekstuuriyksikköön 0
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, albedoTexture);
gl.uniform1i(albedoLocation, 0);
// Sido normaalikartta tekstuuriyksikköön 1
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, normalTexture);
gl.uniform1i(normalLocation, 1);
// Sido karheuskartta tekstuuriyksikköön 2
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, roughnessTexture);
gl.uniform1i(roughnessLocation, 2);
// ... sitten piirrä ...
Tekstuurit datana (GPGPU)
Käyttääksesi tekstuureja yleiskäyttöiseen laskentaan, tarvitset usein enemmän tarkkuutta kuin standardi 8 bittiä per kanava (`UNSIGNED_BYTE`). WebGL 2 tarjoaa erinomaisen tuen liukulukutekstuureille.
Tekstuuria luodessa määrittäisit erilaisen sisäisen formaatin ja tyypin:
// 32-bittiselle liukulukutekstuurille, jossa on 4 kanavaa (RGBA)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, width, height, 0,
gl.RGBA, gl.FLOAT, myFloat32ArrayData);
Keskeinen tekniikka GPGPU:ssa on laskennan tuloksen renderöinti toiseen tekstuuriin käyttämällä Framebuffer Objectia (FBO). Tämä mahdollistaa monimutkaisten, monivaiheisten simulaatioiden (kuten nestedynamiikan tai hiukkasjärjestelmien) luomisen kokonaan GPU:lla, malli jota kutsutaan usein "ping-pongingiksi" kahden tekstuurin välillä.
Cube Mapit ympäristökartoitukseen (Environment Mapping)
Realististen heijastusten tai skyboxien luomiseksi käytämme cube mapia, joka koostuu kuudesta 2D-tekstuurista kuution sivuilla. API on hieman erilainen.
- Sidontakohde: `gl.TEXTURE_CUBE_MAP`
- GLSL Sampler-tyyppi: `samplerCube`
- Hakuvektori: 2D-koordinaattien sijaan luet siitä 3D-suuntavektorilla.
GLSL-esimerkki heijastukselle:
uniform samplerCube u_skybox;
in vec3 v_reflectionVector;
void main() {
// Lue (sample) cube mapista suuntavektorilla
vec4 reflectionColor = texture(u_skybox, v_reflectionVector);
// ...
}
Suorituskykyyn liittyviä huomioita ja parhaita käytäntöjä
- Minimoi tilanmuutokset: Kutsut kuten `gl.bindTexture()` ovat suhteellisen kalliita. Optimaalisen suorituskyvyn saavuttamiseksi ryhmittele piirtokutsusi materiaalin mukaan. Renderöi kaikki objektit, jotka käyttävät samaa tekstuurijoukkoa, ennen kuin vaihdat uuteen joukkoon.
- Käytä pakattuja formaatteja: Raaka tekstuuridata kuluttaa merkittävästi VRAM-muistia ja muistikaistaa. Käytä laajennuksia pakatuille formaateille, kuten S3TC, ETC tai ASTC. Nämä formaatit antavat GPU:n pitää tekstuuridatan pakattuna muistissa, mikä tarjoaa valtavia suorituskykyhyötyjä erityisesti muistirajoitteisilla laitteilla.
- Kahden potenssiin -mitat (POT): Vaikka WebGL 2:lla on hyvä tuki ei-kahden-potenssiin (NPOT) -tekstuureille, on edelleen olemassa reunatapauksia, erityisesti WebGL 1:ssä, joissa POT-tekstuurit (esim. 256x256, 512x512) vaaditaan mipmappauksen ja tiettyjen kierrätystilojen toimimiseksi. POT-mittojen käyttö on edelleen turvallinen paras käytäntö.
- Käytä Sampler-objekteja (WebGL 2): WebGL 2 esitteli Sampler-objektit. Nämä mahdollistavat samplerin tilan (suodatus, kierrätys) erottamisen tekstuuri-objektista. Voit luoda muutamia yleisiä sampler-konfiguraatioita (esim. "repeating_linear", "clamped_nearest") ja sitoa niitä tarpeen mukaan sen sijaan, että konfiguroisit jokaisen tekstuurin uudelleen. Tämä on tehokkaampaa ja paremmin linjassa modernien grafiikka-API:en kanssa.
Tulevaisuus: Katsaus WebGPU:hun
WebGL:n seuraaja, WebGPU, tekee näistä käsitteistä vieläkin selkeämpiä ja jäsennellympiä. WebGPU:ssa erilliset roolit on määritelty selkeästi omilla API-objekteillaan:
GPUTexture: Edustaa raakaa tekstuuridataa GPU:lla.GPUSampler: Objekti, joka määrittelee ainoastaan samplerin tilan (suodatus, kierrätys jne.).GPUTextureView: Tämä on kirjaimellisesti "Shader Resource View". Se määrittelee, miten shader näkee tekstuuridatan (esim. 2D-tekstuurina, tekstuuritaulukon yhtenä kerroksena, tiettynä mip-tasona jne.).
Tämä selkeä erottelu vähentää API:n monimutkaisuutta ja estää kokonaisia virheluokkia, jotka ovat yleisiä WebGL:n tilakonemallissa. Käsitteellisten roolien ymmärtäminen WebGL:ssä – tekstuuridata, samplerin tila ja shaderin pääsy – on täydellinen valmistautuminen siirtymiseen WebGPU:n tehokkaampaan ja vankempaan arkkitehtuuriin.
Yhteenveto
Tekstuurit ovat paljon enemmän kuin staattisia kuvia; ne ovat pääasiallinen mekanismi suurten, jäsenneltyjen tietomassojen syöttämiseksi GPU:n massiivisen rinnakkaisille prosessoreille. Niiden käytön hallitseminen edellyttää selkeää ymmärrystä koko putkesta: CPU-puolen orkestroinnista WebGL JavaScript API:lla resurssien luomiseksi, sitomiseksi, lataamiseksi ja konfiguroimiseksi, sekä GPU-puolen pääsystä GLSL-shadereiden sisällä samplereiden ja tekstuuriyksiköiden kautta.
Sisäistämällä tämän virtauksen – "Shader Resource View'n" WebGL-vastaavuuden – siirryt pelkkien kuvien asettamisesta kolmioille. Saat kyvyn toteuttaa edistyneitä renderöintitekniikoita, suorittaa nopeita laskutoimituksia ja todella valjastaa GPU:n uskomattoman tehon suoraan mistä tahansa modernista verkkoselaimesta. Canvas on sinun komennossasi.