Începeți cu WebGL: desenați un pătrat

Autor: Laura McKinney
Data Creației: 9 Aprilie 2021
Data Actualizării: 19 Iunie 2024
Anonim
Începeți cu WebGL: desenați un pătrat - Creator
Începeți cu WebGL: desenați un pătrat - Creator

Conţinut

  • Cunoștințe necesare: Cunoștințe intermediare HTML / JavaScript
  • Necesită: Ultimele Chrome sau Firefox 4/5 care acceptă WebGL
  • Timpul proiectului: 2-3 ore
  • Fișier de asistență

Prima întâlnire cu WebGL poate fi intimidantă. API-ul nu are nimic din bibliotecile prietenoase orientate pe obiecte pe care le-ați fi putut folosi. WebGL se bazează pe OpenGL, care este o bibliotecă de tip C destul de veche. Dispune de o listă lungă de funcții utilizate pentru a seta diferite stări și pentru a transmite datele către GPU. Toate acestea sunt descrise în caietul de sarcini oficial. Acest document nu este destinat începătorilor, a crezut că va fi foarte util odată ce începeți să vă găsiți calea în jurul API-ului. Dar nu vă temeți: cu o abordare bună, toate acestea pot fi îmblânzite și, în curând, veți începe să vă simțiți confortabil!

O concepție greșită obișnuită despre WebGL este că este un fel de motor 3D sau API. Deși WebGL are de fapt o mulțime de caracteristici care vă vor ajuta să dezvoltați aplicații 3D, în sine nu este 3D. Este mult mai bine să vă gândiți la WebGL ca la un API de desen care vă oferă acces la grafică accelerată hardware.


01. Desenarea unui pătrat

În acest tutorial ne vom concentra pe înțelegerea modului în care funcționează WebGL desenând o formă 2D simplă. Dar mai întâi lucrurile. Înainte de a scrie orice cod, trebuie să creăm un document HTML pentru a-l păstra.

html>
cap>
script id = "vertex" type = "x-shader"> / script>
script id = "fragment" type = "x-shader"> / script>
tip de script = "text / javascript">
funcția init () {
}
/ script>
/ cap>
body onload = "init ()">
canvas id = "mycanvas" width = "800" height = "500"> / canvas>
/ corp>
/ html>

În afară de codul HTML obișnuit, documentul conține câteva lucruri specifice WebGL. În primul rând definim două etichete script care în loc de JavaScript vor găzdui codul shader. Shaders sunt o caracteristică centrală a WebGL și vom reveni la ele mai târziu.

Celălalt element de care vom avea nevoie este pânza. Toate WebGL sunt desenate pe un element de pânză.

În cele din urmă, definim o funcție numită init care va fi invocată imediat ce documentul se încarcă. Aici vom emite comenzi necesare pentru a desena orice pe ecran. Să începem să adăugăm un cod în această funcție.


02. Contextul și fereastra de vizualizare WebGL

În funcția init trebuie să obținem contextul WebGL din elementul canvas.

canvas = document.getElementById ("mycanvas");
gl = canvas.getContext ("experimental-webgl");

Obținem o referință la elementul de pânză definit în documentul HTML și apoi obținem contextul său WebGL. Obiectul returnat ne va permite să accesăm API-ul WebGL. Puteți să-l numiți orice doriți, dar „gl” pare o convenție bună.

Rețineți că contextul 3D se numește „experimental-webgl”. Aceasta este o soluție temporară până când producătorii de browsere decid că este stabil. Până atunci numele se va schimba în „webgl”.

După ce avem contextul, este timpul să definim fereastra de vizualizare și să-i setăm o culoare implicită. Fereastra de vizualizare definește zona pe care doriți să desenați conținutul WebGL: în cazul nostru va fi întreaga pânză. Apoi definim o culoare implicită pentru fereastra de vizualizare și apelăm funcția de claritate pentru a seta fereastra de vizualizare la această culoare. Continuați adăugând următoarele rânduri:


