Contours and Curvature

By Leia Chang

CSC 471 - Winter 2016


My final project for Intro to Graphics was an exploration into non-photorealistic rendering, specifically attempting to use contours and curvature to portray an object, and studying line widths to convey curvature. In particular, I was interested in studying the simulation of brush strokes in artistically rendering objects.

Learned Concepts


Contours are the silhouette of the model. They are formed when the normal of a vertex is perpendicular to that of the view vector: that is, the normal vector to the point (n) and the view vector (v) are perpendicular. We calculate this by determining where n dot v is equal to 0, meaning the angle between the two is 0.

Line Widths

Much of non-photorealistic rendering is an attempt to emulate artistic styles. Line width is often used as an artistic tool to represent the artist's intention as well as the object in question: thinner lines often mean either sharper curves and edges, or objects that are farther away. This is exemplified in cartoon-style drawings, where line widths from brush pens are used to convey weight.


An Isophote map is a curve on a chart joining points of equal light intensity from a given source. More commonly, it's a thresholded curve chart of similar light intensities on an object. The chart can be used to help determine line weights when calculating brush strokes.

Other concepts and technologies learned

  1. Rendering vertice lists as lines
  2. Representing and manipulating object representations on the CPU


A video of the final output. The first representation is line-drawn silhouette contours, providing a sketch-like rendering of the bunny. The second is the ndotv fragment-shader representation of the silhouette contours with isophote shading. The last section is the silhouette contours from the fragment shader shown alone.

I hope to continue this project, to learn how to render brush strokes and better represent objects using artistic approaches.


I encountered a lot of problems trying to figure out how to use TriMesh. The different data structures ended up messing up a lot of things, and until the last day or so I was unable to draw using the TriMesh structure.

In addition to that, all example renderings I found using TriMesh used methods of rendering that were linked to older versions of GLSL, passing vertices and data directly to be rendered, rather than in buffers to shaders. These did not use shaders at all, as far as I could tell, and therefore left me very frustrated in trying to figure out how to adapt this to use the shaders I needed. In the end, I ended up passing the TriMesh data (vertices, normals, etc) directly to the shaders using buffers, similarly to how Shape does. Given the chance to go back, I would have taken more time to gain familiarity with TriMesh.

Another problem I encountered is the support for GlLineWidth(). In general this function is used to define the line width that is drawn when using GlDrawArrays. However, the implementation and support for this varies based on machine and setup, and the actual support for maximum width is extremely variable. It so happened that on my setup, the support was limited to a line width of 1 pixel, making representations of "pencil lines" much harder, and the resulting lines drawn can be hard to see.


  1. CPE 471 - Base code
  2. Isophote Distance: A Shading Approach to Artistic Stroke Thickness
  3. Contour Line Rendering by Jeroen Bert
  4. Trimesh2, Princeton
  5. Illustrating Sooth Surfaces