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