Overview
In this project, I implemented a cloth simulator and a series of Open GL shaders. From the building of the cloth structure of masses and springs to handling collisions with other objects and the cloth itself, it was interesting to see how different data structures are helpful in speeding up simulation. It was also fascinating to implement different shaders while learning a language which was made specifically for realtime shading.
Part I: Masses and Springs
To represent the masses and springs in 1D vectors, I first added all of the elements to the data structures using rowmajor order: [num_width_points * y + x]. The grid of masses contains num_width_points by num_height_points, so we perform the following operations num_total_mass times. With a horizontal cloth orientation, I set the y coordinate for every point mass to 1, since the material is completely flat. Otherwise, the orientation is vertical and we need a randomly generated offset for the z coordinate. In both cases, the remaining coordinates were calculated by varying positions over the xz or xy planes. I also set the mass’s pinned boolean to true for each mass contained in the pinned vector. I created the springs next by applying the structural, bending, and shearing constraints. I added each based off of certain conditions about which masses each type of constraint can exist between.
structural, shearing, and bending

shear only

structural and bending

Part II: Simulation via Numerical Integration
The next part involved applying forces to point masses to simulate realistic movement. I calculated both external forces like gravity and spring correction forces. To get the total external force, I summed up all of the external accelerations and multiplied by the mass. For the correction forces, I used Hooke’s Law to compute the force applied to the point masses that connect the spring. I multiplied the spring constant by the magnitude of the distance between masses and subtracted the spring’s rest length. This is the force for one mass and the other has an equal (but opposite) force. Next, I used Verlet integration to calculate new point mass positions. To simulate damping, I multiplied a damping percentage by the difference between our new and last positions. I added the total acceleration and multiplied by the timestep squared. I add all of this to the current position, storing the position from the last time step as well. Fiinally , I made sure to constrain position updates by making sure that the spring’s new length is at most 10% greater than its rest length. I scaled with a correction vector and made sure that the vector direction was unchanged.
density: 15 g/cm^2 & ks: 5000 N/m

ks 9000

ks 1000

Changing the spring constant ks creates visible changes in the cloth. When the constant is increased, the cloth springs up a little and it appears lighter in the simulation overall. When the constant is decreased, the cloth droops deeper and creates an effect of being much heavier.
density 1

density 100

I decreased the density to 1, which made the cloth super light. There were barely any folds and it looked almost paperlike. When increasing the density to 100, the cloth hangs lower and has much deeper folds, when comparing it to the default density of 15.
damping 0.1

damping 0.9

Turning down the damping causes more oscillation in the cloth. To slow down the oscillation, we increase the damping which acts opposite to the direction of motion.
pinned4 with density: 30 g/cm^2 & ks: 1000 N/m

Part III: Handling Collisions With Other Objects
I first handled collisions with spheres, which was a bit simpler for me than intersection with planes. I calculated where the point masses should have rested on the spheres surface by comparing the distance from the origin to the point mass’s position to the radius of the sphere. I computed a correction vector using the surface intersection tangent point and applied it to the last position of the point mass and scaled by friction. I updated the simulate method to test for intersection with all of the collision objects, as well. For plane intersection, if the point mass passes over the plane, I calculate a tangent point of where the mass should have rested. Using dot products of the mass’s current and last positions with the normal, I could determine whether that mass has passed through the plane or not. If so, I use the tangent point, a correction vector, and a small surface offset to adjust the mass’s last position, finally setting it to the current position.
With a larger ks value, the cloth has less weight and bounces back up towards the top of the sphere when changed midsimulation. With a lower ks, the cloth begins to droop more.
ks 500

ks 5000

ks 50000

plane intersection

Part IV: Handling SelfCollisions
In order to keep the cloth from clipping though itself, I implemented a fix for selfcollisions. Instead of looping through each pair of point masses, I used spatial hashing. Spatial hashing involves mapping a float to a vector of point masses. By creating a hash function that can uniquely map each point map position to a float, we can store points in boxes that represent specific 3D box volumes. For each point mass, we loop through the other masses that are most likely to collide with it. Thus, we can take all of the other masses in our 3D box (represented by a specific hash bucket) and compare them easily to our current mass. A small issue I had was forgetting to check if the hash bucket is not initially null. If so, I needed to populate it with a vector, since the current point mass would be the first member of that bucket.
3 seconds in

about 9 seconds in

resting state

resting state top view

Below are images of the cloth with the density and ks values changed from the defaut values. When the ks is increased, there are larger folds with less wrinkles and the cloth appears more stiff. With a lower ks value, there are more wrinkles and the cloth is less stiff. Increasing the density created many more folds while decreasing density created wider folds with a more neat, less wrinkly resting state.
ks 2000

ks 50000

density 5

density 40

Part V: Shaders
In this section, I implemented some GLSL shaders, which run parallel on GPU, speeding up render time from the raytracing done in previous projects. GLSL is a language that has two basic shader types that take inputs and give back a 4dimensional vector: vertex shaders and fragment shaders. Vertex shaders apply transforms to verticies while fragment shaders compute output colors.
I first implemented diffuse shading, which uses the formula for diffuse lighting: Ld = kd(I/r^2)max(0, dot(n, l)). This was a fragment shader, so I wrote the final color to out_color.
diffuse shader

For BlinnPhong shading, I built upon the diffuse shader to add ambient light and specular components and compute the output light. Below are the effects of isolating different components of the shader as well as the entire Blinn Phong model.
ambient

specular

diffuse

Blinn Phong

For texture mapping with GLSL, I sampled from a texture that I chose using the builtin texture(tex, uv) function.
my donut texture!
