5.10 - Converting OBJ Model Data to Buffer Objects

Building complex models for WebGL programs should be done using modeling tools such as Blender. However, getting a model from Blender into a WebGL application is non-trivial. You can use the JavaScript class described below to convert an OBJ model file into WebGL arrays suitable for GPU buffer objects.

You have two fundamental choices when you organize the data of a model for rendering.

  1. Optimize for speed.
    • How:
      • Minimize the number of WebGL Javascript commands issued to render a model.
      • Minimize context switching as you render a scene.
      • Render an entire model (or multiple models) using one call to gl.drawArrays().
    • Implications:
      • Everything is rendered using gl.TRIANGLE mode.
      • All data a shader program needs is in buffer objects organized by vertices.
      • A single shader program is used to render an entire model (or scene).
      • Model data will be duplicated for individual vertices resulting in very large buffer objects.

  2. Minimize memory usage.
    • How:
      • Use gl.TRIANGLE_FAN and/or gl.TRIANGLE_STRIP as often as possible to render collections of triangles.
      • Use uniform variables in your vertex shader programs for values that are constant for multiple triangles.
    • Implications:
      • Many calls to WebGL Javascript commands.
      • Many calls to gl.drawArrays().
      • Many context switches.
      • Rendering will be slower.

It is impossible to organize model data to get fast rendering and efficient memory usage at the same time. You have to make trade-offs based on the application you are creating.

Function createModelsFromOBJ()

The JavaScript file learn_webgl_obj_to_arrays.js contains class definitions and global functions that will convert a model defined in an *.obj data file into a set of 1D arrays suitable for buffer objects. The function createModelsFromOBJ, (see function prototype below), receives the text description of one or more models, along with a set of material property definitions, and returns a set of ModelArrays objects.

/**
 * Given an OBJ text model description, convert the data into 1D arrays
 * that can be rendered in WebGL.
 * @param model_description String Contains the model data.
 * @param materials_dictionary Dictionary of material objects.
 * @param out An object that knows where to display output messages
 * @return Object A set of ModelArray objects accessible by name or index.
 */
function createModelsFromOBJ(model_description, materials_dictionary, out) {

The arrays in an ModelArrays object are optimizes for fast rendering, not efficient memory usage. The buffers are organized around gl.drawArrays() rendering modes.

  • A ModelArrays object contains 3 sub-objects:
    • Points object, which can be rendered using gl.POINTS mode.
      • vertices 1D array: [x1,y1,z1, x2,y2,z2, ...]
      • colors 1D array: [r1,g1,b1, r2,g2,b2, ...]
      • Material properties for all points.
    • Lines object, which can be rendered using gl.LINES mode.
      • vertices 1D array: [x1,y1,z1, x2,y2,z2, ...]
      • colors 1D array: [r1,g1,b1, r2,g2,b2, ...]
      • textures 1D array: [t1, t2, t3, ...]
      • Material properties for all lines.
    • Triangle object, which can be rendered using gl.TRIANGLES mode.
      • vertices 1D array: [x1,y1,z1, x2,y2,z2, ...]
      • colors 1D array: [r1,g1,b1, r2,g2,b2, ...]
      • flat_normals vector 1D array: [dx1,dy1,dz1, dx2,dy2,dz2, ...]
      • smooth_normals vector 1D array: [dx1,dy1,dz1, dx2,dy2,dz2, ...]
      • textures 1D array: [s1,t1, s2,t2, ...]
      • Material properties for all triangles.

Note the following about arrays in a ModelArrays object:

  • If the OBJ file contains normal vectors, the vectors from the file are used.
  • If the OBJ file contains no normal vector information:
    • If a face is marked as “smooth” by a ‘s on‘ line in the data file, then the normal vector for a vertex is calculated as an average of the normal vectors of the triangles that uses that vertex.
    • If a face is marked as “flat” by a ‘s off‘ line in the data file, then the normal vector for a vertex is the face’s normal vector.
  • If the file contains no texture coordinates, the texture coordinates array will be empty.
  • The color of a vertex is the Kd value of the active material property, which comes from a *.mtl file. The Kd value is the diffuse color of the material’s properties. It is assumed for this implementation that all of the elements in a model will use the same ambient, specular, specular highlight, and other material properties. If you use these properties in your shader programs they can be set as uniform values in your shaders. (If any of the material properties change from vertex to vertex, and you want to use them as attributes in your shader program, you would need to modify the code in the ObjToArrays class to create an appropriate 1D array for the values.)

OBJ Model Data Demo

The code in Learn_webgl_matrix.js can be studied in the following demo. You don’t need to fully understand the code, but you should get an overall idea of what the code is accomplishing. You send the text from an .obj file which contains the description of one or more models, and it returns you an array of ModelArray objects. Each ModelArray object contains a set of 1D arrays containing the model data organized by vertices. These arrays are ready to be put into GPU buffer objects and then linked to shader program variables.

Show: Code Canvas Run Info
./obj_to_arrays_demo/obj_to_arrays_demo.html

Render various models using various shader programs.
Note: It may take several seconds for the large Dragon model to load.

Please use a browser that supports "canvas"
Rendering options:
Use just the color of the model - no lighting calculations.
Flat shaded - use lighting calculations with one normal vector per triangle.
Smooth shaded - use lighting calculations with one normal vector per vertex.
Wireframe - only draw the triangle edges.
Animate
Model options:
Cube: 8 vertices, 12 triangles
Sphere: 134 vertices, 264 triangles
Dragon: 418,336 vertices, 836,672 triangles
Shader errors, Shader warnings, Javascript info
Open this webgl program in a new tab or window

The object returned by the createModelsFromOBJ function contains one or more models. The models can be accessed “by name” or by array indexes. To access the models as an array:

// Create ModelArrays objects from the obj data
models = createModelsFromOBJ(obj_text, obj_materials, out);

// Access the models by array index
for (j = 0; j < models.number_models; j += 1) {
  one_model = models[j];

  // Do something with one_model
}

If you know the exact name of your models, you can access them using those names. The “name” of a model comes from the name you assigned a set of geometry in Blender. The object return by the createModelsFromOBJ function has properties using those names. Suppose you named your models “Bear”, “Monkey”, and “Goat”. You can access the individual models using those names like this:

// Create ModelArrays objects from the obj data
models = createModelsFromOBJ(obj_text, obj_materials, out);

models.Bear
models.Monkey
models.Goat

Conclusion

This concludes the lessons on rendering. We will discuss shader programs again in sections 9, 10, & 11 because all surface materials and lighting effects are done in shader programs.

Next Section - 6.1 - Introduction to Transformations