CS 184: Computer Graphics and Imaging, Spring 2022

Project 4: Cloth Simulator

Saagar Sanghavi, Rishi Parikh

Webpage Link: https://cal-cs184-student.github.io/sp22-project-webpages-ssanghavi404/proj4/index.html Github Repo: https://github.com/cal-cs184-student/p4-clothsim-sp22-rs_proj4

Overview

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.

Part 1: Masses and springs

In this first part, we implemented a piece of cloth as an grid of point masses, connected by springs. The structural constraints (ie. the cloth should hold together) were implemented by springs connecting adjacent points. Shearing constraints (ie. the cloth should resist being pulled from opposite corners) were implemented by springs connecting masses diagonal from one another. Finally, bending constraints were implemented by springs connecting grid points to other points two units away. These constraints can be visualized in the images below.
Angled View
Close Up
Without any shearing constraints, we only see the structural and bending constraints that form the grid of the cloth.
Non-Shearing Constraints
With only the shearing constraints, we see the diagonals of the former grid.
Shearing Constraints
With all the constraints, we see both the grid and the diagonals.
All Constraints

Part 2: Simulation via numerical integration

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.

ks = 200000
ks = 50

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.

Density = 1 g/cc
Density = 10000 g/cc

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.

Oscilatory behavior due to no damping
Settled behavior with damping = 1

The screenshot below shows the cloth with all 4 corners pinned in its resting state.

All 4 Corners Pinned

Part 3: Collisions with other objects

In this part of the project, we implemented collisions with other simple objects like spheres and planes. To do this, we check if the particles in the cloth collide with another object and if so, we update the positions of the particles to remain outside the boundary of the object. Beow are some pictures of the resting state of the cloth on a sphere for different values of the spring constant ks.
ks = 500
ks = 5000
ks = 50000
Notice that a stiffer springs (higher ks) leads to a "stiffer" fabric that folds in less places, while a smaller spring constant leads to a more "soft" fabric that has many more finer folds as it falls on the sphere. This makes sense intuitively, as the stiffer springs will be more resistant to bending. We also implemented collisions with flat planes. Below is a picture of our textured cloth lying on a plane.
Textured Fabric on Plane

Part 4: Handling self-collisions

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.

Initial
Falling
Final resting state
We can vary the density and observe its effect on how the cloth behaves.
density = 1 g/cc
density = 500 g/cc
While the less dense cloth falls lightly and curls on itself more gracefully, the denser cloth is more "crunched up" on itself as it falls. We can also vary the spring constants and see their effect.
ks = 50
ks = 50000
A higher spring constant leads to less bending, while a lower spring constant means the cloth bends more and has more self-collisions.

Part 5: Shaders

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.

Ambient Component
Diffuse Component
Specular Component
Final Result of Phong Shading

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.

Texture Mapping

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, coarse (resolution = 16)
Bump Mapping, coarse (resolution = 16)
Bump Mapping, fine (resolution = 128)
Bump Mapping, fine (resolution = 128)
Displacement Mapping, coarse (resolution = 16)
Displacement Mapping, coarse (resolution = 16)
Displacement Mapping, fine (resolution = 128)
Displacement Mapping, fine (resolution = 128)

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.

Mirror Shader
Mirror Shader

Part 6: Extra Credit

The extra credit code can be found here!

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.

Here we see a scene with two balls that can each be moved in the x, y, z direction and change in size

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.

Here we see a screenshot representing the three stages of cloth falling and interacting with objects in the scene. Overall this followed as we expected, as we saw the middle droop down first, and the sides follow.

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.

Cool examples

A cool bug that happens when you enlargen the size of the sphere after the ball is resting on the sphere.

We can also decrease the size of said sphere! This follows much closer to what we expected.

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.