CS424 Notes, 20 February 2012
- The Teapot
- We will continue with the discussion of drawing polygonal meshes, including the teapot example from Friday's notes.
- We will also discuss texture coordinates for objects like spheres and cylinders.
- A handout for class with some code segments from today's examples.
- Wavefront OBJ files
- There are many standard file formats for 3D models. One common, simple format is Wavefront OBJ files. The simplest OBJ files specify indexed face sets given as vertices and faces. Each vertex is represented as a line beginning with a "v", followed by three numbers giving the coordinates of the vertex. All such lines in the file are considered to be consecutively numbered, starting with 1. Faces are given by lines starting with an "f", followed by the indices of the vertices of the face, where an index gives the position of the vertex in the list of all "v" lines. (There can be other lines in the file, but we can ignore them.)
- Here, for example, is a simple OBJ file for the pyramid example that we saw last time:
v 1 0 1 v 1 0 -1 v -1 0 -1 v -1 0 1 v 0 1 0 f 5 4 1 f 5 1 2 f 5 2 3 f 5 3 4 f 1 4 3 2
Note in particular that the numbering of the vertices in an OBJ file starts with 1, not 0! For a flat-faced polyhedron, an OBJ file of this type contains enough information to draw the object (without a texture, at least). You still need normal vectors for the faces, but they can be computed from the vertices of the face. - An OBJ file can also contain information about normals and/or texture coordinates. Normals are specified by lines that begin with "vn", followed by three numbers giving the coordinates of the normal vector. The normal coordinates are also considered to be in a list, with numbering starting at one. Texture coordinates are specified by lines that start with "vt", following by one, two, or three numbers giving texture coordinates. Again, vt lines are considered to be numbered starting from 1.
- When normals and texture coordinates are included, faces become more complicated. Each vertex of a face is given by a string of the form v/t/n, where v is the index of a vertex coordinate line, t is the index of a texture coordinate line, and n in the index of a normal vector line. If only normals are given, the t can be omitted, but the two slashes are still required. For example, "5/3/12" means to use vertex coordinates number 5, texture coordinates number 3, and normal vector number 12, and 71//4 means vertex number 71 with normal vector number 4.
- Blender can export OBJ files of its models. Select the object(s) you want
to export and use the File / Export / Wavefront(.obj) command. You will see
a set of options along the left edge of the file browser window, including
options to include normal vectors and to triangulate the faces (meaning every
face in the export will have three vertices). Here is a cube exported by
Blender. (Lines starting with #, o, and s can be ignored.)
# Blender v2.61 (sub 0) OBJ File: '' # www.blender.org o Cube v 1.000000 -1.000000 -1.000000 v 1.000000 -1.000000 1.000000 v -1.000000 -1.000000 1.000000 v -1.000000 -1.000000 -1.000000 v 1.000000 1.000000 -0.999999 v 0.999999 1.000000 1.000001 v -1.000000 1.000000 1.000000 v -1.000000 1.000000 -1.000000 vn 0.000000 -1.000000 0.000000 vn 0.000000 1.000000 0.000000 vn 1.000000 0.000000 0.000000 vn -0.000000 -0.000000 1.000000 vn -1.000000 -0.000000 -0.000000 vn 0.000000 0.000000 -1.000000 s off f 1//1 2//1 3//1 4//1 f 5//2 8//2 7//2 6//2 f 1//3 5//3 6//3 2//3 f 2//4 6//4 7//4 3//4 f 3//5 7//5 8//5 4//5 f 5//6 1//6 4//6 8//6
Blender can include texture coordinates as well, but only in the form of "UV's", which are texture coordinates that have been specified in Blender's complicated UV mapping interface. We'll leave out texture coordinates for now. - OBJ files can be more complicated than these simple examples, but we will not need any of the complications.
- Discussion: How could you draw such OBJ files in WebGL? What if all the sides were triangular? How does having separate indices for coordinates, normals, and texture coordinates complicate things?
- SimpleView3D
- We will start looking more seriously at 3D transformations and viewing soon. For now -- in particular for this week's lab -- we will use a small SimpleView3D class for viewing. It is defined in glutil3d-simple.js, which is used in cubetest.html and teapot.html.
- Note that glutil3d-simple.js also defines a function installRotator(canvas,simpleView3d) that installs a mouse handler on a canvas which lets the user rotate the view by dragging the mouse.
- The constructor new SimpleView3D(max,viewpoint,viewup) creates a view with a "view volume" extending from -max to max in each direction. This ensures that objects within max units of the origin will be visible. The viewpoint and viewup are optional. If present, they should be Vector3D's, a class also defined in glutil3d-simple.js. The viewpoint specifies the direction in which the viewer is initially looking; the user looks from the viewpoint towards the origin. The default is new Vector3D(0,0,20). The viewup vector specifies a vector that will point upwards in the view; the default value is new Vector3D(0,1,0)
- A SimpleView3D works together with a rotator created by the installRotator function; dragging the mouse changes the view.
- If view is a SimpleView3D, then view.modelMatrix() returns a 3-by-3 matrix in a format
that can be used with gl.uniformMatrix3fv to set the coordinate transformation matrix associated with
the view. This matrix can be applied to vertex coordinates and to the normal vectors.
And view.projectionMatrix() returns a 4-by-4 matrix for use with gl.uniformMatrix4fv that can be applied to the transformed
vertex coordinates to project them to clip coordinates. You can see how these matrices are used
in the vertex shader from cubetest.html:
attribute vec3 vertexCoords; uniform mat3 transform; // from view.modelMatrix() uniform mat4 projection; // from view.projectionMatrix(); void main() { vec3 transformedVertex = transform*vertexCoords; gl_Position = projection * vec4(transformedVertex, 1.0); }
- AJAX Loader
- We should also look at the ajaxLoad function, which is used in
teapot.html to load the
teapot model:
/** * Loads a file from a given url using AJAX. The AJAX code * that is used here should work in any browser that supports * WebGL, but not in Internet Explorer [except maybe new versions] * The first parameter is the url of the file to be loaded, presumably * a relative url. onSuccess is a function that will be called when * the AJAX requests succeeds; the first parameter to onSuccess is * the response text from the AJAX call (that is, the content loaded * from the url), and the second (optional) paramter is the XMLHttpRequest * object in case you want to get more information from that object. * onFailure is a function that will be called if the AJAX request * fails; the first parameter is an error message, and the second (optinal) * parameter is the XMLHttpRequest object. If timeLimit is given, * then the request will fail if there is no response within timeLimit * milliseconds. Only the url parameter is required; the others can * be missing or null. */ function ajaxLoad(url, onSuccess, onFailure, timeLimit) { var timeout = null; var request = new XMLHttpRequest(); // (doesn't work in IE) request.open("GET", url); request.onreadystatechange = function () { if (request.readyState == 4) { if (timeout) cancelTimeout(timeout); if (request.status == 200) { if (onSuccess) onSuccess(request.responseText, request); } else { if (onFailure) onFailure(request.statusText, request); } } } if (timeLimit) { setTimeout(function() { timeout = null; request.abort(); if (onFailure) onFailure("Request timed out", request); }, timeLimit); } request.send(); }
This function is defined in glutil3d-simple.js. In teapot.html, it is called as:ajaxLoad("teapot.json", function(responsetext){ try { teapotData = JSON.parse(responsetext); } catch (e) { document.getElementById('message').innerHTML = "Could not load model: Can't understand data that was received"; return; } document.getElementById('message').innerHTML = "Drag your mouse on the teapot to rotate it."; init(); }, function(errortext){ document.getElementById('message').innerHTML = "Could not load model: " + errortext; } );
We will talk about what all this means in class. But note that since the teapot model is loaded using AJAX, the teapot example must be loaded from a web server, not from the file system, for the loading of the model to succeed.
- We should also look at the ajaxLoad function, which is used in
teapot.html to load the
teapot model: