diff --git a/resources/textures/container2.png b/resources/textures/container2.png index 596e8da..7eeb17a 100644 Binary files a/resources/textures/container2.png and b/resources/textures/container2.png differ diff --git a/resources/textures/matrix.jpg b/resources/textures/matrix.jpg new file mode 100644 index 0000000..70c761e Binary files /dev/null and b/resources/textures/matrix.jpg differ diff --git a/src/2.lighting/1.colors/colors.cpp b/src/2.lighting/1.colors/colors.cpp index 6fb2b65..4ef0b87 100644 --- a/src/2.lighting/1.colors/colors.cpp +++ b/src/2.lighting/1.colors/colors.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include diff --git a/src/2.lighting/2.1.basic_lighting_diffuse/basic_lighting_diffuse.cpp b/src/2.lighting/2.1.basic_lighting_diffuse/basic_lighting_diffuse.cpp index 7cd2fdc..b48407b 100644 --- a/src/2.lighting/2.1.basic_lighting_diffuse/basic_lighting_diffuse.cpp +++ b/src/2.lighting/2.1.basic_lighting_diffuse/basic_lighting_diffuse.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include diff --git a/src/2.lighting/2.2.basic_lighting_specular/basic_lighting_specular.cpp b/src/2.lighting/2.2.basic_lighting_specular/basic_lighting_specular.cpp index d53df2d..71b5f90 100644 --- a/src/2.lighting/2.2.basic_lighting_specular/basic_lighting_specular.cpp +++ b/src/2.lighting/2.2.basic_lighting_specular/basic_lighting_specular.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include diff --git a/src/2.lighting/3.1.materials/materials.cpp b/src/2.lighting/3.1.materials/materials.cpp index b6dfa10..a232312 100644 --- a/src/2.lighting/3.1.materials/materials.cpp +++ b/src/2.lighting/3.1.materials/materials.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include diff --git a/src/2.lighting/3.2.materials_exercise1/materials_exercise1.cpp b/src/2.lighting/3.2.materials_exercise1/materials_exercise1.cpp index 11fbfcc..baebf6c 100644 --- a/src/2.lighting/3.2.materials_exercise1/materials_exercise1.cpp +++ b/src/2.lighting/3.2.materials_exercise1/materials_exercise1.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include diff --git a/src/2.lighting/4.1.lighting_maps_diffuse_map/4.1.lighting_maps.fs b/src/2.lighting/4.1.lighting_maps_diffuse_map/4.1.lighting_maps.fs new file mode 100644 index 0000000..9c91eed --- /dev/null +++ b/src/2.lighting/4.1.lighting_maps_diffuse_map/4.1.lighting_maps.fs @@ -0,0 +1,45 @@ +#version 330 core +out vec4 fragColor; + +struct Material { + sampler2D diffuse; + vec3 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 * material.specular); + + vec3 result = ambient + diffuse + specular; + fragColor = vec4(result, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/4.1.lighting_maps_diffuse_map/4.1.lighting_maps.vs b/src/2.lighting/4.1.lighting_maps_diffuse_map/4.1.lighting_maps.vs new file mode 100644 index 0000000..0258c98 --- /dev/null +++ b/src/2.lighting/4.1.lighting_maps_diffuse_map/4.1.lighting_maps.vs @@ -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.0f)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; + + gl_Position = projection * view * vec4(FragPos, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/4.1.lighting_maps_diffuse_map/lighting_maps_diffuse.cpp b/src/2.lighting/4.1.lighting_maps_diffuse_map/lighting_maps_diffuse.cpp new file mode 100644 index 0000000..a5876bf --- /dev/null +++ b/src/2.lighting/4.1.lighting_maps_diffuse_map/lighting_maps_diffuse.cpp @@ -0,0 +1,334 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); +unsigned int loadTexture(const char *path); + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = 800.0f / 2.0; +float lastY = 600.0 / 2.0; +bool firstMouse = true; + +float deltaTime = 0.0f; // time between current frame and last frame +float lastFrame = 0.0f; + +// lighting +glm::vec3 lightPos(1.2f, 1.0f, 2.0f); + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); + glfwMakeContextCurrent(window); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile our shader zprogram + // ------------------------------------ + Shader lightingShader("4.1.lighting_maps.vs", "4.1.lighting_maps.fs"); + Shader lampShader("1.lamp.vs", "1.lamp.fs"); + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + float vertices[] = { + // positions // normals // texture coords + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + // first, configure the cube's VAO (and VBO) + unsigned int VBO, cubeVAO; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &VBO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindVertexArray(cubeVAO); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glEnableVertexAttribArray(2); + + // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube) + unsigned int lightVAO; + glGenVertexArrays(1, &lightVAO); + glBindVertexArray(lightVAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + // note that we update the lamp's position attribute's stride to reflect the updated buffer data + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // load textures (we now use a utility function to keep the code more organized) + // ----------------------------------------------------------------------------- + unsigned int diffuseMap = loadTexture(FileSystem::getPath("resources/textures/container2.png").c_str()); + + // shader configuration + // -------------------- + lightingShader.use(); + lightingShader.setInt("material.diffuse", 0); + + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // be sure to activate shader when setting uniforms/drawing objects + lightingShader.use(); + lightingShader.setVec3("light.position", lightPos); + lightingShader.setVec3("viewPos", camera.Position); + + // light properties + lightingShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f); + lightingShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); + lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f); + + // material properties + lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f); + lightingShader.setFloat("material.shininess", 64.0f); + + // view/projection transformations + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f); + glm::mat4 view = camera.GetViewMatrix(); + lightingShader.setMat4("projection", projection); + lightingShader.setMat4("view", view); + + // world transformation + glm::mat4 model; + lightingShader.setMat4("model", model); + + // bind diffuse map + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, diffuseMap); + + // render the cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + + + // also draw the lamp object + lampShader.use(); + lampShader.setMat4("projection", projection); + lampShader.setMat4("view", view); + model = glm::mat4(); + model = glm::translate(model, lightPos); + model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube + lampShader.setMat4("model", model); + + glBindVertexArray(lightVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &cubeVAO); + glDeleteVertexArrays(1, &lightVAO); + glDeleteBuffers(1, &VBO); + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + float cameraSpeed = 2.5 * deltaTime; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +// utility function for loading a 2D texture from file +// --------------------------------------------------- +unsigned int loadTexture(char const * path) +{ + unsigned int textureID; + glGenTextures(1, &textureID); + + int width, height, nrComponents; + unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0); + if (data) + { + GLenum format; + if (nrComponents == 1) + format = GL_RED; + else if (nrComponents == 3) + format = GL_RGB; + else if (nrComponents == 4) + format = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + 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); + + stbi_image_free(data); + } + else + { + std::cout << "Texture failed to load at path: " << path << std::endl; + stbi_image_free(data); + } + + return textureID; +} \ No newline at end of file diff --git a/src/2.lighting/4.2.lighting_maps_specular_map/4.2.lighting_maps.fs b/src/2.lighting/4.2.lighting_maps_specular_map/4.2.lighting_maps.fs new file mode 100644 index 0000000..4c9097e --- /dev/null +++ b/src/2.lighting/4.2.lighting_maps_specular_map/4.2.lighting_maps.fs @@ -0,0 +1,45 @@ +#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.0f); +} \ No newline at end of file diff --git a/src/2.lighting/4.2.lighting_maps_specular_map/4.2.lighting_maps.vs b/src/2.lighting/4.2.lighting_maps_specular_map/4.2.lighting_maps.vs new file mode 100644 index 0000000..0258c98 --- /dev/null +++ b/src/2.lighting/4.2.lighting_maps_specular_map/4.2.lighting_maps.vs @@ -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.0f)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; + + gl_Position = projection * view * vec4(FragPos, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/4.2.lighting_maps_specular_map/lighting_maps_specular.cpp b/src/2.lighting/4.2.lighting_maps_specular_map/lighting_maps_specular.cpp new file mode 100644 index 0000000..245dbb1 --- /dev/null +++ b/src/2.lighting/4.2.lighting_maps_specular_map/lighting_maps_specular.cpp @@ -0,0 +1,338 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); +unsigned int loadTexture(const char *path); + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = 800.0f / 2.0; +float lastY = 600.0 / 2.0; +bool firstMouse = true; + +float deltaTime = 0.0f; // time between current frame and last frame +float lastFrame = 0.0f; + +// lighting +glm::vec3 lightPos(1.2f, 1.0f, 2.0f); + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); + glfwMakeContextCurrent(window); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile our shader zprogram + // ------------------------------------ + Shader lightingShader("4.2.lighting_maps.vs", "4.2.lighting_maps.fs"); + Shader lampShader("1.lamp.vs", "1.lamp.fs"); + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + float vertices[] = { + // positions // normals // texture coords + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + // first, configure the cube's VAO (and VBO) + unsigned int VBO, cubeVAO; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &VBO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindVertexArray(cubeVAO); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glEnableVertexAttribArray(2); + + // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube) + unsigned int lightVAO; + glGenVertexArrays(1, &lightVAO); + glBindVertexArray(lightVAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + // note that we update the lamp's position attribute's stride to reflect the updated buffer data + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // load textures (we now use a utility function to keep the code more organized) + // ----------------------------------------------------------------------------- + unsigned int diffuseMap = loadTexture(FileSystem::getPath("resources/textures/container2.png").c_str()); + unsigned int specularMap = loadTexture(FileSystem::getPath("resources/textures/container2_specular.png").c_str()); + + // shader configuration + // -------------------- + lightingShader.use(); + lightingShader.setInt("material.diffuse", 0); + lightingShader.setInt("material.specular", 1); + + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // be sure to activate shader when setting uniforms/drawing objects + lightingShader.use(); + lightingShader.setVec3("light.position", lightPos); + lightingShader.setVec3("viewPos", camera.Position); + + // light properties + lightingShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f); + lightingShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); + lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f); + + // material properties + lightingShader.setFloat("material.shininess", 64.0f); + + // view/projection transformations + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f); + glm::mat4 view = camera.GetViewMatrix(); + lightingShader.setMat4("projection", projection); + lightingShader.setMat4("view", view); + + // world transformation + glm::mat4 model; + lightingShader.setMat4("model", model); + + // bind diffuse map + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, diffuseMap); + // bind specular map + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, specularMap); + + // render the cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + + + // also draw the lamp object + lampShader.use(); + lampShader.setMat4("projection", projection); + lampShader.setMat4("view", view); + model = glm::mat4(); + model = glm::translate(model, lightPos); + model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube + lampShader.setMat4("model", model); + + glBindVertexArray(lightVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &cubeVAO); + glDeleteVertexArrays(1, &lightVAO); + glDeleteBuffers(1, &VBO); + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + float cameraSpeed = 2.5 * deltaTime; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +// utility function for loading a 2D texture from file +// --------------------------------------------------- +unsigned int loadTexture(char const * path) +{ + unsigned int textureID; + glGenTextures(1, &textureID); + + int width, height, nrComponents; + unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0); + if (data) + { + GLenum format; + if (nrComponents == 1) + format = GL_RED; + else if (nrComponents == 3) + format = GL_RGB; + else if (nrComponents == 4) + format = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + 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); + + stbi_image_free(data); + } + else + { + std::cout << "Texture failed to load at path: " << path << std::endl; + stbi_image_free(data); + } + + return textureID; +} \ No newline at end of file diff --git a/src/2.lighting/4.3.lighting_maps_exercise4/4.3.lighting_maps.fs b/src/2.lighting/4.3.lighting_maps_exercise4/4.3.lighting_maps.fs new file mode 100644 index 0000000..053b2e6 --- /dev/null +++ b/src/2.lighting/4.3.lighting_maps_exercise4/4.3.lighting_maps.fs @@ -0,0 +1,49 @@ +#version 330 core +out vec4 fragColor; + +struct Material { + sampler2D diffuse; + sampler2D specular; + sampler2D emission; + 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; + + // emission + vec3 emission = texture(material.emission, TexCoords).rgb; + + vec3 result = ambient + diffuse + specular + emission; + fragColor = vec4(result, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/4.3.lighting_maps_exercise4/4.3.lighting_maps.vs b/src/2.lighting/4.3.lighting_maps_exercise4/4.3.lighting_maps.vs new file mode 100644 index 0000000..0258c98 --- /dev/null +++ b/src/2.lighting/4.3.lighting_maps_exercise4/4.3.lighting_maps.vs @@ -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.0f)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; + + gl_Position = projection * view * vec4(FragPos, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/4.3.lighting_maps_exercise4/lighting_maps_exercise4.cpp b/src/2.lighting/4.3.lighting_maps_exercise4/lighting_maps_exercise4.cpp new file mode 100644 index 0000000..f8adb59 --- /dev/null +++ b/src/2.lighting/4.3.lighting_maps_exercise4/lighting_maps_exercise4.cpp @@ -0,0 +1,343 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); +unsigned int loadTexture(const char *path); + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = 800.0f / 2.0; +float lastY = 600.0 / 2.0; +bool firstMouse = true; + +float deltaTime = 0.0f; // time between current frame and last frame +float lastFrame = 0.0f; + +// lighting +glm::vec3 lightPos(1.2f, 1.0f, 2.0f); + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); + glfwMakeContextCurrent(window); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile our shader zprogram + // ------------------------------------ + Shader lightingShader("4.3.lighting_maps.vs", "4.3.lighting_maps.fs"); + Shader lampShader("1.lamp.vs", "1.lamp.fs"); + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + float vertices[] = { + // positions // normals // texture coords + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + // first, configure the cube's VAO (and VBO) + unsigned int VBO, cubeVAO; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &VBO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindVertexArray(cubeVAO); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glEnableVertexAttribArray(2); + + // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube) + unsigned int lightVAO; + glGenVertexArrays(1, &lightVAO); + glBindVertexArray(lightVAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + // note that we update the lamp's position attribute's stride to reflect the updated buffer data + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // load textures (we now use a utility function to keep the code more organized) + // ----------------------------------------------------------------------------- + unsigned int diffuseMap = loadTexture(FileSystem::getPath("resources/textures/container2.png").c_str()); + unsigned int specularMap = loadTexture(FileSystem::getPath("resources/textures/container2_specular.png").c_str()); + unsigned int emissionMap = loadTexture(FileSystem::getPath("resources/textures/matrix.jpg").c_str()); + + // shader configuration + // -------------------- + lightingShader.use(); + lightingShader.setInt("material.diffuse", 0); + lightingShader.setInt("material.specular", 1); + lightingShader.setInt("material.emission", 2); + + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // be sure to activate shader when setting uniforms/drawing objects + lightingShader.use(); + lightingShader.setVec3("light.position", lightPos); + lightingShader.setVec3("viewPos", camera.Position); + + // light properties + lightingShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f); + lightingShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); + lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f); + + // material properties + lightingShader.setFloat("material.shininess", 64.0f); + + // view/projection transformations + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f); + glm::mat4 view = camera.GetViewMatrix(); + lightingShader.setMat4("projection", projection); + lightingShader.setMat4("view", view); + + // world transformation + glm::mat4 model; + lightingShader.setMat4("model", model); + + // bind diffuse map + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, diffuseMap); + // bind specular map + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, specularMap); + // bind emission map + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, emissionMap); + + // render the cube + glBindVertexArray(cubeVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + + + // also draw the lamp object + lampShader.use(); + lampShader.setMat4("projection", projection); + lampShader.setMat4("view", view); + model = glm::mat4(); + model = glm::translate(model, lightPos); + model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube + lampShader.setMat4("model", model); + + glBindVertexArray(lightVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &cubeVAO); + glDeleteVertexArrays(1, &lightVAO); + glDeleteBuffers(1, &VBO); + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + float cameraSpeed = 2.5 * deltaTime; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +// utility function for loading a 2D texture from file +// --------------------------------------------------- +unsigned int loadTexture(char const * path) +{ + unsigned int textureID; + glGenTextures(1, &textureID); + + int width, height, nrComponents; + unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0); + if (data) + { + GLenum format; + if (nrComponents == 1) + format = GL_RED; + else if (nrComponents == 3) + format = GL_RGB; + else if (nrComponents == 4) + format = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + 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); + + stbi_image_free(data); + } + else + { + std::cout << "Texture failed to load at path: " << path << std::endl; + stbi_image_free(data); + } + + return textureID; +} \ No newline at end of file diff --git a/src/2.lighting/5.1.light_casters_directional/5.1.light_casters.fs b/src/2.lighting/5.1.light_casters_directional/5.1.light_casters.fs new file mode 100644 index 0000000..155160d --- /dev/null +++ b/src/2.lighting/5.1.light_casters_directional/5.1.light_casters.fs @@ -0,0 +1,47 @@ +#version 330 core +out vec4 fragColor; + +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; + +struct Light { + //vec3 position; + vec3 direction; + + 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); + vec3 lightDir = normalize(-light.direction); + 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.0f); +} \ No newline at end of file diff --git a/src/2.lighting/5.1.light_casters_directional/5.1.light_casters.vs b/src/2.lighting/5.1.light_casters_directional/5.1.light_casters.vs new file mode 100644 index 0000000..0258c98 --- /dev/null +++ b/src/2.lighting/5.1.light_casters_directional/5.1.light_casters.vs @@ -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.0f)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; + + gl_Position = projection * view * vec4(FragPos, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/5.1.light_casters_directional/light_casters_directional.cpp b/src/2.lighting/5.1.light_casters_directional/light_casters_directional.cpp new file mode 100644 index 0000000..8058fa2 --- /dev/null +++ b/src/2.lighting/5.1.light_casters_directional/light_casters_directional.cpp @@ -0,0 +1,361 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); +unsigned int loadTexture(const char *path); + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = 800.0f / 2.0; +float lastY = 600.0 / 2.0; +bool firstMouse = true; + +float deltaTime = 0.0f; // time between current frame and last frame +float lastFrame = 0.0f; + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); + glfwMakeContextCurrent(window); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile our shader zprogram + // ------------------------------------ + Shader lightingShader("5.1.light_casters.vs", "5.1.light_casters.fs"); + Shader lampShader("1.lamp.vs", "1.lamp.fs"); + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + float vertices[] = { + // positions // normals // texture coords + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + // positions all containers + glm::vec3 cubePositions[] = { + glm::vec3( 0.0f, 0.0f, 0.0f), + glm::vec3( 2.0f, 5.0f, -15.0f), + glm::vec3(-1.5f, -2.2f, -2.5f), + glm::vec3(-3.8f, -2.0f, -12.3f), + glm::vec3( 2.4f, -0.4f, -3.5f), + glm::vec3(-1.7f, 3.0f, -7.5f), + glm::vec3( 1.3f, -2.0f, -2.5f), + glm::vec3( 1.5f, 2.0f, -2.5f), + glm::vec3( 1.5f, 0.2f, -1.5f), + glm::vec3(-1.3f, 1.0f, -1.5f) + }; + // first, configure the cube's VAO (and VBO) + unsigned int VBO, cubeVAO; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &VBO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindVertexArray(cubeVAO); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glEnableVertexAttribArray(2); + + // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube) + unsigned int lightVAO; + glGenVertexArrays(1, &lightVAO); + glBindVertexArray(lightVAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + // note that we update the lamp's position attribute's stride to reflect the updated buffer data + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // load textures (we now use a utility function to keep the code more organized) + // ----------------------------------------------------------------------------- + unsigned int diffuseMap = loadTexture(FileSystem::getPath("resources/textures/container2.png").c_str()); + unsigned int specularMap = loadTexture(FileSystem::getPath("resources/textures/container2_specular.png").c_str()); + + // shader configuration + // -------------------- + lightingShader.use(); + lightingShader.setInt("material.diffuse", 0); + lightingShader.setInt("material.specular", 1); + + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // be sure to activate shader when setting uniforms/drawing objects + lightingShader.use(); + lightingShader.setVec3("light.direction", -0.2f, -1.0f, -0.3f); + lightingShader.setVec3("viewPos", camera.Position); + + // light properties + lightingShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f); + lightingShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); + lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f); + + // material properties + lightingShader.setFloat("material.shininess", 32.0f); + + // view/projection transformations + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f); + glm::mat4 view = camera.GetViewMatrix(); + lightingShader.setMat4("projection", projection); + lightingShader.setMat4("view", view); + + // world transformation + glm::mat4 model; + lightingShader.setMat4("model", model); + + // bind diffuse map + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, diffuseMap); + // bind specular map + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, specularMap); + + // render the cube + // glBindVertexArray(cubeVAO); + // glDrawArrays(GL_TRIANGLES, 0, 36);*/ + + // render containers + glBindVertexArray(cubeVAO); + for (unsigned int i = 0; i < 10; i++) + { + // calculate the model matrix for each object and pass it to shader before drawing + glm::mat4 model; + model = glm::translate(model, cubePositions[i]); + float angle = 20.0f * i; + model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f)); + lightingShader.setMat4("model", model); + + glDrawArrays(GL_TRIANGLES, 0, 36); + } + + + // a lamp object is weird when we only have a directional light, don't render the light object + // lampShader.use(); + // lampShader.setMat4("projection", projection); + // lampShader.setMat4("view", view); + // model = glm::mat4(); + // model = glm::translate(model, lightPos); + // model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube + // lampShader.setMat4("model", model); + + // glBindVertexArray(lightVAO); + // glDrawArrays(GL_TRIANGLES, 0, 36); + + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &cubeVAO); + glDeleteVertexArrays(1, &lightVAO); + glDeleteBuffers(1, &VBO); + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + float cameraSpeed = 2.5 * deltaTime; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +// utility function for loading a 2D texture from file +// --------------------------------------------------- +unsigned int loadTexture(char const * path) +{ + unsigned int textureID; + glGenTextures(1, &textureID); + + int width, height, nrComponents; + unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0); + if (data) + { + GLenum format; + if (nrComponents == 1) + format = GL_RED; + else if (nrComponents == 3) + format = GL_RGB; + else if (nrComponents == 4) + format = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + 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); + + stbi_image_free(data); + } + else + { + std::cout << "Texture failed to load at path: " << path << std::endl; + stbi_image_free(data); + } + + return textureID; +} \ No newline at end of file diff --git a/src/2.lighting/5.2.light_casters_point/5.2.light_casters.fs b/src/2.lighting/5.2.light_casters_point/5.2.light_casters.fs new file mode 100644 index 0000000..66ca963 --- /dev/null +++ b/src/2.lighting/5.2.light_casters_point/5.2.light_casters.fs @@ -0,0 +1,57 @@ +#version 330 core +out vec4 fragColor; + +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; + +struct Light { + vec3 position; + + vec3 ambient; + vec3 diffuse; + vec3 specular; + + float constant; + float linear; + float quadratic; +}; + +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; + + // attenuation + float distance = length(light.position - FragPos); + float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); + + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + + vec3 result = ambient + diffuse + specular; + fragColor = vec4(result, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/5.2.light_casters_point/5.2.light_casters.vs b/src/2.lighting/5.2.light_casters_point/5.2.light_casters.vs new file mode 100644 index 0000000..0258c98 --- /dev/null +++ b/src/2.lighting/5.2.light_casters_point/5.2.light_casters.vs @@ -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.0f)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; + + gl_Position = projection * view * vec4(FragPos, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/5.2.light_casters_point/light_casters_point.cpp b/src/2.lighting/5.2.light_casters_point/light_casters_point.cpp new file mode 100644 index 0000000..7ffc167 --- /dev/null +++ b/src/2.lighting/5.2.light_casters_point/light_casters_point.cpp @@ -0,0 +1,363 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); +unsigned int loadTexture(const char *path); + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = 800.0f / 2.0; +float lastY = 600.0 / 2.0; +bool firstMouse = true; + +float deltaTime = 0.0f; // time between current frame and last frame +float lastFrame = 0.0f; + +// lighting +glm::vec3 lightPos(1.2f, 1.0f, 2.0f); + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); + glfwMakeContextCurrent(window); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile our shader zprogram + // ------------------------------------ + Shader lightingShader("5.2.light_casters.vs", "5.2.light_casters.fs"); + Shader lampShader("1.lamp.vs", "1.lamp.fs"); + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + float vertices[] = { + // positions // normals // texture coords + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + // positions all containers + glm::vec3 cubePositions[] = { + glm::vec3( 0.0f, 0.0f, 0.0f), + glm::vec3( 2.0f, 5.0f, -15.0f), + glm::vec3(-1.5f, -2.2f, -2.5f), + glm::vec3(-3.8f, -2.0f, -12.3f), + glm::vec3( 2.4f, -0.4f, -3.5f), + glm::vec3(-1.7f, 3.0f, -7.5f), + glm::vec3( 1.3f, -2.0f, -2.5f), + glm::vec3( 1.5f, 2.0f, -2.5f), + glm::vec3( 1.5f, 0.2f, -1.5f), + glm::vec3(-1.3f, 1.0f, -1.5f) + }; + // first, configure the cube's VAO (and VBO) + unsigned int VBO, cubeVAO; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &VBO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindVertexArray(cubeVAO); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glEnableVertexAttribArray(2); + + // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube) + unsigned int lightVAO; + glGenVertexArrays(1, &lightVAO); + glBindVertexArray(lightVAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + // note that we update the lamp's position attribute's stride to reflect the updated buffer data + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // load textures (we now use a utility function to keep the code more organized) + // ----------------------------------------------------------------------------- + unsigned int diffuseMap = loadTexture(FileSystem::getPath("resources/textures/container2.png").c_str()); + unsigned int specularMap = loadTexture(FileSystem::getPath("resources/textures/container2_specular.png").c_str()); + + // shader configuration + // -------------------- + lightingShader.use(); + lightingShader.setInt("material.diffuse", 0); + lightingShader.setInt("material.specular", 1); + + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // be sure to activate shader when setting uniforms/drawing objects + lightingShader.use(); + lightingShader.setVec3("light.position", lightPos); + lightingShader.setVec3("viewPos", camera.Position); + + // light properties + lightingShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f); + lightingShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); + lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f); + lightingShader.setFloat("light.constant", 1.0f); + lightingShader.setFloat("light.linear", 0.09f); + lightingShader.setFloat("light.quadratic", 0.032f); + + // material properties + lightingShader.setFloat("material.shininess", 32.0f); + + // view/projection transformations + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f); + glm::mat4 view = camera.GetViewMatrix(); + lightingShader.setMat4("projection", projection); + lightingShader.setMat4("view", view); + + // world transformation + glm::mat4 model; + lightingShader.setMat4("model", model); + + // bind diffuse map + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, diffuseMap); + // bind specular map + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, specularMap); + + // render containers + glBindVertexArray(cubeVAO); + for (unsigned int i = 0; i < 10; i++) + { + // calculate the model matrix for each object and pass it to shader before drawing + glm::mat4 model; + model = glm::translate(model, cubePositions[i]); + float angle = 20.0f * i; + model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f)); + lightingShader.setMat4("model", model); + + glDrawArrays(GL_TRIANGLES, 0, 36); + } + + + // also draw the lamp object + lampShader.use(); + lampShader.setMat4("projection", projection); + lampShader.setMat4("view", view); + model = glm::mat4(); + model = glm::translate(model, lightPos); + model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube + lampShader.setMat4("model", model); + + glBindVertexArray(lightVAO); + glDrawArrays(GL_TRIANGLES, 0, 36); + + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &cubeVAO); + glDeleteVertexArrays(1, &lightVAO); + glDeleteBuffers(1, &VBO); + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + float cameraSpeed = 2.5 * deltaTime; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +// utility function for loading a 2D texture from file +// --------------------------------------------------- +unsigned int loadTexture(char const * path) +{ + unsigned int textureID; + glGenTextures(1, &textureID); + + int width, height, nrComponents; + unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0); + if (data) + { + GLenum format; + if (nrComponents == 1) + format = GL_RED; + else if (nrComponents == 3) + format = GL_RGB; + else if (nrComponents == 4) + format = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + 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); + + stbi_image_free(data); + } + else + { + std::cout << "Texture failed to load at path: " << path << std::endl; + stbi_image_free(data); + } + + return textureID; +} \ No newline at end of file diff --git a/src/2.lighting/5.3.light_casters_spot/5.3.light_casters.fs b/src/2.lighting/5.3.light_casters_spot/5.3.light_casters.fs new file mode 100644 index 0000000..927739f --- /dev/null +++ b/src/2.lighting/5.3.light_casters_spot/5.3.light_casters.fs @@ -0,0 +1,72 @@ +#version 330 core +out vec4 fragColor; + +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; + +struct Light { + vec3 position; + vec3 direction; + float cutOff; + float outerCutOff; + + vec3 ambient; + vec3 diffuse; + vec3 specular; + + float constant; + float linear; + float quadratic; +}; + +in vec3 FragPos; +in vec3 Normal; +in vec2 TexCoords; + +uniform vec3 viewPos; +uniform Material material; +uniform Light light; + +void main() +{ + vec3 lightDir = normalize(light.position - FragPos); + + // check if lighting is inside the spotlight cone + float theta = dot(lightDir, normalize(-light.direction)); + + if(theta > light.cutOff) // remember that we're working with angles as cosines instead of degrees so a '>' is used. + { + // ambient + vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb; + + // diffuse + vec3 norm = normalize(Normal); + 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; + + // attenuation + float distance = length(light.position - FragPos); + float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); + + // ambient *= attenuation; // remove attenuation from ambient, as otherwise at large distances the light would be darker inside than outside the spotlight due the ambient term in the else branche + diffuse *= attenuation; + specular *= attenuation; + + vec3 result = ambient + diffuse + specular; + fragColor = vec4(result, 1.0f); + } + else + { + // else, use ambient light so scene isn't completely dark outside the spotlight. + fragColor = vec4(light.ambient * texture(material.diffuse, TexCoords).rgb, 1.0f); + } +} \ No newline at end of file diff --git a/src/2.lighting/5.3.light_casters_spot/5.3.light_casters.vs b/src/2.lighting/5.3.light_casters_spot/5.3.light_casters.vs new file mode 100644 index 0000000..0258c98 --- /dev/null +++ b/src/2.lighting/5.3.light_casters_spot/5.3.light_casters.vs @@ -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.0f)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; + + gl_Position = projection * view * vec4(FragPos, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/5.3.light_casters_spot/light_casters_spot.cpp b/src/2.lighting/5.3.light_casters_spot/light_casters_spot.cpp new file mode 100644 index 0000000..922b4df --- /dev/null +++ b/src/2.lighting/5.3.light_casters_spot/light_casters_spot.cpp @@ -0,0 +1,364 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); +unsigned int loadTexture(const char *path); + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = 800.0f / 2.0; +float lastY = 600.0 / 2.0; +bool firstMouse = true; + +float deltaTime = 0.0f; // time between current frame and last frame +float lastFrame = 0.0f; + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); + glfwMakeContextCurrent(window); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile our shader zprogram + // ------------------------------------ + Shader lightingShader("5.3.light_casters.vs", "5.3.light_casters.fs"); + Shader lampShader("1.lamp.vs", "1.lamp.fs"); + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + float vertices[] = { + // positions // normals // texture coords + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + // positions all containers + glm::vec3 cubePositions[] = { + glm::vec3( 0.0f, 0.0f, 0.0f), + glm::vec3( 2.0f, 5.0f, -15.0f), + glm::vec3(-1.5f, -2.2f, -2.5f), + glm::vec3(-3.8f, -2.0f, -12.3f), + glm::vec3( 2.4f, -0.4f, -3.5f), + glm::vec3(-1.7f, 3.0f, -7.5f), + glm::vec3( 1.3f, -2.0f, -2.5f), + glm::vec3( 1.5f, 2.0f, -2.5f), + glm::vec3( 1.5f, 0.2f, -1.5f), + glm::vec3(-1.3f, 1.0f, -1.5f) + }; + // first, configure the cube's VAO (and VBO) + unsigned int VBO, cubeVAO; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &VBO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindVertexArray(cubeVAO); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glEnableVertexAttribArray(2); + + // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube) + unsigned int lightVAO; + glGenVertexArrays(1, &lightVAO); + glBindVertexArray(lightVAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + // note that we update the lamp's position attribute's stride to reflect the updated buffer data + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // load textures (we now use a utility function to keep the code more organized) + // ----------------------------------------------------------------------------- + unsigned int diffuseMap = loadTexture(FileSystem::getPath("resources/textures/container2.png").c_str()); + unsigned int specularMap = loadTexture(FileSystem::getPath("resources/textures/container2_specular.png").c_str()); + + // shader configuration + // -------------------- + lightingShader.use(); + lightingShader.setInt("material.diffuse", 0); + lightingShader.setInt("material.specular", 1); + + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // be sure to activate shader when setting uniforms/drawing objects + lightingShader.use(); + lightingShader.setVec3("light.position", camera.Position); + lightingShader.setVec3("light.direction", camera.Front); + lightingShader.setFloat("light.cutOff", glm::cos(glm::radians(12.5f))); + lightingShader.setVec3("viewPos", camera.Position); + + // light properties + lightingShader.setVec3("light.ambient", 0.1f, 0.1f, 0.1f); + // we configure the diffuse intensity slightly higher; the right lighting conditions differ with each lighting method and environment. + // each environment and lighting type requires some tweaking to get the best out of your environment. + lightingShader.setVec3("light.diffuse", 0.8f, 0.8f, 0.8f); + lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f); + lightingShader.setFloat("light.constant", 1.0f); + lightingShader.setFloat("light.linear", 0.09f); + lightingShader.setFloat("light.quadratic", 0.032f); + + // material properties + lightingShader.setFloat("material.shininess", 32.0f); + + // view/projection transformations + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f); + glm::mat4 view = camera.GetViewMatrix(); + lightingShader.setMat4("projection", projection); + lightingShader.setMat4("view", view); + + // world transformation + glm::mat4 model; + lightingShader.setMat4("model", model); + + // bind diffuse map + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, diffuseMap); + // bind specular map + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, specularMap); + + // render containers + glBindVertexArray(cubeVAO); + for (unsigned int i = 0; i < 10; i++) + { + // calculate the model matrix for each object and pass it to shader before drawing + glm::mat4 model; + model = glm::translate(model, cubePositions[i]); + float angle = 20.0f * i; + model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f)); + lightingShader.setMat4("model", model); + + glDrawArrays(GL_TRIANGLES, 0, 36); + } + + + // again, a lamp object is weird when we only have a spot light, don't render the light object + // lampShader.use(); + // lampShader.setMat4("projection", projection); + // lampShader.setMat4("view", view); + // model = glm::mat4(); + // model = glm::translate(model, lightPos); + // model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube + // lampShader.setMat4("model", model); + + // glBindVertexArray(lightVAO); + // glDrawArrays(GL_TRIANGLES, 0, 36); + + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &cubeVAO); + glDeleteVertexArrays(1, &lightVAO); + glDeleteBuffers(1, &VBO); + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + float cameraSpeed = 2.5 * deltaTime; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +// utility function for loading a 2D texture from file +// --------------------------------------------------- +unsigned int loadTexture(char const * path) +{ + unsigned int textureID; + glGenTextures(1, &textureID); + + int width, height, nrComponents; + unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0); + if (data) + { + GLenum format; + if (nrComponents == 1) + format = GL_RED; + else if (nrComponents == 3) + format = GL_RGB; + else if (nrComponents == 4) + format = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + 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); + + stbi_image_free(data); + } + else + { + std::cout << "Texture failed to load at path: " << path << std::endl; + stbi_image_free(data); + } + + return textureID; +} \ No newline at end of file diff --git a/src/2.lighting/5.4.light_casters_spot_soft/5.4.light_casters.fs b/src/2.lighting/5.4.light_casters_spot_soft/5.4.light_casters.fs new file mode 100644 index 0000000..e695ec1 --- /dev/null +++ b/src/2.lighting/5.4.light_casters_spot_soft/5.4.light_casters.fs @@ -0,0 +1,66 @@ +#version 330 core +out vec4 fragColor; + +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; + +struct Light { + vec3 position; + vec3 direction; + float cutOff; + float outerCutOff; + + vec3 ambient; + vec3 diffuse; + vec3 specular; + + float constant; + float linear; + float quadratic; +}; + +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; + + // spotlight (soft edges) + float theta = dot(lightDir, normalize(-light.direction)); + float epsilon = (light.cutOff - light.outerCutOff); + float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); + diffuse *= intensity; + specular *= intensity; + + // attenuation + float distance = length(light.position - FragPos); + float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + + vec3 result = ambient + diffuse + specular; + fragColor = vec4(result, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/5.4.light_casters_spot_soft/5.4.light_casters.vs b/src/2.lighting/5.4.light_casters_spot_soft/5.4.light_casters.vs new file mode 100644 index 0000000..0258c98 --- /dev/null +++ b/src/2.lighting/5.4.light_casters_spot_soft/5.4.light_casters.vs @@ -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.0f)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; + + gl_Position = projection * view * vec4(FragPos, 1.0f); +} \ No newline at end of file diff --git a/src/2.lighting/5.4.light_casters_spot_soft/light_casters_spot_soft.cpp b/src/2.lighting/5.4.light_casters_spot_soft/light_casters_spot_soft.cpp new file mode 100644 index 0000000..a52de33 --- /dev/null +++ b/src/2.lighting/5.4.light_casters_spot_soft/light_casters_spot_soft.cpp @@ -0,0 +1,365 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); +unsigned int loadTexture(const char *path); + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = 800.0f / 2.0; +float lastY = 600.0 / 2.0; +bool firstMouse = true; + +float deltaTime = 0.0f; // time between current frame and last frame +float lastFrame = 0.0f; + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); + glfwMakeContextCurrent(window); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile our shader zprogram + // ------------------------------------ + Shader lightingShader("5.4.light_casters.vs", "5.4.light_casters.fs"); + Shader lampShader("1.lamp.vs", "1.lamp.fs"); + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + float vertices[] = { + // positions // normals // texture coords + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + // positions all containers + glm::vec3 cubePositions[] = { + glm::vec3( 0.0f, 0.0f, 0.0f), + glm::vec3( 2.0f, 5.0f, -15.0f), + glm::vec3(-1.5f, -2.2f, -2.5f), + glm::vec3(-3.8f, -2.0f, -12.3f), + glm::vec3( 2.4f, -0.4f, -3.5f), + glm::vec3(-1.7f, 3.0f, -7.5f), + glm::vec3( 1.3f, -2.0f, -2.5f), + glm::vec3( 1.5f, 2.0f, -2.5f), + glm::vec3( 1.5f, 0.2f, -1.5f), + glm::vec3(-1.3f, 1.0f, -1.5f) + }; + // first, configure the cube's VAO (and VBO) + unsigned int VBO, cubeVAO; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &VBO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindVertexArray(cubeVAO); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glEnableVertexAttribArray(2); + + // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube) + unsigned int lightVAO; + glGenVertexArrays(1, &lightVAO); + glBindVertexArray(lightVAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + // note that we update the lamp's position attribute's stride to reflect the updated buffer data + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // load textures (we now use a utility function to keep the code more organized) + // ----------------------------------------------------------------------------- + unsigned int diffuseMap = loadTexture(FileSystem::getPath("resources/textures/container2.png").c_str()); + unsigned int specularMap = loadTexture(FileSystem::getPath("resources/textures/container2_specular.png").c_str()); + + // shader configuration + // -------------------- + lightingShader.use(); + lightingShader.setInt("material.diffuse", 0); + lightingShader.setInt("material.specular", 1); + + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // be sure to activate shader when setting uniforms/drawing objects + lightingShader.use(); + lightingShader.setVec3("light.position", camera.Position); + lightingShader.setVec3("light.direction", camera.Front); + lightingShader.setFloat("light.cutOff", glm::cos(glm::radians(12.5f))); + lightingShader.setFloat("light.outerCutOff", glm::cos(glm::radians(17.5f))); + lightingShader.setVec3("viewPos", camera.Position); + + // light properties + lightingShader.setVec3("light.ambient", 0.1f, 0.1f, 0.1f); + // we configure the diffuse intensity slightly higher; the right lighting conditions differ with each lighting method and environment. + // each environment and lighting type requires some tweaking to get the best out of your environment. + lightingShader.setVec3("light.diffuse", 0.8f, 0.8f, 0.8f); + lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f); + lightingShader.setFloat("light.constant", 1.0f); + lightingShader.setFloat("light.linear", 0.09f); + lightingShader.setFloat("light.quadratic", 0.032f); + + // material properties + lightingShader.setFloat("material.shininess", 32.0f); + + // view/projection transformations + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f); + glm::mat4 view = camera.GetViewMatrix(); + lightingShader.setMat4("projection", projection); + lightingShader.setMat4("view", view); + + // world transformation + glm::mat4 model; + lightingShader.setMat4("model", model); + + // bind diffuse map + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, diffuseMap); + // bind specular map + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, specularMap); + + // render containers + glBindVertexArray(cubeVAO); + for (unsigned int i = 0; i < 10; i++) + { + // calculate the model matrix for each object and pass it to shader before drawing + glm::mat4 model; + model = glm::translate(model, cubePositions[i]); + float angle = 20.0f * i; + model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f)); + lightingShader.setMat4("model", model); + + glDrawArrays(GL_TRIANGLES, 0, 36); + } + + + // again, a lamp object is weird when we only have a spot light, don't render the light object + // lampShader.use(); + // lampShader.setMat4("projection", projection); + // lampShader.setMat4("view", view); + // model = glm::mat4(); + // model = glm::translate(model, lightPos); + // model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube + // lampShader.setMat4("model", model); + + // glBindVertexArray(lightVAO); + // glDrawArrays(GL_TRIANGLES, 0, 36); + + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &cubeVAO); + glDeleteVertexArrays(1, &lightVAO); + glDeleteBuffers(1, &VBO); + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + float cameraSpeed = 2.5 * deltaTime; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +// utility function for loading a 2D texture from file +// --------------------------------------------------- +unsigned int loadTexture(char const * path) +{ + unsigned int textureID; + glGenTextures(1, &textureID); + + int width, height, nrComponents; + unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0); + if (data) + { + GLenum format; + if (nrComponents == 1) + format = GL_RED; + else if (nrComponents == 3) + format = GL_RGB; + else if (nrComponents == 4) + format = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + 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); + + stbi_image_free(data); + } + else + { + std::cout << "Texture failed to load at path: " << path << std::endl; + stbi_image_free(data); + } + + return textureID; +} \ No newline at end of file diff --git a/src/2.lighting/6.multiple_lights/6.multiple_lights.cpp b/src/2.lighting/6.multiple_lights/6.multiple_lights.cpp new file mode 100644 index 0000000..e898607 --- /dev/null +++ b/src/2.lighting/6.multiple_lights/6.multiple_lights.cpp @@ -0,0 +1,418 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void processInput(GLFWwindow *window); +unsigned int loadTexture(const char *path); + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); +float lastX = 800.0f / 2.0; +float lastY = 600.0 / 2.0; +bool firstMouse = true; + +float deltaTime = 0.0f; // time between current frame and last frame +float lastFrame = 0.0f; + +// lighting +glm::vec3 lightPos(1.2f, 1.0f, 2.0f); + +int main() +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); + glfwMakeContextCurrent(window); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // configure global opengl state + // ----------------------------- + glEnable(GL_DEPTH_TEST); + + // build and compile our shader zprogram + // ------------------------------------ + Shader lightingShader("6.multiple_lights.vs", "6.multiple_lights.fs"); + Shader lampShader("1.lamp.vs", "1.lamp.fs"); + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + float vertices[] = { + // positions // normals // texture coords + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + // positions all containers + glm::vec3 cubePositions[] = { + glm::vec3( 0.0f, 0.0f, 0.0f), + glm::vec3( 2.0f, 5.0f, -15.0f), + glm::vec3(-1.5f, -2.2f, -2.5f), + glm::vec3(-3.8f, -2.0f, -12.3f), + glm::vec3( 2.4f, -0.4f, -3.5f), + glm::vec3(-1.7f, 3.0f, -7.5f), + glm::vec3( 1.3f, -2.0f, -2.5f), + glm::vec3( 1.5f, 2.0f, -2.5f), + glm::vec3( 1.5f, 0.2f, -1.5f), + glm::vec3(-1.3f, 1.0f, -1.5f) + }; + // positions of the point lights + glm::vec3 pointLightPositions[] = { + glm::vec3( 0.7f, 0.2f, 2.0f), + glm::vec3( 2.3f, -3.3f, -4.0f), + glm::vec3(-4.0f, 2.0f, -12.0f), + glm::vec3( 0.0f, 0.0f, -3.0f) + }; + // first, configure the cube's VAO (and VBO) + unsigned int VBO, cubeVAO; + glGenVertexArrays(1, &cubeVAO); + glGenBuffers(1, &VBO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindVertexArray(cubeVAO); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glEnableVertexAttribArray(2); + + // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube) + unsigned int lightVAO; + glGenVertexArrays(1, &lightVAO); + glBindVertexArray(lightVAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + // note that we update the lamp's position attribute's stride to reflect the updated buffer data + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // load textures (we now use a utility function to keep the code more organized) + // ----------------------------------------------------------------------------- + unsigned int diffuseMap = loadTexture(FileSystem::getPath("resources/textures/container2.png").c_str()); + unsigned int specularMap = loadTexture(FileSystem::getPath("resources/textures/container2_specular.png").c_str()); + + // shader configuration + // -------------------- + lightingShader.use(); + lightingShader.setInt("material.diffuse", 0); + lightingShader.setInt("material.specular", 1); + + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // input + // ----- + processInput(window); + + // render + // ------ + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // be sure to activate shader when setting uniforms/drawing objects + lightingShader.use(); + lightingShader.setVec3("viewPos", camera.Position); + lightingShader.setFloat("material.shininess", 32.0f); + + /* + Here we set all the uniforms for the 5/6 types of lights we have. We have to set them manually and index + the proper PointLight struct in the array to set each uniform variable. This can be done more code-friendly + by defining light types as classes and set their values in there, or by using a more efficient uniform approach + by using 'Uniform buffer objects', but that is something we'll discuss in the 'Advanced GLSL' tutorial. + */ + // directional light + lightingShader.setVec3("dirLight.direction", -0.2f, -1.0f, -0.3f); + lightingShader.setVec3("dirLight.ambient", 0.05f, 0.05f, 0.05f); + lightingShader.setVec3("dirLight.diffuse", 0.4f, 0.4f, 0.4f); + lightingShader.setVec3("dirLight.specular", 0.5f, 0.5f, 0.5f); + // point light 1 + lightingShader.setVec3("pointLights[0].position", pointLightPositions[0]); + lightingShader.setVec3("pointLights[0].ambient", 0.05f, 0.05f, 0.05f); + lightingShader.setVec3("pointLights[0].diffuse", 0.8f, 0.8f, 0.8f); + lightingShader.setVec3("pointLights[0].specular", 1.0f, 1.0f, 1.0f); + lightingShader.setFloat("pointLights[0].constant", 1.0f); + lightingShader.setFloat("pointLights[0].linear", 0.09); + lightingShader.setFloat("pointLights[0].quadratic", 0.032); + // point light 2 + lightingShader.setVec3("pointLights[1].position", pointLightPositions[1]); + lightingShader.setVec3("pointLights[1].ambient", 0.05f, 0.05f, 0.05f); + lightingShader.setVec3("pointLights[1].diffuse", 0.8f, 0.8f, 0.8f); + lightingShader.setVec3("pointLights[1].specular", 1.0f, 1.0f, 1.0f); + lightingShader.setFloat("pointLights[1].constant", 1.0f); + lightingShader.setFloat("pointLights[1].linear", 0.09); + lightingShader.setFloat("pointLights[1].quadratic", 0.032); + // point light 3 + lightingShader.setVec3("pointLights[2].position", pointLightPositions[2]); + lightingShader.setVec3("pointLights[2].ambient", 0.05f, 0.05f, 0.05f); + lightingShader.setVec3("pointLights[2].diffuse", 0.8f, 0.8f, 0.8f); + lightingShader.setVec3("pointLights[2].specular", 1.0f, 1.0f, 1.0f); + lightingShader.setFloat("pointLights[2].constant", 1.0f); + lightingShader.setFloat("pointLights[2].linear", 0.09); + lightingShader.setFloat("pointLights[2].quadratic", 0.032); + // point light 4 + lightingShader.setVec3("pointLights[3].position", pointLightPositions[3]); + lightingShader.setVec3("pointLights[3].ambient", 0.05f, 0.05f, 0.05f); + lightingShader.setVec3("pointLights[3].diffuse", 0.8f, 0.8f, 0.8f); + lightingShader.setVec3("pointLights[3].specular", 1.0f, 1.0f, 1.0f); + lightingShader.setFloat("pointLights[3].constant", 1.0f); + lightingShader.setFloat("pointLights[3].linear", 0.09); + lightingShader.setFloat("pointLights[3].quadratic", 0.032); + // spotLight + lightingShader.setVec3("spotLight.position", camera.Position); + lightingShader.setVec3("spotLight.direction", camera.Front); + lightingShader.setVec3("spotLight.ambient", 0.0f, 0.0f, 0.0f); + lightingShader.setVec3("spotLight.diffuse", 1.0f, 1.0f, 1.0f); + lightingShader.setVec3("spotLight.specular", 1.0f, 1.0f, 1.0f); + lightingShader.setFloat("spotLight.constant", 1.0f); + lightingShader.setFloat("spotLight.linear", 0.09); + lightingShader.setFloat("spotLight.quadratic", 0.032); + lightingShader.setFloat("spotLight.cutOff", glm::cos(glm::radians(12.5f))); + lightingShader.setFloat("spotLight.outerCutOff", glm::cos(glm::radians(15.0f))); + + // view/projection transformations + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), 800.0f / 600.0f, 0.1f, 100.0f); + glm::mat4 view = camera.GetViewMatrix(); + lightingShader.setMat4("projection", projection); + lightingShader.setMat4("view", view); + + // world transformation + glm::mat4 model; + lightingShader.setMat4("model", model); + + // bind diffuse map + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, diffuseMap); + // bind specular map + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, specularMap); + + // render containers + glBindVertexArray(cubeVAO); + for (unsigned int i = 0; i < 10; i++) + { + // calculate the model matrix for each object and pass it to shader before drawing + glm::mat4 model; + model = glm::translate(model, cubePositions[i]); + float angle = 20.0f * i; + model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f)); + lightingShader.setMat4("model", model); + + glDrawArrays(GL_TRIANGLES, 0, 36); + } + + + // also draw the lamp object(s) + lampShader.use(); + lampShader.setMat4("projection", projection); + lampShader.setMat4("view", view); + + // we now draw as many light bulbs as we have point lights. + glBindVertexArray(lightVAO); + for (unsigned int i = 0; i < 4; i++) + { + model = glm::mat4(); + model = glm::translate(model, pointLightPositions[i]); + model = glm::scale(model, glm::vec3(0.2f)); // Make it a smaller cube + lampShader.setMat4("model", model); + glDrawArrays(GL_TRIANGLES, 0, 36); + } + + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &cubeVAO); + glDeleteVertexArrays(1, &lightVAO); + glDeleteBuffers(1, &VBO); + + // glfw: terminate, clearing all previously allocated GLFW resources. + // ------------------------------------------------------------------ + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void processInput(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + float cameraSpeed = 2.5 * deltaTime; + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +// utility function for loading a 2D texture from file +// --------------------------------------------------- +unsigned int loadTexture(char const * path) +{ + unsigned int textureID; + glGenTextures(1, &textureID); + + int width, height, nrComponents; + unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0); + if (data) + { + GLenum format; + if (nrComponents == 1) + format = GL_RED; + else if (nrComponents == 3) + format = GL_RGB; + else if (nrComponents == 4) + format = GL_RGBA; + + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + + 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); + + stbi_image_free(data); + } + else + { + std::cout << "Texture failed to load at path: " << path << std::endl; + stbi_image_free(data); + } + + return textureID; +} \ No newline at end of file diff --git a/src/2.lighting/6.multiple_lights/6.multiple_lights.fs b/src/2.lighting/6.multiple_lights/6.multiple_lights.fs new file mode 100644 index 0000000..3b2ab8c --- /dev/null +++ b/src/2.lighting/6.multiple_lights/6.multiple_lights.fs @@ -0,0 +1,147 @@ +#version 330 core +out vec4 fragColor; + +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; + +struct DirLight { + vec3 direction; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +struct PointLight { + vec3 position; + + float constant; + float linear; + float quadratic; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +struct SpotLight { + vec3 position; + vec3 direction; + float cutOff; + float outerCutOff; + + float constant; + float linear; + float quadratic; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +#define NR_POINT_LIGHTS 4 + +in vec3 FragPos; +in vec3 Normal; +in vec2 TexCoords; + +uniform vec3 viewPos; +uniform DirLight dirLight; +uniform PointLight pointLights[NR_POINT_LIGHTS]; +uniform SpotLight spotLight; +uniform Material material; + +// function prototypes +vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir); +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir); +vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir); + +void main() +{ + // properties + vec3 norm = normalize(Normal); + vec3 viewDir = normalize(viewPos - FragPos); + + // == ===================================================== + // Our lighting is set up in 3 phases: directional, point lights and an optional flashlight + // For each phase, a calculate function is defined that calculates the corresponding color + // per lamp. In the main() function we take all the calculated colors and sum them up for + // this fragment's final color. + // == ===================================================== + // phase 1: directional lighting + vec3 result = CalcDirLight(dirLight, norm, viewDir); + // phase 2: point lights + for(int i = 0; i < NR_POINT_LIGHTS; i++) + result += CalcPointLight(pointLights[i], norm, FragPos, viewDir); + // phase 3: spot light + result += CalcSpotLight(spotLight, norm, FragPos, viewDir); + + fragColor = vec4(result, 1.0); +} + +// calculates the color when using a directional light. +vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir) +{ + vec3 lightDir = normalize(-light.direction); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // combine results + vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); + vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)); + vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); + return (ambient + diffuse + specular); +} + +// calculates the color when using a point light. +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) +{ + vec3 lightDir = normalize(light.position - fragPos); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); + // combine results + vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); + vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)); + vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + return (ambient + diffuse + specular); +} + +// calculates the color when using a spot light. +vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir) +{ + vec3 lightDir = normalize(light.position - fragPos); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); + // spotlight intensity + float theta = dot(lightDir, normalize(-light.direction)); + float epsilon = light.cutOff - light.outerCutOff; + float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); + // combine results + vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); + vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)); + vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); + ambient *= attenuation * intensity; + diffuse *= attenuation * intensity; + specular *= attenuation * intensity; + return (ambient + diffuse + specular); +} \ No newline at end of file diff --git a/src/2.lighting/6.multiple_lights/6.multiple_lights.vs b/src/2.lighting/6.multiple_lights/6.multiple_lights.vs new file mode 100644 index 0000000..0258c98 --- /dev/null +++ b/src/2.lighting/6.multiple_lights/6.multiple_lights.vs @@ -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.0f)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; + + gl_Position = projection * view * vec4(FragPos, 1.0f); +} \ No newline at end of file