En boks
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;
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); } }
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.
I funksjonen initBuffers() setter vi opp de punktene som beskriver modellen vår og ssom etterhvert skal sendes til vertex-shaderen.
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
Funksjonen setMatrixUniforms() setter status på transformasjonsmatriser til shaderen.
De to funksjonene initShaders() og getShader() laster inn shaderne, kompilerer dem og etablerer shaderprogrammet.
Dette er resultatet:
Du kan også inspisere resultatet og kildekoden på en enklere side:
index.html
http://www.it.hiof.no/~borres/dw/wgl/cube/index.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, 640.0/480.0, 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(); }
Du kan inspisere resultatet og kildekoden her:
index.html
http://www.it.hiof.no/~borres/dw/wgl/cube/indexIA.html