About the Project

What I Did

When watching the many exciting demos in class, I was most intrigued by the concept of metaballs and, therefore, wanted to explore them in my final project. I was able to implement a CubeGrid, which constituted the main part of the underlying code of these metaballs. In particular, the concept of implicit surfaces was not yet something we had explored in mathematic depth, so when reading the sources I used to prepare for implementation (see "Sources I Used" below), it was quite fascinating to learn exactly how to introduce this into the scenegraph that we'd built over several past assignments.

I wanted to incorporate as many additional features are possible so a user can interact with the metaballs and see how they act under different parameters. Besides the implementation of the metaballs, which was, of course, the trickiest part of this project, here are a few other features I implemented.

Metaball Animation/Pulsation

Implementing this feature was very similar to Asst 7. I used the same animation technique with glutTimerFunc to update positions of each metaball. Their position was determined based on trigonometric functions. You an increase the speed of the animation using the key command + and slow it down using the key command -.

Individual Metaball Color

This was a feature I at first thought I wouldn't be able to implement, given the nature of the g_gridCubeGeometry being treated as one shape to draw. However, I discovered that I could pass an individual color into a custom fragment shader to make it appear as if each metaball were separate from the next, even if they were draw using the same material.

I created a vertex shader and fragment shader pair called metaball-gl3.vshader and metaball-gl3.fshader respectively, which acted similar to the basic-gl3.vshader and specular-gl3.fshader. Then I created a new material g_balltexMat that used these shaders, similar to g_shinyMat. However, it takes in the variable aColor, which it pulls from a new geometry SimpleGeometryPNC, made up of a new type of vertex VertexPNC. This matched the format of VertexPNX except instead of taking in texture coordinates, it takes a Cvec3 color directly in as a property.

Then, as I built the g_cubeGridGeometry from the surface vertices of the CubeGrid I calculated what the closest metaball origin to that particular surface vertex, using a simple distance formula. From this, I created a relationship between the vertex and a specific metaball index and determined the color to pass to the shaders from this.

Increase/Decrease Radius

This was not particularly complicated. I just had a global variable, g_radius, which could be incremented using the keyboard command r to increase adn e to decrease. When this was triggered, the metaballs had to be updated and re-rendered, so the CubeGrid would be called gain and interpreted to render the implicit surfaces.

Add/Remove Metaballs

Since I saved the metaballs in a global vector of Metaball, they could vary in number quite easily using the C++ std::vector functions. I capped them at 10, otherwise the program begins to run pretty slowly. Similar to the radius-changing, this particular feature required the entire CubeGrid to be called again, so that the implicit surfaces could be calculated and re-drawn.

Moving Individual Metaballs

This was particular confusing because I could not use the Picker from the previous assignments to simply pick a metaball. This, of course, is due to the fact that metaballs are implicit surfaces derived from the CubeGrid, so they do not actually exist as separate entities in the scenegraph. My workaround included the idea that you must first pause the animation, or "pulsation" as I call it, in order to select an individual metaball using the key command q. This immediately shows the arcball over the first metaball. In order to cycle through the metaballs to select the one of your liking, you use the key command p, which will move the metaball. Rotation is obsolete in this scenario with the metaballs. Instead, it is only relevant to translate the metaballs because we are essentially only moving the metaball center, defined as Metaball.position. Therefore the right mouse click and middle mouse click (or spacebar-left click) are the only actions that garner response from the metaballs. You can move the metaball to a new location and then press q to restart the animation.

Cushion Floor

I thought it would be fun to change the texture of the floor, so I downloaded a texture from a blog (see "Sources I Used") and put that on the ground plane.

What I Tried

or what I wanted to try

One thing I tried but then avoided because I did not want to mess up the project I already had before submission was the implementation of texture mapping onto the metaballs. I did a lot of research on the matter, and I wasn't able to find much about UV mapping onto 3d metaballs. I did find such for a sphere, but it did not work when I tried it out, so I decided to abandon this task.

I also was trying to create a system where there was better blending of color between two metaballs. In particular, I wanted to track the second closest metaball, just as I tracked the first closest. Then I would create a weighted ratio between the two that would make for a blended color for that particular vertex. I have commented out a few lines of trial code that did not quite work for me. (What ended up happening was all the metaballs would just gradually turn yellow, which didn't seem really exciting, though I guess they were blending.)

If I had more time, I wanted to explore the concept of refraction. In particular, I believe refracting metaballs would be an amazing thing to implement in the future because they would resemble bubbles!

What I Learned

I thoroughly enjoyed the process of this project. It was certainly a bit of a daunting task because I didn't want to start anything too ambitious and end up with nothing to submit; therefore, I was a bit scared to tackle metaballs, which was a foreign topic that I was not sure I could handle. Luckily, it worked out in the end.

Reading through articles on metaballs and marching cubes were perhaps where I learned the most. It was fascinating to see how such an organic object can be built from something as rigid as a 3-dimensional lattice.

As mentioned earlier, it was definitely a challenge dealing with something that appeared visually like it fit in with other objects we'd built in the past but in reality was quite different because of the nature of its derivation.

While researching things to try and implement, like texture mapping to the metaballs or color blending, I learned about these concepts a bit more as well.

How To Run The Program

In my final-project folder, you can run the program with the following commands. (Note: I built this on Mac OS X.)

$ make clean
$ make OPT=1
$ ./final

Once you have the program running, you can use the following keyboard commands to interact with the metaballs:

hShow Help Menu
sSave Screenshot
vCycle View
aToggle Arcball Display
mAdd 1 Metaball
nRemove 1 Metaball
rIncrease Metaball Radius
eDecrease Metaball Radius
qStart/Stop Metaball Pulsation
pToggle Between Metaball Picking
+Speed Up Animation
-Slow Down Animation