In this project, we implemented the physics of simulating a piece of cloth falling on a sphere. We modelled the cloth as lattice of point masses, connected by springs. We wrote out the differential equations that describe the dynamics of the system and used Verlet integration to simulate how the objects would behave over time. Next, we added handling for collisions with other (fixed) objects in the scene, like spheres and planes. We also added handling for self-collisions (ie. when pieces of the cloth hit other parts of the cloth) using spatial hashing to quickly lookup the positions of point masses. Finally, we implemented various shaders (Lambertian diffuse, Blinn-Phong, Texture Mapping, Bump/Displacement mapping, and mirror reflection) using the GLSL language to allow for different textures and light effects on the surface of the object.
Overall this was a fun project that allowed us to apply much of the theory we learned in this class about physical simulation and turn these ideas into code. We specifically found the task of writing out all the forces affecting each object and performing the integration to be interesting and challenging, and we spent quite some time debugging this section and making sure we understood each step of the implementation. We also found the handling of self-collisions to be challenging task since there were many edge cases we had to consider.
|
|
|
|
|
In this part we implemented the logic to calculate all the forces on each point-mass in the system. In our case, this primarily concerned the spring forces and the force of gravity acting on all the masses. Using these forces on each object, we could write out the differential equations that describe the behavior of the object's evolution over time. We used Verlet integration to calculate numerical solutions for these differential equations as time progresses. Finally, we implemented an additional constraint that a spring's length should not stretch by more than 10% of its rest length to avoid the cloth appearing to be too "stretchy".
The spring constant ks seems to affect how "loose" or flexible the piece of cloth is. A higher spring constant would mean the cloth is more firm, thus stays tighter as the simulation plays but also oscillates more and is more jittery before coming to rest. A lower spring constant on the other hand means the cloth is more flexible and is more "droopy". The images below show the rest state for different values of ks. Note that for low ks the cloth is more "droopy" while for high ks it stays firm.
|
|
The density affects how "heavy" the cloth is, since it affects the weight at each point mass. Keeping the spring constants the same, heavier pointmasses would further extend the springs compared to lighter point-masses. Thus a more dense material droops more than the less dense one. Contrary to what we expected, regardless of the density the speed at which the cloth falls seems to be roughly the same. However, this makes sense as the acceleration due to gravity is the same regardless of the mass (as Galileo would have you know). The did did have a slight effect on how much it oscillated though (the heavier cloth did tend to oscillate more, especially along the top edge between the two points that were pinned.
|
|
The damping had a clear effect on how much the cloth would oscillate, and only on the time it would take to settle. It had virtually no effect on the final resting state of the cloth (unless the damping was 0, in which case it would never settle and oscillate continuously). High damping would lead to the cloth settling down without oscillating (and appearing to move very slowly) while low damping would lead to more oscillation and wild, jittery behavior of the cloth. Note that the there was a "critically damped" amount of time where it settled in the least amount of time. For damping below this critical damping, the cloth would take more time to settle since it would jitter around too much. For damping above this critical damping, the cloth would move too slowly (ie. wouldn't react enough to the spring forces) and it would thus also end up taking longer to settle.
|
|
The screenshot below shows the cloth with all 4 corners pinned in its resting state.
|
|
|
|
|
While the collision handling in the previous part took care of the cloth interacting with other objects, it did not consider the cloth folding in on itself. In this part, we implemented self-collisions so that the cloth would not pass through itself when it folded onto itself. Rather than naively have a nested loop over all pairs of point masses, we used spatial hashing so that we can easily lookup if a point mass of the fabric is in collision with another point on the fabric. Below are some screenshots of the cloth falling and folding on itself.
|
|
|
|
|
|
|
A shader is a highly optimized program that runs in parallel and helps speed up parts of the graphics pipeline. There are two types of shaders: vertex and fragment shaders. Vertex shaders transform the object vertices and can modify their position, normal, and other geometric properties before writing the final position. Fragment shaders process fragments of a scene (recall a fragment is all the geometric information necessary to draw a single pixel) and write a single color corresponding to what the pixel value should be. Vertex shaders calculate all the necessary information for vertices in a 3D mesh, and then we can use barycentric coordinates to interpolate across the surfaces. Afterwards, fragment shaders calculate the actual pixel values and apply the visual surface effects (like shading and material effects) using the information from the vertex shaders.
Blinn-Phong is a simple shading model that aims to make realistic-looking reflections. Specifically, the shading value for each point involves an ambient component, diffuse component, and specular component. In our implementation of this part we found referring to the project 2 code to be helpful. The ambient component corresponds to effects from ambient light in the scene. The diffuse component corresponds to reflected light that is scattered equally in all directions from a surface (like Lambertian diffuse). The specular component corresponds to the "glinty" reflections from light sources in the scene. Below, we see the effects of the three components of Phong shading and the final result of combining them.
|
|
|
|
We also implemented texture mapping. This involved a fairly straightforward set of function calls. Below we see our custom texture (picture of a basketball) mapped onto a flat piece of cloth.
|
We also implemented bump mapping and displacement mapping. Bump mapping involves assigning a height-level (topography) for each point on a surface, and adjusting the shading effects accordingly without actually changing the locations of the vertices. Bump mapping thus only requires making changes to the fragment shader. Displacement mapping is similar to bup mapping but actually moves the vertices based on the height, and thus requires making changes to the vertex shader, too. Below are some images using bump and displacement mapping on the sphere and cloth in the scene. We used Texture 4.
|
|
|
|
|
|
|
|
Bump mapping seems to affect the appearance, but not the behavior of the objects in the scene. This makes sense as it only affects the pixel values seen, not the underlying geometry. Displacement mapping on the other hand seems to have more of an effect on the physics and actually moves the vertices according to the height map. The coarseness of the sphere had a very slight effect on the appearance—notice that the coarse sphere is not as round. Otherwise there were no real differences.
We also implemented a mirror shader, which reflects the surrounding environment.
|
|
For the extra credit, we wanted to model the relationship of a cloth interacting with multiple objects at once. To do this, we wanted to model how a cloth would behave when falling between two spheres. In doing this, we quickly became curious about what happens when you change the size and location of the spheres.
The first step was to modify the skeleton code to accept multiple inputs via the JSON. The sphere collision JSON representation is now a list. We iterate through this list to create multiple spheres in our scene. This part was not very conceptually challenging, but took some time to figure out the pointer manipulation and interacting with the JSON. We can now initialize any number of objects in the scene simply by changing our json input file.
The next step was to move collision objects. To do this, we modifying the interface to add sliders for each collision object in the scene. These sliders allow us to get inputs for different parameters such as sphere radius or x and y locations. By modifying these, we can change our scene in real time. In order to change the parameters of each of the collision objects, we created instance methods that allow us to change the radius and position of each sphere in the scene. This change can happen before the simulation starts, or even while the simulation is running. This feature allows us to run some cool interations between objects. A few interesting phenomena that I noticed were: Increasing the size of the sphere when a cloth was already resting on top of said sphere, moving a sphere left and right when an object hits it and more. A few examples of cool scenes that we were able to produce have been added below. Since we wanted to allow for an arbitary number of objects, we made all of this code work for a vector and not a set size. For this part of the project, we wanted to focus on modifying spheres, but this implementation can easily be modified to move planes as well.
Some challenges we had included accessing and modifying values of the sphere object. Since we stored a list of collision objects, we were unable to cast from a collision object to a sphere.