WebGL Essentials partea I

WebGL este un renderer 3D în browser, bazat pe OpenGL, care vă permite să afișați conținutul 3D direct într-o pagină HTML5. În acest tutorial voi acoperi toate lucrurile esențiale de care aveți nevoie pentru a începe să utilizați acest cadru.


Introducere

Există câteva lucruri pe care trebuie să le cunoașteți înainte de a începe. WebGL este un API JavaScript care redă conținut 3D într-o panza HTML5. Aceasta face acest lucru prin utilizarea a două scenarii care sunt cunoscute în "lumea 3D" ca shader. Cele două shadere sunt:

  • Shaderul vârfurilor
  • Fragmentul de shader

Acum nu fi prea nervos când auziți aceste nume; este doar un mod fantezist de a spune, "calculator pozition" si "chooser de culoare", respectiv. Shader-ul fragmentului este cel mai ușor de înțeles; pur și simplu spune WebGL ce culoare trebuie să fie un anumit punct pe modelul dvs. Shader-ul de vârfuri este puțin mai tehnic, dar în esență convertește punctele din modelele 3D în coordonate 2D. Deoarece toate monitoarele de calculator sunt suprafețe plate 2D și când vedeți obiecte 3D pe ecran, acestea sunt doar o iluzie de perspectivă.

Dacă doriți să știți exact cum funcționează acest calcul, va trebui să întrebați un matematician, deoarece utilizează multiplicări matrice avansate de 4 x 4, care sunt puțin dincolo de tutorialul "Essentials". Din fericire, nu trebuie să știți cum funcționează, deoarece WebGL va avea grijă de cea mai mare parte a acesteia. Deci sa începem.


Pasul 1: Configurarea WebGL

WebGL are o mulțime de setări mici pe care trebuie să le configurați aproape de fiecare dată când desenați ceva pe ecran. Pentru a economisi timp și a face codul tău curat, voi face un obiect JavaScript care va conține toate lucrurile "în spatele scenei" într-un fișier separat. Pentru a începe, creați un nou fișier numit "WebGL.js" și plasați în el următorul cod:

