WebGL är en 3D-återgivning i webbläsare baserat på OpenGL, som låter dig visa ditt 3D-innehåll direkt på en HTML5-sida. I den här handledningen kommer jag att täcka alla de grundläggande förutsättningarna du behöver för att börja använda denna ram.
Det finns några saker du borde veta innan vi börjar. WebGL är ett JavaScript-API som gör 3D-innehåll till en HTML5-kanfas. Det gör det genom att använda två skript som är kända i "3D-världen" som shaders. De två shadersna är:
Nu blir du inte för nervös när du hör dessa namn. Det är bara ett fint sätt att säga, "position calculator" och "color chooser" respektive. Fragmentskärmen är det enklare att förstå; det berättar helt enkelt för WebGL vilken färg en viss punkt på din modell ska vara. Vertex shader är lite mer teknisk, men omvandlar i grunden punkterna i dina 3D-modeller till 2D-koordinater. Eftersom alla datorskärmar är platta 2D-ytor, och när du ser 3D-objekt på skärmen är de bara en illusion av perspektiv.
Om du vill veta exakt hur denna beräkning fungerar, skulle du behöva fråga en matematiker, eftersom den använder avancerade 4 x 4 matrixmultiplikationer, som ligger lite längre än "Essentials" -handledningen. Lyckligtvis behöver du inte veta hur det fungerar, eftersom WebGL tar hand om det mesta. Så låt oss börja.
WebGL har många små inställningar som du måste installera nästan varje gång du ritar något till skärmen. För att spara tid och göra din kod snygging ska jag göra ett JavaScript-objekt som innehåller alla "bakom scenen" saker i en separat fil. För att komma igång, skapa en ny fil som heter 'WebGL.js' och placera följande kod inuti den:
funktionen WebGL (CID, FSID, VSID) var canvas = document.getElementById (CID); om (! canvas.getContext ("webgl") &&! canvas.getContext ("experimental-webgl")) alert ("Din webbläsare stöder inte WebGL"); annars this.GL = (canvas.getContext ("webgl"))? canvas.getContext ("webgl"): canvas.getContext ("experimental-webgl"); this.GL.clearColor (1,0, 1,0, 1,0, 1,0); // detta är färgen this.GL.enable (this.GL.DEPTH_TEST); // Aktivera djuptestning this.GL.depthFunc (this.GL.LEQUAL); // Ange perspektiv Visa this.AspectRatio = canvas.width / canvas.height; // Ladda Shaders Here
Denna konstruktörsfunktion tar in kanvasens ID och de två skuggobjekten. Först får vi dukelementet och se till att det stöder WebGL. Om det gör så tilldelar vi WebGL-kontexten till en lokal variabel som heter "GL". Den klara färgen är helt enkelt bakgrundsfärgen och det är värt att notera att i WebGL går de flesta parametrarna från 0,0 till 1,0, så du måste dela dina rgb-värden med 255. Så i vårt exempel betyder 1,0, 1,0, 1,0, 1,0 En vit bakgrund med 100% synlighet (ingen öppenhet). De två följande raderna berättar för WebGL att beräkna djup och perspektiv så att ett objekt närmare dig kommer att blockera objekt bakom det. Slutligen bestämmer vi bildförhållandet som beräknas genom att dividera dukens bredd med sin höjd.
Innan vi fortsätter och laddar de två shadersna, låt oss skriva dem. Jag ska skriva dessa i HTML-filen där vi ska sätta själva kanvaselementet. Skapa en HTML-fil, och placera följande två skriptelement precis innan den stängande koden tagg:
Vertex shader skapas först och vi definierar två attribut:
Därefter skapar vi variabler för transformations- och perspektivmatriserna. Dessa används för att konvertera 3D-modellen till en 2D-bild. Nästa rad skapar en delad variabel i fragmentskärmen, och i huvudfunktionen beräknar vi gl_Position (den sista 2D-positionen). Vi tilldelar sedan den nuvarande textkoordinaten till den delade variabeln.
I fragmentskärmen tar vi bara de koordinater vi definierade i vertex shader och vi "provar" strukturen vid den koordinaten. I grund och botten får vi bara färgen i texturen som motsvarar den aktuella punkten på vår geometri.
Nu när vi har skrivit shadersna kan vi gå tillbaka för att ladda dem i vår JS-fil. Så ersätt "// Load Shaders Here" med följande kod:
var FShader = document.getElementById (FSID); var VShader = document.getElementById (VSID); om (! FShader ||! VShader) alert ("Fel kunde inte hitta Shaders"); annars // Ladda och kompilera fragment Shader var Code = LoadShader (FShader); FShader = this.GL.createShader (this.GL.FRAGMENT_SHADER); this.GL.shaderSource (FShader, Code); this.GL.compileShader (FShader); // Ladda och kompilera Vertex Shader Code = LoadShader (VShader); VShader = this.GL.createShader (this.GL.VERTEX_SHADER); this.GL.shaderSource (VShader, Code); this.GL.compileShader (VShader); // Skapa Shader-programmet this.ShaderProgram = this.GL.createProgram (); this.GL.attachShader (this.ShaderProgram, FShader); this.GL.attachShader (this.ShaderProgram, VShader); this.GL.linkProgram (this.ShaderProgram); this.GL.useProgram (this.ShaderProgram); // Länk Vertex Position Attribut från Shader this.VertexPosition = this.GL.getAttribLocation (this.ShaderProgram, "VertexPosition"); this.GL.enableVertexAttribArray (this.VertexPosition); // Link Texture Coordinate Attribut från Shader this.VertexTexture = this.GL.getAttribLocation (this.ShaderProgram, "TextureCoord"); this.GL.enableVertexAttribArray (this.VertexTexture);
Dina texturer måste vara i jämn byte storlek eller du kommer att få ett fel ... som 2x2, 4x4, 16x16, 32x32 ...
Vi först ser till att shadersna finns och sedan fortsätter vi att ladda dem en åt gången. Processen får i princip shaderens källkod, sammanställer den och bifogar den till det centrala shader-programmet. Det finns en funktion, kallad LoadShader, som får skuggkoden från HTML-filen. vi kommer till det på en sekund. Vi använder "shader-programmet" för att länka de två shadersna tillsammans, och det ger oss tillgång till deras variabler. Vi lagrar de två attribut som vi definierade i shadersna; så vi kan senare lägga in vår geometri.
Låt oss nu titta på LoadShader-funktionen. Du bör placera detta utanför WebGL-funktionen:
funktion LoadShader (Script) var Code = ""; var CurrentChild = Script.firstChild; medan (CurrentChild) if (CurrentChild.nodeType == CurrentChild.TEXT_NODE) Kod + = CurrentChild.textContent; CurrentChild = CurrentChild.nextSibling; returkod;
Det går i grund och botten bara genom skuggaren och samlar källkoden.
För att rita objekt i WebGL behöver du följande tre arrays:
Detta kallas UV-kartläggning. För vårt exempel låt oss skapa en grundläggande kub. Jag kommer att dela kuben i 4 hörn per sida som ansluter till två trianglar. låt oss göra en variabel som kommer att hålla en kub s arrays.
var Cube = Vertices: [// X, Y, Z Koordinater // 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, // Tillbaka 1,0, 1,0, 1,0, 1,0, -1,0, 1,0, -1,0, 1,0, 1,0, -1,0, -1,0, 1,0, // Rätt 1,0, 1,0, 1,0, 1,0, -1,0, 1,0, 1,0, 1,0 , -1,0, 1,0, -1,0, -1,0, // vänster -1,0, 1,0, 1,0, -1,0, -1,0, 1,0, -1,0, 1,0, -1,0, -1,0, -1,0, -1,0, // topp 1,0, 1,0, 1,0, -1,0, -1,0, 1,0, 1,0, -1,0, -1,0, -1,0, -1,0, -1,0, / / Botten 1,0, -1,0, 1,0, -1,0, -1,0, 1,0, 1,0 , -1,0, -1,0, -1,0, -1,0, -1,0], Trianglar: [// Också i tre grupper för att definiera de tre punkterna i varje triangel // Numren här är indexnumren i vertexuppsättningen // Front 0, 1, 2, 1, 2, 3, // Tillbaka 4, 5, 6, 5, 6, 7, // Höger 8, 9, 10, 9, 10, 11, // Vänster 12, 13, 14, 13, 14, 15, // Topp 16, 17, 18, 17, 18, 19, // Botten 20, 21, 22, 21, 22, 23], Textur: [// Denna array är i grupper av två, x- och y-koordinaterna (aka U, V) i texturen // Antalet går från 0,0 till 1,0, Ett par för varje toppunkt // Fram 1,0, 1,0, 1,0, 0,0 , 0,0, 1,0, 0,0, 0,0, / / Tillbaka 0,0, 1,0, 0,0, 0,0, 1,0, 1,0, 1,0, 0,0, // Höger 1,0, 1,0, 1,0, 0,0, 0,0, 1,0, 0,0, 0,0, // Vänster 0,0, 0,0, 1,0, 1,0, 0,0, 1,0, 1,0, 0,0, 1,0, 1,0, 0,0, 1,0, 1,0, 0,0, 1,0, 1,0];
Det kan verka som mycket data för en enkel kub, men i del två av denna handledning kommer jag att göra ett manus som kommer att importera dina 3D-modeller så att du inte behöver oroa dig för att beräkna dessa.
Du kanske också undrar varför jag gjorde 24 poäng (4 för varje sida), när det bara finns åtta totalt unika poäng på en kub? Jag gjorde detta eftersom du bara kan tilldela en texturkoordinat per vertex; så om vi bara skulle sätta in de 8 poängen, så skulle hela kuben se ut på samma sätt eftersom det skulle vikla texturen runt alla sidor som vertexen berör. Men på så sätt har varje sida sina egna poäng så att vi kan lägga en annan del av texturen på varje sida.
Vi har nu denna kubvariabel och är redo att börja rita den. Låt oss gå tillbaka till WebGL-metoden och lägga till en Dra
fungera.
Förfarandet för ritning av objekt i WebGL har många steg; så det är en bra idé att göra en funktion för att förenkla processen. Grundsidan är att ladda de tre raderna till WebGL buffertar. Vi kopplar sedan dessa buffertar till de attribut vi definierade i shadersna tillsammans med transformations- och perspektivmatriserna. Därefter måste vi ladda texturen i minnet, och till sist kan vi ringa dra
kommando. Så låt oss börja.
Följande kod går in i WebGL-funktionen:
this.Draw = funktionen (Objekt, Texture) var VertexBuffer = this.GL.createBuffer (); // Skapa en ny buffert // Bind den som den aktuella bufferten this.GL.bindBuffer (this.GL.ARRAY_BUFFER, VertexBuffer); // Fyll det med data this.GL.bufferData (this.GL.ARRAY_BUFFER, nya Float32Array (Object.Vertices), this.GL.STATIC_DRAW); // Anslut buffert till Shaders attribut här.GL.vertexAttribPointer (this.VertexPosition, 3, this.GL.FLOAT, false, 0, 0); // Repeat For Next Two var TextureBuffer = this.GL.createBuffer (); this.GL.bindBuffer (this.GL.ARRAY_BUFFER, TextureBuffer); this.GL.bufferData (this.GL.ARRAY_BUFFER, ny Float32Array (Object.Texture), this.GL.STATIC_DRAW); this.GL.vertexAttribPointer (this.VertexTexture, 2, this.GL.FLOAT, false, 0, 0);
var TriangleBuffer = this.GL.createBuffer (); this.GL.bindBuffer (this.GL.ELEMENT_ARRAY_BUFFER, TriangleBuffer); // Generera perspektivmatrisen var PerspectiveMatrix = MakePerspective (45, this.AspectRatio, 1, 10000.0); var TransformMatrix = MakeTransform (Object); // Ställ in 0 som den aktiva Texturen this.GL.activeTexture (this.GL.TEXTURE0); // Ladda i texturen till minnet this.GL.bindTexture (this.GL.TEXTURE_2D, Texture); // Uppdatera Textur Sampler i fragmentet skuggare för att använda plats 0 this.GL.uniform1i (this.GL.getUniformLocation (this.ShaderProgram, "uSampler"), 0); // Ange perspektiv och transformationsmatriser var pmatrix = this.GL.getUniformLocation (this.ShaderProgram, "PerspectiveMatrix"); this.GL.uniformMatrix4fv (pmatrix, false, new Float32Array (PerspectiveMatrix)); var tmatrix = this.GL.getUniformLocation (this.ShaderProgram, "TransformationMatrix"); this.GL.uniformMatrix4fv (tmatrix, false, new Float32Array (TransformMatrix)); // Rita trianglarna this.GL.drawElements (this.GL.TRIANGLES, Object.Trinagles.length, this.GL.UNSIGNED_SHORT, 0); ;
Vertex shader positionerar, roterar och skalerar ditt objekt baserat på transformations- och perspektivmatriserna. Vi kommer att gå mer djupt i omvandlingar i den andra delen av denna serie.
Jag har lagt till två funktioner: MakePerspective ()
och MakeTransform ()
. Dessa skapar bara de nödvändiga 4x4-matriserna för WebGL. De MakePerspective ()
funktionen accepterar det vertikala synfältet, bildförhållandet och de närmaste och längsta punkterna som argument. Allt som är närmare än 1 enhet och mer än 10000 enheter kommer inte att visas, men du kan redigera dessa värden för att få den effekt du söker. Låt oss nu titta på dessa två funktioner:
funktion MakePerspective (FOV, AspectRatio, Närmsta, Fjärsta) var YLimit = Närmaste * Math.tan (FOV * Math.PI / 360); var A = - (längst + närmast) / (längst - närmast); var B = -2 * Fjärsta * Närmaste / (Fjärsta - Närmaste); var C = (2 * närmast) / ((YLimit * AspectRatio) * 2); var D = (2 * närmast) / (YLimit * 2); returnera [C, O, O, O, D, O, O, O, O, A, -1, O, O, B, 0]; funktion MakeTransform (Object) returnera [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -6, 1];
Båda dessa matriser påverkar ditt objekts slutliga utseende, men perspektivmatrisen ändrar din "3D-värld" som synfältet och de synliga objekten medan transformationsmatrisen ändrar de enskilda objekten som deras rotationsskala och position. Med detta gjort är vi nästan redo att rita, allt som finns kvar är en funktion för att konvertera en bild till en WebGL-textur.
Att ladda en textur är en tvåstegs process. Först måste vi ladda en bild som du skulle i en vanlig JavaScript-applikation, och då måste vi konvertera den till en WebGL-textur. Så låt oss börja med den andra delen eftersom vi redan är i JS-filen. Lägg till följande längst ner i WebGL-funktionen direkt efter kommandot Rita:
this.LoadTexture = funktion (Img) // Skapa en ny textur och ange den som den aktiva var var TempTex = this.GL.createTexture (); this.GL.bindTexture (this.GL.TEXTURE_2D, TempTex); // Flip Positive Y (Valfritt) this.GL.pixelStorei (this.GL.UNPACK_FLIP_Y_WEBGL, true); // Ladda in bilden this.GL.texImage2D (this.GL.TEXTURE_2D, 0, this.GL.RGBA, this.GL.RGBA, this.GL.UNSIGNED_BYTE, Img); // Inställningsskalningsegenskaper this.GL.texParameteri (this.GL.TEXTURE_2D, this.GL.TEXTURE_MAG_FILTER, this.GL.LINEAR); this.GL.texParameteri (this.GL.TEXTURE_2D, this.GL.TEXTURE_MIN_FILTER, this.GL.LINEAR_MIPMAP_NEAREST); this.GL.generateMipmap (this.GL.TEXTURE_2D); // Unbind texturen och returnera den. this.GL.bindTexture (this.GL.TEXTURE_2D, null); returnera TempTex; ;
Det är värt att notera att dina texturer måste vara i jämn byte storlekar, eller du får ett fel; så de måste vara dimensioner, som 2x2, 4x4, 16x16, 32x32 och så vidare. Jag lade till raden för att vända Y-koordinaterna helt enkelt för att mina 3D-programmets Y-koordinater var bakåt, men det beror på vad du använder. Detta beror på att vissa program gör 0 i Y-axeln i övre vänstra hörnet och vissa applikationer gör det till vänstra hörnet. Skalningsegenskaperna som jag ställer in berättar bara för WebGL hur bilden ska uppskalas och nedskalas. Du kan leka med olika alternativ för att få olika effekter, men jag trodde att de fungerade bäst.
Nu när vi är färdiga med JS-filen, låt oss återvända till HTML-filen och genomföra allt detta.
Som jag nämnde tidigare ger WebGL till ett dukelement. Det är allt vi behöver i kroppssektionen. Efter att du har lagt in dukelementet, ska din HTML-sida se ut som följer:
Det är en ganska enkel sida. I huvudområdet har jag länkat till vår JS-fil. Låt oss nu genomföra vår färdiga funktion som heter när sidan laddas:
// Detta kommer att hålla vår WebGL-variabel var GL; // Vår färdiga textur var Textur; // Detta kommer att hålla texturerna bild var TextureImage; funktion Klar () GL = ny WebGL ("GLCanvas", "FragmentShader", "VertexShader"); TextureImage = ny bild (); TextureImage.onload = function () Texture = GL.LoadTexture (TextureImage); GL.Draw (Cube, Texture); ; TextureImage.src = "Texture.png";
Så vi skapar ett nytt WebGL-objekt och skickar in ID-erna för duken och shaders. Därefter laddar vi texturbilden. En gång laddad, vi kallar Dra()
metod med kuben och strukturen. Om du följde efter bör din skärm ha en statisk kub med en textur på den.
Nu trots att jag sa att vi kommer att täcka omvandlingar nästa gång, kan jag inte bara lämna dig med en statisk kvadrat; det är inte 3D nog. Låt oss gå tillbaka och lägga till en liten rotation. I HTML-filen, ändra onload
funktion att se ut så här:
TextureImage.onload = function () Texture = GL.LoadTexture (TextureImage); setInterval (Uppdatering, 33); ;
Detta kommer att ringa en funktion som heter Uppdatering()
var 33: e millisekunder som ger oss en bildfrekvens på cirka 30 bilder / sek. Här är uppdateringsfunktionen:
funktionuppdatering () GL.GL.clear (16384 | 256); GL.Draw (GL.Cube, Texture);
Detta är en ganska enkel funktion; det rensar skärmen och drar sedan den uppdaterade kuben. Nu, låt oss gå till JS-filen för att lägga till rotationskoden.
Jag kommer inte att fullt ut genomföra transformationer, för jag sparar det för nästa gång, men låt oss lägga till en rotation runt Y-axeln. Det första du gör är att lägga till en rotationsvariabel till vårt kubobjekt. Detta kommer att hålla reda på den aktuella vinkeln och låta oss fortsätta att öka rotationen. Så toppen av din Cube-variabel ska se ut så här:
var Cube = Rotation: 0, // De övriga tre arraysna;
Nu ska vi uppdatera MakeTransform ()
funktion för att införliva rotationen:
funktion MakeTransform (Object) 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); Object.Rotation + = .3; returnera [A, O, B, O, 0, 1, 0, 0, C, 0, D, 0, 0, 0, -6, 1];
Och det är allt! I nästa handledning täcker vi laddningsmodeller och utför transformationer. Jag hoppas att du haft denna handledning Lämna gärna några frågor eller kommentarer som du kan ha nedan.