Some Documentation for glsim.js
glsim.js is an open-source JavaScript library, written by David Eck, that implements a subset of OpenGL 1.1 by translating OpenGL function calls into WebGL 1.0. Note in particular that it requires WebGL support. The subset of OpenGL includes the part of the API that is discussed in Chapters 3 and 4 of the on-line computer graphics textbook at http://math.hws.edu/graphicsbook (except for display lists). The purpose of glsim.js is simply to support that textbook. It is not in any way meant as a serious implementation of OpenGL! It is also by no means fully tested, and it is rather inefficient. However, it does work in the simple applications for which I have used it, and it might be useful for basic experimentation and practice with OpenGL 1.1, with the goal of learning something about 3D graphics concepts, without the hassle of setting up an OpenGL development environment for a language like C or Java.
Sample programs using glsim.js can be found in http://math.hws.edu/graphicsbook/source/glsim/. It is also used for some of the live demos in the book, which can be found in http://math.hws.edu/graphicsbook/demos/c3/ and http://math.hws.edu/graphicsbook/demos/c4/.
Some of the code in glsim.js is taken from the popular JavaScript matrix and vector math library glmatrix, written by Brandon Jones and Colin MacKenzie IV, which can be obtained from http://glmatrix.net.
glsim.js defines many functions and constants in the global namespace, including all OpenGL functions and constants that it implements, as well as several utility functions with names that begin with "glsim." It defines a class named GLSim to represent OpenGL graphics contexts, but that class will not usually be referenced directly in code that uses glsim.js. There is also a class named Camera representing a camera abstraction that can be used as an aid for setting up viewing and projection in OpenGL.
Creating the OpenGL Graphics Context
Since glsim is based on WebGL, you need a <canvas> element to draw on. To set up a canvas for OpenGL drawing, call
glsimUse(canvas);
The parameter can be either a string giving the id of a <canvas> element or the DOM object corresponding to a <canvas> element (generally obtained using document.getElementById).
The function call glsimUse(canvas) sets the canvas to be the current (simulated!) OpenGL context. All OpenGL functions apply to the current context and will throw an exception if no such context has been established. It is possible to have multiple canvasses, each with its own OpenGL context. glsimUse() can be used to switch among contexts. If a context has already been created for a canvas when glsimUse(canvas) is called, then it simply uses the existing context for the canvas. Any state from previous uses of the context is retained. That is, glsimUse() can be used to switch among several exiting OpenGL contexts at any time.
An OpenGL context is represented by an object of type GLSim. You can also create the context directly, by calling the GLSim constructor; glsimUse is simply a convenience for creating and managing the contexts.
The function glsimUse can take a second parameter, to control options for the WebGL drawing context that is actually used on the canvas. If the second parameter is omitted, then default options for glsim will produce a color buffer with no alpha component (RGB colors rather than RGBA). This is generally desirable, since alpha transparency in the color buffer allows the background of the canvas on the web page to show through the canvas (but it is not the same default as in the GLSim constructor). To get an RGBA color buffer, you can pass null as the second parameter to glsimUse when creating a new drawing context. Furthermore the default options allow WebGL to discard the WebGL color buffer between drawing operations. Note that this is not correct behavior for OpenGL, but the correct OpenGL behavior is inefficient in WebGL and is not needed as long as all drawing is done in a function that completely redraws the image (as is the case for all of my examples). To preserve the color buffer between drawing operations, you can pass { preserveDrawingBuffer: true } as the second parameter to glsimUse(), or pass { alpha:false, preserveDrawingBuffer: true } as the second parameter to get an RGB color buffer.
Note that the second parameter to glsimUse() applies only when a a new OpenGL context is created, not when it merely switches to one that already exists. (The second parameter can actually be any object that represents a valid set of WebGL options; it is simply passed to the call to getContext() that creates the WebGL context.)
Other "glsim" Functions
The functions glsimTexImage(image) and glsimCopyTexImage(x,y,width,height) are defined as simple abbreviations for
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,0,0,0,GL_RGBA,GL_UNSIGNED_BYTE,image);
and
glCopyTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,x,y,width,height,0);
since the parameters are so hard to get right. (Note that the width and the height in glTexImage2D are ignored.) The function
glsimLoadImage(imageURL,callback);
will load an image from the specified imageURL asynchronously. The imageURL parameter must be a string. The callback parameter, if present, must be a function. When the image has been loaded, the callback function will be called with two parameters: the Image object containing the loaded image and the imageURL that was the first parameter to glLoadImage. A typical use would be to install the image as a texture and enable texturing in the callback. For example,
glsimLoadImage("myTextureImage.jpg", function(image) { glsimTexImage(image); glEnable(GL_TEXTURE_2D); display(); // Redraw the image, with the texture. });
If an error occurs while loading the function, a message is printed to the console, and the callback function is not called, but otherwise the error is ignored.
The current OpenGL context, which is a GLSim object, is available as GLSim.currentContext. The corresponding WebGL context is available as GLSim.currentContext.gl. When glsim.js detects an error, it calls the function GLSim.error(message), where the parameter is an error message. This function could be changed to customize error handling. The default function throws the error message as an exception and prints the message and a stack trace to the console.
OpenGL Functions
This is a list of OpenGL functions that are supported by glsim.js. They should work in the same way as the real OpenGL functions, except as noted. These functions are defined as global identifiers in glsim.js. Any constants that they use are also defined globally, but do not necessarily have the same value as they do in OpenGL. All of these functions will throw an exception if they are called when there is no current OpenGL context.
glEnable(param)/glDisable(param)
— The parameter can be one of the
following constants:
GL_DEPTH_TEST,
GL_COLOR_MATERIAL,
GL_BLEND,
GL_LIGHT0 through GL_LIGHT7,
GL_LIGHTING,
GL_NORMALIZE,
GL_POINT_SMOOTH,
GL_POLYGON_OFFSET_FILL,
GL_TEXTURE_2D.
Note that only four lights are supported by default; to enable additional lights, set
GLSim.lightCount equal to the number of lights before calling glsimUse().
(I found that trying to have eight
lights seemed to require more data in the WebGL shader programs than they could handle on some computers -- specifically
my old desktop -- leading to hard-to-diagnose errors.)
glEnableClientState(param)/glDisableClientState(param)
— The parameter can
be one of the following constants: GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_NORMAL_ARRAY.
glPushAttrib(mask)
and glPopAttrib()
— The mask
can contain only the following constants, OR'ed together: GL_CURRENT_BIT, GL_ENABLE_BIT,
GL_LIGHTING_BIT, GL_VIEWPORT_BIT. And of course, only values that are used by glsim are
saved and restored.
glGetIntegerv(item,array)
— The supported values of item are GL_VIEWPORT,
GL_LIGHT_MODEL_TWO_SIDE, and GL_LIGHT_MODEL_LOCAL_VIEWER. The second parameter must be an array,
of sufficient size to hold the value.
glGetFLOATv(item,array)
— The supported values of item are GL_POINT_SIZE,
GL_LINE_WIDTH, GL_CURRENT_COLOR, GL_CURRENT_NORMAL, GL_CURRENT_TEXTURE_COORDS, GL_LIGHT_MODEL_AMBIENT,
GL_MODELVIEW_MATRIX, GL_PROJECTION_MATRIX, and GL_TEXTURE_MATRIX. The second parameter must be an array,
of sufficient size to hold the value.
glIsEnabled(item)
— The supported values of item the same as the supported
parameters for glEnable/glDisable.
glGetMaterialfv(face, property, array)
— All parameter are supported.
The second parameter must be an array, of sufficient size to hold the value.
glGetLightfv(light, property, array)
— The supported values of property are those for
which glLightfv are defines: GL_POSITION, GL_AMBIENT, GL_DIFFUSE, and GL_SPECULAR. The second parameter must be an array,
of sufficient size to hold the value.
glViewport(x,y,width,height)
glClearColor(r,g,b,a)
glClear(buffers)
— The parameter can be GL_COLOR_BUFFER_BIT, or
GL_DEPTH_BUFFER_BIT, or those two constants OR'ed together.
glLineWidth(size)
glPointSize(size)
glBegin(primitive)/glEnd()
— The primitive can be any of the ten
OpenGL 1.1 primitives: GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES,
GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP, GL_QUADS, GL_QUAD_STRIP, GL_POLYGON.
glColor*
— The suffixes 3f, 4f, 3d, 4d, 3ub, 4ub,
3fv, 4fv, 3dv, 4dv, 3ubv, and 4ubv are supported (but no distinction is made between
the f and the d versions). The v versions can take an ordinary JavaScript array or
a typed array as parameter.
glVertex*
— The suffixes 2i, 3i, 2f, 3f, 2d, 3d,
2iv, 3iv, 2fv, 3fv, 2dv, and 3dv are supported, but no real distinction is made among
the various numeric types. The v versions can take an ordinary JavaScript array or
a typed array as parameter. Note that the 4-coordinate versions of glVertex* are
not available.
glNormal*
— The suffixes 3f, 3d, 3fv, and 3dv are supported, but
no distinction is made between the f and d versions. The v versions can take an ordinary
JavaScript array or a typed array as parameter.
glTexCoord*
— The suffixes 2f, 2d, 2i, 2fv, 2dv, and 2iv are supported,
but no distinction is made between the f, d, and i versions. The v versions can take
an ordinary JavaScript array or a typed array as parameter. Note that only the 2-coordinate
versions of glTexCoord* are supported (and only 2D textures are supported).
glRect*
— The suffixes i, f, and d are supported, but they are all
really the same function.
glMaterialfv(side,property,value)
— The side can be GL_FRONT, GL_BACK,
or GL_FRONT_AND_BACK. The property can be GL_AMBIENT_AND_DIFFUSE, GL_AMBIENT, GL_DIFFUSE,
GL_SPECULAR, or GL_EMISSION. The value can be an ordinary JavaScript or typed array of
3 or 4 numbers. (In OpenGL, 4 numbers are required.)
glMaterialf(side,property,value)
— The side can be GL_FRONT, GL_BACK,
or GL_FRONT_AND_BACK. The property must be GL_SHININESS. The value is a number, which should
be in the range 0 to 128. (You can use the name glMateriali instead of glMaterialf, but
it's the same function.)
glColorMaterial(side,property)
— The parameter take the
same values as for glMaterialfv.
glLightfv(light,property,value)
— The first parameter should be
one of the constants GL_LIGHT0, GL_LIGHT1, GL_LIGHT2, or GL_LIGHT3 (unless you have changed
the number of lights, as discussed above under glEnable). The second parameter
must be one of GL_POSITION, GL_AMBIENT, GL_DIFFUSE, or GL_SPECULAR. (Note that not
all OpenGL lighting properties are defined; there are no spotlights or attenuation.)
The third parameter must be a JavaScript or typed array of 3 or 4 numbers. (In OpengGL,
4 numbers are required.)
glLightModelfv(property, value)
— The property must be
GL_LIGHT_MODEL_AMBIENT, and the value must be a JavaScript or typed array of 3 or 4 numbers.
(In OpenGL, 4 numbers are required).
glLightModeli(property,value)
— The property must be
GL_LIGHT_MODEL_TWO_SIDE or GL_LIGHT_MODEL_LOCAL_VIEWER. The value is treated as
a boolean. (Can also use glLightModelf.)
glTexImage2D(target,level,internalFormat,width,height,border,format,type,image)
—
The target must be GL_TEXTURE_2D. The level must be a non-negative integer. The internalFormat
and format must be GL_RGBA. The type must be GL_UNSIGNED_INT. The image
must be an Image object (or an HTMLImageElement). The width, height, and border
are ignored. (Consider using glsimTexImage(image), which is described earlier on this page, instead.)
glCopyTexImage2D(target,level,internalFormat,x,y,width,height,border)
—
The target must be GL_TEXTURE_2D. The level must be a non-negative integer. The internalFormat
must be GL_RGBA. The border parameter must be 0 if present. The values of x, y, width, and
height must be integers. (Consider using glsimCopyTexImage(x,y,width,height) instead.)
glTexParameteri(target,pname,value)
The target must be GL_TEXTURE_2D.
The pname can be GL_TEXTURE_WRAP_S or GL_TEXTURE_WRAP_T (with value
GL_CLAMP or GL_REPEAT); GL_TEXTURE_MAG_FILTER (with value
GL_NEAREST or GL_LINEAR); GL_TEXTURE_MIN_FILTER (with value
GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST_MIPMAP_LINEAR,
GL_LINEAR_MIPMAP_NEAREST, or GL_NEAREST_MIPMAP_NEAREST).
glGenTextures(count,array)
— The first parameter must be a positive integer,
and the second must be an array. The generated texture ID's are stored in the array.
glCreateTexture()
— This is not a standard OpenGL function (though it is in WebGL).
It generates one textureID, as by a call to glGenTextures, and it returns that texture ID.
glBindTexture(texureID)
— The textureID must be an integer ID returned by
glGenTextures or glCreateTexture. Initially, a default texture object is bound.
If glBindTexture is called with no parameter (or any value that evaluates to false as
a boolean), then the default initial texture object is rebound.
glMatrixMode(mode)
— The parameter must be
GL_PROJECTION, GL_MODELVIEW, or GL_TEXTURE.
glLoadIdentity()
glPushMatrix()
glPopMatrix()
glTranslatef(dx,dy,dz)/glTranslated(dx,dy,dz)
— These are actually the same function.
glScalef(sx,sy,sz)/glScaled(sx,sy,sz)
— These are actually the same function.
glRotatef(degrees,x,y,z)/glRotated(degrees,x,y,z)
— These are actually the same function.
glOrtho(xmin,xmax,ymin,ymax,near,far)
glFrustum(left,right,bottom,top,near,far)
gluPerspective(fovy,aspect,near,far)
— Note that this
is a function from the GLU library rather than an OpenGL function.
gluLookAt(eyeX,eyeY,eyeZ,refX,refY,refZ,upX,upY,upZ)
— Note that this
is a function from the GLU library rather than an OpenGL function.
glVertexPointer(coordsPerVertex,dataType,stride,vertexArray)
—
coordsPerVertex can be 2 or 3 (4 is not supported). The dataType must be GL_INT,
GL_FLOAT, or GL_DOUBLE, but the actual value is ignored. The stride must
be zero. The vertexArray can be a JavaScript array of numbers or a typed array.
glColorPointer(componentsPerColor,dataType,stride,colorArray)
—
componentsPerColor can be 3 or 4. The dataType can be GL_FLOAT, GL_DOUBLE, or
GL_UNSIGNED_BYTE. For GL_UNSIGNED_BYTE, color values are divided by 255, then
treated as floats. The other two parameters are as for glVertexPointer.
glNormalPointer(dataType,stride,normalArray)
— The parameters
are as for the same parameters in glVertexPointer.
glTexCoordPointer(size,dataType,stride,texCoordArray)
— The first
parameter, size, must be 2. The remaining parameters
are as for the parameters in glVertexPointer.
glDrawArrays(primitive,start,count)
— The primitive can be
any of the ten primitive types, as for glBegin. Note that GL_QUADS
is inefficient, since it has to be converted to GL_TRIANGLES, and that requires
rebuilding the vertex arrays.
glDrawElements(primitive,vertexCount,indexType,indexArray)
—
The primitive can be any of the ten primitive types, with the same comment about
GL_QUADS as for glDrawArrays. The indexType can be
GL_UNSIGNED_INT, GL_UNSIGNED_BYTE, or GL_UNSIGNED_SHORT, but the values are always
converted to 16-bit integers. The indexArray can be a JavaScript array of integers
or a typed array of integers.
glPolygonOffset(units,value)
glFlush()
— This is defined, but calling it shouldn't be necessary.
glDepthMask(enableWriteToDepthBuffer)
— The parameter is treated
as a boolean value.
glFrontFace(face)
— The parameter must be GL_CW or GL_CCW.
glBlendFunc(a,b)
— The parameters are passed to WebGL without checking
them for validity, but really only glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) should be
called (and none of the other constants that could be used as parameters in OpenGL are defined in glsim.js).
The Camera
The Camera class that is defined by glsim.js is designed to take over the job of setting up the OpenGL projection and viewing transforms. It can, optionally, be used to rotate the view, like a trackball.
To use a camera, you need a global variable to refer to it. I assume that that variable is named camera. The camera is created by a constructor without parameters:
camera = new Camera();
camera.apply()
— Sets the projection and viewing transform
for the current OpenGL context, according to the configuration of the camera.
This completely replaces the current projection and modelview transforms.
It would ordinarily be called at the start of the display function.
The camera can be configured with the following functions:
camera.lookAt(eyeX,eyeY,eyeZ,refX,refY,refZ,upX,upY,upZ)
— sets up
the viewing transform. The viewing transform will be set up by calling gluLookAt
with the same parameters, when camera.apply() is called. The default
configuration has the eye at (0,0,30), the ref point at (0,0,0), and the up vector
as (0,1,0). If you call the function with fewer than 9 parameters, the up vector
is set to (0,1,0). If you call it with fewer than 6 parameters, the ref point
is set to (0,0,0). The first three parameters are mandatory.
camera.setLimits(xmin,xmax,ymin,ymax,zmin,zmax)
—
Set the limits of the view volume. The limits are set with respect to the
viewing coordinates. That is, the view center is assumed to be at the point
(0,0) in the plane of the screen. The view up vector (more precisely, its projection
onto the screen) points upwards on the screen. The z-axis is perpendicular to the
screen, with the positive direction of the z-axis pointing out of the screen.
In this coordinate system, xmin and xmax give the horizontal limits on the screen,
ymin and ymax give the vertical limits on the screen, and zmin and zmax give
the limits of the view volume along the z-axis. (Note that this is NOT exactly
the same as the parameters in either glOrtho or glFrustum! Most important to
note is that zmin and zmax are given with reference to the view center, not the
eye. They are not the same as the near and far parameters in glOrtho or glFrustum.)
Note that xmin/xmax or ymin/ymax might be adjusted to match the aspect
ratio of the viewport. The default limits are (-5,5,-5,5,-10,10).
camera.setScale(s)
— Shorthand for camera.setLimits(-s,s,-s,s,-2*s,2*s);
camera.setPreserveAspect(preserve)
— The parameter is a boolean which
says whether or not to respect the aspect ratio of the viewport. The default is true.
If the value is true, then the requested limits will be adjusted, if necessary, to match
the aspect ratio of the viewport.
camera.setOrthographic(ortho)
— The parameter is a boolean which says
whether the projection should be a perspective or an orthographic projection. The default
is perspective.
To use the trackball rotation feature, just call the function
camera.installTrackball(displayFunction)
— This installs a mouse
listener on the canvas that is used by the current OpenGL context. (So, it must be called
after creating the context with glsimUse.) When the user drags the mouse on the
canvas, the listener responds by modifying the camera's viewpoint in a way that simulates
rotating the scene about the point (0,0,0). Note that only rotation about the origin
is supported. The parameter is a function that will be called each time the view is
modified. Presumably, it will be a function that redraws the entire scene and that includes
a call to camera.apply().