Tutustu WebGL:n Mesh Shader -putkeen. Opi, miten Task Amplification mahdollistaa massiivisen reaaliaikaisen geometrian luonnin ja edistyneen karsinnan verkkografiikassa.
Geometrian Vapauttaminen: Syväsukellus WebGL:n Mesh Shader -tehtävän vahvistusputkeen
Verkko ei ole enää staattinen, kaksiulotteinen väline. Se on kehittynyt elinvoimaiseksi alustaksi rikkaille, immersiivisille 3D-kokemuksille, aina henkeäsalpaavista tuotekonfiguraattoreista ja arkkitehtuurisista visualisoinneista monimutkaisiin datamalleihin ja täysimittaisiin peleihin. Tämä kehitys asettaa kuitenkin ennennäkemättömiä vaatimuksia grafiikkaprosessorille (GPU). Vuosikymmenten ajan standardi reaaliaikainen grafiikkaputki, vaikka tehokas, on näyttänyt ikänsä, toimien usein pullonkaulana modernien sovellusten vaatimalle geometriselle monimutkaisuudelle.
Esittelyssä Mesh Shader -putki, paradigmaa muuttava ominaisuus, joka on nyt saatavilla verkossa WEBGL_mesh_shader-laajennuksen kautta. Tämä uusi malli muuttaa perustavanlaatuisesti sitä, miten ajattelemme ja käsittelemme geometriaa GPU:lla. Sen ytimessä on voimakas käsite: tehtävän vahvistus (Task Amplification). Tämä ei ole vain asteittainen päivitys; se on mullistava harppaus, joka siirtää aikataulutuksen ja geometrian luontilogiikan suoraan CPU:lta GPU:n erittäin rinnakkaiselle arkkitehtuurille, avaten mahdollisuuksia, jotka olivat aiemmin epäkäytännöllisiä tai mahdottomia verkkoselaimessa.
Tämä kattava opas vie sinut syvälle mesh shader -geometriaputkeen. Tutustumme sen arkkitehtuuriin, ymmärrämme Task- ja Mesh-shaderien erilliset roolit ja paljastamme, miten tehtävän vahvistusta voidaan hyödyntää visuaalisesti upeiden ja suorituskykyisten verkkosovellusten seuraavan sukupolven rakentamiseen.
Pikakertaus: Perinteisen geometriaputken rajoitukset
Jotta mesh-shaderien innovaatioita todella arvostaisi, meidän on ensin ymmärrettävä putki, jonka ne korvaavat. Vuosikymmenten ajan reaaliaikaista grafiikkaa on hallinnut suhteellisen kiinteätoiminen putki:
- Vertex Shader: Käsittelee yksittäisiä verteksejä muuntaen ne näytön koordinaatistoon.
- (Valinnainen) Tessellation Shaders: Jakaa geometrian "laikkuja" hienomman yksityiskohdan luomiseksi.
- (Valinnainen) Geometry Shader: Voi luoda tai tuhota primitiivejä (pisteitä, viivoja, kolmioita) lennossa.
- Rasterizer: Muuntaa primitiivit pikseleiksi.
- Fragment Shader: Laskee kunkin pikselin lopullisen värin.
Tämä malli palveli meitä hyvin, mutta siinä on luontaisia rajoituksia, erityisesti kun kohtaukset monimutkaistuvat:
- CPU-sidotut piirtokutsut: CPU:lla on valtava tehtävä selvittää tarkalleen, mitä on piirrettävä. Tähän sisältyy frustum culling (kameran näkymän ulkopuolella olevien kohteiden poistaminen), occlusion culling (muiden kohteiden peittämien kohteiden poistaminen) ja yksityiskohtaisuuden tason (LOD) järjestelmien hallinta. Miljoonien kohteiden kohtauksessa tämä voi johtaa siihen, että CPU:sta tulee ensisijainen pullonkaula, joka ei pysty syöttämään nälkäistä GPU:ta tarpeeksi nopeasti.
- Jäykkä syöttörakenne: Putki on rakennettu jäykän syötteen käsittelymallin ympärille. Input Assembler syöttää verteksit yksitellen, ja shaderit käsittelevät ne suhteellisen rajoitetusti. Tämä ei ole ihanteellista moderneille GPU-arkkitehtuureille, jotka ovat erinomaisia yhtenäisessä, rinnakkaisessa datankäsittelyssä.
- Tehoton vahvistus: Vaikka Geometry Shaders mahdollistivat geometrian vahvistuksen (uusien kolmioiden luomisen syöttöprimitiivistä), ne olivat tunnetusti tehottomia. Niiden tulostuskäyttäytyminen oli usein ennustamatonta laitteistolle, mikä johti suorituskykyongelmiin, jotka tekivät niistä käyttökelvottomia monissa laajamittaisissa sovelluksissa.
- Hukkaan mennyt työ: Perinteisessä putkessa, jos lähetät kolmion renderöitäväksi, vertex-shader suoritetaan kolme kertaa, vaikka tämä kolmio lopulta karsittaisiin tai olisi takapuolelle osoittava pikseliohut suikale. Paljon käsittelytehoa kuluu geometriaan, joka ei edistä lopullista kuvaa lainkaan.
Paradigman muutos: Mesh Shader -putken esittely
Mesh Shader -putki korvaa Vertex-, Tessellation- ja Geometry-shader-vaiheet uudella, joustavammalla kaksivaiheisella mallilla:
- Task Shader (valinnainen): Korkean tason ohjausvaihe, joka määrittää kuinka paljon työtä on tehtävä. Tunnetaan myös nimellä Amplification Shader.
- Mesh Shader: Työhevosvaihe, joka käsittelee dataklustereita pienten, itsenäisten geometriapakettien, \"meshletien\", luomiseksi.
Tämä uusi lähestymistapa muuttaa perustavanlaatuisesti renderöintifilosofiaa. Sen sijaan, että CPU mikromanageröisi jokaista piirtokutsua jokaiselle kohteelle, se voi nyt antaa yhden, tehokkaan piirtokomennon, joka periaatteessa kertoo GPU:lle: \"Tässä on korkean tason kuvaus monimutkaisesta kohtauksesta; sinä selvität yksityiskohdat.\"
GPU, hyödyntäen Task- ja Mesh-shadereita, voi sitten suorittaa karsintaa, LOD-valintaa ja proseduurallista generointia erittäin rinnakkain, käynnistäen vain tarvittavan työn geometrian luomiseksi, joka todella näkyy. Tämä on GPU-ohjatun renderöintiputken ydin, ja se muuttaa peliä suorituskyvyn ja skaalautuvuuden osalta.
Kapellimestari: Task (Amplification) Shaderin ymmärtäminen
Task Shader on uuden putken aivot ja avain sen uskomattomaan tehoon. Se on valinnainen vaihe, mutta siinä tapahtuu \"vahvistus\". Sen ensisijainen rooli ei ole verteksien tai kolmioiden luominen, vaan toimia työn jakelijana.
Mikä on Task Shader?
Ajattele Task Shaderia massiivisen rakennusprojektin projektipäällikkönä. CPU antaa päällikölle korkean tason tavoitteen, kuten \"rakentaa kaupunginosa\". Projektipäällikkö (Task Shader) ei itse muuraa tiiliä. Sen sijaan se arvioi kokonaistehtävän, tarkistaa piirustukset ja päättää, mitä rakennusryhmiä (Mesh Shader -työryhmiä) tarvitaan ja kuinka monta. Se voi päättää, ettei tiettyä rakennusta tarvita (karsinta) tai että tietty alue vaatii kymmenen ryhmää, kun toinen tarvitsee vain kaksi.
Teknisesti Task Shader toimii laskennan kaltaisena työryhmänä. Se voi käyttää muistia, suorittaa monimutkaisia laskutoimituksia ja, mikä tärkeintä, päättää, kuinka monta Mesh Shader -työryhmää käynnistetään. Tämä päätös on sen voiman ydin.
Vahvistuksen voima
Termi \"vahvistus\" (amplification) tulee Task Shaderin kyvystä ottaa yksi oma työryhmänsä ja käynnistää nolla, yksi tai monta Mesh Shader -työryhmää. Tämä ominaisuus on mullistava:
- Käynnistä nolla: Jos Task Shader päättää, että objekti tai osa kohtauksesta ei ole näkyvissä (esim. kameran näkymäkarsinnan ulkopuolella), se voi yksinkertaisesti päättää käynnistää nolla Mesh Shader -työryhmää. Kaikki kyseiseen objektiin liittyvä potentiaalinen työ katoaa ilman, että sitä käsiteltäisiin enempää. Tämä on uskomattoman tehokasta karsintaa, joka suoritetaan kokonaan GPU:lla.
- Käynnistä yksi: Tämä on suora läpivienti. Task Shader -työryhmä päättää, että yksi Mesh Shader -työryhmä tarvitaan.
- Käynnistä monta: Tässä tapahtuu proseduurallisen generoinnin taika. Yksi Task Shader -työryhmä voi analysoida syöttöparametreja ja päättää käynnistää tuhansia Mesh Shader -työryhmiä. Esimerkiksi se voisi käynnistää työryhmän jokaiselle ruohonkorrelle pellolla tai jokaiselle asteroidille tiheässä klusterissa, kaikki yhdellä CPU:n lähettämällä kutsukomennolla.
Käsitteellinen katsaus Task Shader GLSL:ään
Vaikka yksityiskohdat voivat muuttua monimutkaisiksi, GLSL:n (WebGL-laajennukselle) ydinvahvistusmekanismi on yllättävän yksinkertainen. Se pyörii EmitMeshTasksEXT()-funktion ympärillä.
Huom: Tämä on yksinkertaistettu, käsitteellinen esimerkki.
#version 310 es
#extension GL_EXT_mesh_shader : require
layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
// Uniforms passed from the CPU
uniform mat4 u_viewProjectionMatrix;
uniform uint u_totalObjectCount;
// A buffer containing bounding spheres for many objects
struct BoundingSphere {
vec4 centerAndRadius;
};
layout(std430, binding = 0) readonly buffer ObjectBounds {
BoundingSphere bounds[];
} objectBounds;
void main() {
// Each thread in the workgroup can check a different object
uint objectIndex = gl_GlobalInvocationID.x;
if (objectIndex >= u_totalObjectCount) {
return;
}
// Perform frustum culling on the GPU for this object's bounding sphere
BoundingSphere sphere = objectBounds.bounds[objectIndex];
bool isVisible = isSphereInFrustum(sphere.centerAndRadius, u_viewProjectionMatrix);
// If it's visible, launch one Mesh Shader workgroup to draw it.
// Note: This logic could be more complex, using atomics to count visible
// objects and having one thread dispatch for all of them.
if (isVisible) {
// This tells the GPU to launch a mesh task. The parameters can be used
// to pass information to the Mesh Shader workgroup.
// For simplicity, we imagine each task shader invocation can directly map to a mesh task.
// A more realistic scenario involves grouping and dispatching from a single thread.
// A simplified conceptual dispatch:
// We'll pretend each visible object gets its own task, though in reality
// one task shader invocation would manage dispatching multiple mesh shaders.
EmitMeshTasksEXT(1u, 0u, 0u); // This is the key amplification function
}
// If not visible, we do nothing! The object is culled with zero GPU cost beyond this check.
}
Todellisessa skenaariossa työryhmän yksi säie saattaa koota tulokset ja tehdä yhden `EmitMeshTasksEXT`-kutsun kaikille näkyville objekteille, joista työryhmä on vastuussa.
Työvoima: Mesh Shaderin rooli geometrian luomisessa
Kun Task Shader on lähettänyt yhden tai useamman työryhmän, Mesh Shader ottaa vastuun. Jos Task Shader on projektipäällikkö, Mesh Shader on ammattitaitoinen rakennusryhmä, joka todella rakentaa geometrian.
Työryhmistä meshleteiksi
Kuten Task Shader, myös Mesh Shader suoritetaan säikeiden yhteistyökykyisenä työryhmänä. Koko työryhmän yhteisenä tavoitteena on tuottaa yksi pieni erä geometriaa, jota kutsutaan meshletiksi. Meshlet on yksinkertaisesti kokoelma verteksä ja ne yhdistäviä primitiivejä (kolmioita). Tyypillisesti meshlet sisältää pienen määrän verteksä (esim. enintään 128) ja kolmioita (esim. enintään 256), koko, joka on erittäin suotuisa moderneille GPU-välimuisteille ja käsittelymalleille.
Tämä on perustavanlaatuinen poikkeama vertex-shaderista, jolla ei ollut käsitystä naapureistaan. Mesh Shaderissa kaikki työryhmän säikeet voivat jakaa muistia ja koordinoida ponnistelujaan meshletin rakentamiseksi tehokkaasti.
Verteksien ja primitiivien luominen
Sen sijaan, että se palauttaisi yhden `gl_Position`-arvon, Mesh Shader -työryhmä täyttää tulostusmatriisit meshletinsä täydellisillä tiedoilla. Säikeet työskentelevät yhdessä kirjoittaakseen verteksien sijainnit, normaalit, UV-koordinaatit ja muut attribuutit näihin matriiseihin. Ne määrittelevät myös primitiivit määrittelemällä, mitkä verteksit muodostavat kunkin kolmion.
Mesh Shaderin viimeinen vaihe on kutsua funktio, kuten `SetMeshOutputsEXT()`, ilmoittamaan tarkalleen, kuinka monta verteksiä ja primitiiviä se on luonut. Laitteisto ottaa sitten tämän meshletin ja välittää sen suoraan rasterizerille.
Käsitteellinen katsaus Mesh Shader GLSL:ään
Tässä on käsitteellinen esimerkki Mesh Shaderista, joka luo yksinkertaisen nelikulmion. Huomaa, miten säikeet tekevät yhteistyötä `gl_LocalInvocationID`:nsä perusteella.
#version 310 es
#extension GL_EXT_mesh_shader : require
// Define the maximum outputs for our meshlet
layout(max_vertices = 4, max_primitives = 2) out;
layout(triangles) out;
layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in;
// We write vertex data to these built-in output arrays
out gl_MeshVerticesEXT {
vec4 position;
vec2 uv;
} vertices[];
// We write triangle indices to this array
out uint gl_MeshPrimitivesEXT[];
uniform mat4 u_modelViewProjectionMatrix;
void main() {
// Total vertices and primitives to generate for this meshlet
const uint vertexCount = 4;
const uint primitiveCount = 2;
// Tell the hardware how many vertices and primitives we are actually outputting
SetMeshOutputsEXT(vertexCount, primitiveCount);
// Define the vertex positions and UVs for a quad
vec4 positions[4] = vec4[4](
vec4(-0.5, 0.5, 0.0, 1.0),
vec4(-0.5, -0.5, 0.0, 1.0),
vec4(0.5, 0.5, 0.0, 1.0),
vec4(0.5, -0.5, 0.0, 1.0)
);
vec2 uvs[4] = vec2[4](
vec2(0.0, 1.0),
vec2(0.0, 0.0),
vec2(1.0, 1.0),
vec2(1.0, 0.0)
);
// Let each thread in the workgroup generate one vertex
uint id = gl_LocalInvocationID.x;
if (id < vertexCount) {
vertices[id].position = u_modelViewProjectionMatrix * positions[id];
vertices[id].uv = uvs[id];
}
// Let the first two threads generate the two triangles for the quad
if (id == 0) {
// First triangle: 0, 1, 2
gl_MeshPrimitivesEXT[0] = 0u;
gl_MeshPrimitivesEXT[1] = 1u;
gl_MeshPrimitivesEXT[2] = 2u;
}
if (id == 1) {
// Second triangle: 1, 3, 2
gl_MeshPrimitivesEXT[3] = 1u;
gl_MeshPrimitivesEXT[4] = 3u;
gl_MeshPrimitivesEXT[5] = 2u;
}
}
Käytännön taikaa: Task Amplificationin käyttötapaukset
Tämän putken todellinen voima paljastuu, kun sovellamme sitä monimutkaisiin, todellisiin renderöintihaasteisiin.
Käyttötapaus 1: Massiivinen proseduurallisen geometrian luominen
Kuvittele renderöiväsi tiheää asteroidikenttää satojen tuhansien ainutlaatuisten asteroidien kanssa. Vanhan putken avulla CPU joutuisi luomaan kunkin asteroidin verteksitiedot ja antamaan erillisen piirtokutsun jokaiselle, mikä olisi täysin kestämätön lähestymistapa.
Mesh Shader -työnkulku:- CPU antaa yhden piirtokutsun: `drawMeshTasksEXT(1, 1)`. Se välittää myös joitakin korkean tason parametreja, kuten kentän säteen ja asteroidien tiheyden, yhtenäisessä puskurissa.
- Yksi Task Shader -työryhmä suoritetaan. Se lukee parametrit ja laskee, että esimerkiksi 50 000 asteroidia tarvitaan. Se kutsuu sitten `EmitMeshTasksEXT(50000, 0, 0)`.
- GPU käynnistää 50 000 Mesh Shader -työryhmää rinnakkain.
- Jokainen Mesh Shader -työryhmä käyttää ainutlaatuista tunnustaan (`gl_WorkGroupID`) siemenenä luodakseen proseduurallisesti verteksit ja kolmiot yhdelle ainutlaatuiselle asteroidille.
Tuloksena on massiivinen, monimutkainen kohtaus, joka on luotu lähes kokonaan GPU:lla, vapauttaen CPU:n käsittelemään muita tehtäviä, kuten fysiikkaa ja tekoälyä.
Käyttötapaus 2: GPU-ohjattu karsinta laajassa mittakaavassa
Harkitse yksityiskohtaista kaupunkinäkymää miljoonine yksittäisine objekteineen. CPU ei yksinkertaisesti voi tarkistaa jokaisen objektin näkyvyyttä jokaisessa ruudussa.
Mesh Shader -työnkulku:- CPU lataa suuren puskurin, joka sisältää kaikkien kohtauksen yksittäisten objektien rajaustilavuudet (esim. pallot tai laatikot). Tämä tapahtuu kerran tai vain, kun objektit liikkuvat.
- CPU antaa yhden piirtokutsun, käynnistäen riittävästi Task Shader -työryhmiä käsittelemään koko rajaustilavuusluettelon rinnakkain.
- Jokaiselle Task Shader -työryhmälle on osoitettu osa rajaustilavuusluettelosta. Se käy läpi osoitetut objektit, suorittaa frustum culling (ja mahdollisesti occlusion culling) jokaiselle ja laskee, kuinka monta on näkyvissä.
- Lopuksi se käynnistää täsmälleen niin monta Mesh Shader -työryhmää, välittäen näkyvien objektien tunnukset.
- Jokainen Mesh Shader -työryhmä vastaanottaa objektin tunnuksen, hakee mesh-tiedot puskurista ja luo vastaavat meshletit renderöintiä varten.
Tämä siirtää koko karsintaprosessin GPU:lle, mahdollistaen sellaisten kohtausten monimutkaisuuden, joka halvaannuttaisi välittömästi CPU-pohjaisen lähestymistavan.
Käyttötapaus 3: Dynaaminen ja tehokas yksityiskohtaisuuden taso (LOD)
LOD-järjestelmät ovat kriittisiä suorituskyvyn kannalta, vaihtamalla yksinkertaisempiin malleihin kaukana oleville objekteille. Mesh-shaderit tekevät tästä prosessista yksityiskohtaisemman ja tehokkaamman.
Mesh Shader -työnkulku:- Objektin data esikäsitellään meshletien hierarkiaksi. Karkeammat LOD-tasot käyttävät vähemmän, suurempia meshletejä.
- Tämän objektin Task Shader laskee sen etäisyyden kamerasta.
- Etäisyyden perusteella se päättää, mikä LOD-taso on sopiva. Se voi sitten suorittaa karsintaa meshlet-kohtaisesti kyseiselle LOD-tasolle. Esimerkiksi suurelle objektille se voi karsia objektin takapuolella olevat meshletit, jotka eivät ole näkyvissä.
- Se käynnistää vain valitun LOD:n näkyvien meshletien Mesh Shader -työryhmät.
Tämä mahdollistaa hienojakoisen, reaaliaikaisen LOD-valinnan ja karsinnan, joka on paljon tehokkaampaa kuin CPU:n koko mallien vaihtaminen.
Aloitus: `WEBGL_mesh_shader`-laajennuksen käyttö
Valmis kokeilemaan? Tässä ovat käytännön vaiheet aloittamiseen mesh shadereilla WebGL:ssä.
Tuen tarkistaminen
Ennen kaikkea tämä on huippuluokan ominaisuus. Sinun on varmistettava, että käyttäjän selain ja laitteisto tukevat sitä.
const gl = canvas.getContext('webgl2');
const meshShaderExtension = gl.getExtension('WEBGL_mesh_shader');
if (!meshShaderExtension) {
console.error("Your browser or GPU does not support WEBGL_mesh_shader.");
// Fallback to a traditional rendering path
}
Uusi piirtokutsu
Unohda `drawArrays` ja `drawElements`. Uusi putki käynnistetään uudella komennolla. `getExtension`-kutsusta saamasi laajennusobjekti sisältää uudet funktiot.
// Launch 10 Task Shader workgroups.
// Each workgroup will have the local_size defined in the shader.
meshShaderExtension.drawMeshTasksEXT(0, 10);
`count`-argumentti määrittää, kuinka monta Task Shaderin paikallista työryhmää käynnistetään. Jos et käytä Task Shaderia, tämä käynnistää suoraan Mesh Shader -työryhmät.
Shaderin käännös ja linkitys
Prosessi on samanlainen kuin perinteisessä GLSL:ssä, mutta luot shadereita tyyppiä `meshShaderExtension.MESH_SHADER_EXT` ja `meshShaderExtension.TASK_SHADER_EXT`. Linkität ne ohjelmaan samalla tavalla kuin vertex- ja fragment-shaderin.
Ratkaisevaa on, että molempien shaderien GLSL-lähdekoodin on aloitettava direktiivillä laajennuksen sallimiseksi:
#extension GL_EXT_mesh_shader : require
Suorituskyvyn huomioita ja parhaat käytännöt
- Valitse oikea työryhmän koko: Shaderisi `layout(local_size_x = N)` on kriittinen. Koko 32 tai 64 on usein hyvä lähtökohta, koska se sopii hyvin taustalla olevien laitteistoarkkitehtuurien kanssa, mutta profiloi aina löytääksesi optimaalisen koon juuri sinun työkuormallesi.
- Pidä Task Shaderisi kevyenä: Task Shader on tehokas työkalu, mutta se on myös potentiaalinen pullonkaula. Täällä suoritettavan karsinnan ja logiikan tulisi olla mahdollisimman tehokasta. Vältä hitaita, monimutkaisia laskutoimituksia, jos ne voidaan esilaskea.
- Optimoi meshletin koko: On olemassa laitteistosta riippuvainen ihanteellinen piste verteksien ja primitiivien määrälle meshletissä. Ilmoitettavat `max_vertices` ja `max_primitives` tulisi valita huolellisesti. Liian pieni, ja työryhmien käynnistämisen ylikuormitus dominoi. Liian suuri, ja menetät rinnakkaisuuden ja välimuistin tehokkuuden.
- Datan yhtenäisyydellä on väliä: Suorittaessasi karsintaa Task Shaderissa järjestä rajaustilavuustiedot muistiin edistääksesi yhtenäisiä pääsymalleja. Tämä auttaa GPU:n välimuisteja toimimaan tehokkaasti.
- Tiedä, milloin niitä kannattaa välttää: Mesh shaderit eivät ole hopealuoti. Muutaman yksinkertaisen objektin renderöinnissä mesh-putken ylikuormitus voi olla hitaampi kuin perinteisen vertex-putken. Käytä niitä, missä niiden vahvuudet loistavat: valtavat objektimäärät, monimutkainen proseduurallinen generointi ja GPU-ohjatut työkuormat.
Johtopäätös: Reaaliaikaisen verkkografiikan tulevaisuus on nyt
Mesh Shader -putki tehtävän vahvistuksen kanssa edustaa yhtä merkittävimmistä edistysaskeleista reaaliaikaisessa grafiikassa viimeisen vuosikymmenen aikana. Siirtämällä paradigman jäykästä, CPU-hallitusta prosessista joustavaan, GPU-ohjattuun, se murtaa aiemmat esteet geometriselle monimutkaisuudelle ja kohtauksen mittakaavalle.
Tämä teknologia, joka on linjassa modernien grafiikka-API:en, kuten Vulkanin, DirectX 12 Ultimaten ja Metalin, suunnan kanssa, ei ole enää rajoitettu huippuluokan natiivisovelluksiin. Sen saapuminen WebGL:ään avaa oven uudelle verkkopohjaisten kokemusten aikakaudelle, jotka ovat yksityiskohtaisempia, dynaamisempia ja immersiivisempiä kuin koskaan ennen. Kehittäjille, jotka ovat valmiita omaksumaan tämän uuden mallin, luovat mahdollisuudet ovat käytännössä rajattomat. Voima luoda kokonaisia maailmoja lennossa on, ensimmäistä kertaa, kirjaimellisesti käden ulottuvilla, suoraan verkkoselaimessa.