diff --git a/projects/voxelization/resources/shaders/shader.frag b/projects/voxelization/resources/shaders/shader.frag
index 24f4673b0d751b7ca8fb4617e885bed60a5692fe..445caea832331b510e33df21f9787a3be2d5099f 100644
--- a/projects/voxelization/resources/shaders/shader.frag
+++ b/projects/voxelization/resources/shaders/shader.frag
@@ -19,13 +19,50 @@ layout(set=0, binding=0) uniform sunBuffer {
 layout(set=0, binding=1) uniform texture2D  shadowMap;
 layout(set=0, binding=2) uniform sampler    shadowMapSampler;
 
+layout(set=0, binding=3) uniform cameraBuffer {
+    vec3 cameraPos;
+};
+
+const float pi = 3.1415; 
+
+vec3 lambertBRDF(vec3 albedo){
+    return albedo / pi;
+}
+
+vec3 fresnelSchlick(float cosTheta, vec3 f0){
+    return f0 + (vec3(1) - f0) * pow(1 - cosTheta, 5);
+}
+
+float GGXDistribution(float r, float NoH){
+    float r2    = r * r;
+    float denom = pi * pow(NoH * NoH * (r2 - 1) + 1, 2);
+    return r2 / max(denom, 0.000001);
+}
+
+float GGXSmithShadowingPart(float r, float cosTheta){
+    float nom   = cosTheta * 2;
+    float r2    = r * r;
+    float denom = cosTheta + sqrt(r2 + (1 - r2) * cosTheta * cosTheta);
+    return nom / max(denom, 0.000001);
+}
+
+float GGXSmithShadowing(float r, float NoV, float NoL){
+    return GGXSmithShadowingPart(r, NoV) * GGXSmithShadowingPart(r, NoL);
+}
+
 void main()	{
 
     vec3 albedoTexel    = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb;
     vec3 normalTexel    = texture(sampler2D(normalTexture, textureSampler), passUV).rgb;
     vec3 specularTexel  = texture(sampler2D(specularTexture, textureSampler), passUV).rgb;
     
-    vec3 albedo = albedoTexel;
+    float r             = specularTexel.g;
+    r                   *= r;   // remapping roughness for perceptual linearity
+    
+    float metal         = specularTexel.b;
+    vec3 albedo         = mix(albedoTexel, vec3(0), metal);
+    vec3 f0_dielectric  = vec3(0.04f);
+    vec3 f0             = mix(f0_dielectric, albedo, metal);
     
     vec3 T      = normalize(passTangent.xyz);
     vec3 N_geo  = normalize(passNormal);
@@ -33,9 +70,27 @@ void main()	{
     mat3 TBN    = mat3(T, B, N_geo);
     normalTexel = normalTexel * 2 - 1;
     
-    vec3 N          = TBN * normalTexel;
-    vec3 sun        = lightInfo.sunStrength * lightInfo.sunColor * clamp(dot(N, lightInfo.L), 0, 1);
+    vec3 N  = TBN * normalTexel;
+    vec3 L  = lightInfo.L;
+    vec3 V  = normalize(cameraPos - passPos);
+    vec3 H  = normalize(L + V);
+    
+    float NoH = clamp(dot(N, H), 0, 1);
+    float NoL = clamp(dot(N, L), 0, 1);
+    float NoV = clamp(dot(N, V), 0, 1);
+    
+    vec3 F          = fresnelSchlick(NoH, f0);
+    float D         = GGXDistribution(r, NoH);
+    float G         = GGXSmithShadowing(r, NoV, NoL);
+    vec3 specular   = (F * D * G) / max(4 * NoV * NoL, 0.000001);
+    
+    vec3 sun        = lightInfo.sunStrength * lightInfo.sunColor * NoL;
     sun             *= shadowTest(passPos, lightInfo, shadowMap, shadowMapSampler);
     vec3 ambient    = vec3(0.05);
-	outColor        = albedo * (sun + ambient); 
+    
+    vec3 F_in       = fresnelSchlick(NoL, f0);
+    vec3 F_out      = fresnelSchlick(NoV, f0);
+    vec3 diffuse    = lambertBRDF(albedo) * (1 - F_in) * (1 - F_out);
+    
+	outColor        = (diffuse + specular) * sun + lambertBRDF(albedo) * ambient;
 }
\ No newline at end of file
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 33b6cc20f970f2b14a6fda62a53ebe01c6cd28c4..281faaf1d234aa0bf5e2b8fed6de56921904a9b4 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -159,7 +159,7 @@ int main(int argc, const char** argv) {
 		glm::vec3   direction;
 		float       padding;
 		glm::vec3   sunColor    = glm::vec3(1.f);
-		float       sunStrength = 8.f;
+		float       sunStrength = 15.f;
 		glm::mat4   lightMatrix;
 	};
 	LightInfo lightInfo;
@@ -168,8 +168,12 @@ int main(int argc, const char** argv) {
 	vkcv::DescriptorSetHandle forwardShadingDescriptorSet = 
 		core.createDescriptorSet({ forwardProgram.getReflectedDescriptors()[0] });
 
+	vkcv::Buffer<glm::vec3> cameraPosBuffer = core.createBuffer<glm::vec3>(vkcv::BufferType::UNIFORM, 1);
+
 	vkcv::DescriptorWrites forwardDescriptorWrites;
-	forwardDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(0, lightBuffer.getHandle()) };
+	forwardDescriptorWrites.uniformBufferWrites = { 
+		vkcv::UniformBufferDescriptorWrite(0, lightBuffer.getHandle()),
+		vkcv::UniformBufferDescriptorWrite(3, cameraPosBuffer.getHandle()) };
 	forwardDescriptorWrites.sampledImageWrites  = { vkcv::SampledImageDescriptorWrite(1, shadowMap.getHandle()) };
 	forwardDescriptorWrites.samplerWrites       = { vkcv::SamplerDescriptorWrite(2, shadowSampler) };
 	core.writeDescriptorSet(forwardShadingDescriptorSet, forwardDescriptorWrites);
@@ -393,6 +397,7 @@ int main(int argc, const char** argv) {
 
 		start = end;
 		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+		cameraPosBuffer.fill({ cameraManager.getActiveCamera().getPosition() });
 
 		glm::vec2 lightAngleRadian = glm::radians(lightAngles);
 		lightInfo.direction = glm::normalize(glm::vec3(