6491 and 7491 PROJECTS
Alexander Powell

Surface Fairing

CS6491: Computer Graphics
Alexander 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

Pictures

Click 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.


about me
navigate
links