diff --git a/CMakeLists.txt b/CMakeLists.txt index 75a219b..cfd22ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,7 +112,7 @@ set(5.advanced_lighting 3.2.point_shadows # 3.3.csm 4.normal_mapping - # 5.parallax_mapping + 5.parallax_mapping # 6.hdr # 7.bloom # 8.deferred_shading diff --git a/includes/learnopengl/mesh.h b/includes/learnopengl/mesh.h index 88930cc..e99b0d5 100644 --- a/includes/learnopengl/mesh.h +++ b/includes/learnopengl/mesh.h @@ -58,6 +58,7 @@ public: GLuint diffuseNr = 1; GLuint specularNr = 1; GLuint normalNr = 1; + GLuint heightNr = 1; for(GLuint i = 0; i < this->textures.size(); i++) { glActiveTexture(GL_TEXTURE0 + i); // Active proper texture unit before binding @@ -71,6 +72,8 @@ public: ss << specularNr++; // Transfer GLuint to stream else if(name == "texture_normal") ss << normalNr++; // Transfer GLuint to stream + else if(name == "texture_height") + ss << heightNr++; // Transfer GLuint to stream number = ss.str(); // Now set the sampler to the correct texture unit glUniform1i(glGetUniformLocation(shader.Program, (name + number).c_str()), i); diff --git a/includes/learnopengl/model.h b/includes/learnopengl/model.h index c322d63..a05e493 100644 --- a/includes/learnopengl/model.h +++ b/includes/learnopengl/model.h @@ -157,6 +157,9 @@ private: // 3. Normal maps std::vector normalMaps = this->loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal"); textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); + // 4. Height maps + std::vector heightMaps = this->loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height"); + textures.insert(textures.end(), heightMaps.begin(), heightMaps.end()); } // Return a mesh object created from the extracted mesh data diff --git a/resources/textures/bricks2.jpg b/resources/textures/bricks2.jpg index d6dcc64..3583508 100644 Binary files a/resources/textures/bricks2.jpg and b/resources/textures/bricks2.jpg differ diff --git a/resources/textures/bricks2_disp.jpg b/resources/textures/bricks2_disp.jpg index 9356ce1..48ab26f 100644 Binary files a/resources/textures/bricks2_disp.jpg and b/resources/textures/bricks2_disp.jpg differ diff --git a/resources/textures/bricks2_normal.jpg b/resources/textures/bricks2_normal.jpg index 65b3e59..aa6643d 100644 Binary files a/resources/textures/bricks2_normal.jpg and b/resources/textures/bricks2_normal.jpg differ diff --git a/resources/textures/toy_box_diffuse.png b/resources/textures/toy_box_diffuse.png new file mode 100644 index 0000000..e28e2ae Binary files /dev/null and b/resources/textures/toy_box_diffuse.png differ diff --git a/resources/textures/toy_box_disp.png b/resources/textures/toy_box_disp.png new file mode 100644 index 0000000..230bbbe Binary files /dev/null and b/resources/textures/toy_box_disp.png differ diff --git a/resources/textures/toy_box_normal.png b/resources/textures/toy_box_normal.png new file mode 100644 index 0000000..8b39dff Binary files /dev/null and b/resources/textures/toy_box_normal.png differ diff --git a/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.cpp b/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.cpp index b51efb8..693c4b5 100644 --- a/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.cpp +++ b/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.cpp @@ -11,6 +11,7 @@ // GL includes #include #include +#include // GLM Mathemtics #include @@ -21,7 +22,7 @@ #include // Properties -GLuint screenWidth = 800, screenHeight = 600; +const GLuint SCR_WIDTH = 800, SCR_HEIGHT = 600; // Function prototypes void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode); @@ -29,16 +30,17 @@ void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); void mouse_callback(GLFWwindow* window, double xpos, double ypos); void Do_Movement(); GLuint loadTexture(GLchar* path); +void RenderQuad(); // Camera Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); -bool keys[1024]; -GLfloat lastX = 400, lastY = 300; -bool firstMouse = true; GLfloat deltaTime = 0.0f; GLfloat lastFrame = 0.0f; +GLboolean parallax_mapping = true; +GLfloat height_scale = 0.1; + // The MAIN function, from here we start our application and run our Game loop int main() { @@ -49,7 +51,7 @@ int main() glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); - GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed glfwMakeContextCurrent(window); // Set the required callback functions @@ -65,103 +67,33 @@ int main() glewInit(); // Define the viewport dimensions - glViewport(0, 0, screenWidth, screenHeight); + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); // Setup some OpenGL options glEnable(GL_DEPTH_TEST); - // glDepthFunc(GL_ALWAYS); // Set to always pass the depth test (same effect as glDisable(GL_DEPTH_TEST)) // Setup and compile our shaders - Shader shader("depth_testing.vs", "depth_testing.frag"); - - #pragma region "object_initialization" - // Set the object data (buffers, vertex attributes) - GLfloat cubeVertices[] = { - // Positions // Texture Coords - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, - - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - - -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, - - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 0.0f, 1.0f - }; - GLfloat planeVertices[] = { - // Positions // Texture Coords (note we set these higher than 1 that together with GL_REPEAT as texture wrapping mode will cause the floor texture to repeat) - 5.0f, -0.5f, 5.0f, 2.0f, 0.0f, - -5.0f, -0.5f, 5.0f, 0.0f, 0.0f, - -5.0f, -0.5f, -5.0f, 0.0f, 2.0f, - - 5.0f, -0.5f, 5.0f, 2.0f, 0.0f, - -5.0f, -0.5f, -5.0f, 0.0f, 2.0f, - 5.0f, -0.5f, -5.0f, 2.0f, 2.0f - }; - // Setup cube VAO - GLuint cubeVAO, cubeVBO; - glGenVertexArrays(1, &cubeVAO); - glGenBuffers(1, &cubeVBO); - glBindVertexArray(cubeVAO); - glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); - glBindVertexArray(0); - // Setup plane VAO - GLuint planeVAO, planeVBO; - glGenVertexArrays(1, &planeVAO); - glGenBuffers(1, &planeVBO); - glBindVertexArray(planeVAO); - glBindBuffer(GL_ARRAY_BUFFER, planeVBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), &planeVertices, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); - glBindVertexArray(0); + Shader shader("parallax_mapping.vs", "parallax_mapping.frag"); // Load textures - GLuint cubeTexture = loadTexture("../../../resources/textures/marble.jpg"); - GLuint floorTexture = loadTexture("../../../resources/textures/metal.png"); - #pragma endregion + GLuint diffuseMap = loadTexture("../../../resources/textures/bricks2.jpg"); + GLuint normalMap = loadTexture("../../../resources/textures/bricks2_normal.jpg"); + GLuint heightMap = loadTexture("../../../resources/textures/bricks2_disp.jpg"); + //GLuint diffuseMap = loadTexture("../../../resources/textures/wood.png"); + //GLuint normalMap = loadTexture("../../../resources/textures/toy_box_normal.png"); + //GLuint heightMap = loadTexture("../../../resources/textures/toy_box_disp.png"); + + // Set texture units + shader.Use(); + glUniform1i(glGetUniformLocation(shader.Program, "diffuseMap"), 0); + glUniform1i(glGetUniformLocation(shader.Program, "normalMap"), 1); + glUniform1i(glGetUniformLocation(shader.Program, "depthMap"), 2); + + // Light position + glm::vec3 lightPos(0.5f, 1.0f, 0.3f); // Game loop - while(!glfwWindowShouldClose(window)) + while (!glfwWindowShouldClose(window)) { // Set frame time GLfloat currentFrame = glfwGetTime(); @@ -176,31 +108,34 @@ int main() glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // Draw objects - shader.Use(); - glm::mat4 model; + // Configure view/projection matrices + shader.Use(); glm::mat4 view = camera.GetViewMatrix(); - glm::mat4 projection = glm::perspective(camera.Zoom, (float)screenWidth/(float)screenHeight, 0.1f, 100.0f); + glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)SCR_WIDTH / (GLfloat)SCR_HEIGHT, 0.1f, 100.0f); glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); - // Cubes - glBindVertexArray(cubeVAO); - glBindTexture(GL_TEXTURE_2D, cubeTexture); // We omit the glActiveTexture part since TEXTURE0 is already the default active texture unit. (sampler used in fragment is set to 0 as well as default) - model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f)); + // Render normal-mapped quad + glm::mat4 model; + model = glm::rotate(model, (GLfloat)glfwGetTime() * -10, glm::normalize(glm::vec3(1.0, 0.0, 1.0))); // Rotates the quad to show parallax mapping works in all directions glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); - glDrawArrays(GL_TRIANGLES, 0, 36); - model = glm::mat4(); - model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f)); - glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); - glDrawArrays(GL_TRIANGLES, 0, 36); - // Floor - glBindVertexArray(planeVAO); - glBindTexture(GL_TEXTURE_2D, floorTexture); - model = glm::mat4(); - glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); + glUniform3fv(glGetUniformLocation(shader.Program, "lightPos"), 1, &lightPos[0]); + glUniform3fv(glGetUniformLocation(shader.Program, "viewPos"), 1, &camera.Position[0]); + glUniform1f(glGetUniformLocation(shader.Program, "height_scale"), height_scale); + glUniform1i(glGetUniformLocation(shader.Program, "parallax"), parallax_mapping); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, diffuseMap); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, normalMap); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, heightMap); + RenderQuad(); + // render light source (simply renders a smaller plane at the light's position for debugging/visualization) + model = glm::mat4(); + model = glm::translate(model, lightPos); + model = glm::scale(model, glm::vec3(0.1f)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + //RenderQuad(); // Swap the buffers glfwSwapBuffers(window); @@ -210,6 +145,99 @@ int main() return 0; } +// RenderQuad() Renders a 1x1 quad in NDC +GLuint quadVAO = 0; +GLuint quadVBO; +void RenderQuad() +{ + if (quadVAO == 0) + { + // positions + glm::vec3 pos1(-1.0, 1.0, 0.0); + glm::vec3 pos2(-1.0, -1.0, 0.0); + glm::vec3 pos3(1.0, -1.0, 0.0); + glm::vec3 pos4(1.0, 1.0, 0.0); + // texture coordinates + glm::vec2 uv1(0.0, 1.0); + glm::vec2 uv2(0.0, 0.0); + glm::vec2 uv3(1.0, 0.0); + glm::vec2 uv4(1.0, 1.0); + // normal vector + glm::vec3 nm(0.0, 0.0, 1.0); + + // calculate tangent/bitangent vectors of both triangles + glm::vec3 tangent1, bitangent1; + glm::vec3 tangent2, bitangent2; + // - triangle 1 + glm::vec3 edge1 = pos2 - pos1; + glm::vec3 edge2 = pos3 - pos1; + glm::vec2 deltaUV1 = uv2 - uv1; + glm::vec2 deltaUV2 = uv3 - uv1; + + GLfloat f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y); + + tangent1.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x); + tangent1.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y); + tangent1.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z); + tangent1 = glm::normalize(tangent1); + + bitangent1.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x); + bitangent1.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y); + bitangent1.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z); + bitangent1 = glm::normalize(bitangent1); + + // - triangle 2 + edge1 = pos3 - pos1; + edge2 = pos4 - pos1; + deltaUV1 = uv3 - uv1; + deltaUV2 = uv4 - uv1; + + f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y); + + tangent2.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x); + tangent2.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y); + tangent2.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z); + tangent2 = glm::normalize(tangent2); + + + bitangent2.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x); + bitangent2.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y); + bitangent2.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z); + bitangent2 = glm::normalize(bitangent2); + + + GLfloat quadVertices[] = { + // Positions // normal // TexCoords // Tangent // Bitangent + pos1.x, pos1.y, pos1.z, nm.x, nm.y, nm.z, uv1.x, uv1.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, + pos2.x, pos2.y, pos2.z, nm.x, nm.y, nm.z, uv2.x, uv2.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, + pos3.x, pos3.y, pos3.z, nm.x, nm.y, nm.z, uv3.x, uv3.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, + + pos1.x, pos1.y, pos1.z, nm.x, nm.y, nm.z, uv1.x, uv1.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z, + pos3.x, pos3.y, pos3.z, nm.x, nm.y, nm.z, uv3.x, uv3.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z, + pos4.x, pos4.y, pos4.z, nm.x, nm.y, nm.z, uv4.x, uv4.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z + }; + // Setup plane VAO + glGenVertexArrays(1, &quadVAO); + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat))); + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(8 * sizeof(GLfloat))); + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(11 * sizeof(GLfloat))); + } + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); +} + // This function loads a texture from file. Note: texture loading functions like these are usually // managed by a 'Resource Manager' that manages all resources (like textures, models, audio). // For learning purposes we'll just define it as a utility function. @@ -218,55 +246,78 @@ GLuint loadTexture(GLchar* path) //Generate texture ID and load texture data GLuint textureID; glGenTextures(1, &textureID); - int width,height; + int width, height; unsigned char* image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGB); // Assign texture to ID glBindTexture(GL_TEXTURE_2D, textureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); - glGenerateMipmap(GL_TEXTURE_2D); + glGenerateMipmap(GL_TEXTURE_2D); // Parameters - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); SOIL_free_image_data(image); return textureID; - } #pragma region "User input" +bool keys[1024]; +bool keysPressed[1024]; // Moves/alters the camera positions based on user input void Do_Movement() { // Camera controls - if(keys[GLFW_KEY_W]) + if (keys[GLFW_KEY_W]) camera.ProcessKeyboard(FORWARD, deltaTime); - if(keys[GLFW_KEY_S]) + if (keys[GLFW_KEY_S]) camera.ProcessKeyboard(BACKWARD, deltaTime); - if(keys[GLFW_KEY_A]) + if (keys[GLFW_KEY_A]) camera.ProcessKeyboard(LEFT, deltaTime); - if(keys[GLFW_KEY_D]) + if (keys[GLFW_KEY_D]) camera.ProcessKeyboard(RIGHT, deltaTime); + + // Change parallax height scale + if (keys[GLFW_KEY_Q]) + height_scale -= 0.001; + else if (keys[GLFW_KEY_E]) + height_scale += 0.001; + + // Enable/disable parallax mapping + if (keys[GLFW_KEY_SPACE] && !keysPressed[GLFW_KEY_SPACE]) + { + parallax_mapping = !parallax_mapping; + keysPressed[GLFW_KEY_SPACE] = true; + } } // Is called whenever a key is pressed/released via GLFW void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) { - if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE); - if(action == GLFW_PRESS) - keys[key] = true; - else if(action == GLFW_RELEASE) - keys[key] = false; + if (key >= 0 && key <= 1024) + { + if (action == GLFW_PRESS) + keys[key] = true; + else if (action == GLFW_RELEASE) + { + keys[key] = false; + keysPressed[key] = false; + } + } } +GLfloat lastX = 400, lastY = 300; +bool firstMouse = true; +// Moves/alters the camera positions based on user input void mouse_callback(GLFWwindow* window, double xpos, double ypos) { - if(firstMouse) + if (firstMouse) { lastX = xpos; lastY = ypos; @@ -274,13 +325,13 @@ void mouse_callback(GLFWwindow* window, double xpos, double ypos) } GLfloat xoffset = xpos - lastX; - GLfloat yoffset = lastY - ypos; - + GLfloat yoffset = lastY - ypos; + lastX = xpos; lastY = ypos; camera.ProcessMouseMovement(xoffset, yoffset); -} +} void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { diff --git a/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.frag b/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.frag index 5f3992f..d5444cc 100644 --- a/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.frag +++ b/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.frag @@ -1,16 +1,96 @@ #version 330 core -out vec4 color; +out vec4 FragColor; -float LinearizeDepth(float depth) // Note that this ranges from [0,1] instead of up to 'far plane distance' since we divide by 'far' -{ - float near = 0.1; - float far = 100.0; - float z = depth * 2.0 - 1.0; // Back to NDC - return (2.0 * near) / (far + near - z * (far - near)); +in VS_OUT { + vec3 FragPos; + vec2 TexCoords; + vec3 TangentLightPos; + vec3 TangentViewPos; + vec3 TangentFragPos; +} fs_in; + +uniform sampler2D diffuseMap; +uniform sampler2D normalMap; +uniform sampler2D depthMap; + +uniform bool parallax; +uniform float height_scale; + +vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir) +{ + // float height = texture(depthMap, texCoords).r; + // return texCoords - viewDir.xy * (height * height_scale); + + // number of depth layers + const float minLayers = 10; + const float maxLayers = 20; + float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir))); + // calculate the size of each layer + float layerDepth = 1.0 / numLayers; + // depth of current layer + float currentLayerDepth = 0.0; + // the amount to shift the texture coordinates per layer (from vector P) + vec2 P = viewDir.xy / viewDir.z * height_scale; + vec2 deltaTexCoords = P / numLayers; + + // get initial values + vec2 currentTexCoords = texCoords; + float currentDepthMapValue = texture(depthMap, currentTexCoords).r; + + while(currentLayerDepth < currentDepthMapValue) + { + // shift texture coordinates along direction of P + currentTexCoords -= deltaTexCoords; + // get depthmap value at current texture coordinates + currentDepthMapValue = texture(depthMap, currentTexCoords).r; + // get depth of next layer + currentLayerDepth += layerDepth; + } + + // -- parallax occlusion mapping interpolation from here on + // get texture coordinates before collision (reverse operations) + vec2 prevTexCoords = currentTexCoords + deltaTexCoords; + + // get depth after and before collision for linear interpolation + float afterDepth = currentDepthMapValue - currentLayerDepth; + float beforeDepth = texture(depthMap, prevTexCoords).r - currentLayerDepth + layerDepth; + + // interpolation of texture coordinates + float weight = afterDepth / (afterDepth - beforeDepth); + vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); + + return finalTexCoords; + // return currentTexCoords; } void main() -{ - float depth = LinearizeDepth(gl_FragCoord.z); - color = vec4(vec3(depth), 1.0f); +{ + // Offset texture coordinates with Parallax Mapping + vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos); + vec2 texCoords = fs_in.TexCoords; + if(parallax) + texCoords = ParallaxMapping(fs_in.TexCoords, viewDir); + + if(texCoords.x > 1.0 || texCoords.y > 1.0 || texCoords.x < 0.0 || texCoords.y < 0.0) + discard; + + // Obtain normal from normal map + vec3 normal = texture(normalMap, texCoords).rgb; + normal = normalize(normal * 2.0 - 1.0); + + // Get diffuse color + vec3 color = texture(diffuseMap, texCoords).rgb; + // Ambient + vec3 ambient = 0.1 * color; + // Diffuse + vec3 lightDir = normalize(fs_in.TangentLightPos - fs_in.TangentFragPos); + float diff = max(dot(lightDir, normal), 0.0); + vec3 diffuse = diff * color; + // Specular + vec3 reflectDir = reflect(-lightDir, normal); + vec3 halfwayDir = normalize(lightDir + viewDir); + float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0); + + vec3 specular = vec3(0.2) * spec; + FragColor = vec4(ambient + diffuse + specular, 1.0f); } \ No newline at end of file diff --git a/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.vs b/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.vs index 3abdaa6..f71888f 100644 --- a/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.vs +++ b/src/5.advanced_lighting/5.parallax_mapping/parallax_mapping.vs @@ -1,15 +1,38 @@ #version 330 core layout (location = 0) in vec3 position; -layout (location = 1) in vec2 texCoords; +layout (location = 1) in vec3 normal; +layout (location = 2) in vec2 texCoords; +layout (location = 3) in vec3 tangent; +layout (location = 4) in vec3 bitangent; -out vec2 TexCoords; +out VS_OUT { + vec3 FragPos; + vec2 TexCoords; + vec3 TangentLightPos; + vec3 TangentViewPos; + vec3 TangentFragPos; +} vs_out; -uniform mat4 model; -uniform mat4 view; uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +uniform vec3 lightPos; +uniform vec3 viewPos; void main() { gl_Position = projection * view * model * vec4(position, 1.0f); - TexCoords = texCoords; + vs_out.FragPos = vec3(model * vec4(position, 1.0)); + vs_out.TexCoords = texCoords; + + + vec3 T = normalize(mat3(model) * tangent); + vec3 B = normalize(mat3(model) * bitangent); + vec3 N = normalize(mat3(model) * normal); + mat3 TBN = transpose(mat3(T, B, N)); + + vs_out.TangentLightPos = TBN * lightPos; + vs_out.TangentViewPos = TBN * viewPos; + vs_out.TangentFragPos = TBN * vs_out.FragPos; } \ No newline at end of file