gl.viewport (0, 0, canvas.width, canvas.height);
gl.clearColor (0, 0,5, 0, 1);
gl.clear (gl.COLOR_BUFFER_BIT);

Observați că culorile din WebGL nu sunt definite utilizând notația hexagonală, ci de obicei ca patru numere, fiecare în intervalul [0-1] care definește valorile pentru canalele roșu, verde, albastru și alfa separat. Dacă deschideți fișierul în browser, ar trebui să vedeți zona pânzei pictată în verde închis.

Dacă nu vedeți culoarea verde închis pe pânză și sunteți sigur că codul este corect, verificați acest link. Este posibil ca unele plăci grafice vechi să nu funcționeze cu WebGL, chiar dacă aveți un browser care îl acceptă.

03. Shaders: umbră vertex

În acest moment, să părăsim funcția inițială pentru o vreme și să ne concentrăm asupra umbrelor. Shaders sunt fundamentale pentru WebGL: ele definesc modul în care este desenat orice pe ecran. Un program de umbrire este compus din două părți: un vârf și un fragment de umbrire. Vertical shader procesează puncte în geometria formei pe care o redăm, în timp ce fragmentul shader procesează fiecare pixel care umple această formă.

Pentru exemplul nostru, vom crea câteva umbrere cu adevărat de bază. Să începem cu cel de la vârf. În interiorul etichetei scriptului cu „vertex” id adăugați următoarele linii:

atribut vec2 aVertexPosition;

void main () {
gl_Position = vec4 (aVertexPosition, 0.0, 1.0);
}

Fiecare shader are o funcție numită main care va fi executată în timpul redării. Variabilele declarate în antet sunt parametri ai acelei funcții - pot fi fie atribute, fie uniforme. Vom vedea o variabilă uniformă mai târziu, acum să analizăm mai atent atributul.

Un atribut este o matrice de date care vor fi procesate - funcția principală a shader-ului va fi apelată pentru fiecare element din această matrice. De obicei, un atribut va conține date legate de pozițiile vârfurilor, culorile sau coordonatele texturii acestora. Cu toate acestea, nu se limitează doar la asta - este o serie de numere și shader-ul le poate interpreta în orice mod doriți. Vom trece datele atributelor de la JavaScript la shader mai târziu.

Ceea ce se întâmplă aici este că luăm valoarea atributului și îl atribuim unei variabile speciale numite gl_Position. Acesta este modul în care umbrele returnează valori - nu există un cuvânt cheie „return”, dar va trebui să atribuiți o valoare acestei variabile speciale.

Rețineți că alegem să utilizăm un vector cu două componente ca tip al atributului nostru - ceea ce are sens, deoarece vom desena o formă folosind coordonatele 2D. Cu toate acestea, gl_Position așteaptă un vector cu patru componente. Ceea ce vom face este să codificăm hard cele două valori rămase, deoarece acestea vor fi întotdeauna aceleași în acest caz.

Dacă vă întrebați ce reprezintă aceste două valori: a treia, pe care o setăm la 0, este „adâncimea” vârfului. Poate avea orice valoare, dar dacă este> 1 sau -1 acel vârf nu va fi desenat. Orice lucru din mijloc va fi desenat și obiectele cu adâncime mai mică vor fi desenate în față. Gândiți-vă că este similar cu indexul z din CSS. Adâncimea este crucială în redarea scenelor 3D.

Al patrulea număr este așa-numita coordonată omogenă utilizată în proiecția în perspectivă. Este dincolo de sfera acestui tutorial să îl discutăm, dar în cazuri simple ca acesta ar trebui să aibă valoarea 1.

04. Shaders: fragment shader

Acum să definim fragmentul de umbrire. În interiorul etichetei de script cu „fragment” id adăugați următoarele rânduri:

#ifdef GL_ES
flotor highp de precizie;
#endif

uniform vec4 uColor;

void main () {
gl_FragColor = uColor;
}

La fel ca în cazul umbrelor de vârf, umbrele de fragmente sunt în esență o funcție și trebuie numit „principal”. Primele trei linii sunt codul plăcii cazanului - definește precizia utilizată cu valori în virgulă mobilă și trebuie doar să fie acolo.

