Introduction to OpenGL/GLSL and WebGL GLSL
Objectives! Give you an overview of the software that you will be using this semester! OpenGL, WebGL, and GLSL! What are they?! How do you use them?! What does the code look like?! Evolution! All of them are required to write modern graphics code, although alternatives exist
What is OpenGL?! An application programming interface (API)! A (low-level) Graphics rendering API! Generate high-quality images from geometric and image primitives
Maximal Portability! Display device independent! Window system independent! Operating system independent Without a standard API (such as OpenGL) - impossible to port (100, 50) Line(100,50,150,80) - device/lib 1 (150, 100) Moveto(100,50) - device/lib 2 Lineto(150,100)
Brief History of OpenGL! Originated from a proprietary API called Iris GL from Silicon Graphics, Inc.! Provide access to graphics hardware capabilities at the lowest possible level that still provides hardware independence! The evolution is controlled by OpenGL Architecture Review Board, or ARB.! OpenGL 1.0 API finalized in 1992, first implementation in 1993! In 2006, OpenGL ARB became a workgroup of the Khronos Group! 10+ revisions since 1992
OpenGL Evolution! 1.1 (1997): vertex arrays and texture objects! 1.2 (1998): 3D textures! 1.3 (2001): cubemap textures, compressed textures, multitextures! 1.4 (2002): mipmap generation, shadow map textures, etc! 1.5 (2003): vertex buffer object, shadow comparison functions, occlusion queries, nonpower-of-2 textures
OpenGL Evolution! 2.0 (2004): vertex and fragment shading (GLSL 1.1), multiple render targets, etc! 2.1 (2006): GLSL 1.2, pixel buffer objects, etc! 3.0 (2008): GLSL 1.3, deprecation model, etc! 3.1 (2009): GLSL 1.4, texture buffer objects, move much of deprecated functions to ARB compatible extension! 3.2 (2009)! 4.X (2012 and on)
OpenGL Basics! OpenGL s primary function Rendering! Rendering? converting geometric/mathematical object descriptions into frame buffer values! OpenGL can render:! Geometric primitives! Bitmaps and Images (Raster primitives)
OpenGL Example Cube with flat color Cube with lighting Cube with textures
WebGL! JavaScript implementation of OpenGL! Run in all modern browsers (Chrome, Safari, Firefox, IE, etc).! Application can be located on a remote server! Rendering is done within browser using local hardware! Uses HTML5 canvas! Integrated with standard Web packages and apps (CSS, jquery, etc.)
What do you need to know! Web Environment and Execution! Modern OpenGL basics! Pipeline architecture! Shaders (vertex and fragment) based OpenGL! OpenGL Shading Language (GLSL)! Javascript
OpenGL ES and WebGL! OpenGL ES! A subset of OpenGL 3.1 API (lightweight)! Designed for embedded system (mobile phones etc.)! Shader based! WebGL! Javascript implementation of ES 2.0! Runs on browswers
WebGL Rendering Pipeline
WebGL Programming! General Structure:! Set up canvas to render to! Generate data/geometry in application! Create shader programs! Create buffer objects and load data to them! Connect buffer objects and data to shader variables! Set up proper viewing and lighting conditions! Render
WebGL Programming! WebGL needs a place to render into! HTML5 canvas element! All the code can be put in a single HTML file! Or you can put setup code in an HTML file and your application code in separate Javascript files! HTML file includes shaders! HTML file reads in application and utilities
Example 0! A very simple example that creates a canvas but display nothing except a yellow background WebGL Javascript canvas <!DOCTYPE html> <html> <head> <title>hwshen WebGL code00 </title> <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"> <script type="text/javascript" src="code00.js"></script> </head> <body onload="webglstart(); > <canvas id="code00-canvas" style="border: none;" width="700" height="500"></ canvas> <br/> </body> </html>
code00.js var gl; function initgl(canvas) { try { gl = canvas.getcontext("experimental-webgl"); gl.viewportwidth = canvas.width; gl.viewportheight = canvas.height; catch (e) { if (!gl) { alert("could not initialise WebGL, sorry :-("); function webglstart() { var canvas = document.getelementbyid("code00- canvas"); initgl(canvas); gl.clearcolor(1.0, 1.0, 0.0, 1.0); // yellow drawscene(); viewport function drawscene() { gl.viewport(0, 0, gl.viewportwidth, gl.viewportheight); gl.clear(gl.color_buffer_bit gl.depth_buffer_bit);
Simple OpenGL commands! HTML will call webglstart();! canvas is an HTML5 element for drawing! You need a OpenGL context from the canvas for storing internal states! canvas.getcontext();! All OpenGL commands in WebGL start with gl.! gl.viewport(); specify the display area! gl.clearcolor(); specify the background color! gl.clear(); clear the foreground and background
Output
Example 1! Now let s draw something (two triangles) on the canvas! You will need to provide vertex and fragment shaders
code01.html Fragment Shader Vertex Shader <!DOCTYPE html> <html> <head> <title>hwshen WebGL code01 </title> <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"> <!-- ************** Fragment Shader ************* --> <script id="shader-fs" type="x-shader/x-fragment"> void main(void) { gl_fragcolor = vec4(1.0, 0, 0, 1.0); </script> <!-- ************** Vertex Shader ************* --> <script id="shader-vs" type="x-shader/x-vertex"> attribute vec3 avertexposition; void main(void) { gl_position = vec4(avertexposition, 1.0); </script>
code01.html (cont d) <script type="text/javascript" src="shaders_setup.js"></script> <script type="text/javascript" src="code01.js"></script> </head> <body onload="webglstart(); > <canvas id="code00-canvas" style="border: none;" width="700" height="500"></ canvas> <br/> </body> </html>
code01.js var gl; function initgl(canvas) { // same as before. function initshaderers() { // setup shaders // load the source, compile and link the code // in shaders_setup.js function initbuffers() { // set up vertex buffer objects for geometry // I will explain next function drawscene() function webglstart() { var canvas = document.getelementbyid("code00- canvas"); initgl(canvas); initshaders(); initbuffers(); gl.clearcolor(1.0, 1.0, 0.0, 1.0); // yellow drawscene(); gl.viewport(0, 0, gl.viewportwidth, gl.viewportheight); gl.clear(gl.color_buffer_bit gl.depth_buffer_bit); gl.bindbuffer(gl.array_buffer, squarevertexpositionbuffer); gl.vertexattribpointer(shaderprogram.vertexpositionattribute, squarevertexpositionbuffer.itemsize, gl.float, false, 0, 0); gl.drawarrays(gl.triangle_strip, 0, squarevertexpositionbuffer.numitems);
Output
code01.js! In order to fully understand code01.js, we need to explain! Vertex Buffer Objects this is how you define your geometry! Shader setup and simple GLSL concepts! How to draw the scene! Below I will start with Shaders setup and then Vertex Buffer Objects (VBO)
OpenGL Shading Language (GLSL)! A C-like language and incorporated into OpenGL 2.0 and on! Used to write vertex program and fragment program! No distinction in the syntax between a vertex program and a fragment program! Platform independent
Setup of Shader Prgorams! A shader is defined as an array of strings! Steps to set up shaders 1. Create a shader program 2. Create shader objects (vertex and fragment) 3. Send source code to the shader objects 4. Compile the shader 1. Create program object by linking compiled shaders together 2. Use the linked program object
shaders_setup.js function initshaders() { shaderprogram = gl.createprogram(); // create a shader program var fragmentshader = getshader(gl, "shader-fs"); // create and compile a vertex shader object var vertexshader = getshader(gl, "shader-vs"); // create and compile a fragment shader object gl.attachshader(shaderprogram, vertexshader); // attach the vertex shader to the shader program gl.attachshader(shaderprogram, fragmentshader); // attach the fragment shader gl.linkprogram(shaderprogram); // link the vertex and fragment shaders if (!gl.getprogramparameter(shaderprogram, gl.link_status)) { alert("could not initialise shaders"); gl.useprogram(shaderprogram); // use the shader program
shaders_setup.js function getshader(gl, id) { var shaderscript = document.getelementbyid(id); if (!shaderscript) { return null; 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)) { alert(gl.getshaderinfolog(shader)); return null; return shader;
Vertex Buffer Objects! Provide per-vertex input to the GPU! Vertex: a point where the edges of geometry meet! All geometric primitives are composed of vertices, their attributes, and the connectivity! Allow significant increases in vertex throughput between CPU and GPU! The mechanism to provide generic attributes to the shader, and store vertex data in video memory! User s program is free to define an arbitrary set of pervertex attributes to the vertex shader
Create a VBO! Step 1: generate a new buffer object with gl.createbuffer()! This function returns an identifier for the buffer object! Step 2: Bind the buffer with a particular buffer type with gl.bindbuffer()! Specify the target (i.e. what kind of buffer) to which the buffer object is bound! Choice: gl.array_buffer, gl.elementary_buffer, gl.pixel_pack_buffer, gl.pixel_unpack_buffer! gl.array_buffer is to provide vertex attributes, and! gl.elementary_buffer is to provide triangle indices! Step 3: Copy the vertex data to the buffer object! gl.bufferdata(target, data, usage)! Usage is the access pattern: gl.static_, gl.stream_, gl.dynamic_{draw, COPY, READ
code01.js function initbuffers() { squarevertexpositionbuffer = gl.createbuffer(); gl.bindbuffer(gl.array_buffer, squarevertexpositionbuffer); vertices = [ 0.5, 0.5, 0.0, -0.5, 0.5, 0.0, 0.5, -0.5, 0.0, -0.5, -0.5, 0.0 ]; gl.bufferdata(gl.array_buffer, new Float32Array(vertices), gl.static_draw); squarevertexpositionbuffer.itemsize = 3; squarevertexpositionbuffer.numitems = 4; 3 numbers (x,y,z) per vertex 4 vertices total The data are now sent to GPU I will talk about how to draw the buffer in a moment, but let me first talk about setting up shaders
code01.js (cont d) Use VBO for drawing: 1. Make the vbo created earlier as active - gl.bindbuffer() 2. Connect the buffer to an vertex attribute in the shader (explained next) 3. Draw the vertex array gldrawarrays(type, offset, numbers of vertices) function drawscene() { gl.viewport(0, 0, gl.viewportwidth, gl.viewportheight); gl.clear(gl.color_buffer_bit gl.depth_buffer_bit); gl.bindbuffer(gl.array_buffer, squarevertexpositionbuffer); gl.vertexattribpointer(shaderprogram.vertexpositionattribute, squarevertexpositionbuffer.itemsize, gl.float, false, 0, 0); gl.drawarrays(gl.triangle_strip, 0, squarevertexpositionbuffer.numitems);
code01.js (cont d) Use VBO for drawing: 1. Make the vbo created earlier as active - gl.bindbuffer() 2. Connect the buffer to an vertex attribute in the shader (explained next) 3. Draw the vertex array gldrawarrays(type, offset, numbers of vertices) function drawscene() { gl.viewport(0, 0, gl.viewportwidth, gl.viewportheight); gl.clear(gl.color_buffer_bit gl.depth_buffer_bit); gl.bindbuffer(gl.array_buffer, squarevertexpositionbuffer); gl.vertexattribpointer(shaderprogram.vertexpositionattribute, squarevertexpositionbuffer.itemsize, gl.float, false, 0, 0); gl.drawarrays(gl.triangle_strip, 0, squarevertexpositionbuffer.numitems);
code01.js (cont d) Use VBO for drawing: 1. Make the vbo created earlier as active - gl.bindbuffer() 2. Connect the buffer to an vertex attribute in the shader 3. Draw the vertex array gldrawarrays(type, offset, numbers of vertices) Location of the vertex attribute in the shader function drawscene() { gl.viewport(0, 0, gl.viewportwidth, gl.viewportheight); gl.clear(gl.color_buffer_bit gl.depth_buffer_bit); gl.bindbuffer(gl.array_buffer, squarevertexpositionbuffer); gl.vertexattribpointer(shaderprogram.vertexpositionattribute, squarevertexpositionbuffer.itemsize, gl.float, false, 0, 0); gl.drawarrays(gl.triangle_strip, 0, squarevertexpositionbuffer.numitems);
code01.js (cont d) What is Location of Vertex Attribute in the shader and how to get it? This is the attribute variable in the shader where the vertex position buffer object is connected to code01.js Vertex shader in HTML attribute vec3 avertexposition; void main(void) { gl_position = vec4(avertexposition, 1.0); Query the location of the variable
code01.js (cont d) Now Connect the buffer to an vertex attribute in the shader code01.js function drawscene() { gl.viewport(0, 0, gl.viewportwidth, gl.viewportheight); gl.clear(gl.color_buffer_bit gl.depth_buffer_bit); gl.bindbuffer(gl.array_buffer, squarevertexpositionbuffer); gl.vertexattribpointer(shaderprogram.vertexpositionattribute, squarevertexpositionbuffer.itemsize, gl.float, false, 0, 0); gl.drawarrays(gl.triangle_strip, 0, squarevertexpositionbuffer.numitems);
code02.js! How to add more than one vertex attributes?! You can add as many vertex attributes as you want! code02.html attribute vec3 avertexposition; attribute vec4 avertexcolor; varying vec4 vcolor; How to pass vertex color? void main(void) { gl_position = vec4(avertexposition, 1.0); vcolor = avertexcolor;
! Query the locations of both avertexposition and averetxcolor code02.js code02.js shaderprogram.vertexpositionattribute = gl.getattriblocation(shaderprogram, "avertexposition"); gl.enablevertexattribarray(shaderprogram.vertexpositionattribute); shaderprogram.vertexcolorattribute = gl.getattriblocation(shaderprogram, "avertexcolor"); gl.enablevertexattribarray(shaderprogram.vertexcolorattribute);! Create another VBO for color (next page)
code02.js! Create a color VBO code02.js squarevertexcolorbuffer = gl.createbuffer(); gl.bindbuffer(gl.array_buffer, squarevertexcolorbuffer); var colors = [ 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, ]; gl.bufferdata(gl.array_buffer, new Float32Array(colors), gl.static_draw); squarevertexcolorbuffer.itemsize = 4; // RGBA four components squarevertexcolorbuffer.numitems = 4; // four colors Again, note these two are not part of VBO. Just record it down For later use in drawscene()
code02.js! Now draw with both position and color VBOs code02.js function drawscene() { gl.viewport(0, 0, gl.viewportwidth, gl.viewportheight); gl.clear(gl.color_buffer_bit gl.depth_buffer_bit); gl.bindbuffer(gl.array_buffer, squarevertexpositionbuffer); gl.vertexattribpointer(shaderprogram.vertexpositionattribute, squarevertexpositionbuffer.itemsize, gl.float, false, 0, 0); gl.bindbuffer(gl.array_buffer, squarevertexcolorbuffer); gl.vertexattribpointer(shaderprogram.vertexcolorattribute, squarevertexcolorbuffer.itemsize, gl.float, false, 0, 0); gl.drawarrays(gl.triangle_strip, 0, squarevertexpositionbuffer.numitems);
WebGL Geometric Primitives POINTS, LINES, TRIANGLES
WebGL Geometric Primitives TRIANGLES v1 T1 v2 v3 T2 T3 v4 v5 Var vertices = [ v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, v3.x, v3.y, v3.z, v2.x, v2.y, v2.z, v3.x, v3.y, v3.z, v4.x, v4.y, v4.z, v3.x, v3.y, v3.z, v4.x, v4.y, v4.z, v5.x, v5.y, v5.z ]; More Compact T1 T2 T3 TRIANGLE FAN Var vertices = [ v3.x, v3.y, v3.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, v4.x, v4.y, v4.z, v5.x, v5.y, v5.z ]; TRIANGLE STRIP Var vertices = [ v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, v3.x, v3.y, v3.z, v4.x, v4.y, v4.z, v5.x, v5.y, v5.z ];
Indexed Triangles! A more general way to represent a triangle set! Store vertex positions and their connectivity separately v2 v4 T2 v1 T1 T3 v3 var vertices = [ v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, v3.x, v3.y, v3.z, v4.x, v4.y, v4.z, v5.x, v5.y, v5.z ]; var indices = [ 0, 1, 2, 1, 3, 2, 2, 3, 4 ] T1 T2 T3 v5! How to represent this way using VBO?
Element Array for Triangle Indices! A more general way to represent a triangle set // create an Array Buffer for the vertex positions as before // now create an element array buffer for the indices var indices = [0,1,2,1,3,2,2,3,4]; VertexIndexBuffer = gl.createbuffer(); gl.bindbuffer(gl.element_array_buffer, VertexIndexBuffer); gl.bufferdata(gl.element_array_buffer, new Uint16Array(indices), gl.static_draw); VertexIndexBuffer.itemsize = 1; VertexIndexBuffer.numItems = 9; // 9 index numbers in the buffer, used later
Element Array for Triangle Indices (cont d)! Now draw the triangles. gl.bindbuffer(gl.array_buffer, VertexPositionBuffer); gl.vertexattribpointer(shaderprogram.vertexpositionattribute, VertexPositionBuffer.itemSize, gl.float, false, 0, 0); gl.bindbuffer(gl.array_buffer, VertexColorBuffer); gl.vertexattribpointer(shaderprogram.colorattribute, VertexColorBuffer.itemSize, gl.float, false, 0, 0); // draw elementary arrays - triangle indices gl.bindbuffer(gl.element_array_buffer, VertexIndexBuffer); gl.drawelements(gl.triangles, VertexIndexBuffer.numItems, gl.unsigned_short, 0); Offset, meaning you can draw a partial set if you want