340 likes | 577 Views
Teaching Computer Graphics with Modern OpenGL. Ed Angel University of New Mexico. Overview. 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 ….
E N D
Teaching Computer Graphics with Modern OpenGL Ed AngelUniversity of New Mexico
Overview • 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 …..
In the Beginning … • 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) VertexData Vertex Transform and Lighting Primitive Setup and Rasterization Fragment Coloring and Texturing Blending PixelData TextureStore
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 */ intj, 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(intargc, 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}}; / intj, 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(intargc, 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(intargc, 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(intargc, 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 */ }
The Start of the Programmable Pipeline • 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 VertexData Vertex Transform and Lighting Primitive Setup and Rasterization Fragment Coloring and Texturing Blending PixelData TextureStore
An Evolutionary Change • 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
The Exclusively Programmable Pipeline • 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 VertexData VertexShader Primitive Setup and Rasterization FragmentShader Blending TextureStore PixelData
More Evolution – Context Profiles • 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
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}}; / intj, 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(intargc, 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 */ }
Fully Shader-Based Version • 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
example.cpp #include "Angel.h” const intNumPoints = 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 ( inti = 1; i < NumPoints; ++i ) { intj = 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( intargc, char **argv ) { glutInit( &argc, argv ); glutInitDisplayMode( GLUT_RGBA ); glutInitWindowSize( 512, 512 ); glutCreateWindow( "Sierpinski Gasket" ); init(); glutDisplayFunc( display ); glutMainLoop(); return 0; }
shaders in vec4 vPosition; void main() { gl_Position = vPosition; } out vec4 FragColor; void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }
Issues • 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
Observations • 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?
WebGL version var canvas; vargl; varNumPoints = 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 );
varpointsArray = 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); }
varbufferId = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, bufferId ); gl.bufferData( gl.ARRAY_BUFFER, flatten(pointsArray), gl.STATIC_DRAW ); varvPos = 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