Apoi definim o variabilă uColor uniformă. Spre deosebire de atribute, variabilele uniforme sunt constante. Când se execută umbrele pentru fiecare vârf și pixel de pe ecran, valoarea uniformă va rămâne aceeași la fiecare apel. O folosim pentru a defini culoarea formei pe care o vom desena. Vom transmite o valoare pentru această culoare din JavaScript.

Rețineți că culoarea este, de asemenea, un vector cu patru componente - unul pentru fiecare canal de culoare: roșu, verde și albastru și unul pentru canalul alfa. Spre deosebire de CSS, valorile pentru fiecare canal nu sunt în intervalul 0-255, ci mai degrabă în intervalul 0-1.

Fragmentul de umbrire este foarte simplu - ia doar valoarea culorii și o atribuie unei variabile speciale numite gl_FragColor. Acest nuanță va aplica aceeași culoare la fiecare pixel desenat pe ecran.

05. Compilarea și conectarea umbrelor

Umbrele noastre sunt la locul lor, deci să revenim la JavaScript și să continuăm în funcția init. Imediat după apelul către gl.clear () adăugați următoarele rânduri:

var v = document.getElementById ("vertex"). firstChild.nodeValue;
var f = document.getElementById ("fragment"). firstChild.nodeValue;

var vs = gl.createShader (gl.VERTEX_SHADER);
gl.shaderSource (vs, v);
gl.compileShader (vs);

var fs = gl.createShader (gl.FRAGMENT_SHADER);
gl.shaderSource (fs, f);
gl.compileShader (fs);

program = gl.createProgram ();
gl.attachShader (program, vs);
gl.attachShader (program, fs);
gl.linkProgram (program);

Trebuie să știți un lucru despre umbrere. Nu sunt executate în browser, așa cum o face codul JavaScript. Mai întâi trebuie să compilați shader-ul - și asta face codul de mai sus. Primele două linii folosesc doar modelul DOM pentru a apuca sursa shaderului ca șir. Odată ce avem sursa, creăm la două obiecte shader, trecem sursa și le compilăm.

În această etapă avem două umbrere separate și trebuie să le reunim într-un lucru care se numește „program” în WebGL. Acest lucru se face prin conectarea acestora - ceea ce se face în ultima secțiune a codului.

06. Depanarea umbrelor

Depanarea umbrelor poate fi dificilă. Dacă există o eroare într-un cod shader, acesta va eșua în tăcere și nu există nicio modalitate de a înregistra valori din acesta. Prin urmare, este întotdeauna bine să verificați dacă procesul de compilare și conectare a mers bine. După conectarea programului, adăugați următoarele rânduri:

if (! gl.getShaderParameter (vs, gl.COMPILE_STATUS))
console.log (gl.getShaderInfoLog (vs));

if (! gl.getShaderParameter (fs, gl.COMPILE_STATUS))
console.log (gl.getShaderInfoLog (fs));

if (! gl.getProgramParameter (program, gl.LINK_STATUS))
console.log (gl.getProgramInfoLog (program));

Acum, dacă există o problemă în timpul compilării sau conectării, veți primi un mesaj frumos în consola dvs.

Cu umbrele noastre în loc și compilate, putem trece la definirea coordonatelor pentru forma noastră.

07. Sistem nativ de coordonate

Sistemul de coordonate native WebGL arată astfel:

Pixelul din stânga sus este la -1, -1, partea de jos este la 1,1, indiferent de dimensiunea și proporțiile pânzei. Trebuie să ai grijă singur de proporții.

În WebGL există trei tipuri de primitive de desen: puncte, linii și triunghiuri. Liniile și punctele sunt destul de utile, dar triunghiul este de departe cel mai popular - toate obiectele solide 3D sunt compuse din triunghiuri. Vrem să desenăm un pătrat - iar un pătrat poate fi compus din două triunghiuri.

