Teaching Computer Graphics with Modern OpenGL
Ed AngelUniversity of New Mexico
Starting teaching a top-down approach using OpenGL about 20 years ago
Co-author of Interactive Computer Graphics Sixth edition switched to a fully shader-based
approach Successful but …..
Overview
OpenGL 1.0 was released on July 1st, 1994
Its pipeline was entirely fixed-function
the only operations available were fixed by the implementation
The pipeline evolved, but remained fixed-function through OpenGL versions 1.1 through 2.0 (Sept. 2004)
In the Beginning …
PrimitiveSetup and
Rasterization
Fragment Coloring and
TexturingBlending
VertexData
PixelData
Vertex Transform and
Lighting
TextureStore
Sierpinski Gasket
First Program (old style)
#include <GL/glut.h>
void myinit() { glClearColor(1.0, 1.0, 1.0, 1.0); /* white background */ glColor3f(1.0, 0.0, 0.0); /* draw in red */
glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, 50.0, 0.0, 50.0); glMatrixMode(GL_MODELVIEW);}
void display( void ){
GLfloat vertices[3][2]={{0.0,0.0},{25.0,50.0},{50.0,0.0}}; /* A triangle */
int j, k; int rand(); /* standard random number generator */ GLfloat p[2] ={7.5,5.0}; /* An arbitrary initial point inside traingle */
glClear(GL_COLOR_BUFFER_BIT); /*clear the window */
/* compute and plots 5000 new points */
glBegin(GL_POINTS);
for( k=0; k<5000; k++) { j=rand()%3; /* pick a vertex at random */
/* Compute point halfway between selected vertex and old point */
p[0] = (p[0]+vertices[j][0])/2.0; p[1] = (p[1]+vertices[j][1])/2.0; /* plot new point */
glVertex2fv(p); }
glEnd(); glFlush(); /* clear buffers */ }
void main(int argc, char** argv){
/* Standard GLUT initialization */
glutInit(&argc,argv); glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); /* default, not needed */ glutInitWindowSize(500,500); /* 500 x 500 pixel window */ glutInitWindowPosition(0,0); /* place window top left on display */ glutCreateWindow("Sierpinski Gasket"); /* window title */ glutDisplayFunc(display); /* display callback invoked when window opened */
myinit(); /* set attributes */
glutMainLoop(); /* enter event loop */}
void display( void ) { GLfloat vertices[3][2]={{0.0,0.0},{25.0,50.0}, {50.0,0.0}}; / int j, k; int rand(); /* standard random number generator */ GLfloat p[2] ={7.5,5.0}; /*point inside triangle */
glClear(GL_COLOR_BUFFER_BIT); /*clear the window */
/* compute and plots 5000 new points */
glBegin(GL_POINTS);
for( k=0; k<5000; k++) { j=rand()%3; /* pick a vertex at random */
/* Compute point halfway between selected vertex and old point */
p[0] = (p[0]+vertices[j][0])/2.0; p[1] = (p[1]+vertices[j][1])/2.0; /* plot new point */
glVertex2fv(p); }
glEnd(); glFlush(); /* clear buffers */ }
void main(int argc, char** argv){
/* Standard GLUT initialization */
glutInit(&argc,argv); glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); /* default, not needed */ glutInitWindowSize(500,500); /* 500 x 500 pixel window */ glutInitWindowPosition(0,0); /* place window top left on display */ glutCreateWindow("Sierpinski Gasket"); /* window title */ glutDisplayFunc(display); /* display callback invoked when window opened */
myinit(); /* set attributes */
glutMainLoop(); /* enter event loop */}
glBegin(GL_POINTS); for( k=0; k<5000; k++) { j=rand()%3; /* pick a vertex at random */ p[0] = (p[0]+vertices[j][0])/2.0; p[1] = (p[1]+vertices[j][1])/2.0; glVertex2fv(p); }
glEnd(); glFlush(); /* clear buffers */ }
void main(int argc, char** argv){
/* Standard GLUT initialization */
glutInit(&argc,argv); glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); /* default, not needed */ glutInitWindowSize(500,500); /* 500 x 500 pixel window */ glutInitWindowPosition(0,0); /* place window top left on display */ glutCreateWindow("Sierpinski Gasket"); /* window title */ glutDisplayFunc(display); /* display callback invoked when window opened */
myinit(); /* set attributes */
glutMainLoop(); /* enter event loop */}
void main(int argc, char** argv) { glutInit(&argc,argv); glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(500,500); glutInitWindowPosition(0,0); glutCreateWindow("Sierpinski Gasket"); glutDisplayFunc(display);
myinit();
glutMainLoop(); /* enter event loop */}
OpenGL 2.0 (officially) added programmable shaders vertex shading augmented the fixed-function transform and
lighting stage
fragment shading augmented the fragment coloring stage
However, the fixed-function pipeline was still available
The Start of the Programmable Pipeline
PrimitiveSetup and
Rasterization
Fragment Coloring and
TexturingBlending
VertexData
PixelData
Vertex Transform and
Lighting
TextureStore
OpenGL 3.0 introduced the deprecation model the method used to remove features from OpenGL
The pipeline remained the same until OpenGL 3.1 (released March 24th, 2009)
Introduced a change in how OpenGL contexts are used
An Evolutionary Change
Context Type Description
Full Includes all features (including those marked deprecated) available in the current version of OpenGL
Forward Compatible Includes all non-deprecated features (i.e., creates a context that would be similar to the next version of OpenGL)
OpenGL 3.1 removed the fixed-function pipeline programs were required to use only shaders
Additionally, almost all data is GPU-resident all vertex data sent using buffer objects
The Exclusively Programmable Pipeline
PrimitiveSetup and
Rasterization
FragmentShader Blending
VertexData
PixelData
VertexShader
TextureStore
OpenGL 3.2 also introduced context profiles profiles control which features are exposed
it’s like GL_ARB_compatibility, only not insane currently two types of profiles: core and compatible
More Evolution – Context Profiles
Context Type Profile Description
Fullcore All features of the current release
compatible All features ever in OpenGL
Forward Compatiblecore All non-deprecated features
compatible Not supported
First Program (old style)
#include <GL/glut.h>
void myinit() { glClearColor(1.0, 1.0, 1.0, 1.0); /* white background */ glColor3f(1.0, 0.0, 0.0); /* draw in red */
glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, 50.0, 0.0, 50.0); glMatrixMode(GL_MODELVIEW);}
void display( void ) { GLfloat vertices[3][2]={{0.0,0.0},{25.0,50.0}, {50.0,0.0}}; / int j, k; int rand(); /* standard random number generator */ GLfloat p[2] ={7.5,5.0}; /*point inside triangle */
glClear(GL_COLOR_BUFFER_BIT); /*clear the window */
/* compute and plots 5000 new points */
glBegin(GL_POINTS); for( k=0; k<5000; k++) { j=rand()%3; /* pick a vertex at random */ p[0] = (p[0]+vertices[j][0])/2.0; p[1] = (p[1]+vertices[j][1])/2.0; glVertex2fv(p); }
glEnd(); glFlush(); /* clear buffers */ }
void main(int argc, char** argv) { glutInit(&argc,argv); glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(500,500); glutInitWindowPosition(0,0); glutCreateWindow("Sierpinski Gasket"); glutDisplayFunc(display);
myinit();
glutMainLoop(); /* enter event loop */}
Let’s look at same example Contend not significantly harder
But model is different: retained mode
Still use GLUT (or freeglut) C++ better
Provide mat.h and vec.h to match GLSL
Fully Shader-Based Version
example.cpp
#include "Angel.h”const int NumPoints = 5000;
void init( void ) { vec2 points[NumPoints]; vec2 vertices[3] = {vec2(-1.0, -1.0), vec2(0.0, 1.0), vec2(1.0, -1.0 )}; points[0] = vec2( 0.25, 0.50 );
for ( int i = 1; i < NumPoints; ++i ) { int j = rand() % 3; points[i] = ( points[i - 1] + vertices[j] ) / 2.0; }
example.cpp GLuint vao[1]; glGenVertexArrays ( 1, vao ); glBindVertexArray ( vao[0] ); GLuint buffer; glGenBuffers( 1, &buffer ); glBindBuffer( GL_ARRAY_BUFFER, buffer ); glBufferData( GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW );
GLuint program = InitShader( "vshader21.glsl”, "fshader21.glsl" ); glUseProgram( program );
example.cpp
GLuint loc = glGetAttribLocation( program, "vPosition" ); glEnableVertexAttribArray( loc ); glVertexAttribPointer( loc, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
glClearColor( 1.0, 1.0, 1.0, 1.0 ); // white background}
example.cpp
void display( ){ glClear( GL_COLOR_BUFFER_BIT ); // clear the window glDrawArrays( GL_POINTS, 0, NumPoints ); // draw the points glFlush();}
example.cpp
int main( int argc, char **argv ){ glutInit( &argc, argv ); glutInitDisplayMode( GLUT_RGBA ); glutInitWindowSize( 512, 512 ); glutCreateWindow( "Sierpinski Gasket" ); init(); glutDisplayFunc( display ); glutMainLoop(); return 0;}
shaders
out vec4 FragColor;void main(){ gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );}
in vec4 vPosition;void main(){ gl_Position = vPosition;}
Not hard to get students started Present situation has some problems
No GLU
What to do about GLUT
Interactivity more difficult
System dependencies
Web is what is happening
Issues
We are teaching Computer Graphics We are not teaching OpenGL as anything but a tool to help
No negative feedback from instructors
Top-down programming approach is still best
Must keep up with advances in hardware
Interactivity a problem
Will WebGL replace desktop OpenGL for teaching?
Observations
WebGL versionvar canvas;var gl;var NumPoints = 5000;
window.onload = init;
function init() { canvas = document.getElementById( "gl-canvas" ); gl = WebGLUtils.setupWebGL( canvas ); if ( !gl ) { alert( "WebGL isn't available" ); } gl.viewport( 0, 0, canvas.width, canvas.height ); gl.clearColor( 1.0, 1.0, 1.0, 1.0 );
var vertices2 = new Array(3);
vertices2[0] = point2.create([-1, -1]); vertices2[1] = point2.create([0, 1]); vertices2[2] = point2.create([1, -1]);
// Load shaders and initialize attribute buffers var program = initShaders( gl, "vertex-shader", "fragment-shader" ); gl.useProgram( program );
var pointsArray = new Array(NumPoints); pointsArray[0] = vec2.create([0.25,0.50]);
for (i = 1; i < NumPoints; i++) { j = Math.floor(Math.random() * 3); pointsArray[i] = point2.create(); point2.add(pointsArray[i-1], vertices2[j], pointsArray[i]); point2.scale(pointsArray[i], 0.5); }
var bufferId = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, bufferId ); gl.bufferData( gl.ARRAY_BUFFER, flatten(pointsArray), gl.STATIC_DRAW );
var vPos = gl.getAttribLocation( program, "vPosition" ); gl.vertexAttribPointer( vPos, 2, gl.FLOAT, false, 0, 0 ); gl.enableVertexAttribArray( vPos );
render();}
function render() { gl.clear( gl.COLOR_BUFFER_BIT ); gl.drawArrays( gl.POINTS, 0, NumPoints );
// window.requestAnimFrame( render, canvas );
}
example.html<html><script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;void main() { gl_Position = vPosition;}</script>
<script id="fragment-shader" type="x-shader/x-fragment">
void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );}</script>
example.html
<script type="text/javascript" src="../Common/webgl-utils.js”></script><script type="text/javascript" src="../Common/initShaders.js”></script><script type="text/javascript" src="chap2ex1.js"></script><script type="text/javascript" src="../Common/glMatrixEA.js”></script>
<body><canvas id="gl-canvas" width="512” height="512”Oops ... your browser doesn't support the HTML5 canvas element</canvas></body></html>
Examples in all forms on my website:
www.cs.unm.edu/~angel
under book support
Fifth edition for old examples
Sixth edition for shader-based in C++ and WebGL