webglAan de slag met webgl


Opmerkingen

WebGL is een rasterisatie-API die meestal op uw GPU wordt uitgevoerd en waarmee u snel 2D- en 3D-afbeeldingen kunt tekenen. WebGL kan ook worden gebruikt om berekeningen uit te voeren op gegevensreeksen.

WebGL is een API op zeer laag niveau. Op een basisniveau is WebGL een engine die 2 door de gebruiker geleverde functies op de GPU uitvoert. Een functie wordt een hoekpuntshader genoemd . De taak van een hoekpuntshader is om hoekpuntposities te berekenen. Op basis van de posities kan de functie-uitvoer WebGL vervolgens verschillende soorten primitieven rasteriseren, waaronder punten, lijnen of driehoeken. Bij het rasteren van deze primitieven roept het een tweede door de gebruiker geleverde functie aan, genaamd een fragment-arcering . De taak van een fragment-arcering is om een kleur te berekenen voor elke pixel van de primitief die momenteel wordt getekend.

Deze functies zijn geschreven in een taal genaamd GLSL die enigszins op C / C ++ lijkt en strikt wordt getypt.

Het is aan de programmeur om die functies te leveren om WebGL 2d, 3d te laten tekenen of iets te laten berekenen. Bijna elk onderdeel van WebGL gaat over het instellen van die 2 functies en vervolgens gegevens aan hen verstrekken.

Gegevens kunnen uit 4 bronnen worden geleverd.

  • uniformen

Uniformen zijn ingangen voor shader-functies, net als functieparameters of globale variabelen. Ze worden eenmaal ingesteld voordat een arcering wordt uitgevoerd en blijven constant tijdens het uitvoeren

  • attributen

Attributen leveren alleen gegevens aan hoekpuntschaduwen. Attributen definiëren hoe gegevens uit buffers worden gehaald. U kunt bijvoorbeeld posities, normale en textuurcoördinaten in een buffer plaatsen. Met attributen kunt u WebGL vertellen hoe u die gegevens uit uw buffers kunt halen en aan een hoekpuntshader kunt leveren. Een hoekpuntshader wordt een door de gebruiker opgegeven aantal keren genoemd door gl.drawArrays of gl.drawElements en een telling op te geven. Elke keer dat de huidige hoekpuntshader wordt genoemd, wordt de volgende set gegevens opgehaald uit de door de gebruiker opgegeven buffers en worden de kenmerken ingevoerd

  • textures

Texturen zijn 2D-arrays met gegevens tot 4 kanalen. Meestal zijn die 4 kanalen rood, groen, blauw en alfa van een afbeelding. WebGL maakt het niet uit wat de gegevens zijn. In tegenstelling tot attributen en buffers kunnen shaders waarden lezen uit texturen met willekeurige toegang.

  • Varyings

Variëren is een manier voor een hoekpunt-arcering om gegevens door te geven aan een fragment-arcering. Variërende waarden worden geïnterpoleerd tussen de waarden die worden uitgevoerd door de hoekpuntshader terwijl een primitief wordt gerasterd met behulp van een fragmentshader

Hallo Wereld

Zoals in de opmerkingen wordt vermeld, moeten we twee functies leveren. Een hoekpunt-arcering en een fragment-arcering

Laten we beginnen met een hoekpuntshader

// an attribute will receive data from a buffer
attribute vec4 position;

// all shaders have a main function
void main() {

  // gl_Position is a special variable a vertex shader 
  // is responsible for setting
  gl_Position = position;
}
 

Als het hele ding in JavaScript in plaats van GLSL was geschreven, zou je je kunnen voorstellen dat het zo zou worden gebruikt

// *** PSUEDO CODE!! ***

var positionBuffer = [
  0, 0, 0, 0,
  0, 0.5, 0, 0,
  0.7, 0, 0, 0,
];
var attributes = {};
var gl_Position;

