6491 and 7491 PROJECTS Alexander Powell 

Surface FairingCS6491: Computer GraphicsAlexander Powell  [ alex at powelltown dot com ] September 25, 2003 This project is an implementation of Taubin's surface fairing algorithm. I implemented the project on top of the TMesh class in my group's Project 1 source. The particular method which performs the surface fairing is as follows: void TMesh::fairSurface(GLfloat lm, WeightType wt) { int ii; deque<Vector> newVertices(numVertices); deque<bool> tagged(numVertices); for (ii = 0; ii < numVertices; ii++) tagged[ii] = false; // We loop through each corner for(unsigned int a = 0; a < numTriangles * 3; a++) { if(!tagged[cornerTable[a]]) { // Create lists to keep track of our neighbors and their weights deque<Vector> neighborhood; deque<GLfloat> weights; // Visit the neighbors to calculate their weights int start = n(a); int current = start; do { // Add this vertex to our neighborhood list neighborhood.push_back(vt[cornerTable[current]]); // Calculate the weight for this edge GLfloat curWeight; if (wt == DISTANCE) curWeight = ((vt[cornerTable[a]]  vt[cornerTable[current]]).length()); else { Vector v1 = vt[cornerTable[a]]  vt[cornerTable[n(a)]]; Vector v2 = vt[cornerTable[a]]  vt[cornerTable[p(a)]]; Vector v3 = vt[cornerTable[o(n(current))]]  vt[cornerTable[n(o(n(current)))]]; Vector v4 = vt[cornerTable[o(n(current))]]  vt[cornerTable[p(o(n(current)))]]; curWeight = (v1.cross(v2).length() + v3.cross(v4).length()); } weights.push_back(curWeight); current = o(n(current)); } while (current != start); // Normalize weights GLfloat totalWeight = 0.0; for (ii = 0; ii < weights.size(); ii++) totalWeight += weights[ii]; for (ii = 0; ii < weights.size(); ii++) weights[ii] = weights[ii] / totalWeight; // Compute new vertex position based on the Gaussian smoothing step in Eq. 4 Vector dx; for (ii = 0; ii < neighborhood.size(); ii++) dx += weights[ii] * (neighborhood[ii]  vt[cornerTable[a]]); newVertices[cornerTable[a]] = vt[cornerTable[a]] + dx * lm; // Tag this vertex so we don't recompute its location tagged[cornerTable[a]] = true; } } //replace old vertices for(unsigned int a = 0; a < newVertices.size(); a++) vt[a] = newVertices[a]; } } This method performs a gaussian smoothing, and therefore must be called once with a positive "lambda" value, and a second time with the negative "mu" value in order to preserve the size of the meshes. A typical method call would be like so: GLfloat lambda = 0.4; GLfloat Kpb = 0.01; GLfloat mu = 1.0 / (Kpb  (1.0 / lambda)); for (int ii = 0; ii < 100; ii++) { tmesh>fairSurface(lambda, AREA); tmesh>fairSurface(mu, AREA); } tmesh>calculateNormals(); The full source can be retrieved here: P2.tar.gz PicturesClick on a picture for a bigger (but likely of worse quality) version. dresser.csg  From left to right: Original isosurface :: 20 iterations (mu=0.2, Kpa=0.1) :: 50 iterations :: 50 iterations with wireframe. box.csg  From left to right: Original isosurface :: isosurface with wireframe :: 20 iterations (mu=0.6307, Kpa=0.1) :: 20 iterations with wireframe :: 50 iterations :: 50 iterations with wireframe :: 200 iterations :: 200 iterations with wireframe. spheres.csg  From left to right: Original isosurface :: 10 iterations (mu=0.6307, Kpa=0.1) :: 50 iterations :: 50 iterations smooth shaded. twospheres.csg  From left to right: Voxel grid :: isosurface :: 200 iterations (mu=0.6307, Kpa=0.01) :: comparison between voxel grid and smoothed surface. random.csg  From left to right: Voxel grid :: isosurface :: 200 iterations (mu=0.6307, Kpa=0.001) :: some ridiculous amount of iterations. chair.csg  From left to right: Original isosurface :: 100 iterations (mu=0.6307, Kpa=0.001) :: 200 iterations :: 400 iterations :: a fatter chair, lots of iterations. Note: The asymmetry is the result of the particular triangulation of the isosurface. squares.csg  From left to right: Voxel grid :: isosurface :: isosurface smooth shaded :: 200 iterations (mu=0.4, Kpa=0.01) :: 500 iterations :: different view, to demonstrate slight asymmetry. twospheres.csg  From left to right: Smoothed mesh, 2000 iterations :: same, but with wireframe :: surface smoothed with "distance" weighting, not "area" weighting :: distance weighting example, without wireframe. Note: There is very little difference between the "distance" and "area" methods for calculating weights in our isosurface meshes, because they contain fairly consistently sized triangles. I usually prefer area calculations over edge sizes because the surface is much better represented by solid triangles than simple edges. Also, the size difference in the images is not intentional the second image is not supposed to be bigger than the third. 

