Project 2: MeshEdit

By Alana Li

Overview

In this project I implemented Bezier curves and surfaces using de Casteljau subdivision. I also implemented smoothing of meshes with averaged normals, as well as loop subdivision for upsampling meshes.

Part 1: Bezier Curves with 1D de Casteljau Subdivision

Step 0

Step 1

Step 2


Step 3

Step 4

Step 5


Step 6

Final Curve

Modified t

Given a Bezier curve with n control points, we can apply de Casteljau's algoritm to repeatedly compute linear interpolations between the points to end up with a smooth curve. We do this by adding lines between neighboring pairs of lines between control points at position t. We then repeat the process with the calculated new lines until we end up with just one point, which becomes a point on the Bezier curve.

To do this, I used a for loop to call lerp on every pair of adjacent control points. Each new calculated point is added to the vector array and the process continues until there is only one point left.

Different Control Points

Part 2: Bezier Surfaces with Serarable 1D de Casteljau

Mesh Teapot

Bezier surfaces can be represented as a series of Bezier curves lined up. We can use de Casteljau's algorithm to calculate the control point of each curve in one dimension with our u variable, before linearly interpolating in the other direction with the input v.

This is done by evaluating the control points one row at a time in the u dimension. This determines the Bezier curves. We then evaluate the u points at t in the v direction, creating the final Bezier surface.

Part 3: Area-Weighted Vertex Normals

Flat Shading
Phong Shading

I used a HalfedgeCIter object to keep track of the starting halfedge, looping until we eventually come back to the start. Halfedge objects have a pointer to a trianglar face with 3 vertices. By following the halfedge's next() and position pointers we can get the positions of all 3 vertices of the face. I then took the cross product of the difference between adjacent vectors and added that to the sum. I then move on to the next surface by accessing the halfedge's twin() then next() pointer. When the loop is finished, the normalized vector of the sum is returned.

Part 4: Edge Flip

Before Flipping
After Flipping

To implement this feature I labeled all the vertices and edges before and after an edge flip, as well as noted the changes in face. It was very helpful to draw a diagram and label everything before and after a flip. I assigned all the initial vertices, faces, halfedges, and edges to variables. Then I reassigned the pointers using setNeighbors to change them to the vertices, halfedges, edges, and faces after the flip.

I ran into some bugs that involved holes in my mesh, but I solved it by updating the faces after flipping.

Part 5: Edge Split

No Splitting
Splits
Splits and Flips

Once again I labeled the different verticies and edges before any splitting, then reassigned them after the split. This involved adding a vertex, 2 faces, 3 edges, and 6 halfedges to the mesh.

Part 6: Loop Subdivision for Mesh Upsampling

No Upsampling

Upsampling x1

Upsampling x2

Upsampling x3

Loop subdivision was implemented by calculating new positions for the mesh vertices. To do this I iterated through the old vertices and updated their positions by using the weighted positions of their neighbors. I then calculated the new vertex positions by apply the formula 3/8 * (A + B) + 1/8 * (C + D). The mesh's original edges were then split and the new vertices were assigned the position calculated in the previous steps. Afterwards, all edges with one new and one old vertex were flipped, and the positions of all the vertices were updated.

As you can see from the cow mesh above, loop subdivision creates a smoother mesh. The sharp edges are rounded out as the positions are updated to be the weighted average of the positions of nearby vertices. As a result of this, nearby vertices that are farther apart have a much more drastic change in position compared to nearby vertices that are close together. This means that the less dense vertices are in a mesh, the more smoothing.

To reduce this effect, we can pre-split the edges so that vertices are closer together, which will reduce the amount of smoothing we get.

Before Pre-Splitting:

No Upsampling

Upsampling x1

Upsampling x2

Upsampling x3


After Pre-Splitting:

No Upsampling

Upsampling x1

Upsampling x2

Upsampling x3


Notice that the mesh after pre-splitting is also symetrical, compared to the mesh without. This is because the mesh before pre-splitting was not completely symetrical as vertices had different numbers of neighbors. This causes the cube to become asymetical as the number of neighbors of a vertex affects the change in position.

Before Pre-Splitting:

No Upsampling

Upsampling x1


After Pre-Splitting:

No Upsampling

Upsampling x1


One bug I ran into was where I forgot to store the starting value of the halfedge before iterating on it in a do while loop. This caused a very interesting and slightly creepy looking mesh after loop subdivision:

Interesting Bug