diff --git a/INSTRUCTIONS.md b/INSTRUCTIONS.md
new file mode 100644
index 0000000..790c8e5
--- /dev/null
+++ b/INSTRUCTIONS.md
@@ -0,0 +1,85 @@
+# CIS 566 Homework 2: Implicit Surfaces
+
+## Objective
+- Gain experience with signed distance functions
+- Experiment with animation curves
+
+## Base Code
+
+Please feel free to use this code as a base (https://www.shadertoy.com/view/fsdXzM)
+
+The code we have provided for this assignment features the following:
+- A square that spans the range [-1, 1] in X and Y that is rendered with a
+shader that does not apply a projection matrix to it, thus rendering it as the
+entirety of your screen
+- TypeScript code just like the code in homework 1 to set up a WebGL framework
+- Code that passes certain camera attributes (listed in the next section),
+the screen dimensions, and a time counter to the shader program.
+
+## Assignment Requirements
+- __(10 points)__ Modify the provided `flat-frag.glsl` to cast rays from a
+virtual camera. We have set up uniform variables in your shader that take in
+the eye position, reference point position, and up vector of the `Camera` in
+the provided TypeScript code, along with a uniform that stores the screen width
+and height. Using these uniform variables, and only these uniform variables,
+you must write a function that uses the NDC coordinates of the current fragment
+(i.e. its fs_Pos value) and projects a ray from that pixel. Refer to the [slides
+on ray casting](https://docs.google.com/presentation/d/e/2PACX-1vSN5ntJISgdOXOSNyoHimSVKblnPnL-Nywd6aRPI-XPucX9CeqzIEGTjFTwvmjYUgCglTqgvyP1CpxZ/pub?start=false&loop=false&delayms=60000&slide=id.g27215b64c6_0_107)
+from CIS 560 for reference on how to cast a ray without an explicit
+view-projection matrix. You'll have to compute your camera's Right vector based
+on the provided Up vector, Eye point, and Ref point. You can test your ray
+casting function by converting your ray directions to colors using the formula
+`color = 0.5 * (dir + vec3(1.0, 1.0, 1.0))`. If your screen looks like the
+following image, your rays are being cast correctly:
+![](rayDir.png)
+- __(70 points)__ Create a scene using raymarched signed distance functions.
+The subject of your scene should be based on some reference image, such as a
+shot from a movie or a piece of artwork. Your scene should incorporate the
+following elements:
+  - The SDF combination operation Smooth Blend.
+  - Basic Lambertian reflection using a hard-coded light source and SDF surface normals.
+  - Animation of at least one element of the scene, with at least two Toolbox Functions
+  used to control the animation(s).
+  - Hard-edged shadows cast by shapes in the scene onto one another using a shadow-feeler ray.
+
+For the next assignment you will build upon this scene with procedural textures and more
+advanced lighting and reflection models, so don't worry if your scene looks a bit drab
+given the requirements listed above.
+
+- __(10 points)__ Following the specifications listed
+[here](https://github.com/pjcozzi/Articles/blob/master/CIS565/GitHubRepo/README.md),
+create your own README.md, renaming this file to INSTRUCTIONS.md. Don't worry
+about discussing runtime optimization for this project. Make sure your
+README contains the following information:
+  - Your name and PennKey
+  - Citation of any external resources you found helpful when implementing this
+  assignment.
+  - A link to your live github.io demo (refer to the pinned Piazza post on
+    how to make a live demo through github.io)
+  - An explanation of the techniques you used to model and animate your scene.
+
+## Useful Links
+- [IQ's Article on SDFs](http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm)
+- [IQ's Article on Smooth Blending](http://www.iquilezles.org/www/articles/smin/smin.htm)
+- [IQ's Article on Useful Functions](http://www.iquilezles.org/www/articles/functions/functions.htm)
+- [Breakdown of Rendering an SDF Scene](http://www.iquilezles.org/www/material/nvscene2008/rwwtt.pdf)
+
+
+## Submission
+Commit and push to Github, then submit a link to your commit on Canvas. Remember
+to make your own README!
+
+## Inspiration
+- [Alien Corridor](https://www.shadertoy.com/view/4slyRs)
+- [The Evolution of Motion](https://www.shadertoy.com/view/XlfGzH)
+- [Fractal Land](https://www.shadertoy.com/view/XsBXWt)
+- [Voxel Edges](https://www.shadertoy.com/view/4dfGzs)
+- [Snail](https://www.shadertoy.com/view/ld3Gz2)
+- [Cubescape](https://www.shadertoy.com/view/Msl3Rr)
+- [Journey Tribute](https://www.shadertoy.com/view/ldlcRf)
+- [Stormy Landscape](https://www.shadertoy.com/view/4ts3z2)
+<<<<<<< HEAD
+- [Generators](https://www.shadertoy.com/view/Xtf3Rn)
+=======
+- [Generators](https://www.shadertoy.com/view/Xtf3Rn)
+>>>>>>> d6d1f4369c6f85aa7c0591fc6ca457dd133f45f7
diff --git a/Pisa1.png b/Pisa1.png
new file mode 100644
index 0000000..f0263de
Binary files /dev/null and b/Pisa1.png differ
diff --git a/README.md b/README.md
index ae3da83..98161a2 100644
--- a/README.md
+++ b/README.md
@@ -1,81 +1,22 @@
-# CIS 566 Homework 2: Implicit Surfaces
+# Leaning Tower of Pisa - SDF Recreation
 
-## Objective
-- Gain experience with signed distance functions
-- Experiment with animation curves
+By An Duong
 
-## Base Code
+Pennkey: onlyname
 
-Please feel free to use this code as a base (https://www.shadertoy.com/view/fsdXzM)
+## Overview
+* **Leaning Tower of Pisa Recreation**
+<img src="Pisa1.png" width="800"/>
 
-The code we have provided for this assignment features the following:
-- A square that spans the range [-1, 1] in X and Y that is rendered with a
-shader that does not apply a projection matrix to it, thus rendering it as the
-entirety of your screen
-- TypeScript code just like the code in homework 1 to set up a WebGL framework
-- Code that passes certain camera attributes (listed in the next section),
-the screen dimensions, and a time counter to the shader program.
+* **Inspiration**
+<img src="pisa-leaning-tower1.jpg" width="800"/>
 
-## Assignment Requirements
-- __(10 points)__ Modify the provided `flat-frag.glsl` to cast rays from a
-virtual camera. We have set up uniform variables in your shader that take in
-the eye position, reference point position, and up vector of the `Camera` in
-the provided TypeScript code, along with a uniform that stores the screen width
-and height. Using these uniform variables, and only these uniform variables,
-you must write a function that uses the NDC coordinates of the current fragment
-(i.e. its fs_Pos value) and projects a ray from that pixel. Refer to the [slides
-on ray casting](https://docs.google.com/presentation/d/e/2PACX-1vSN5ntJISgdOXOSNyoHimSVKblnPnL-Nywd6aRPI-XPucX9CeqzIEGTjFTwvmjYUgCglTqgvyP1CpxZ/pub?start=false&loop=false&delayms=60000&slide=id.g27215b64c6_0_107)
-from CIS 560 for reference on how to cast a ray without an explicit
-view-projection matrix. You'll have to compute your camera's Right vector based
-on the provided Up vector, Eye point, and Ref point. You can test your ray
-casting function by converting your ray directions to colors using the formula
-`color = 0.5 * (dir + vec3(1.0, 1.0, 1.0))`. If your screen looks like the
-following image, your rays are being cast correctly:
-![](rayDir.png)
-- __(70 points)__ Create a scene using raymarched signed distance functions.
-The subject of your scene should be based on some reference image, such as a
-shot from a movie or a piece of artwork. Your scene should incorporate the
-following elements:
-  - The SDF combination operation Smooth Blend.
-  - Basic Lambertian reflection using a hard-coded light source and SDF surface normals.
-  - Animation of at least one element of the scene, with at least two Toolbox Functions
-  used to control the animation(s).
-  - Hard-edged shadows cast by shapes in the scene onto one another using a shadow-feeler ray.
+[Live Demo](https://onlyname1.github.io/hw02-raymarching-sdfs/)
 
-For the next assignment you will build upon this scene with procedural textures and more
-advanced lighting and reflection models, so don't worry if your scene looks a bit drab
-given the requirements listed above.
+## Implementation
+The Tower of Pisa was recreated using a fragment shader that rendered the scene through raymarched signed distance functions. The scene also includes basic Lambertian reflection, hard shadows implemented via raymarched shadow feeler rays, as well as basic materials. The tower is made up of the smooth blend and union of SDF cylinders. The floors of the tower were created using the repetition of a singular floor through the SDF limited modullo function. The tower leans over time via a sine function. The crowds of people were also created by repeating an SDF cylinder over an area. The crowds jump up and down by offsetting their position with a triangle wave function dependent on time and a specific person's position.
 
-- __(10 points)__ Following the specifications listed
-[here](https://github.com/pjcozzi/Articles/blob/master/CIS565/GitHubRepo/README.md),
-create your own README.md, renaming this file to INSTRUCTIONS.md. Don't worry
-about discussing runtime optimization for this project. Make sure your
-README contains the following information:
-  - Your name and PennKey
-  - Citation of any external resources you found helpful when implementing this
-  assignment.
-  - A link to your live github.io demo (refer to the pinned Piazza post on
-    how to make a live demo through github.io)
-  - An explanation of the techniques you used to model and animate your scene.
+## Citations
+[Useful SDF Transformations](http://jamie-wong.com/2016/07/15/ray-marching-signed-distance-functions/#rotation-and-translation)
 
-## Useful Links
-- [IQ's Article on SDFs](http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm)
-- [IQ's Article on Smooth Blending](http://www.iquilezles.org/www/articles/smin/smin.htm)
-- [IQ's Article on Useful Functions](http://www.iquilezles.org/www/articles/functions/functions.htm)
-- [Breakdown of Rendering an SDF Scene](http://www.iquilezles.org/www/material/nvscene2008/rwwtt.pdf)
-
-
-## Submission
-Commit and push to Github, then submit a link to your commit on Canvas. Remember
-to make your own README!
-
-## Inspiration
-- [Alien Corridor](https://www.shadertoy.com/view/4slyRs)
-- [The Evolution of Motion](https://www.shadertoy.com/view/XlfGzH)
-- [Fractal Land](https://www.shadertoy.com/view/XsBXWt)
-- [Voxel Edges](https://www.shadertoy.com/view/4dfGzs)
-- [Snail](https://www.shadertoy.com/view/ld3Gz2)
-- [Cubescape](https://www.shadertoy.com/view/Msl3Rr)
-- [Journey Tribute](https://www.shadertoy.com/view/ldlcRf)
-- [Stormy Landscape](https://www.shadertoy.com/view/4ts3z2)
-- [Generators](https://www.shadertoy.com/view/Xtf3Rn)
+[SDF Functions](https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm)
diff --git a/dist/index.html b/dist/index.html
deleted file mode 100644
index 062fed2..0000000
--- a/dist/index.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!doctype html>
-<html>
-  <head>
-      <title>Project 0: Getting Started | CIS 566</title>
-      <style>
-        html, body {
-          margin: 0;
-          overflow: hidden;
-        }
-        #canvas {
-          width: 100%;
-          height: 100%;
-        }
-      </style>
-  </head>
-  <body>
-    <canvas id="canvas"></canvas>
-    <script type="text/javascript" src="bundle.js"></script>
-  </body>
-</html>
diff --git a/pisa-leaning-tower1.jpg b/pisa-leaning-tower1.jpg
new file mode 100644
index 0000000..f49d5c5
Binary files /dev/null and b/pisa-leaning-tower1.jpg differ
diff --git a/src/main.ts b/src/main.ts
index fe526f9..ea141d5 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,6 +1,4 @@
 import {vec2, vec3} from 'gl-matrix';
-// import * as Stats from 'stats-js';
-// import * as DAT from 'dat-gui';
 import Square from './geometry/Square';
 import OpenGLRenderer from './rendering/gl/OpenGLRenderer';
 import Camera from './Camera';
diff --git a/src/shaders/flat-frag.glsl b/src/shaders/flat-frag.glsl
index 50434bd..31aed58 100644
--- a/src/shaders/flat-frag.glsl
+++ b/src/shaders/flat-frag.glsl
@@ -1,6 +1,11 @@
 #version 300 es
 precision highp float;
 
+const int MAX_RAY_STEPS = 256;
+const float EPSILON = 1e-4;
+const float FOV = 60.0;
+const float MAX_DISTANCE = 20.0;
+
 uniform vec3 u_Eye, u_Ref, u_Up;
 uniform vec2 u_Dimensions;
 uniform float u_Time;
@@ -8,6 +13,347 @@ uniform float u_Time;
 in vec2 fs_Pos;
 out vec4 out_Col;
 
+struct Ray 
+{
+    vec3 origin;
+    vec3 direction;
+};
+
+struct Intersection 
+{
+    vec3 position;
+    vec3 normal;
+    float distance_t;
+    int material_id;
+};
+
+// HELPERS //
+vec3 rgb(vec3 col)
+{
+  return col / 255.0;
+}
+
+float triangleWave(float x, float freq, float amplitude)
+{
+  return abs(mod(x * freq, amplitude) - (0.5 * amplitude));
+}
+
+// SDF CODE //
+mat4 rotateX(float theta) {
+    float c = cos(theta);
+    float s = sin(theta);
+
+    mat4 m = mat4(
+        vec4(c, 0, s, 0),
+        vec4(0, 1, 0, 0),
+        vec4(-s, 0, c, 0),
+        vec4(0, 0, 0, 1)
+    );
+    return inverse(m);
+}
+
+mat4 rotateY(float theta) {
+    float c = cos(theta);
+    float s = sin(theta);
+
+    mat4 m = mat4(
+        vec4(1, 0, 0, 0),
+        vec4(0, c, -s, 0),
+        vec4(0, s, c, 0),
+        vec4(0, 0, 0, 1)
+    );
+    return inverse(m);
+}
+
+mat4 rotateZ(float theta) {
+    float c = cos(theta);
+    float s = sin(theta);
+
+    mat4 m = mat4(
+        vec4(c, -s, 0, 0),
+        vec4(s, c, 0, 0),
+        vec4(0, 0, 1, 0),
+        vec4(0, 0, 0, 1)
+    );
+    return inverse(m);
+}
+
+vec3 bendPoint(vec3 p, float k)
+{
+    float c = cos(k*p.y);
+    float s = sin(k*p.y);
+    mat2  m = mat2(c,-s,s,c);
+    vec3  q = vec3(m*p.xy,p.z);
+    return q;
+}
+
+float sdfSphere(vec3 query_position, vec3 position, float radius)
+{
+    return length(query_position - position) - radius;
+}
+
+// from IQ
+float sdCylinder(vec3 p, vec3 a, vec3 b, float r)
+{
+  vec3  ba = b - a;
+  vec3  pa = p - a;
+  float baba = dot(ba,ba);
+  float paba = dot(pa,ba);
+  float x = length(pa*baba-ba*paba) - r*baba;
+  float y = abs(paba-baba*0.5)-baba*0.5;
+  float x2 = x*x;
+  float y2 = y*y*baba;
+  float d = (max(x,y)<0.0)?-min(x2,y2):(((x>0.0)?x2:0.0)+((y>0.0)?y2:0.0));
+  return sign(d)*sqrt(abs(d))/baba;
+}
+
+float sdRoundBox( vec3 p, vec3 b, float r )
+{
+  vec3 q = abs(p) - b;
+  return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0) - r;
+}
+
+float plane(vec3 p, vec4 n)
+{
+  return dot(p,n.xyz) + n.w;
+}
+
+float smin( float a, float b, float k )
+{
+    float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 );
+    return mix( b, a, h ) - k*h*(1.0-h);
+}
+
+// from IQ
+vec3 opRep(vec3 p, vec3 c)
+{
+    return mod(p+0.5*c,c)-0.5*c;
+}
+
+vec3 opRepLim(vec3 p, float c, vec3 l)
+{
+    return p-c*clamp(round(p/c),-l,l);
+}
+
+vec3 opTwist( vec3 p )
+{
+    const float k = 10.0; // or some other amount
+    float c = cos(k*p.y);
+    float s = sin(k*p.y);
+    mat2  m = mat2(c,-s,s,c);
+    return vec3(m*p.xz,p.y);
+}
+
+vec3 opRevolution( in vec3 p, float o )
+{
+    return vec3( length(p.xz) - o, p.y , 0);
+}
+
+float pillar(vec3 pos, float rotation)
+{
+  return sdCylinder(pos, (rotateX(rotation) * vec4(0.57, -0.3, 0.0, 1.0)).xyz, 
+    (rotateX(rotation) * vec4(0.57, 0.3, 0.0, 1.0)).xyz, 0.02);
+}
+
+float towerFloor(vec3 pos)
+{
+  float rotation = radians(45.0);
+  float tFloor = sdCylinder(pos, vec3(0.0, -0.05, 0.0), vec3(0.0, 0.05, 0.0), 0.64);
+  float p1 = pillar(pos, 0.0);
+  float p2 = pillar(pos, rotation * 1.0);
+  float p3 = pillar(pos, rotation * 2.0);
+  float p4 = pillar(pos, rotation * 3.0);
+  float p5 = pillar(pos, rotation * 4.0);
+  float p6 = pillar(pos, rotation * 5.0);
+  float p7 = pillar(pos, rotation * 6.0);
+  float p8 = pillar(pos, rotation * 7.0);
+
+  return smin(tFloor, min(p1, min(p2, min(p3, min(p4, min(p5, min(p6, min(p7, p8))))))), 0.05);
+}
+
+float towerBody(vec3 pos)
+{
+  vec3 displacement = vec3(0.0, 0.4, 0.0);
+  float middle = sdCylinder(pos, vec3(0.0, -1.5, 0.0), vec3(0.0, 1.9, 0.0), 0.5);
+  float levels = towerFloor(opRepLim(pos, 0.5, vec3(0.0, 2.0, 0.0)));
+  float top = sdCylinder(pos + vec3(0.0, -1.3, 0.0), vec3(0.0, -0.05, 0.0), vec3(0.0, 0.05, 0.0), 0.64);
+  return smin(middle, min(levels, top), 0.05);
+}
+
+float towerBase(vec3 pos)
+{
+  float middle = sdCylinder(pos, vec3(0.0, -0.5, 0.0), vec3(0.0, 0.5, 0.0), 0.64);
+  return middle;
+}
+
+float tower(vec3 pos)
+{
+  float rotation = radians(-25.0) * (sin(u_Time / 50.0) + 1.0) / 2.0;
+  vec3 rotatedPos = (rotateZ(rotation) * vec4(pos + vec3(0.0, 3.0, 0.0), 1.0)).xyz + vec3(0.0, -3.0, 0.0) + vec3(0.0, 0.2, 0.0);
+  return min(towerBody(rotatedPos), towerBase(rotatedPos + vec3(0.0, 1.8, 0.0)));
+}
+
+float people(vec3 pos, float size)
+{
+  vec3 movement = vec3(0.0, 0.08, 0.0) * triangleWave((u_Time + 3.5 * fs_Pos.x + 1.2 * fs_Pos.y) / 25.0, 10.0, 2.0);
+  float crowd = sdCylinder(opRepLim(pos + vec3(0.0, 2.4, 0.0) + movement, 0.25, vec3(size, 0.0, size)), vec3(0.0, 0.0,0.0), vec3(0.0,0.05,0.0), 0.02);
+  return crowd;
+}
+
+// SCENE //
+#define FLOOR plane(queryPos, vec4(0.0, 1.0, 0.0, 2.5))
+#define BODY tower(queryPos + vec3(0.0, 0.2, 0.0))
+#define THING min(people(queryPos + vec3(-1.3, 0.0, 1.2), 3.0), min(people(queryPos + vec3(2.0, 0.0, -2.4), 2.0), people(queryPos + vec3(-1.5, 0.0, -1.5), 4.0)))
+
+#define FLOOR_NUM 0
+#define BODY_NUM 2
+#define THING_NUM 3
+
+float sceneSDF(vec3 queryPos)
+{
+  float dist = FLOOR;
+  dist = min(dist, BODY);
+  dist = min(dist, THING);
+  return dist;
+}
+
+void sceneSDF(vec3 queryPos, out float dist, out int material_id) 
+{
+  dist = FLOOR;
+  float dist2;
+  material_id = FLOOR_NUM;
+  if ((dist2 = BODY) < dist)
+  {
+    dist = dist2;
+    material_id = BODY_NUM;
+  }
+  if ((dist2 = THING) < dist)
+  {
+    dist = dist2;
+    material_id = THING_NUM;
+  }
+}
+
+vec3 sdfNormal(vec3 pos)
+{
+  vec2 epsilon = vec2(0.0, EPSILON);
+  return normalize( vec3( sceneSDF(pos + epsilon.yxx) - sceneSDF(pos - epsilon.yxx),
+                            sceneSDF(pos + epsilon.xyx) - sceneSDF(pos - epsilon.xyx),
+                            sceneSDF(pos + epsilon.xxy) - sceneSDF(pos - epsilon.xxy)));
+}
+
+// RAYMARCH CODE //
+Ray getRay(vec2 uv)
+{
+    Ray r;
+    
+    vec3 look = normalize(u_Ref - u_Eye);
+    vec3 camera_RIGHT = normalize(cross(look, u_Up));
+    vec3 camera_UP = cross(camera_RIGHT, look);
+    float len = distance(u_Eye, u_Ref);
+    
+    float aspect_ratio = u_Dimensions.x / u_Dimensions.y;
+    vec3 screen_vertical = camera_UP * len * tan(FOV); 
+    vec3 screen_horizontal = camera_RIGHT * len * aspect_ratio * tan(FOV);
+    vec3 screen_point = (look + uv.x * screen_horizontal + uv.y * screen_vertical);
+    
+    r.origin = u_Eye;
+    r.direction = normalize(screen_point - u_Eye);
+   
+    return r;
+}
+
+Intersection getRaymarchedIntersection(vec2 uv)
+{
+    Ray ray = getRay(uv);
+    Intersection intersection;
+    
+    intersection.distance_t = -1.0;
+    intersection.position = u_Eye;
+    intersection.normal = vec3(0, 0, 0);
+    intersection.material_id = -1;
+
+    float t = 0.0;
+    for (int i = 0; i < MAX_RAY_STEPS; i++)
+    {
+      // get position
+      vec3 pos = ray.origin + t * ray.direction;
+      if (t > MAX_DISTANCE)
+      {
+        break;
+      }
+
+      float dist;
+      int material_id;
+      sceneSDF(pos, dist, material_id);
+      // if dist is on surface of sdf, then we're done
+      if (dist < EPSILON)
+      {
+        intersection.position = pos;
+        intersection.distance_t = t;
+        intersection.normal = sdfNormal(intersection.position);
+        intersection.material_id = material_id;
+        break;
+      }
+      t += dist;
+    }
+
+    return intersection;
+}
+
+float hardShadow(vec3 origin, vec3 dir, float min_t, float max_t) {
+    for(float t = min_t; t < max_t;) 
+    {
+        vec3 pos = origin + t * dir;
+        float dist = sceneSDF(origin + t * dir);
+        if(dist < EPSILON) 
+        {
+            return 0.0;
+        }
+        t += dist;
+    }
+    return 1.0;
+}
+
+vec3 calculateMaterial(int material_id, vec3 normal, vec3 lightDir)
+{
+  float ambient = 0.2;
+  float lambert = max(0.0, dot(normal, lightDir)) + ambient;
+
+  switch (material_id)
+  {
+    case FLOOR_NUM:
+      return rgb(vec3(82, 143, 47)) * lambert;
+      break;
+    case BODY_NUM:
+      return rgb(vec3(226, 227, 200)) * lambert;
+      break;
+    case THING_NUM:
+      return rgb(vec3(88, 230, 232)) * lambert;
+      break;
+    case -1:
+      return rgb(vec3(149, 228, 252));
+      break;
+  }
+  return rgb(vec3(153, 139, 83));
+}
+
+vec3 getSceneColor(vec2 uv, vec3 lightPos)
+{
+  Intersection intersection = getRaymarchedIntersection(uv);
+  vec3 lightDir = normalize(lightPos - intersection.position);
+  vec3 light_t = (lightPos - intersection.position) / lightDir;
+  float hardShadow = hardShadow(intersection.position, lightDir, 0.1, light_t.x);
+  vec3 col = calculateMaterial(intersection.material_id, intersection.normal, lightDir);
+  return col * hardShadow;
+}
+
 void main() {
-  out_Col = vec4(0.5 * (fs_Pos + vec2(1.0)), 0.5 * (sin(u_Time * 3.14159 * 0.01) + 1.0), 1.0);
+  vec3 lightPos = vec3(-5.0, 7.45, -5.0);
+
+  vec2 uv = fs_Pos;
+
+  vec3 col = getSceneColor(uv, lightPos);
+
+  out_Col = vec4(col, 1.0);
 }