Ceea ce trebuie să facem acum este să creăm o matrice, care în WebGL se numește tampon, cu coordonatele celor două triunghiuri. Iată codul:

var aspect = canvas.width / canvas.height;

var vertices = new Float32Array ([
-0,5, 0,5 * aspect, 0,5, 0,5 * aspect, 0,5, -0,5 * aspect, // Triunghi 1
-0,5, 0,5 * aspect, 0,5, -0,5 * aspect, -0,5, -0,5 * aspect // Triangle 2
]);

vbuffer = gl.createBuffer ();
gl.bindBuffer (gl.ARRAY_BUFFER, vbuffer);
gl.bufferData (gl.ARRAY_BUFFER, vârfuri, gl.STATIC_DRAW);

item Size = 2;
numItems = vertices.length / itemSize;

În primul rând, pentru a face ca pătratul să aibă proporțiile potrivite pe o pânză de orice dimensiune, calculăm raportul de aspect. Apoi creăm o matrice tastată cu 12 coordonate - șase puncte în dimensiuni 2D formând două triunghiuri. Înmulțim valorile Y ale fiecărui vârf cu raportul de aspect. Rețineți că aceste două triunghiuri se vor „lipi” de o parte pentru a crea pătratul, dar nu sunt deloc conectate.

Apoi trecem la funcțiile WebGL pentru a crea un buffer și a conecta matricea de vârfuri la acest buffer. Modalitatea de a face acest lucru este de a lega bufferul: făcându-l bufferul "curent" în WebGL. Când este legat, orice apel către o funcție va funcționa pe acest buffer și acest lucru se întâmplă atunci când invocăm bufferData.

Acesta este modul în care se întâmplă multe lucruri în WebGL: setați o valoare la o proprietate sau setați un obiect ca „curent” și până când îl setați la altceva sau la nul, această valoare va fi utilizată în fiecare apel către API . În acest sens, totul este global în WebGL, deci păstrați-vă codul organizat!

În cele din urmă, atribuim dimensiunea unui singur vârf unei variabile și cantitatea de vârfuri din matrice altei, pentru o utilizare ulterioară.

08. Setarea uniformelor și atributelor

Aproape am terminat, dar trebuie totuși să transmitem toate datele din JavaScript către umbrele înainte de a putea desena ceva. Dacă vă amintiți, umbrele vertexului au un atribut de tip vec2 numit aVertexAttribute, iar fragmentul umbrelor are o variabilă uniformă de tip vec4 numită uColor. Deoarece ambele umbrere au fost compilate într-un singur program, folosim o referință la acest program pentru a le atribui valori semnificative.

Să continuăm în interiorul funcției init:

gl.useProgram (program);

program.uColor = gl.getUniformLocation (program, "uColor");
gl.uniform4fv (program.uColor, [0,0, 0,3, 0,0, 1,0]);

program.aVertexPosition = gl.getAttribLocation (program, "aVertexPosition");
gl.enableVertexAttribArray (program.aVertexPosition);
gl.vertexAttribPointer (program.aVertexPosition, itemSize, gl.FLOAT, false, 0, 0);

Prima linie instruiește WebGL să utilizeze programul curent pentru orice apeluri ulterioare.

Folosind funcția getUniformLocation putem obține o referință la locația variabilei uniforme în fragmentul nostru de umbrire. Rețineți că dacă variabila uniformă ar fi în umbrele vertexului, codul ar fi exact același. Odată ce îl avem, îl păstrăm într-o variabilă dinamică a obiectului programului (am putea să-l păstrăm și într-o variabilă globală sau locală, dar astfel acest cod face un pic mai organizat).

Apoi atribuim uniformei uColor o valoare. În acest caz, trebuie să fie o matrice de patru numere, care să definească canalele roșu, verde, albastru și respectiv alfa.

După ce setăm variabila uniformă, trecem la atribut. Este nevoie de câțiva pași suplimentari. Obținem locația atributului în umbră într-un mod similar cu obținerea uniformei - deși folosim o funcție diferită pentru asta.

