 
 vec3 applySpotLight(Light light, MaterialInfo materialInfo, vec3 normal, vec3 pointToView)
 {
-    return vec3(0.0, 0.0, 0.0);
+    vec3 pointToLight = light.position - v_Position;
+    float distance = length(pointToLight);
+    float rangeAttenuation = getRangeAttenuation(light.range, distance);
+    float spotAttenuation = getSpotAttenuation(pointToLight, light.direction, light.outerConeAngle, light.innerConeAngle);
+    vec3 shade = getPointShade(pointToLight, materialInfo, normal, pointToView);
+    return rangeAttenuation * spotAttenuation * light.intensity * light.color * shade;
 }
 
 void main()

    return max(min(1.0 - pow(distance / range, 4.0), 1.0), 0.0) / pow(distance, 2.0);
}

vec3 getPointShade(vec3 pointToLight, MaterialInfo materialInfo, vec3 normal, vec3 pointToView)
{
    AngularInfo angularInfo = getAngularInfo(pointToLight, normal, pointToView);

    // Calculate the shading terms for the microfacet specular shading model
    vec3 F = specularReflection(materialInfo, angularInfo);
    float G = geometricOcclusion(materialInfo, angularInfo);
    float D = microfacetDistribution(materialInfo, angularInfo);

    // Calculation of analytical lighting contribution
    vec3 diffuseContrib = (1.0 - F) * diffuse(materialInfo);
    vec3 specContrib = F * G * D / (4.0 * angularInfo.NdotL * angularInfo.NdotV);

    // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
    return angularInfo.NdotL * (diffuseContrib + specContrib);
}

vec3 applyDirectionalLight(Light light, MaterialInfo materialInfo, vec3 normal, vec3 pointToView)

    );
}

vec3 getPointShade(vec3 pointToLight, MaterialInfo materialInfo, vec3 normal, vec3 pointToView)
{
    AngularInfo angularInfo = getAngularInfo(pointToLight, normal, pointToView);

    // Calculate the shading terms for the microfacet specular shading model
    vec3 F = specularReflection(materialInfo, angularInfo);
    float G = geometricOcclusion(materialInfo, angularInfo);
    float D = microfacetDistribution(materialInfo, angularInfo);

    // Calculation of analytical lighting contribution
    vec3 diffuseContrib = (1.0 - F) * diffuse(materialInfo);
    vec3 specContrib = F * G * D / (4.0 * angularInfo.NdotL * angularInfo.NdotV);

    // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
    return angularInfo.NdotL * (diffuseContrib + specContrib);
}

float getRangeAttenuation(float range, float distance)
{
    if (range < 0.0)
