diff --git a/Images/black.png b/Images/black.png
new file mode 100644
index 0000000000000000000000000000000000000000..3cf27f47d2f6feba4ef91b8eba997329bf447d58
Binary files /dev/null and b/Images/black.png differ
diff --git a/Images/container2.png b/Images/container2.png
new file mode 100644
index 0000000000000000000000000000000000000000..596e8da31ad9bba6f5b2f9db07bff69d9363e507
Binary files /dev/null and b/Images/container2.png differ
diff --git a/Images/container2_specular.png b/Images/container2_specular.png
new file mode 100644
index 0000000000000000000000000000000000000000..681bf6ef32f8ad13a151bc80d63fd2ce6810bb8a
Binary files /dev/null and b/Images/container2_specular.png differ
diff --git a/Images/magenta.png b/Images/magenta.png
new file mode 100644
index 0000000000000000000000000000000000000000..d325aa25cada98b13e07d5e7c2db48debcd82641
Binary files /dev/null and b/Images/magenta.png differ
diff --git a/Images/orange.png b/Images/orange.png
new file mode 100644
index 0000000000000000000000000000000000000000..664c14b677a106f9a40bfca542c864d923db8b96
Binary files /dev/null and b/Images/orange.png differ
diff --git a/Images/white.png b/Images/white.png
new file mode 100644
index 0000000000000000000000000000000000000000..818c71d03f435db011069584cda25c1f66af1a85
Binary files /dev/null and b/Images/white.png differ
diff --git a/LearnOpenGL/OpenGLWrapper.cpp b/LearnOpenGL/OpenGLWrapper.cpp
index ee357c0f574664e6793714d3439236d65859c68a..4d39a5cf0887e79728043cd747abc2466e9d0576 100644
--- a/LearnOpenGL/OpenGLWrapper.cpp
+++ b/LearnOpenGL/OpenGLWrapper.cpp
@@ -38,6 +38,27 @@ void draw_mesh(Mesh &mesh)
 	glBindVertexArray(0);
 }
 
+Material::Material(GLuint _prog, GLuint _diffuseMap, GLuint _specularMap)
+{
+	prog = _prog;
+
+	diffuseMap = _diffuseMap;
+	specularMap = _specularMap;
+}
+
+GLuint Material::get_program()
+{
+	return prog;
+}
+GLuint Material::get_diffuseMap()
+{
+	return diffuseMap;
+}
+GLuint Material::get_specularMap()
+{
+	return specularMap;
+}
+
 RenderObject::RenderObject(Mesh * _mesh)
 {
 	translate = glm::vec3(0.0f, 0.0f, 0.0f);
@@ -49,9 +70,9 @@ RenderObject::RenderObject(Mesh * _mesh)
 	update_model_matrix();
 }
 
-GLuint RenderObject::get_programs()
+Material *RenderObject::get_material()
 {
-	return prog;
+	return material;
 }
 
 GLuint RenderObject::get_vertex_count()
@@ -111,31 +132,44 @@ void RenderObject::move(glm::vec3 _direction, glm::vec1 _velocity)
 	update_model_matrix();
 }
 
-void RenderObject::set_program(GLuint _prog)
+void RenderObject::set_material(Material *_material)
 {
-	prog = _prog;
+	material = _material;
 }
 
 void RenderObject::render(Camera &camera)
 {
+	auto prog = material->get_program();
 	glUseProgram(prog);
 
-	auto objectColor = glm::vec3(1.0f, 0.5f, 0.31f);
-	set_uniform_value(prog, "objectColor", objectColor);
-
-	auto lightColor = glm::vec3(1.0f, 1.0f, 1.0f);
-	set_uniform_value(prog, "lightColor", lightColor);
-	set_uniform_value(prog, "lightPos", _lightPos);
+	set_uniform_value(prog, "light.position", _lightPos);
 	set_uniform_value(prog, "viewPos", camera.Position);
 
-	glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)_SCR_WIDTH / (float)_SCR_HEIGHT, 0.1f, 100.0f);
-	set_uniform_value(prog, "projection", projection);
+	glm::vec3 a = { 0.2f, 0.2f, 0.2f };
+	glm::vec3 d = { 0.5f, 0.5f, 0.5f };
+	glm::vec3 f = { 1.0f, 1.0f, 1.0f };
+	set_uniform_value(prog, "light.ambient", a);
+	set_uniform_value(prog, "light.diffuse", d);
+	set_uniform_value(prog, "light.specular", f);
+
+	set_uniform_value(prog, "material.shininess", glm::fvec1{ 64.0f });
 