funcția WebGL (CID, FSID, VSID) var canvas = document.getElementById (CID); dacă (! canvas.getContext ("webgl") &&! canvas.getContext ("experimental-webgl") alertă ("Browserul dvs. nu suportă WebGL"); altfel this.GL = (canvas.getContext ("webgl"))? canvas.getContext ("webgl"): canvas.getContext ("experimental-webgl"); this.GL.clearColor (1.0, 1.0, 1.0, 1.0); // aceasta este culoarea this.GL.enable (this.GL.DEPTH_TEST); // Activați testarea în profunzime this.GL.depthFunc (this.GL.LEQUAL); // Setați perspectiva View this.AspectRatio = canvas.width / canvas.height; // Încărcați Shaders aici

Această funcție constructor ia ID-urile panzei și cele două obiecte shader. Mai întâi, primim elementul de pânză și asigurăm că suportă WebGL. Dacă se întâmplă acest lucru, atunci atribuim contextul WebGL unei variabile locale numite "GL". Culoarea clară este pur și simplu culoarea de fundal și merită remarcat faptul că în WebGL majoritatea parametrilor merg de la 0.0 la 1.0, așa că va trebui să vă împărțiți valorile rgb cu 255. Deci, în exemplul 1.0, 1.0, 1.0, 1.0 înseamnă un fundal alb cu vizibilitate 100% (fără transparență). Următoarele două linii spun WebGL pentru a calcula adâncimea și perspectiva astfel încât un obiect mai aproape de tine să blocheze obiectele din spatele acestuia. În cele din urmă, am stabilit raportul de aspect care este calculat prin împărțirea lățimii panzei la înălțimea acesteia.

Înainte de a continua și de a încărca cele două shadere, să le scriem. Am de gând să le scriu în fișierul HTML unde vom pune elementul real de panza. Creați un fișier HTML și plasați următoarele două elemente de script chiar înainte de eticheta de închidere a corpului:

 

Se creează mai întâi shaderul vârfurilor și definiți două atribute:

  • poziția vârfului, care este poziția în coordonatele x, y și z ale vârfului curent (Punct în modelul dvs.)
  • coordonata texturii; locația din imaginea texturii care ar trebui atribuită acestui punct

Apoi, vom crea variabile pentru matricele de transformare și perspectivă. Acestea sunt folosite pentru a converti modelul 3D într-o imagine 2D. Următoarea linie creează o variabilă comună la shader-ul fragmentului, iar în funcția principală se calculează gl_Position (poziția finală 2D). Apoi atribuim "coordonatele actuale de textura" variabilei partajate.

În fragmentul de shader luăm coordonatele pe care le-am definit în shaderul de vârfuri și "eșantionăm" textura la acea coordonată. Practic, obținem doar culoarea în textura care corespunde punctului actual pe geometria noastră.

Acum că am scris shaderele, putem să ne întoarcem la încărcarea lor în fișierul JS. Deci, înlocuiți "// Load Shaders Here" cu următorul cod:

var FShader = document.getElementById (FSID); var VShader = document.getElementById (VSID); dacă (! FShader ||! VShader) alertă ("Eroare, nu s-ar putea găsi Shaders"); altfel // Încărcați și compilați Fragment Shader var Cod = LoadShader (FShader); FShader = acest.GL.createShader (acest.GL.FRAGMENT_SHADER); this.GL.shaderSource (FShader, Cod); this.GL.compileShader (FShader); // Încărcați și compilați Codul de Shader Vertex = LoadShader (VShader); VShader = acest.GL.createShader (acest.GL.VERTEX_SHADER); this.GL.shaderSource (VShader, Cod); this.GL.compileShader (VShader); // Creați programul Shader this.ShaderProgram = acest.GL.createProgram (); acest.GL.attachShader (acest.ShaderProgram, FShader); acest.GL.attachShader (acest.ShaderProgram, VShader); this.GL.linkProgram (this.ShaderProgram); this.GL.useProgram (this.ShaderProgram); // Link Atributul de poziție a vârfului din Shader this.VertexPosition = this.GL.getAttribLocation (acest.ShaderProgram, "VertexPosition"); this.GL.enableVertexAttribArray (this.VertexPosition); // Atributul de coordonate al texturii de legătură din Shader this.VertexTexture = this.GL.getAttribLocation (acest.ShaderProgram, "TextureCoord"); this.GL.enableVertexAttribArray (this.VertexTexture); 

Texturile dvs. trebuie să fie în dimensiuni egale de octeți sau veți primi o eroare ... cum ar fi 2x2, 4x4, 16x16, 32x32 ...

Mai întâi ne asigurăm că shaderele există și apoi continuăm să le încărcăm unul câte unul. Procesul obține de fapt codul sursă al shader-ului, îl compilează și îl atașează programului central de shader. Există o funcție, numită LoadShader, care primește codul shader din fișierul HTML; vom ajunge la asta într-o secundă. Folosim programul "shader" pentru a lega împreună cele două shadere și ne dă acces la variabilele lor. Stocăm cele două atribute pe care le-am definit în shadere; astfel încât să putem introduce geometria noastră în ele mai târziu.

Acum, să ne uităm la funcția LoadShader, ar trebui să puneți acest lucru în afara funcției WebGL:

funcția LoadShader (Script) var Code = ""; var CurrentChild = Script.firstChild; în timp ce (CurrentChild) if (CurrentChild.nodeType == CurrentChild.TEXT_NODE) ​​Cod + = CurrentChild.textContent; CurrentChild = CurrentChild.nextSibling;  returnează codul; 

În principiu, doar cicluri prin shader și colectează codul sursă.


Pasul 2: Cubul "simplu"

Pentru a desena obiecte în WebGL veți avea nevoie de următoarele trei tablouri:

  • noduri; punctele care alcătuiesc obiectele
  • triunghiuri; spune WebGL cum se conectează vârfurile în suprafețe
  • texturi de coordonate; definește modul în care sunt însemnate nodurile pe imaginea texturii

Aceasta este denumită mapare UV. Pentru exemplul nostru să creăm un cub de bază. Voi împărți cubul în 4 noduri pe fiecare parte care se conectează în două triunghiuri. să facem o variabilă care să dețină o matrice de cuburi.

var Cube = Vertex: [// X, Y, Z Coordonate // Front 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, // 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, // 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 , -1.0, 1.0, -1.0, -1.0, // Stânga -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, // Sus 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, / Bottom 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0 , -1,0, -1,0, -1,0, -1,0, -1,0], Triunghiuri: [// De asemenea, în grupuri de trei, pentru a defini cele trei puncte ale fiecărui triunghi // Numerele de aici sunt numerele indexului din matricea vârfurilor // Partea dreaptă 0, 1, 2, 1, 2, 3, // Înapoi 4, 5, 6, 5, 6, 7, // Dreapta 8, 9, 10, 9, 10, 14, 13, 14, 15, // Top 16, 17, 18, 17, 18, 19, // Bot 20, 21, 22, 21, 22, 23], Textură: [// Această matrice este în grupuri de două, coordonatele x și y (aka U, V) în textura // Numerele merg de la 0.0 la 1.0, O pereche pentru fiecare vertex // Front 1.0, 1.0, 1.0, 0.0 , 0.0, 1.0, 0.0, 0.0, // Înapoi 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, // Dreapta 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, // Top 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0];

Poate părea o mulțime de date pentru un simplu cub, totuși, în partea a doua a acestui tutorial, voi face un script care va importa modelele 3D, astfel încât să nu trebuie să vă faceți griji cu privire la calcularea acestora.

S-ar putea sa te intrebi si de ce am facut 24 de puncte (4 pentru fiecare parte), cand exista doar opt puncte unice pe un cub? Am făcut acest lucru pentru că puteți atribui doar o coordonate de textura pe fiecare vertex; dacă aș pune doar cele 8 puncte, atunci întregul cub ar trebui să arate la fel pentru că ar înfășura textura în jurul tuturor laturilor pe care le atinge vertexul. Dar în acest fel, fiecare parte are punctele sale proprii, astfel încât să putem pune o parte diferită a texturii pe fiecare parte.

Acum avem această variabilă cub și suntem gata să începem să o desenăm. Să ne întoarcem la metoda WebGL și să adăugăm o A desena funcţie.


Pasul 3: Funcția Draw

Procedura de desenare a obiectelor în WebGL are numeroși pași; deci este o idee bună să faceți o funcție pentru a simplifica procesul. Ideea de bază este de a încărca cele trei tablouri în tampoane WebGL. Apoi conectăm aceste tampoane la atributele definite în shadere împreună cu matricele de transformare și perspectivă. Apoi, trebuie să încărăm textura în memorie și, în cele din urmă, putem apela a desena comanda. Deci sa începem.

Următorul cod intră în funcția WebGL:

this.Draw = funcție (Obiect, Textură) var VertexBuffer = this.GL.createBuffer (); // Creați un buffer nou // Legați-l ca Bufferul curent this.GL.bindBuffer (this.GL.ARRAY_BUFFER, VertexBuffer); // Completați-l cu datele this.GL.bufferData (this.GL.ARRAY_BUFFER, noi Float32Array (Object.Vertices), this.GL.STATIC_DRAW); // Connect Buffer Pentru atributul Shader this.GL.vertexAttribPointer (this.VertexPosition, 3, this.GL.FLOAT, false, 0, 0); // Repeat pentru următorii doi var TextureBuffer = this.GL.createBuffer (); this.GL.bindBuffer (acest.GL.ARRAY_BUFFER, TextureBuffer); this.GL.bufferData (acest.GL.ARRAY_BUFFER, nou Float32Array (Object.Texture), this.GL.STATIC_DRAW); this.GL.vertexAttribPointer (acest.VertexTexture, 2, this.GL.FLOAT, false, 0, 0);
 var TriangleBuffer = acest.GL.createBuffer (); this.GL.bindBuffer (acest.GL.ELEMENT_ARRAY_BUFFER, TriangleBuffer); // Generați Perspectiva Matrix var PerspectiveMatrix = MakePerspective (45, this.AspectRatio, 1, 10000.0); var TransformMatrix = MakeTransform (obiect); // Setați slotul 0 ca textura activă this.GL.activeTexture (this.GL.TEXTURE0); // Încărcați în textură în memorie this.GL.bindTexture (this.GL.TEXTURE_2D, Textură); // Actualizați proba de textură în shaderul de fragmente pentru a utiliza slotul 0 this.GL.uniform1i (acest.GL.getUniformLocation (acest.ShaderProgram, "uSampler"), 0); // Setați matricea de perspectivă și transformare var pmatrix = this.GL.getUniformLocation (acest.ShaderProgram, "PerspectiveMatrix"); this.GL.uniformMatrix4fv (pmatrix, false, noul Float32Array (PerspectiveMatrix)); var tmatrix = acest.GL.getUniformLocation (acest.ShaderProgram, "TransformationMatrix"); this.GL.uniformMatrix4fv (tmatrix, false, noul Float32Array (TransformMatrix)); // Desenați triunghiurile this.GL.drawElements (this.GL.TRIANGLES, Object.Trinagles.length, this.GL.UNSIGNED_SHORT, 0); ;

Pozițiile de shader al vârfurilor se rotesc și vă ajustează obiectul pe baza matricelor de transformare și de perspectivă. Vom merge mai profund în transformări în a doua parte a acestei serii.

Am adăugat două funcții: MakePerspective () și MakeTransform (). Acestea generează doar matricele necesare 4x4 pentru WebGL. MakePerspective () Funcția acceptă câmpul vertical de vizualizare, raportul de aspect și cele mai apropiate și cele mai îndepărtate puncte ca argumente. Orice care este mai aproape de 1 unitate și mai mult de 10000 de unități nu va fi afișat, dar puteți edita aceste valori pentru a obține efectul pe care îl căutați. Acum, să aruncăm o privire asupra acestor două funcții:

funcția MakePerspective (FOV, AspectRatio, Closest, Farest) var YLimit = Cel mai apropiat * Math.tan (FOV * Math.PI / 360); var A = - (Farest + mai apropiat) / (Farest - cel mai apropiat); var B = -2 * Farest * cel mai apropiat / (Farest - cel mai apropiat); var C = (2 * cel mai apropiat) / ((YLimit * AspectRatio) * 2); var D = (2 * cel mai apropiat) / (YLimit * 2); returnați [C, 0, 0, 0, 0, D, 0, 0, 0, 0, A, -1, 0, 0, B, 0];  funcția MakeTransform (obiect) return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -6; 

Ambele matrice afectează aspectul final al obiectelor dvs., dar matricea perspectivă vă editează "lumea 3D", precum câmpul vizual și obiectele vizibile, în timp ce matricea de transformare editează obiectele individuale, cum ar fi scala și poziția lor de rotație. Cu acest lucru suntem aproape gata să desenați, tot ce a mai rămas este o funcție de a converti o imagine într-o textură WebGL.


Pasul 4: Încărcarea texturilor

Încărcarea unei texturi este un proces în două etape. Mai întâi trebuie să încărcăm o imagine ca și cum ați fi într-o aplicație standard JavaScript și apoi trebuie să o convertim într-o textură WebGL. Deci, să începem cu a doua parte, deoarece suntem deja în dosarul JS. Adăugați următoarele în partea de jos a funcției WebGL imediat după comanda Draw:

this.LoadTexture = funcția (Img) // Creați o nouă textura și asociați-o ca una activă var TempTex = this.GL.createTexture (); this.GL.bindTexture (acest.GL.TEXTURE_2D, TempTex); // Flip pozitiv Y (opțional) this.GL.pixelStorei (this.GL.UNPACK_FLIP_Y_WEBGL, true); // Încărcați în Imagine this.GL.texImage2D (this.GL.TEXTURE_2D, 0, this.GL.RGBA, this.GL.RGBA, this.GL.UNSIGNED_BYTE, Img); // Instalarea proprietăților de scalare this.GL.texParameteri (this.GL.TEXTURE_2D, this.GL.TEXTURE_MAG_FILTER, this.GL.LINEAR); this.GL.texParameteri (acest.GL.TEXTURE_2D, this.GL.TEXTURE_MIN_FILTER, this.GL.LINEAR_MIPMAP_NEAREST); this.GL.generateMipmap (this.GL.TEXTURE_2D); // Decupați textura și returnați-o. this.GL.bindTexture (acest.GL.TEXTURE_2D, null); returnați TempTex; ;

Merită remarcat faptul că texturile dvs. trebuie să aibă dimensiuni egale de octeți sau veți primi o eroare; astfel încât acestea trebuie să fie dimensiuni, cum ar fi 2x2, 4x4, 16x16, 32x32, și așa mai departe. Am adăugat linia pentru a răsturna coordonatele Y pur și simplu pentru că coordonatele Y ale aplicației mele 3D erau înapoi, dar va depinde de ceea ce utilizați. Acest lucru se datorează faptului că unele programe fac 0 în axa Y colțul din stânga sus și unele aplicații fac colțul din stânga jos. Proprietățile de scalare pe care le-am stabilit spun doar WebGL cum ar trebui să scadă imaginea și să scadă în jos. Puteți juca în jurul valorii de cu diferite opțiuni pentru a obține efecte diferite, dar m-am gândit că acestea au lucrat cel mai bine.

Acum că am terminat cu fișierul JS, să revenim la fișierul HTML și să implementăm toate acestea.


Pasul 5: Înfășurați-o

După cum am menționat mai devreme, WebGL se transformă într-un element de panza. Asta e tot ce avem nevoie în secțiunea corpului. După adăugarea elementului de pânză, pagina dvs. html ar trebui să arate după cum urmează:

        Browserul dvs. nu acceptă Canvasul HTML5.     

Este o pagină destul de simplă. În zona capului m-am conectat la fișierul JS. Acum, să implementăm funcția Ready, care este apelată atunci când se încarcă pagina:

// Aceasta va menține variabila noastră WebGL var GL; // textura noastră fină var Textura; // Aceasta va tine imaginea texturilor var TextureImage; funcția Ready () GL = WebGL nou ("GLCanvas", "FragmentShader", "VertexShader"); TextureImage = imagine nouă (); TextureImage.onload = funcție () Textură = GL.LoadTexture (TextureImage); GL.Draw (Cub, Textură); ; TextureImage.src = "Textura.png"; 

Așadar, vom crea un nou obiect WebGL și vom transmite ID-urile pentru pânză și shadere. Apoi, încărcăm imaginea texturii. Odată încărcate, sunăm A desena() cu Cubul și textura. Dacă ați urmat de-a lungul, ecranul dvs. ar trebui să aibă un cub static, cu o textură pe ea.

Acum, chiar dacă am spus că vom acoperi transformările data viitoare, nu pot să te las cu un pătrat static; nu este destul de 3D. Să ne întoarcem și să adăugăm o rotație mică. În fișierul HTML, schimbați onload funcția de a arăta astfel:

TextureImage.onload = funcție () Textură = GL.LoadTexture (TextureImage); setInterval (Actualizare, 33); ;

Aceasta va numi o funcție numită Actualizați() la fiecare 33 milisecunde care ne va da o rată a cadrelor de aproximativ 30 fps. Aici este funcția de actualizare:

funcția Update () GL.GL.clear (16384 | 256); GL.Draw (GL.Cube, Textură); 

Aceasta este o funcție destul de simplă; șterge ecranul și apoi trage Cube-ul actualizat. Acum, hai să mergem la fișierul JS pentru a adăuga codul de rotație.


Pasul 6: Adăugarea unor rotiri

Nu voi implementa complet transformările, pentru că o salvez pentru data viitoare, dar să adăugăm o rotație în jurul axei Y. Primul lucru pe care trebuie să-l faceți este să adăugați o variabilă de rotație obiectului Cube. Aceasta va ține evidența unghiului curent și ne va permite să continuăm să creștem rotația. Deci, partea de sus a variabilei Cube ar trebui să arate astfel:

var Cube = Rotire: 0, // Celelalte trei arrayuri;

Acum, să actualizăm MakeTransform () funcția de a include rotația:

funcția MakeTransform (obiect) var y = Object.Rotation * (Math.PI / 180.0); var A = Math.cos (y); var B = -1 * Math.sin (y); var C = Math.sin (y); var D = Math.cos (y); Obiect.Rotație + = .3; returnați [A, 0, B, 0, 0, 1, 0, 0, C, 0, D, 0, 0, 0, -6, 1]; 

Concluzie

Si asta e! În următorul tutorial, vom acoperi modelele de încărcare și transformările efectuate. Sper că ți-a plăcut acest tutorial; nu ezitați să lăsați orice întrebări sau comentarii pe care ar trebui să le aveți mai jos.

Cod