drawArrays(..., offset, count) {
  for (var i = 0; i < count; ++i) {
     // copy the next 4 values from positionBuffer to the position attribute
     attributes.position = positionBuffer.slice((offset + i) * 4, 4); 
     runVertexShader();
     ...
     doSomethingWith_gl_Position();
}
 

Vervolgens hebben we een fragment-arcering nodig

// fragment shaders don't have a default precision so we need
// to pick one. mediump, short for medium precision, is a good default.
precision mediump float;

void main() {
  // gl_FragColor is a special variable a fragment shader
  // is responsible for setting
  gl_FragColor = vec4(1, 0, 0.5, 1); // return redish-purple 
}
 

Hierboven stellen we gl_FragColor op 1, 0, 0.5, 1 wat 1 is voor rood, 0 voor groen, 0,5 voor blauw, 1 voor alfa. Kleuren in WebGL gaan van 0 naar 1.

Nu we de 2 functies hebben geschreven, kunnen we aan de slag met WebGL

Eerst hebben we een HTML canvas-element nodig

 <canvas id="c"></canvas>
 

Dan kunnen we dat in JavaScript opzoeken

 var canvas = document.getElementById("c");
 

Nu kunnen we een WebGLRenderingContext maken

 var gl = canvas.getContext("webgl");
 if (!gl) {
    // no webgl for you!
    ...
 

Nu moeten we die shaders compileren om ze op de GPU te plaatsen, dus eerst moeten we ze in strings krijgen. Je kunt je snaren krijgen zoals je normaal snaren krijgt. Door samen te voegen, door AJAX te gebruiken, door ze in niet-JavaScript getypte scripttags te plaatsen, of in dit geval door sjabloonregels met meerdere regels te gebruiken

var vertexShaderSource = `
// an attribute will receive data from a buffer
attribute vec4 position;

// all shaders have a main function
void main() {

  // gl_Position is a special variable a vertex shader 
  // is responsible for setting
  gl_Position = position;
}
`;

var fragmentShaderSource = `
// fragment shaders don't have a default precision so we need
// to pick one. mediump is a good default
precision mediump float;

void main() {
  // gl_FragColor is a special variable a fragment shader
  // is responsible for setting
  gl_FragColor = vec4(1, 0, 0.5, 1); // return redish-purple 
}
`;
 

Dan hebben we een functie nodig die een arcering maakt, de bron uploadt en de arcering compileert

function createShader(gl, type, source) {
  var shader = gl.createShader(type);
  gl.shaderSource(shader, source);  
  gl.compileShader(shader);
  var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  if (success) {
    return shader;
  }

  console.log(gl.getShaderInfoLog(shader));
  gl.deleteShader(shader);
}
 

We kunnen die functie nu aanroepen om de 2 shaders te maken

var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
 

We moeten die 2 shaders vervolgens in een programma koppelen

function createProgram(gl, vertexShader, fragmentShader) {
  var program = gl.createProgram();
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);
  var sucesss = gl.getProgramParameter(program, gl.LINK_STATUS);
  if (success) {
    return program;
  }

  console.log(gl.getProgramInfoLog(program));
  gl.deleteProgram(program);
}
 

En noem het maar

var program = createProgram(gl, vertexShader, fragmentShader);
 

Nu we een GLSL-programma op de GPU hebben gemaakt, moeten we er gegevens aan leveren. Het grootste deel van de WebGL API gaat over het instellen van de status voor het leveren van gegevens aan onze GLSL-programma's. In dit geval is onze enige input voor ons GLSL-programma een position die een kenmerk is. Het eerste wat we moeten doen, is de locatie van het kenmerk opzoeken voor het programma dat we zojuist hebben gemaakt

var positionAttributeLocation = gl.getAttribLocation(program, "position");
 

Attributen halen hun gegevens uit buffers, dus we moeten een buffer maken

var positionBuffer = gl.createBuffer();
 

Met WebGL kunnen we veel WebGL-bronnen op globale bindpunten manipuleren. U kunt bindpunten beschouwen als interne globale variabelen in WebGL. Eerst stelt u het bindpunt in voor uw resource. Vervolgens verwijzen alle andere functies naar de resource via het bindpunt. Laten we dus de positiebuffer binden.

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
 

Nu kunnen we gegevens in die buffer plaatsen door ernaar te verwijzen via het bindpunt

// three 2d points
var positions = [
  0, 0, 
  0, 0.5,
  0.7, 0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
 

Er gebeurt hier veel. Het eerste is dat we positions die een JavaScript-array is. WebGL heeft daarentegen sterk getypeerde gegevens nodig, dus de regel new Float32Array(positions) creëert een nieuwe reeks 32bit floating point-nummers en kopieert de waarden van positions . gl.bufferData kopieert die gegevens vervolgens naar de positionBuffer op de GPU. Het gebruikt de positiebuffer omdat we deze aan het ARRAY_BUFFER hierboven hebben gebonden.

Het laatste argument, gl.STATIC_DRAW is een hint voor WebGL over hoe we de gegevens zullen gebruiken. Het kan proberen die info te gebruiken om bepaalde dingen te optimaliseren. gl.STATIC_DRAW vertelt WebGL dat we deze gegevens waarschijnlijk niet veel zullen veranderen.

Nu we gegevens in een buffer hebben geplaatst, moeten we het attribuut vertellen hoe gegevens eruit kunnen worden gehaald. Eerst moeten we het kenmerk inschakelen

gl.enableVertexAttribArray(positionAttributeLocation);
 

Dan moeten we specificeren hoe we de gegevens eruit moeten halen

var size = 2;          // 2 components per iteration
var type = gl.FLOAT;   // the data is 32bit floats
var normalize = false; // use the data as is
var stride = 0;        // 0 = move size * sizeof(type) each iteration
var offset = 0;        // start at the beginning of the buffer
gl.vertexAttribPointer(
   positionAttributeLocation, size, type, normalize, stride, offset)
 

Een verborgen onderdeel van gl.vertexAttribPointer is dat het de huidige ARRAY_BUFFER aan het kenmerk bindt. Met andere woorden, nu dit kenmerk gebonden is aan positionBuffer we iets anders binden aan het ARRAY_BUFFER .

merk op dat vanuit het oogpunt van onze GLSL vertex shader het position attribuut een vec4

attribute vec4 position;
 

vec4 is een waarde van 4 float. In JavaScript kun je denken aan zoiets als position = {x: 0, y: 0, z: 0, w: 0} . Hierboven stellen we size = 2 . Attributen zijn standaard 0, 0, 0, 1 dus dit kenmerk krijgt de eerste 2 waarden (x en y) van onze buffer. De z en w zijn respectievelijk de standaard 0 en 1.

Uiteindelijk kunnen we WebGL eindelijk vragen om het GLSL-programma uit te voeren.

var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 3;
gl.drawArrays(primitiveType, offset, count);
 

Hiermee wordt onze hoekpuntshader 3 keer uitgevoerd. De eerste keer dat position.x en position.y in onze hoekpuntshader worden ingesteld op de eerste 2 waarden van de positionBuffer. De 2e tijd position.xy wordt ingesteld op de 2e 2 waarden. De laatste keer wordt deze ingesteld op de laatste 2 waarden.

Omdat we primitiveType op gl.TRIANGLES , tekent WebGL elke keer dat onze hoekpuntshader driemaal wordt uitgevoerd een driehoek op basis van de 3 waarden waarop we gl_Position instellen. Ongeacht de grootte van ons canvas, deze waarden staan in coördinaten van de clipruimte die van -1 naar 1 in elke richting gaan.

Omdat onze hoekpuntshader eenvoudigweg onze positionBuffer-waarden naar gl_Position de driehoek getekend op coördinaten van de gl_Position

  0, 0, 
  0, 0.5,
  0.7, 0,
 

Hoe deze waarden worden omgezet in pixels, is afhankelijk van de instelling gl.viewport . gl.viewport standaard ingesteld op de oorspronkelijke grootte van het canvas. Omdat we geen maat voor ons canvas hebben ingesteld, is dit de standaardgrootte van 300x150. Converteren van clipruimte naar pixels (vaak schermruimte genoemd in WebGL en OpenGL-literatuur) WebGL gaat een driehoek tekenen op

 clip space      screen space
   0, 0       ->   150, 75
   0, 0.5     ->   150, 112.5
 0.7, 0       ->   255, 75
 

WebGL zal die driehoek nu weergeven. Voor elke pixel die hij gaat tekenen, zal deze onze fragment shader aanroepen. Onze fragment shader stelt gewoon gl_FragColor op 1, 0, 0.5, 1 . Omdat het canvas een canvas met 8 bits per kanaal is, betekent dit dat WebGL de waarden [255, 0, 127, 255] in het canvas gaat schrijven.

Er zijn 3 belangrijke dingen die we nog steeds niet hebben behandeld uit de opmerkingen . Texturen, variaties en uniformen. Elk van deze vereist zijn eigen onderwerp.

Installatie of instellingen

WebGL is een browsertechnologie, dus er is niet veel anders in te stellen dan een browser te hebben. U kunt aan de slag met WebGL op JSFiddle of Codepen of JSBIn of een willekeurig aantal andere sites waarmee u HTML, CSS en JavaScript online kunt bewerken, hoewel er enkele beperkingen zijn (zie hieronder). U kunt ook open source-bestanden hosten op github-pagina's of vergelijkbare services.

Aan de andere kant ga je waarschijnlijk op een bepaald moment lokaal werken. Het is aan te bevelen om een eenvoudige webserver te gebruiken. Er is veel om uit te kiezen die eenvoudig te gebruiken zijn en heel weinig instellingen vereisen.

Node.js gebruiken als server

  1. installeer node.js
  2. Open een terminal- of knooppuntopdrachtprompt en typ npm install -g http-server (zet op OSX daarvoor sudo .
  3. typ http-server om de bestanden in de huidige map weer te geven OF http-server path-to-folder naar server een andere map
  4. Wijs uw browser naar http://localhost:8080/name-of-file om uw WebGL-webpagina te bekijken

Devd gebruiken als server

  1. Devd downloaden
  2. Open een terminal en voer devd uit met devd . serverbestanden uit de huidige map of devd path-to-folder om een andere map te bedienen
  3. Wijs uw browser naar http://localhost:8000/name-of-file om uw WebGL-webpagina te bekijken

Servez gebruiken als server

  1. Servez downloaden
  2. Installeer het, voer het uit
  3. Kies de map om te presenteren
  4. Kies "Start"
  5. Ga naar http://localhost:8080 of kies "Browser starten"

servez

Chrome-extensie ' Webserver voor Chrome ' gebruiken

  1. Installeer de webserver vanuit Chrome

  2. Start het via het pictogram Apps op een nieuwe tabbladpagina.

    hoe de app op Chrome te starten

  3. Stel de map in waar uw bestanden zich bevinden en klik vervolgens op de link http://127.0.0.1:8787

    instellingen dialoogvenster

Beperking van WebGL op online services

In WebGL is het heel gebruikelijk om afbeeldingen te laden. In WebGL gelden beperkingen voor het gebruik van afbeeldingen. In het bijzonder kan WebGL geen afbeeldingen van andere domeinen gebruiken zonder toestemming van de server die de afbeeldingen host. Services die momenteel toestemming geven om afbeeldingen te gebruiken, zijn imgur en flickr. Zie Cross-domeinafbeeldingen laden. Anders moet u de afbeeldingen op dezelfde server hebben als uw webpagina of andere creatieve oplossingen gebruiken, zoals het genereren van afbeeldingen met een canvas-tag