+	glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)_SCR_WIDTH / (float)_SCR_HEIGHT, 0.1f, 100.0f);
 	glm::mat4 view = camera.GetViewMatrix();
+	set_uniform_value(prog, "projection", projection);
 	set_uniform_value(prog, "view", view);
-
 	set_uniform_value(prog, "model", model);
 
+	{
+		glActiveTexture(GL_TEXTURE0);
+		glBindTexture(GL_TEXTURE_2D, material->get_diffuseMap());
+	}
+
+	{
+		glActiveTexture(GL_TEXTURE1);
+		glBindTexture(GL_TEXTURE_2D, material->get_specularMap());
+	}
+
 	draw_mesh(*mesh);
 
 	glUseProgram(0);
@@ -143,7 +177,7 @@ void RenderObject::render(Camera &camera)
 
 RenderObject *make_render_object(Mesh *mesh)
 {
-	RenderObject *ro = new RenderObject{ mesh };
+	RenderObject *ro = new RenderObject(mesh);
 
 	return ro;
 };
@@ -320,12 +354,13 @@ GLuint allocate_VAO()
 Mesh *make_mesh(const std::string fileName)
 {
 	std::vector<glm::vec3> vertices;
+	std::vector<glm::vec3> vertexTexCoord;
 	std::vector<glm::vec3> vertexNormals;
 
 	const std::string ext = get_extension(fileName);
 	if (ext.compare("obj") == 0)
 	{
-		openObj(fileName, vertices, vertexNormals);
+		openObj(fileName, vertices, vertexTexCoord, vertexNormals);
 	}
 	else
 	{
@@ -334,6 +369,10 @@ Mesh *make_mesh(const std::string fileName)
 
 	std::vector<std::vector<glm::vec3> *> vertexInfo;
 	vertexInfo.push_back(&vertices);
+	if (vertexTexCoord.size() > 0)
+	{
+		vertexInfo.push_back(&vertexTexCoord);
+	}
 	if (vertexNormals.size() > 0)
 	{
 		vertexInfo.push_back(&vertexNormals);
diff --git a/LearnOpenGL/OpenGLWrapper.h b/LearnOpenGL/OpenGLWrapper.h
index d15dd3cf45cc679d409bac736ea6d65855d09e2a..bed0e34268606a5f5cf135981a76de5ccbe92f46 100644
--- a/LearnOpenGL/OpenGLWrapper.h
+++ b/LearnOpenGL/OpenGLWrapper.h
@@ -27,12 +27,32 @@ public:
 };
 void draw_mesh(Mesh &mesh);
 
+class Material
+{
+private:
+	GLuint prog;
+
+	GLuint diffuseMap;
+	GLuint specularMap;
+	GLuint ambientMap;
+
+public:
+	Material(GLuint _prog, GLuint _diffuseMap, GLuint _specularMap);
+
+	GLuint get_program();
+
+	GLuint get_diffuseMap();
+	GLuint get_specularMap();
+};
+
 class RenderObject
 {
 private:
 	GLuint id;
-	GLuint prog;
+
 	Mesh *mesh;
+	Material *material;
+
 	glm::mat4 model;
 
 	glm::vec3 translate;
@@ -44,7 +64,7 @@ private:
 public:
 	RenderObject(Mesh * _mesh);
 
-	GLuint get_programs();
+	Material *get_material();
 	GLuint get_vertex_count();
 	glm::mat4 get_model_matrix();
 
@@ -55,7 +75,7 @@ public:
 	void move(glm::vec3 _delta);
 	void move(glm::vec3 _direction, glm::vec1 _velocity);
 
-	void set_program(GLuint _prog);
+	void set_material(Material *_material);
 
 	void render(Camera &camera);
 };
diff --git a/LearnOpenGL/ResourceLoader.cpp b/LearnOpenGL/ResourceLoader.cpp
index a279d5c97c53a0559730acc028bfeedf19691127..07853fc7c476c743270ba2264628985b17ef0313 100644
--- a/LearnOpenGL/ResourceLoader.cpp
+++ b/LearnOpenGL/ResourceLoader.cpp
@@ -31,17 +31,18 @@ std::string get_extension(const std::string &filePath)
 	return filePath.substr(filePath.find_last_of(".") + 1);
 }
 
-bool openObj(const std::string fileName, std::vector<glm::vec3> &vertices, std::vector<glm::vec3> &vertexNormals)
+bool openObj(const std::string fileName, std::vector<glm::vec3> &vertices, std::vector<glm::vec3> &vertexTexCoord, std::vector<glm::vec3> &vertexNormals)
 {
 	vertices.clear();
+	vertexTexCoord.clear();
 	vertexNormals.clear();
 
 	std::ifstream ifs;
 	std::string line;
 
 	char op[3];
-	glm::vec3 pos;
 	std::vector<glm::vec3> vertexIndices;
+	std::vector<glm::vec3> vertexTexCoordIndices;
 	std::vector<glm::vec3> vertexNormalIndices;
 
 	ifs.open("../Models/" + fileName);
@@ -53,8 +54,6 @@ bool openObj(const std::string fileName, std::vector<glm::vec3> &vertices, std::
 
 		sscanf_s(line.c_str(), "%s", op, sizeof(op));
 
-		pos.x = 0; pos.y = 0; pos.z = 0;
-
 		charPos = 0;
 		if ((charPos = line.find(' ')) != std::string::npos)
 		{
@@ -63,23 +62,28 @@ bool openObj(const std::string fileName, std::vector<glm::vec3> &vertices, std::
 
 		if (strcmp(op, "v") == false)
 		{
+			glm::vec3 pos = { 0,0,0 };
 			sscanf_s(line.c_str(), "%f %f %f", &pos.x, &pos.y, &pos.z);
 			vertexIndices.push_back(pos);
 		}
 		else if (strcmp(op, "vn") == false)
 		{
+			glm::vec3 pos = { 0,0,0 };
 			sscanf_s(line.c_str(), "%f %f %f", &pos.x, &pos.y, &pos.z);
 			vertexNormalIndices.push_back(pos);
 		}
 		else if (strcmp(op, "vt") == false)
 		{
-			// TODO : texCoord
+			glm::vec3 pos = { 0,0,0 };
+			sscanf_s(line.c_str(), "%f %f", &pos.x, &pos.y);
+			vertexTexCoordIndices.push_back(pos);
 		}
 
 		if (strncmp(op, "f", 1) == false)
 		{
 			int vIndex = 0, uvIndex = 0, vnIndex = 0;
 			std::vector<int> faceVertexIndicies;
+			std::vector<int> faceVertexTexCoordIndicies;
 			std::vector<int> faceVertexNormalIndicies;
 
 			charPos = 0;
@@ -88,7 +92,14 @@ bool openObj(const std::string fileName, std::vector<glm::vec3> &vertices, std::
 				sscanf_s(line.substr(0, charPos).c_str(), "%d%*[-/]%d%*[-/]%d", &vIndex, &uvIndex, &vnIndex);
 				line.erase(0, charPos + 1);
 
-				faceVertexIndicies.push_back(vIndex - 1);
+				if (vIndex >= 1)
+				{
+					faceVertexIndicies.push_back(vIndex - 1);
+				}
+				if (uvIndex >= 1)
+				{
+					faceVertexTexCoordIndicies.push_back(uvIndex - 1);
+				}
 				if (vnIndex >= 1)
 				{
 					faceVertexNormalIndicies.push_back(vnIndex - 1);
@@ -114,7 +125,29 @@ bool openObj(const std::string fileName, std::vector<glm::vec3> &vertices, std::
 			else
 			{
 				GLchar infoLog[512] = { 0, };
-				log_warn(infoLog, "faceVertexIndices.size() : " + std::to_string(faceVertexIndicies.size()));
+				log_warn(infoLog, fileName + " : " + "faceVertexIndices.size() : " + std::to_string(faceVertexIndicies.size()));
+			}
+
+			if (faceVertexTexCoordIndicies.size() == 3)
+			{
+				vertexTexCoord.push_back(vertexTexCoordIndices[faceVertexTexCoordIndicies[0]]);
+				vertexTexCoord.push_back(vertexTexCoordIndices[faceVertexTexCoordIndicies[1]]);
+				vertexTexCoord.push_back(vertexTexCoordIndices[faceVertexTexCoordIndicies[2]]);
+			}
+			else if (faceVertexTexCoordIndicies.size() == 4)
+			{
+				vertexTexCoord.push_back(vertexTexCoordIndices[faceVertexTexCoordIndicies[0]]);
+				vertexTexCoord.push_back(vertexTexCoordIndices[faceVertexTexCoordIndicies[1]]);
+				vertexTexCoord.push_back(vertexTexCoordIndices[faceVertexTexCoordIndicies[2]]);
+
+				vertexTexCoord.push_back(vertexTexCoordIndices[faceVertexTexCoordIndicies[0]]);
+				vertexTexCoord.push_back(vertexTexCoordIndices[faceVertexTexCoordIndicies[2]]);
+				vertexTexCoord.push_back(vertexTexCoordIndices[faceVertexTexCoordIndicies[3]]);
+			}
+			else
+			{
+				GLchar infoLog[512] = { 0, };
+				log_warn(infoLog, fileName + " : " + "vertexTexCoordIndices.size() : " + std::to_string(faceVertexTexCoordIndicies.size()));
 			}
 
 			if (faceVertexNormalIndicies.size() == 3)
@@ -136,7 +169,7 @@ bool openObj(const std::string fileName, std::vector<glm::vec3> &vertices, std::
 			else
 			{
 				GLchar infoLog[512] = { 0, };
-				log_warn(infoLog, "faceVertexNormalIndices.size() : " + std::to_string(faceVertexNormalIndicies.size()));
+				log_warn(infoLog, fileName + " : " + "faceVertexNormalIndices.size() : " + std::to_string(faceVertexNormalIndicies.size()));
 			}
 		}
 	}
diff --git a/LearnOpenGL/ResourceLoader.h b/LearnOpenGL/ResourceLoader.h
index 1086e7e0636c30a9381198ee9e5c659b0e0ccead..5123afe10061e087c74a4c13added1a5331512a0 100644
--- a/LearnOpenGL/ResourceLoader.h
+++ b/LearnOpenGL/ResourceLoader.h
@@ -6,7 +6,10 @@
 #include <vector>
 
 #include <GL/glew.h>
-#include <glm/vec3.hpp>
+
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/type_ptr.hpp>
 
 #include "Logger.h"
 
@@ -28,6 +31,6 @@ public:
 };
 
 std::string get_extension(const std::string &filePath);
-bool openObj(const std::string fileName, std::vector<glm::vec3> &vertices, std::vector<glm::vec3> &vertexNormals);
+bool openObj(const std::string fileName, std::vector<glm::vec3> &vertices, std::vector<glm::vec3> &vertexTexCoord, std::vector<glm::vec3> &vertexNormals);
 Image *load_Image(std::string fileName, int *width, int *height, int *nrChannels);
 void free_image(Image *img);
\ No newline at end of file
diff --git a/LearnOpenGL/Source.cpp b/LearnOpenGL/Source.cpp
index c55e0089e8b23b6963c88dee9161c7988208bc62..b711dd2257d7d66f1292e00943ad8b41e1c83f86 100644
--- a/LearnOpenGL/Source.cpp
+++ b/LearnOpenGL/Source.cpp
@@ -55,17 +55,25 @@ int main()
 	auto cam_programID = build_program("Camera");
 	auto lighting = build_program("Lighting_Specular");
 	auto lamp = build_program("Lighting_Lamp");
+	auto lightmap = build_program("Lighting_Maps");
 	auto texture_shader = build_program("Texture");
 
 	auto cube = make_mesh("cube.obj");
 
+	auto black = load_image("black.png");
+	auto magenta = load_image("magenta.png");
+	auto orange = load_image("orange.png");
+	auto white = load_image("white.png");
+
+	auto defaultMaterial = new Material(lightmap, orange, white);
+
 	auto teapot = make_render_object(make_mesh("teapot.obj"));
 	{
 		teapot->set_translate(glm::vec3(0.0f, -10.0f, -40.0f));
 		teapot->set_rotate(glm::vec3(-90.0f, 0.0f, 0.0f));
 	}
 	{
-		teapot->set_program(lighting);
+		teapot->set_material(defaultMaterial);
 	}
 
 	auto cube1 = make_render_object(cube);
@@ -75,7 +83,7 @@ int main()
 		cube1->set_scale(glm::vec3(10, 10, 4));
 	}
 	{
-		cube1->set_program(lighting);
+		cube1->set_material(defaultMaterial);
 	}
 
 	auto cube2 = make_render_object(cube);
@@ -85,7 +93,7 @@ int main()
 		cube2->set_scale(glm::vec3(12, 12, 1));
 	}
 	{
-		cube2->set_program(lighting);
+		cube2->set_material(defaultMaterial);
 	}
 
 	auto plane = make_render_object(make_mesh("plane.obj"));
@@ -93,7 +101,7 @@ int main()
 		plane->set_scale(glm::vec3(10, 10, 1));
 	}
 	{
-		plane->set_program(lighting);
+		plane->set_material(defaultMaterial);
 	}
 
 	auto cube3 = make_render_object(cube);
@@ -103,16 +111,7 @@ int main()
 		cube3->set_scale(glm::vec3(50, 50, 0.5));
 	}
 	{
-		cube3->set_program(lighting);
-	}
-
-	GLuint textureID;
-	{
-		textureID = load_image("container.jpg");
-
-		glUseProgram(texture_shader);
-
-		set_uniform_value(texture_shader, "texture1", glm::ivec1(0));
+		cube3->set_material(defaultMaterial);
 	}
 
 	while (!glfwWindowShouldClose(window))
@@ -127,20 +126,13 @@ int main()
 		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
 		{
-			glUseProgram(texture_shader);
-
-			glActiveTexture(GL_TEXTURE0);
-			glBindTexture(GL_TEXTURE_2D, textureID);
-
-			glUseProgram(0);
+			teapot->render(camera);
+			cube1->render(camera);
+			cube2->render(camera);
+			cube3->render(camera);
+			plane->render(camera);
 		}
 
-		teapot->render(camera);
-		cube1->render(camera);
-		cube2->render(camera);
-		cube3->render(camera);
-		plane->render(camera);
-
 		glfwSwapBuffers(window);
 		glfwPollEvents();
 	}
diff --git a/Shaders/Lighting_Maps/Lighting_Maps.frag b/Shaders/Lighting_Maps/Lighting_Maps.frag
new file mode 100644
index 0000000000000000000000000000000000000000..fdab7e521fb0400fdb86ad700f932bce33750532
--- /dev/null
+++ b/Shaders/Lighting_Maps/Lighting_Maps.frag
@@ -0,0 +1,47 @@
+#version 330 core
+out vec4 FragColor;
+
+struct Material
+{
+    sampler2D diffuse;
+    sampler2D specular;    
+    float shininess;
+}; 
+
+struct Light
+{
+    vec3 position;
+
+    vec3 ambient;
+    vec3 diffuse;
+    vec3 specular;
+};
+
+in vec3 FragPos;  
+in vec3 Normal;  
+in vec2 TexCoords;
+  
+uniform vec3 viewPos;
+uniform Material material;
+uniform Light light;
+
+void main()
+{
+    // ambient
+    vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
+  	
+    // diffuse 
+    vec3 norm = normalize(Normal);
+    vec3 lightDir = normalize(light.position - FragPos);
+    float diff = max(dot(norm, lightDir), 0.0);
+    vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;  
+    
+    // specular
+    vec3 viewDir = normalize(viewPos - FragPos);
+    vec3 reflectDir = reflect(-lightDir, norm);  
+    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
+    vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;  
+        
+    vec3 result = ambient + diffuse + specular;
+    FragColor = vec4(result, 1.0);
+} 
\ No newline at end of file
diff --git a/Shaders/Lighting_Maps/Lighting_Maps.vert b/Shaders/Lighting_Maps/Lighting_Maps.vert
new file mode 100644
index 0000000000000000000000000000000000000000..c3142455e70b791c54c23393b79075d3e58eda57
--- /dev/null
+++ b/Shaders/Lighting_Maps/Lighting_Maps.vert
@@ -0,0 +1,21 @@
+#version 330 core
+layout (location = 0) in vec3 aPos;
+layout (location = 1) in vec3 aNormal;
+layout (location = 2) in vec2 aTexCoords;
+
+out vec3 FragPos;
+out vec3 Normal;
+out vec2 TexCoords;
+
+uniform mat4 model;
+uniform mat4 view;
+uniform mat4 projection;
+
+void main()
+{
+    FragPos = vec3(model * vec4(aPos, 1.0));
+    Normal = mat3(transpose(inverse(model))) * aNormal;  
+    TexCoords = aTexCoords;
+    
+    gl_Position = projection * view * vec4(FragPos, 1.0);
+}
\ No newline at end of file