introduction to opengl/glsl and...
TRANSCRIPT
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
(100, 50)
(150, 100)
Line(100,50,150,80) - device/lib 1
Moveto(100,50) - device/lib 2 Lineto(150,100)
Without a standard API (such as OpenGL) - impossible to port
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, non-power-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
! A very simple example that creates a canvas but display nothing except a yellow background
Example 0
<!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>
WebGL Javascript
canvas
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 drawScene() { gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); }
function webGLStart() { var canvas = document.getElementById("code00-
canvas"); initGL(canvas); gl.clearColor(1.0, 1.0, 0.0, 1.0); // yellow drawScene(); }
viewport
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
<!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
Fragment Shader
Vertex Shader
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>
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); }
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 webGLStart() { var canvas = document.getElementById("code00-
canvas"); initGL(canvas); initShaders(); initBuffers(); gl.clearColor(1.0, 1.0, 0.0, 1.0); // yellow drawScene(); }
code01.js
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 per-
vertex 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}
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; }
code01.js
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
3 numbers (x,y,z) per vertex 4 vertices total
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); }
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)
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)
code01.js (cont’d)
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); }
What is “Location of Vertex Attribute in the shader” and how to get it?
code01.js (cont’d)
attribute vec3 aVertexPosition;
void main(void) { gl_Position = vec4(aVertexPosition, 1.0); }
This is the attribute variable in the shader where the vertex position buffer object is connected to
Vertex shader in HTML
Query the location of the variable
code01.js
Now Connect the buffer to an 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)
code01.js
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;
void main(void) { gl_Position = vec4(aVertexPosition, 1.0); vColor = aVertexColor; }
How to pass vertex color?
! Query the locations of both aVertexPosition and aVeretxColor
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor"); gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
code02.js
code02.js
! Create another VBO for color (next page)
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
! Create a color VBO
code02.js
code02.js
Again, note these two are not part of VBO. Just record it down For later use in drawScene()
! Now draw with both position and color VBOs
code02.js
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
v2 v3
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 ];
T1 T2
T3
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 ];
More Compact
Indexed Triangles ! A more general way to represent a triangle set ! Store vertex positions and their connectivity
separately
! How to represent this way using VBO?
v1
v2 v3
v4
v5
T1 T2
T3
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
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