WebGL
WebGL
Børre Stenseth
Basis >En boks

En boks

Hva

Vi skal lage en gul boks som roterer. Eksempelet er modifisert fra Mozilla Development Network [1] .

Det kan være lurt å kikke på OpenGL Shading Language (GLSL) Reference Pages [2] for å få en forklaring av de enkelte metodene i shading-eksemplene og OpenGL ES 2.0 Reference Pages [3] for å få en forklaring av de enkelte metodene i javascriptet.

Fragment shader

Fragment shaderen arbeider med en farge og en lysretning. Begge er satt fra vertex shader. Merk at de har kvalifiseringen varying.

varying mediump vec3 vLighting;
varying lowp vec4 vColor;        
void main(void) {
  gl_FragColor = vec4(vColor.rgb * vLighting, vColor.a);
}

Vertex shader

De tre variablene: uMVMatrix, uPMatrix og uNormalMatrix blir satt fra Javascriptkoden. aVertexPosition representerer det aktuelle punktet, slik det er ordnet i en buffer fra Javascriptet og aVertexNormal er normalen i punktet.

Merk at hele lyssettingen etableres i shaderen.

attribute mediump vec3 aVertexNormal; // or in
attribute mediump vec3 aVertexPosition;
attribute vec4 aVertexColor;
    
uniform mediump mat4 uNormalMatrix;
uniform mediump mat4 uMVMatrix;
uniform mediump mat4 uPMatrix;
varying lowp vec4 vColor;
varying mediump vec3 vLighting;
    
void main(void) {
    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
    // Apply lighting effect        
    mediump vec3 ambientLight = vec3(0.6, 0.6, 0.6);
    mediump vec3 directionalLightColor = vec3(0.5, 0.5, 0.75);
    mediump vec3 directionalVector = vec3(0.85, 0.8, 0.75);
    mediump vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);
    mediump float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
    vLighting = ambientLight + (directionalLightColor * directional);
    // set color
    vColor = aVertexColor;
    // since we have a uniform color we may as well:
    //vColor=vec4(1.0,  1.0,  0.0,  1.0);
}
      

Javascript

Javascriptet som handterer vår tegning er inkludert som egen fil. De viktigste delene av denne koden er kommentert funksjon for funksjon nedenfor. Hele fila ser slik ut:

_cubescript.js

Det kan være lurt å kikke på OpenGL ES 2.0 Reference Pages [3] for å få en forklaring av de enkelte metodene.

Følgende globale variable er definert.

var canvas;
var gl;
var cubeVerticesBuffer;
var cubeVerticesIndexBuffer;
var cubeVerticesColorBuffer;
var cubeVerticesNormalBuffer;
var cubeRotation = 0.0;
var lastCubeUpdateTime = 0;
var mvMatrix;
var perspectiveMatrix;
var shaderProgram;
var vertexPositionAttribute;
var vertexNormalAttribute;
var vertexColorAttribute;
var viewAspect=1.0;

Funkjsonen start() kalles typisk enten ved body onload, eller ved et script i bunnen (etter canvas) i selve websiden. I dette eksempelet er det denne funksjone som drar hele jobben.

function start() {
  canvas = document.getElementById("glcanvas");
  initWebGL(canvas); 
  
  if (gl) {
    gl.clearColor(0.5, 0.5, 0.5, 1.0); 
    gl.clearDepth(1.0); 
    gl.enable(gl.DEPTH_TEST); 
    gl.depthFunc(gl.LEQUAL); 
     
    initShaders();    
    initBuffers();     
    setInterval(drawScene, 15);
    
    // fill window, and draw
    onWindowResize();
    // pick up resizing
    window.addEventListener( 'resize', onWindowResize, false );
  }
}

Funksjonen initWebGL() forsøker å sette opp WebGL i canvas-elementet. Merk at konstanten "experimental-webgl" skal bytte sut med "webgl" etterhvert som denne teknologien modnes hos nettleserne.

_initWebGL

I funksjonen initBuffers() setter vi opp de punktene som beskriver modellen vår og ssom etterhvert skal sendes til vertex-shaderen.

_initBuffers

Funksjonen drawScene() skiller seg fra det vi kjenner fra tradisjonell OpenGL på den måten at vi ikke lenger "tegner" hjørne for hjørne. I stedet sender vi en punktliste til shaderen, sammen med de to matrisene (modelview og perspective). Deretter starter vi tegningen med gl.drawArrays

_drawScene

Funksjonen setMatrixUniforms() setter status på transformasjonsmatriser til shaderen.

_setMatrixUniforms

De to funksjonene initShaders() og getShader() laster inn shaderne, kompilerer dem og etablerer shaderprogrammet.

_initShaders

_getShader

Dette er resultatet lagt i en iframe:

Du kan også inspisere resultatet og kildekoden på en enklere side:
simple.html https://borres.hiof.no/wep/webgl/basis/boks/simple.html

Interaktiv løsning

Vi kan gjøre framstillingen interaktiv ved å introdusere følgende funksjon, som kalles etter at canvas er identifisert:

function initMousing(){
  canvas.onmousedown = function ( ev ){
     drag  = 1;
     xOffs = ev.clientX;  
     yOffs = ev.clientY;
  }
  canvas.onmouseup = function ( ev ){
     drag  = 0;
     xOffs = ev.clientX;  
     yOffs = ev.clientY;
  }
  canvas.onmousemove = function ( ev ){
     if ( drag == 0 ) return;
     if ( ev.shiftKey ) {
        transl *= 1 + (ev.clientY - yOffs)/1000;
        //yRot = - xOffs + ev.clientX; 
    }
     else {
        yRot += - xOffs + ev.clientX;  
        xRot += - yOffs + ev.clientY; 
    }
     xOffs = ev.clientX;;
     yOffs = ev.clientY;
     drawScene();
  }
 }

DrawScene endres slik:

function drawScene() {
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  
  perspectiveMatrix = makePerspective(45, viewAspect, 0.1, 100.0);
  
  loadIdentity();
  
  mvPushMatrix();
  // zoom and rotate according to user action
  mvTranslate([-0.0, 0.0, transl]);  
  mvRotate(xRot, [1, 0, 0]);
  mvRotate(yRot, [0, 1, 0]);
   
  // Draw the cube by binding the array buffer to the cube's vertices
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer);
  gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
  
  // Set the color attribute for the vertices. 
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesColorBuffer);
  gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);
  
  // Set the normal attribute for the vertices. 
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
  gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);
  
  // Draw the cube.
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
  setMatrixUniforms();
  gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
  
  // Restore the original matrix 
    mvPopMatrix();
}

Dette er resultatet lagt i en iframe:

Du kan også inspisere resultatet og kildekoden på en enklere side:
indexIA.html https://borres.hiof.no/wep/webgl/basis/boks/indexIA.html

[4]
Referanser
  1. WebGL Mozilla Developer Network developer.mozilla.org/en/WebGL 14-05-2011
  1. OpenGL Shading Language (GLSL) Reference Pages Opengl.org www.opengl.org/sdk/docs/manglsl/ 14-05-2011
  1. OpenGL ES Software Development Kit Khronos Group www.khronos.org/opengles/sdk/docs/man/ 14-05-2011
  1. Sylvester sylvester.jcoglan.com/ 14-05-2011
Basis >En boks