În continuare, trebuie să activăm în mod explicit atributul și să atribuim un pointer acestui atribut. În cazul în care vă întrebați cum WebGL știe că dorim să utilizăm bufferul declarat mai sus, amintiți-vă de caracterul global al funcțiilor WebGL. Am apelat bindBuffer cu câteva linii înainte, așa că este încă actualul.

Aplicațiile din lumea reală sunt rareori la fel de simple ca acest exemplu și va trebui să aveți grijă suplimentară pentru a ști care tampon sau program sunt legate în prezent și utilizate. De asemenea, o regulă generală pentru optimizarea codului este de a minimiza cantitatea de tampon de legare și de legare sau de comutare între programele shader.

În apelul funcției vertexAttribPointer specificăm dimensiunea elementului datelor din atribut. Este ca și cum ai spune: fiecare atribut este compus din două numere ulterioare din matrice. WebGL le va extrage automat și le va împacheta într-o variabilă de tip vec2 care intră în shader.

09. Desen

Iată ultima și cea mai importantă linie:

gl.drawArrays (gl.TRIANGLES, 0, numItems);

Primul argument al funcției drawArrays specifică modul de desen. Vrem să desenăm un pătrat solid, așa că folosim gl.TRIANGLES. De asemenea, puteți încerca gl.LINES și gl.POINTS.

Această funcție va utiliza buffer-ul care este legat în prezent și va apela programul shader curent pentru fiecare atribut de câte ori specificăm în ultimul argument. De aceea am calculat valoarea numItems înainte. Dacă tamponul nu are suficiente elemente, va fi aruncată o eroare, astfel încât este necesară o atenție suplimentară pentru a vă asigura că datele nu sunt corupte.

Dacă totul a mers bine, ar trebui să vedeți un pătrat de orice culoare treceți la variabila uniformă. Sursa completă a exemplului poate fi găsită în demonstrația din partea de sus a acestui tutorial.

10. Concluzie

S-ar putea să nu pară prea mult - așa că am făcut un pătrat ... De fapt este un exemplu destul de simplu, dar dacă înțelegi elementele de bază, vei putea să te miști destul de repede în învățarea și înțelegerea restului.

Pentru început, este bine să știți despre sistemul de coordonate nativ. Chiar dacă mai târziu nu veți avea de-a face cu el direct, ci mai degrabă prin diverse transformări matriciale, este extrem de util să știți unde se termină totul.

Atributele și uniformele sunt, de asemenea, foarte importante: sunt modalitatea de a comunica între JavaScript și umbrele care rulează pe GPU, astfel încât să le cunoașteți și să le utilizați.

Atât deocamdată. Sper că v-a plăcut tutorialul și WebGL!

Bartek lucrează ca tehnolog creativ la Tool of America de Nord. El scrie un blog axat pe 3D interactiv în timp real, numit Everyday3D și este autorul J3D, un motor WebGL open source. Îl poți urmări pe Twitter: @bartekd.

Sfaturile Noastre
Aardman dezvăluie secretele unei remorci ucigașe
Descoperi

Aardman dezvăluie secretele unei remorci ucigașe

Am deci ca trailerul Hippy Dinner ă fie filmat de la bun început prin tehnici de top-motion. Am vrut ca filmul ă creeze un entiment de amintiri fragmentate și no talgie, iar filmarea acțiunii liv...
Răpirea norilor! 8 mituri Creative Cloud au fost demontate
Descoperi

Răpirea norilor! 8 mituri Creative Cloud au fost demontate

De când Adobe a lan at Creative Cloud, -a di cutat mult de pre modul în care funcționează și ce include, și inevitabil unt făcute unele ipoteze incorecte. Iată primele opt mituri de pre ace ...
Descoperirea proiectului și profesionistul în proiectare
Descoperi

Descoperirea proiectului și profesionistul în proiectare

Ace t articol a apărut pentru prima dată în numărul 225 al revi tei .net - cea mai vândută revi tă din lume pentru de igneri și dezvoltatori de web.Chiar și pentru profe ioniști experimentaț...