@JamesLMilner
jmilner@esriuk.com
loxodrome.io
Open GL ES 2.0 for the web
Allows us to create 2D & 3D graphics
JavaScript API
Integrates into HTML page via canvas
No file formats etc.
No markup
*NPAPI now disabled in Chrome 45
Credit: xkcd.com
Source: Microsoft
HTML5 vs ActionScript
Sources: caniuse.com/webgl and webglstats.com
Ricardo Cabello (Three.js)
"low-level means that WebGL commands are expressed in terms that map relatively directly to how a GPU actually works" - Robert Nyman
Source: University of New Mexico
// Vertices for a square
gl.bufferData( gl.ARRAY_BUFFER,
new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
-1.0, 1.0,
1.0, -1.0,
1.0, 1.0]),
gl.STATIC_DRAW
);
// Some few lines of code later
gl.drawArrays(gl.TRIANGLES, 0, squareVertexPositionBuffer.numItems);
Buffer - a block of memory allocated to storing a specific data
Pass vertices using Typed Arrays
Stateful and implicit; uses last buffer referenced
Shader - "a program that tells a computer how to draw something in a specific and unique way"
Vertex Shader - generates scene coordinates
Fragment Shader - provide colour for current pixel
Vertex and Fragment Shaders
Source: Mozilla
Graphics Library Shader Language
<!-- Drawing a green square -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
// Storage qualifer - type - variable name
void main() {
gl_Position = vec4(a_position, 0, 1); // X, Y, Z, W
// gl_Position is a built in variable
}
</script>
<script id="2d-fragment-shader" type="x-shader/x-fragment">
void main() {
gl_FragColor = vec4(0, 1, 0, 1); // R, G, B, A (Green)
// gl_FragColor is a built in variable
}
</script>
Source: webglfundamentals.org
(Buffer)
(Screen)
Source: webglfundamentals.org
Storage qualifiers e.g. attributes, uniforms
Stored in a string, a script tag, or separate file
Typed, C like language
Has vectors and matrices as primitives
gl_ prefix dictates built in variables
<html>
<head>
<title>WebGL - Black and White Square</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript" src="glMatrix-0.9.5.min.js"></script>
<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
// more info on these matrices at:
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
}
</script>
<script type="text/javascript">
var gl;
function initGL(canvas) {
// Initialise the WebGL context on the canvas element
try {
var gl = canvas.getContext("experimental-webgl");
if (!gl) {
gl = canvas.getContext("webgl");
}
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
} catch (e) {
}
if (!gl) {
alert("Could not initialise WebGL, sorry :-(");
}
}
function getShader(gl, id) {
// Get the shader from the script tag
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null; // No shader script with that id
}
var str = ""; //concatenate the whole shader together
var k = shaderScript.firstChild;
while (k) {
if (k.nodeType == 3) { //text will have nodeType property will return 3.
str += k.textContent;
}
k = k.nextSibling;
}
var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER); // if fragment
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER); // if vertex
} else {
return null;
}
gl.shaderSource(shader, str); // Link type and source
gl.compileShader(shader); // Compile shader
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
var shaderProgram;
function initShaders() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram(); // create shader program
gl.attachShader(shaderProgram, vertexShader); // add vertex shader
gl.attachShader(shaderProgram, fragmentShader); // add vertex shader
gl.linkProgram(shaderProgram); // Link the two to program
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
gl.useProgram(shaderProgram); // Use the shader program
// Explicitly say which variable is the vertex position in vertex shader
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); // Enable it
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
}
var mvMatrix = mat4.create(); // glMatrix functions
var pMatrix = mat4.create();
function setMatrixUniforms() {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}
var triangleVertexPositionBuffer;
var squareVertexPositionBuffer;
function initBuffers() {
//Triangle
triangleVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
var vertices = [
0.0, 1.0, 0.0,
-1.0, -1.0, 0.1,
1.0, -1.0, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
triangleVertexPositionBuffer.itemSize = 3;
triangleVertexPositionBuffer.numItems = 3;
//Square
squareVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
vertices = [
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
-1.0, -1.0, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
squareVertexPositionBuffer.itemSize = 3;
squareVertexPositionBuffer.numItems = 4;
}
function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Clears the color and depth buffers
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
// Field of View = 45 , width-to-height ratio of canvas, min view distance, max view distance
mat4.identity(mvMatrix);
// DRAW THE TRIANGLE
mat4.translate(mvMatrix, [-1.5, 0.0, -7.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);
// DRAW THE SQUARE
mat4.translate(mvMatrix, [3.0, 0.0, 0.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
}
function webGLStart() {
var canvas = document.getElementById("lesson01-canvas"); //Get Canvas
initGL(canvas);
initShaders();
initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0); //Black
gl.enable(gl.DEPTH_TEST);
drawScene();
}
</script>
</head>
<body onload="webGLStart();">
<a href="http://learningwebgl.com/blog/?p=28"><< Back to Lesson 1</a><br />
<canvas id="lesson01-canvas" style="border: none;" width="500" height="500"></canvas>
</body>
<!-- Adapted from: http://learningwebgl.com/blog/?p=28 -->
</html>
This is pretty verbose... we have to be explicit so many things!
Pure WebGL
Three.js, Babylon.js, etc.
Vertices
Buffers
Shaders
Dot Products
Cross Products
Camera
Scene
Objects
Lighting
Matrices
Clipspace Coordinates
Pixel Coordinates
Raw WebGL
TWGL
PhiloGL
Three.js
Babylon.js
Scene.js
OSG.js
GLGE
CopperLicht
WebGL Studio
Increasing Abstraction
"It shouldn't be underestimated how much WebGL adoption has been driven by Three.js."
Queen Flight Visualisation
3D Edition!
Flight Simulator
Physics Intergration
@JamesLMilner
jmilner@esriuk.com