![]() |
Below you can find short comments to a simple 5kb script
cone.
The cone has parameters h, r1, r2 and a normal (in the XY plane)
|
<script src="../CanvasMatrix.js" type="text/javascript">The vertex shader calculates coordinates and colors (diffusive and specular) of rotated verteces.
<script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 aPos; attribute vec3 aNorm; uniform mat4 mvMatrix; uniform mat4 prMatrix; varying vec4 color; const vec3 dirDif = vec3(0., 0., 1.); const vec3 dirHalf = vec3(-.4034, .259, .8776); void main(void) { gl_Position = prMatrix * mvMatrix * vec4(aPos, 1.); vec3 rotNorm = (mvMatrix * vec4(aNorm, .0)).xyz; float i = max( 0., dot(rotNorm, dirDif) ); color = vec4(.9*i, .5*i, 0., 1.); i = pow( max( 0., dot(rotNorm, dirHalf) ), 120.); color += vec4(i, i, i, 0.); } </script>The fragment shader just transfers interpolated colors for every pixel.
<script id="shader-fs" type="x-shader/x-fragment"> precision mediump float; varying vec4 color; void main(void) { gl_FragColor = color; } </script>Common for all scripts getShader() function compiles shaders.
<script type="text/javascript"> function getShader ( gl, id ){ var shaderScript = document.getElementById ( id ); var str = ""; var k = shaderScript.firstChild; while ( k ){ if ( k.nodeType == 3 ) str += k.textContent; k = k.nextSibling; } var shader; if ( shaderScript.type == "x-shader/x-fragment" ) shader = gl.createShader ( gl.FRAGMENT_SHADER ); else if ( shaderScript.type == "x-shader/x-vertex" ) shader = gl.createShader(gl.VERTEX_SHADER); else return null; gl.shaderSource(shader, str); gl.compileShader(shader); if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) == 0) alert(id +"\n"+ gl.getShaderInfoLog(shader)); return shader; }This is the main function executed on load. Initialization of WebGL and canvas matching to the brouser window.
var gl, canvas, h = 1, r1 = .5, r2 = .2, nPhi = 100; function webGLStart() { canvas = document.getElementById("canvas"); var size = Math.min(window.innerWidth, window.innerHeight) - 20; canvas.width = size; canvas.height = size; canvas.getContext("experimental-webgl"); if (!window.WebGLRenderingContext){ alert("Your browser does not support WebGL. See http://get.webgl.org"); return;} try { gl = canvas.getContext("experimental-webgl"); } catch(e) {} if ( !gl ) {alert("Can't get WebGL"); return;} gl.viewport(0, 0, size, size);Shaders compiling.
var prog = gl.createProgram(); gl.attachShader(prog, getShader( gl, "shader-vs" )); gl.attachShader(prog, getShader( gl, "shader-fs" )); gl.linkProgram(prog); gl.useProgram(prog);Make points and normals for our cone, put them into GPU and bind to shader attributes.
var pt = [], nt = []; var Phi = 0, dPhi = 2*Math.PI / (nPhi-1), Nx = r1 - r2, Ny = h, N = Math.sqrt(Nx*Nx + Ny*Ny); Nx /= N; Ny /= N; for (var i = 0; i < nPhi; i++ ){ var cosPhi = Math.cos( Phi ); var sinPhi = Math.sin( Phi ); var cosPhi2 = Math.cos( Phi + dPhi/2 ); var sinPhi2 = Math.sin( Phi + dPhi/2 ); pt.push ( -h/2, cosPhi * r1, sinPhi * r1 ); // points nt.push ( Nx, Ny*cosPhi, Ny*sinPhi ); // normals pt.push ( h/2, cosPhi2 * r2, sinPhi2 * r2 ); // points nt.push ( Nx, Ny*cosPhi2, Ny*sinPhi2 ); // normals Phi += dPhi; } var posLoc = gl.getAttribLocation(prog, "aPos"); gl.enableVertexAttribArray( posLoc ); gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pt), gl.STATIC_DRAW); gl.vertexAttribPointer(posLoc, 3, gl.FLOAT, false, 0, 0); var normLoc = gl.getAttribLocation(prog, "aNorm"); gl.enableVertexAttribArray( normLoc ); gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(nt), gl.STATIC_DRAW); gl.vertexAttribPointer(normLoc, 3, gl.FLOAT, false, 0, 0);Prepare perspective and modelview matrices. Put the first to the vertex shader as uniform. The rotation matrix is used to save integrated rotation.
var prMatrix = new CanvasMatrix4(); prMatrix.perspective(45, 1, .1, 100); gl.uniformMatrix4fv( gl.getUniformLocation(prog,"prMatrix"), false, new Float32Array(prMatrix.getAsArray()) ); var mvMatrix = new CanvasMatrix4(); var rotMat = new CanvasMatrix4(); rotMat.makeIdentity(); var mvMatLoc = gl.getUniformLocation(prog,"mvMatrix");GL initialization.
gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.clearDepth(1.0); gl.clearColor(0, 0, .5, 1); var xOffs = yOffs = 0, drag = 0; var xRot = yRot = 0; var transl = -2.5; drawScene();The drawScene() function prepares cone's rotation and translation then draws its points as one TRIANGLE_STRIP.
function drawScene(){ rotMat.rotate(xRot/5, 1,0,0); rotMat.rotate(yRot/5, 0,1,0); yRot = 0; xRot = 0; mvMatrix.load( rotMat ); mvMatrix.translate(0, 0, transl); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.uniformMatrix4fv( mvMatLoc, false, new Float32Array(mvMatrix.getAsArray()) ); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 2*nPhi); gl.flush (); }On window resize action
canvas.resize = function (){ var size = Math.min(window.innerWidth, window.innerHeight) - 10; canvas.width = size; canvas.height = size; gl.viewport(0, 0, size, size); drawScene(); }Mouse events processing to test if it is "draged" (these methods are outdated but they work).
canvas.onmousedown = function ( ev ){ drag = 1; xOffs = ev.clientX; yOffs = ev.clientY; } canvas.onmouseup = function ( ev ){ drag = 0; xOffs = ev.clientX; yOffs = ev.clientY; }On mouse "drag" we calculate two rotations and translation if the shift key is pressd. Then the cone is repainted.
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(); } }This is the mouse wheel event handler
var wheelHandler = function(ev) { var del = 1.1; if (ev.shiftKey) del = 1.01; var ds = ((ev.detail || ev.wheelDelta) > 0) ? del : (1 / del); transl *= ds; drawScene(); ev.preventDefault(); }; canvas.addEventListener('DOMMouseScroll', wheelHandler, false); canvas.addEventListener('mousewheel', wheelHandler, false); </script>JavaScript initialization on load and window resizing.
<body onload="webGLStart();" onresize="canvas.resize();"> <canvas id="canvas" width="500" height="500"></canvas>