From 0a46f53608e6dbd369eb1635946cb2f5f020e37c Mon Sep 17 00:00:00 2001 From: Joey de Vries Date: Mon, 12 Dec 2016 21:10:58 +0100 Subject: [PATCH] Template PBR lighting tut with folder re-structure to fit PBR tuts. --- CMakeLists.txt | 12 +- .../8.deferred_shading/fbo_debug.frag | 11 + .../8.deferred_shading/fbo_debug.vs | 13 + src/6.pbr/1.1.lighting/lighting.cpp | 473 +++++++++++++++++ src/6.pbr/1.1.lighting/pbr.frag | 150 ++++++ src/6.pbr/1.1.lighting/pbr.vs | 21 + .../lighting_textured.cpp | 487 ++++++++++++++++++ src/6.pbr/1.2.lighting_textured/pbr.frag | 157 ++++++ src/6.pbr/1.2.lighting_textured/pbr.vs | 21 + .../1.debugging/debugging.cpp | 0 .../1.debugging/debugging.frag | 0 .../1.debugging/debugging.vs | 0 12 files changed, 1343 insertions(+), 2 deletions(-) create mode 100644 src/5.advanced_lighting/8.deferred_shading/fbo_debug.frag create mode 100644 src/5.advanced_lighting/8.deferred_shading/fbo_debug.vs create mode 100644 src/6.pbr/1.1.lighting/lighting.cpp create mode 100644 src/6.pbr/1.1.lighting/pbr.frag create mode 100644 src/6.pbr/1.1.lighting/pbr.vs create mode 100644 src/6.pbr/1.2.lighting_textured/lighting_textured.cpp create mode 100644 src/6.pbr/1.2.lighting_textured/pbr.frag create mode 100644 src/6.pbr/1.2.lighting_textured/pbr.vs rename src/{6.in_practice => 7.in_practice}/1.debugging/debugging.cpp (100%) rename src/{6.in_practice => 7.in_practice}/1.debugging/debugging.frag (100%) rename src/{6.in_practice => 7.in_practice}/1.debugging/debugging.vs (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 249a608..dd52e4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,8 @@ set(CHAPTERS 3.model_loading 4.advanced_opengl 5.advanced_lighting - 6.in_practice + 6.pbr + 7.in_practice ) set(1.getting_started @@ -111,7 +112,14 @@ set(5.advanced_lighting 9.ssao ) -set(6.in_practice +set(6.pbr + 1.1.lighting + 1.2.lighting_textured + # 2.1.ibl_irradiance + # 2.2.ibl_specular +) + +set(7.in_practice 1.debugging # 2.text_rendering ) diff --git a/src/5.advanced_lighting/8.deferred_shading/fbo_debug.frag b/src/5.advanced_lighting/8.deferred_shading/fbo_debug.frag new file mode 100644 index 0000000..57e6271 --- /dev/null +++ b/src/5.advanced_lighting/8.deferred_shading/fbo_debug.frag @@ -0,0 +1,11 @@ +// fragment shader +#version 330 core +out vec4 FragColor; +in vec2 TexCoords; + +uniform sampler2D fboAttachment; + +void main() +{ + FragColor = texture(fboAttachment, TexCoords); +} \ No newline at end of file diff --git a/src/5.advanced_lighting/8.deferred_shading/fbo_debug.vs b/src/5.advanced_lighting/8.deferred_shading/fbo_debug.vs new file mode 100644 index 0000000..8e8046b --- /dev/null +++ b/src/5.advanced_lighting/8.deferred_shading/fbo_debug.vs @@ -0,0 +1,13 @@ +// vertex shader +#version 330 core +layout (location = 0) in vec2 position; +layout (location = 1) in vec2 texCoords; + +out vec2 TexCoords; + +void main() +{ + gl_Position = vec4(position, 0.0f, 1.0f); + TexCoords = texCoords; +} + \ No newline at end of file diff --git a/src/6.pbr/1.1.lighting/lighting.cpp b/src/6.pbr/1.1.lighting/lighting.cpp new file mode 100644 index 0000000..13f9002 --- /dev/null +++ b/src/6.pbr/1.1.lighting/lighting.cpp @@ -0,0 +1,473 @@ +// Std. Includes +#include + +// GLEW +#define GLEW_STATIC +#include + +// GLFW +#include + +// GL includes +#include +#include +#include + +// GLM Mathemtics +#include +#include +#include + +// Other Libs +#include +#include + +// Properties +const GLuint SCR_WIDTH = 1280, SCR_HEIGHT = 720; + +// Function prototypes +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void Do_Movement(); +GLuint loadTexture(GLchar const * path); +void RenderQuad(); +void renderSphere(); + + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); + +// timing +GLfloat deltaTime = 0.0f; +GLfloat lastFrame = 0.0f; + + +// The MAIN function, from here we start our application and run our Game loop +int main() +{ + // Init GLFW + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_SAMPLES, 32); + glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); + + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed + glfwMakeContextCurrent(window); + + // Set the required callback functions + glfwSetKeyCallback(window, key_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // Options + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // Initialize GLEW to setup the OpenGL Function pointers + glewExperimental = GL_TRUE; + glewInit(); + + // Define the viewport dimensions + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); + + // Setup some OpenGL options + glEnable(GL_DEPTH_TEST); + + // Setup and compile our shaders + Shader shader("pbr.vs", "pbr.frag"); + + // set (constant) material properties + shader.Use(); + glUniform3f(glGetUniformLocation(shader.Program, "albedo"), 0.5f, 0.0f, 0.0f); + glUniform1f(glGetUniformLocation(shader.Program, "ao"), 1.0f); + + // projection setup + glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); + + // lights + glm::vec3 lightPositions[] = { + glm::vec3(-10.0f, 10.0f, 10.0f), + glm::vec3( 10.0f, 10.0f, 10.0f), + glm::vec3(-10.0f, -10.0f, 10.0f), + glm::vec3( 10.0f, -10.0f, 10.0f), + }; + glm::vec3 lightColors[] = { + glm::vec3(5.0f, 5.0f, 5.0f), + glm::vec3(5.0f, 5.0f, 5.0f), + glm::vec3(5.0f, 5.0f, 5.0f), + glm::vec3(5.0f, 5.0f, 5.0f) + }; + int nrRows = 7; + int nrColumns = 7; + float spacing = 2.5; + + + // Game loop + while (!glfwWindowShouldClose(window)) + { + // set frame time + GLfloat currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // check and call events + glfwPollEvents(); + Do_Movement(); + + // clear the colorbuffer + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // configure view matrix + shader.Use(); + glm::mat4 view = camera.GetViewMatrix(); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); + + // setup relevant shader uniforms + glUniform3fv(glGetUniformLocation(shader.Program, "camPos"), 1, &camera.Position[0]); + glUniform1f(glGetUniformLocation(shader.Program, "exposure"), 1.0f); + + // render rows*column number of spheres with varying metallic/roughness values scaled by rows and columns respectively + glm::mat4 model; + for (int row = 0; row < nrRows; ++row) + { + glUniform1f(glGetUniformLocation(shader.Program, "metallic"), (float)row / (float)nrRows); + for (int col = 0; col < nrColumns; ++col) + { + // we clamp the roughness to 0.025 - 1.0 as perfectly smooth surfaces (roughness of 0.0) tend to look a bit off + // on direct lighting. + glUniform1f(glGetUniformLocation(shader.Program, "roughness"), glm::clamp((float)col / (float)nrColumns, 0.05f, 1.0f)); + + model = glm::mat4(); + model = glm::translate(model, glm::vec3( + (float)(col - (nrColumns / 2)) * spacing, + (float)(row - (nrRows / 2)) * spacing, + 0.0f + )); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + renderSphere(); + } + } + + // render light source (simply re-render sphere at light positions) + // this looks a bit off as we use the same shader, but it'll make their positions obvious and + // keeps the codeprint small. + + for (unsigned int i = 0; i < sizeof(lightPositions) / sizeof(lightPositions[0]); ++i) + //for (unsigned int i = 0; i < 1; ++i) + { + glUniform3fv(glGetUniformLocation(shader.Program, ("lightPositions[" + std::to_string(i) + "]").c_str()), 1, &lightPositions[i][0]); + glUniform3fv(glGetUniformLocation(shader.Program, ("lightColors[" + std::to_string(i) + "]").c_str()), 1, &lightColors[i][0]); + + model = glm::mat4(); + model = glm::translate(model, lightPositions[i]); + model = glm::scale(model, glm::vec3(0.5f)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + renderSphere(); + } + + // Swap the buffers + glfwSwapBuffers(window); + } + + glfwTerminate(); + return 0; +} + +unsigned int sphereVAO = 0; +unsigned int indexCount; +void renderSphere() +{ + if (sphereVAO == 0) + { + glGenVertexArrays(1, &sphereVAO); + + unsigned int vbo, ebo; + glGenBuffers(1, &vbo); + glGenBuffers(1, &ebo); + + std::vector positions; + std::vector uv; + std::vector normals; + std::vector tangents; + std::vector bitangents; + std::vector indices; + + const unsigned int X_SEGMENTS = 64; + const unsigned int Y_SEGMENTS = 64; + const float PI = 3.14159265359; + for (unsigned int y = 0; y <= Y_SEGMENTS; ++y) + { + for (unsigned int x = 0; x <= X_SEGMENTS; ++x) + { + float xSegment = (float)x / (float)X_SEGMENTS; + float ySegment = (float)y / (float)Y_SEGMENTS; + float xPos = std::cos(xSegment * 2.0f * PI) * std::sin(ySegment * PI); // NOTE(Joey): TAU is 2PI + float yPos = std::cos(ySegment * PI); + float zPos = std::sin(xSegment * 2.0f * PI) * std::sin(ySegment * PI); + + positions.push_back(glm::vec3(xPos, yPos, zPos)); + uv.push_back(glm::vec2(xSegment, ySegment)); + normals.push_back(glm::vec3(xPos, yPos, zPos)); + } + } + + bool oddRow = false; + for (int y = 0; y < Y_SEGMENTS; ++y) + { + if (!oddRow) // NOTE(Joey): even rows: y == 0, y == 2; and so on + { + for (int x = 0; x <= X_SEGMENTS; ++x) + { + indices.push_back(y * (X_SEGMENTS + 1) + x); + indices.push_back((y + 1) * (X_SEGMENTS + 1) + x); + } + } + else + { + for (int x = X_SEGMENTS; x >= 0; --x) + { + indices.push_back((y + 1) * (X_SEGMENTS + 1) + x); + indices.push_back(y * (X_SEGMENTS + 1) + x); + } + } + oddRow = !oddRow; + } + indexCount = indices.size(); + std::vector data; + for (int i = 0; i < positions.size(); ++i) + { + data.push_back(positions[i].x); + data.push_back(positions[i].y); + data.push_back(positions[i].z); + if (uv.size() > 0) + { + data.push_back(uv[i].x); + data.push_back(uv[i].y); + } + if (normals.size() > 0) + { + data.push_back(normals[i].x); + data.push_back(normals[i].y); + data.push_back(normals[i].z); + } + if (tangents.size() > 0) + { + data.push_back(tangents[i].x); + data.push_back(tangents[i].y); + data.push_back(tangents[i].z); + } + if (bitangents.size() > 0) + { + data.push_back(bitangents[i].x); + data.push_back(bitangents[i].y); + data.push_back(bitangents[i].z); + } + } + glBindVertexArray(sphereVAO); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(float), &data[0], GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); + //float stride = (3 + 2 + 3 + 3 + 3) * sizeof(float); + float stride = (3 + 2 + 3) * sizeof(float); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(3 * sizeof(float))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(5 * sizeof(float))); + //glEnableVertexAttribArray(3); + /* glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(8 * sizeof(float))); + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(11 * sizeof(float)));*/ + } + + glBindVertexArray(sphereVAO); + glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_INT, 0); +} + +// RenderQuad() Renders a 1x1 quad in NDC +GLuint quadVAO = 0; +GLuint quadVBO; +void RenderQuad() +{ + if (quadVAO == 0) + { + // positions + glm::vec3 pos1(-1.0, 1.0, 0.0); + glm::vec3 pos2(-1.0, -1.0, 0.0); + glm::vec3 pos3(1.0, -1.0, 0.0); + glm::vec3 pos4(1.0, 1.0, 0.0); + // texture coordinates + glm::vec2 uv1(0.0, 1.0); + glm::vec2 uv2(0.0, 0.0); + glm::vec2 uv3(1.0, 0.0); + glm::vec2 uv4(1.0, 1.0); + // normal vector + glm::vec3 nm(0.0, 0.0, 1.0); + + // calculate tangent/bitangent vectors of both triangles + glm::vec3 tangent1, bitangent1; + glm::vec3 tangent2, bitangent2; + // - triangle 1 + glm::vec3 edge1 = pos2 - pos1; + glm::vec3 edge2 = pos3 - pos1; + glm::vec2 deltaUV1 = uv2 - uv1; + glm::vec2 deltaUV2 = uv3 - uv1; + + GLfloat f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y); + + tangent1.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x); + tangent1.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y); + tangent1.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z); + tangent1 = glm::normalize(tangent1); + + bitangent1.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x); + bitangent1.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y); + bitangent1.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z); + bitangent1 = glm::normalize(bitangent1); + + // - triangle 2 + edge1 = pos3 - pos1; + edge2 = pos4 - pos1; + deltaUV1 = uv3 - uv1; + deltaUV2 = uv4 - uv1; + + f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y); + + tangent2.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x); + tangent2.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y); + tangent2.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z); + tangent2 = glm::normalize(tangent2); + + + bitangent2.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x); + bitangent2.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y); + bitangent2.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z); + bitangent2 = glm::normalize(bitangent2); + + + GLfloat quadVertices[] = { + // Positions // normal // TexCoords // Tangent // Bitangent + pos1.x, pos1.y, pos1.z, nm.x, nm.y, nm.z, uv1.x, uv1.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, + pos2.x, pos2.y, pos2.z, nm.x, nm.y, nm.z, uv2.x, uv2.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, + pos3.x, pos3.y, pos3.z, nm.x, nm.y, nm.z, uv3.x, uv3.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, + + pos1.x, pos1.y, pos1.z, nm.x, nm.y, nm.z, uv1.x, uv1.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z, + pos3.x, pos3.y, pos3.z, nm.x, nm.y, nm.z, uv3.x, uv3.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z, + pos4.x, pos4.y, pos4.z, nm.x, nm.y, nm.z, uv4.x, uv4.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z + }; + // Setup plane VAO + glGenVertexArrays(1, &quadVAO); + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat))); + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(8 * sizeof(GLfloat))); + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(11 * sizeof(GLfloat))); + } + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); +} + +// This function loads a texture from file. Note: texture loading functions like these are usually +// managed by a 'Resource Manager' that manages all resources (like textures, models, audio). +// For learning purposes we'll just define it as a utility function. +GLuint loadTexture(GLchar const * path) +{ + //Generate texture ID and load texture data + GLuint textureID; + glGenTextures(1, &textureID); + int width, height; + unsigned char* image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGB); + // Assign texture to ID + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); + glGenerateMipmap(GL_TEXTURE_2D); + + // Parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + SOIL_free_image_data(image); + return textureID; +} + +#pragma region "User input" + +bool keys[1024]; +bool keysPressed[1024]; +// Moves/alters the camera positions based on user input +void Do_Movement() +{ + // Camera controls + if (keys[GLFW_KEY_W]) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (keys[GLFW_KEY_S]) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (keys[GLFW_KEY_A]) + camera.ProcessKeyboard(LEFT, deltaTime); + if (keys[GLFW_KEY_D]) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// Is called whenever a key is pressed/released via GLFW +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GL_TRUE); + + if (key >= 0 && key <= 1024) + { + if (action == GLFW_PRESS) + keys[key] = true; + else if (action == GLFW_RELEASE) + { + keys[key] = false; + keysPressed[key] = false; + } + } +} + +GLfloat lastX = 400, lastY = 300; +bool firstMouse = true; +// Moves/alters the camera positions based on user input +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + GLfloat xoffset = xpos - lastX; + GLfloat yoffset = lastY - ypos; + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +#pragma endregion \ No newline at end of file diff --git a/src/6.pbr/1.1.lighting/pbr.frag b/src/6.pbr/1.1.lighting/pbr.frag new file mode 100644 index 0000000..176ef50 --- /dev/null +++ b/src/6.pbr/1.1.lighting/pbr.frag @@ -0,0 +1,150 @@ +#version 330 core +out vec4 FragColor; +in vec2 TexCoords; +in vec3 WorldPos; +in vec3 Normal; + +// material parameters +uniform vec3 albedo; +uniform float metallic; +uniform float roughness; +uniform float ao; + +// lights +uniform vec3 lightPositions[4]; +uniform vec3 lightColors[4]; + +uniform vec3 camPos; +uniform float exposure; + +const float PI = 3.14159265359; + +vec3 getNormal(vec3 worldNormal, vec3 tangentNormal) +{ + vec3 Q1 = dFdx(WorldPos); + vec3 Q2 = dFdy(WorldPos); + vec2 st1 = dFdx(TexCoords); + vec2 st2 = dFdy(TexCoords); + + vec3 normal = normalize(worldNormal); + vec3 tangent = normalize(Q1*st2.t - Q2*st1.t); + vec3 binormal = -normalize(cross(normal, tangent)); + mat3 TBN = mat3(tangent, binormal, normal); + + return normalize(TBN * tangentNormal); +} + +float DistributionGGX(vec3 N, vec3 H, float roughness) +{ + float a = roughness*roughness; + float a2 = a*a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH*NdotH; + + float nom = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return nom / denom; +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return nom / denom; +} + +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} + +vec3 fresnelSchlick(float cosTheta, vec3 F0) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} +// ---------------------------------------------------------------------------- +vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) +{ + return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); +} + +void main() +{ + vec3 N = normalize(Normal); + vec3 V = normalize(camPos - WorldPos); + vec3 R = reflect(-V, N); + + // NOTE(Joey): calculate color/reflectance at normal incidence + // NOTE(Joey): if dia-electric (like plastic) use F0 as 0.04 and + // if it's a metal, use their albedo color as F0 (metallic workflow) + vec3 F0 = vec3(0.04); // NOTE(Joey): base reflectance at incident angle for non-metallic (dia-conductor) surfaces + F0 = mix(F0, albedo, metallic); + // NOTE(Joey): calculate reflectance w/ (modified for roughness) Fresnel + vec3 F = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); + + // NOTE(Joey): kS is equal to Fresnel + vec3 kS = F; + // NOTE(Joey): for energy conservation, the diffuse and specular light can't + // be above 1.0 (unless the surface emits light) so to preserve this + // relationship the diffuse component (kD) equals 1.0 - kS. + vec3 kD = vec3(1.0) - kS; + // multiply kD by the inverse metalness such that only non-metals + // have diffuse lighting, or a linear blend if partly metal (pure metals have + // no diffuse light). + kD *= 1.0 - metallic; + + // first do ambient lighting (note that the next IBL tutorial will replace the ambient + // lighting with environment lighting). + vec3 ambient = vec3(0.01) * albedo * ao; + + // for every light, calculate their contribution to the reflectance equation + vec3 Lo = vec3(0.0); + for(int i = 0; i < 4; ++i) + { + vec3 L = normalize(lightPositions[i] - WorldPos); + vec3 H = normalize(V + L); + float distance = length(lightPositions[i] - WorldPos); + float attenuation = 1.0 / distance * distance; + vec3 radiance = lightColors[i] * attenuation; + + // NDF + float ndf = DistributionGGX(N, H, roughness); + + // Geometry + float g = GeometrySmith(N, V, L, roughness); + + // cook-torrance brdf + vec3 nominator = ndf * g * F; + float denominator = 4 * max(dot(V, N), 0.0) * max(dot(L, N), 0.0) + 0.001; // 0.001 to prevent divide by zero. + vec3 brdf = nominator / denominator; + + // NOTE(Joey): scale light by NdotL + float NdotL = max(dot(N, L), 0.0); + + // NOTE(Joey): reflectance equation + + Lo += (kD * albedo / PI + kS * brdf) * radiance * NdotL; + } + + vec3 color = ambient + Lo; + + // NOTE(Joey): HDR tonemapping + // color = vec3(1.0) - exp(-color * exposure); + color = color / (color + vec3(1.0)); + // NOTE(Joey): gamma correct + color = pow(color, vec3(1.0/2.2)); + + + FragColor = vec4(color, 1.0); +} diff --git a/src/6.pbr/1.1.lighting/pbr.vs b/src/6.pbr/1.1.lighting/pbr.vs new file mode 100644 index 0000000..2ef028a --- /dev/null +++ b/src/6.pbr/1.1.lighting/pbr.vs @@ -0,0 +1,21 @@ +#version 330 core +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoords; +layout (location = 2) in vec3 normal; + +out vec2 TexCoords; +out vec3 WorldPos; +out vec3 Normal; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +void main() +{ + TexCoords = texCoords; + WorldPos = vec3(model * vec4(pos, 1.0f)); + Normal = mat3(model) * normal; + + gl_Position = projection * view * vec4(WorldPos, 1.0); +} \ No newline at end of file diff --git a/src/6.pbr/1.2.lighting_textured/lighting_textured.cpp b/src/6.pbr/1.2.lighting_textured/lighting_textured.cpp new file mode 100644 index 0000000..15212e6 --- /dev/null +++ b/src/6.pbr/1.2.lighting_textured/lighting_textured.cpp @@ -0,0 +1,487 @@ +// Std. Includes +#include + +// GLEW +#define GLEW_STATIC +#include + +// GLFW +#include + +// GL includes +#include +#include +#include + +// GLM Mathemtics +#include +#include +#include + +// Other Libs +#include +#include + +// Properties +const GLuint SCR_WIDTH = 1280, SCR_HEIGHT = 720; + +// Function prototypes +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void Do_Movement(); +GLuint loadTexture(GLchar const * path); +void RenderQuad(); +void renderSphere(); + + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); + +// timing +GLfloat deltaTime = 0.0f; +GLfloat lastFrame = 0.0f; + + +// The MAIN function, from here we start our application and run our Game loop +int main() +{ + // Init GLFW + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_SAMPLES, 32); + glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); + + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", nullptr, nullptr); // Windowed + glfwMakeContextCurrent(window); + + // Set the required callback functions + glfwSetKeyCallback(window, key_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // Options + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // Initialize GLEW to setup the OpenGL Function pointers + glewExperimental = GL_TRUE; + glewInit(); + + // Define the viewport dimensions + glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); + + // Setup some OpenGL options + glEnable(GL_DEPTH_TEST); + + // load material textures + GLuint albedo = loadTexture(FileSystem::getPath("resources/textures/pbr/rusted_iron/albedo.png").c_str()); + GLuint normal = loadTexture(FileSystem::getPath("resources/textures/pbr/rusted_iron/normal.png").c_str()); + GLuint metallic = loadTexture(FileSystem::getPath("resources/textures/pbr/rusted_iron/metallic.png").c_str()); + GLuint roughness = loadTexture(FileSystem::getPath("resources/textures/pbr/rusted_iron/roughness.png").c_str()); + GLuint ao = loadTexture(FileSystem::getPath("resources/textures/pbr/rusted_iron/ao.png").c_str()); + + // Setup and compile our shaders + Shader shader("pbr.vs", "pbr.frag"); + + // set material texture uniforms + shader.Use(); + glUniform1i(glGetUniformLocation(shader.Program, "albedo"), 0); + glUniform1i(glGetUniformLocation(shader.Program, "normal"), 1); + glUniform1i(glGetUniformLocation(shader.Program, "metallic"), 2); + glUniform1i(glGetUniformLocation(shader.Program, "roughness"), 3); + glUniform1i(glGetUniformLocation(shader.Program, "ao"), 4); + + // projection setup + glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); + + // lights + glm::vec3 lightPositions[] = { + glm::vec3(-10.0f, 10.0f, 10.0f), + glm::vec3( 10.0f, 10.0f, 10.0f), + glm::vec3(-10.0f, -10.0f, 10.0f), + glm::vec3( 10.0f, -10.0f, 10.0f), + }; + glm::vec3 lightColors[] = { + glm::vec3(2.0f, 2.0f, 2.0f), + glm::vec3(2.0f, 2.0f, 2.0f), + glm::vec3(2.0f, 2.0f, 2.0f), + glm::vec3(2.0f, 2.0f, 2.0f) + }; + int nrRows = 7; + int nrColumns = 7; + float spacing = 2.5; + + + // Game loop + while (!glfwWindowShouldClose(window)) + { + // set frame time + GLfloat currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // check and call events + glfwPollEvents(); + Do_Movement(); + + // clear the colorbuffer + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // configure view matrix + shader.Use(); + glm::mat4 view = camera.GetViewMatrix(); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view)); + + // setup relevant shader uniforms + glUniform3fv(glGetUniformLocation(shader.Program, "camPos"), 1, &camera.Position[0]); + + // set material + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, albedo); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, normal); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, metallic); + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, roughness); + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_2D, ao); + + // render rows*column number of spheres with varying metallic/roughness values scaled by rows and columns respectively + glm::mat4 model; + for (int row = 0; row < nrRows; ++row) + { + for (int col = 0; col < nrColumns; ++col) + { + model = glm::mat4(); + model = glm::translate(model, glm::vec3( + (float)(col - (nrColumns / 2)) * spacing, + (float)(row - (nrRows / 2)) * spacing, + 0.0f + )); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + renderSphere(); + } + } + + // render light source (simply re-render sphere at light positions) + // this looks a bit off as we use the same shader, but it'll make their positions obvious and + // keeps the codeprint small. + for (unsigned int i = 0; i < sizeof(lightPositions) / sizeof(lightPositions[0]); ++i) + { + glUniform3fv(glGetUniformLocation(shader.Program, ("lightPositions[" + std::to_string(i) + "]").c_str()), 1, &lightPositions[i][0]); + glUniform3fv(glGetUniformLocation(shader.Program, ("lightColors[" + std::to_string(i) + "]").c_str()), 1, &lightColors[i][0]); + + model = glm::mat4(); + model = glm::translate(model, lightPositions[i]); + model = glm::scale(model, glm::vec3(0.5f)); + glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model)); + renderSphere(); + } + + // Swap the buffers + glfwSwapBuffers(window); + } + + glfwTerminate(); + return 0; +} + +unsigned int sphereVAO = 0; +unsigned int indexCount; +void renderSphere() +{ + if (sphereVAO == 0) + { + glGenVertexArrays(1, &sphereVAO); + + unsigned int vbo, ebo; + glGenBuffers(1, &vbo); + glGenBuffers(1, &ebo); + + std::vector positions; + std::vector uv; + std::vector normals; + std::vector tangents; + std::vector bitangents; + std::vector indices; + + const unsigned int X_SEGMENTS = 64; + const unsigned int Y_SEGMENTS = 64; + const float PI = 3.14159265359; + for (unsigned int y = 0; y <= Y_SEGMENTS; ++y) + { + for (unsigned int x = 0; x <= X_SEGMENTS; ++x) + { + float xSegment = (float)x / (float)X_SEGMENTS; + float ySegment = (float)y / (float)Y_SEGMENTS; + float xPos = std::cos(xSegment * 2.0f * PI) * std::sin(ySegment * PI); // NOTE(Joey): TAU is 2PI + float yPos = std::cos(ySegment * PI); + float zPos = std::sin(xSegment * 2.0f * PI) * std::sin(ySegment * PI); + + positions.push_back(glm::vec3(xPos, yPos, zPos)); + uv.push_back(glm::vec2(xSegment, ySegment)); + normals.push_back(glm::vec3(xPos, yPos, zPos)); + } + } + + bool oddRow = false; + for (int y = 0; y < Y_SEGMENTS; ++y) + { + if (!oddRow) // NOTE(Joey): even rows: y == 0, y == 2; and so on + { + for (int x = 0; x <= X_SEGMENTS; ++x) + { + indices.push_back(y * (X_SEGMENTS + 1) + x); + indices.push_back((y + 1) * (X_SEGMENTS + 1) + x); + } + } + else + { + for (int x = X_SEGMENTS; x >= 0; --x) + { + indices.push_back((y + 1) * (X_SEGMENTS + 1) + x); + indices.push_back(y * (X_SEGMENTS + 1) + x); + } + } + oddRow = !oddRow; + } + indexCount = indices.size(); + std::vector data; + for (int i = 0; i < positions.size(); ++i) + { + data.push_back(positions[i].x); + data.push_back(positions[i].y); + data.push_back(positions[i].z); + if (uv.size() > 0) + { + data.push_back(uv[i].x); + data.push_back(uv[i].y); + } + if (normals.size() > 0) + { + data.push_back(normals[i].x); + data.push_back(normals[i].y); + data.push_back(normals[i].z); + } + if (tangents.size() > 0) + { + data.push_back(tangents[i].x); + data.push_back(tangents[i].y); + data.push_back(tangents[i].z); + } + if (bitangents.size() > 0) + { + data.push_back(bitangents[i].x); + data.push_back(bitangents[i].y); + data.push_back(bitangents[i].z); + } + } + glBindVertexArray(sphereVAO); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(float), &data[0], GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); + //float stride = (3 + 2 + 3 + 3 + 3) * sizeof(float); + float stride = (3 + 2 + 3) * sizeof(float); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(3 * sizeof(float))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(5 * sizeof(float))); + //glEnableVertexAttribArray(3); + /* glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(8 * sizeof(float))); + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(11 * sizeof(float)));*/ + } + + glBindVertexArray(sphereVAO); + glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_INT, 0); +} + +// RenderQuad() Renders a 1x1 quad in NDC +GLuint quadVAO = 0; +GLuint quadVBO; +void RenderQuad() +{ + if (quadVAO == 0) + { + // positions + glm::vec3 pos1(-1.0, 1.0, 0.0); + glm::vec3 pos2(-1.0, -1.0, 0.0); + glm::vec3 pos3(1.0, -1.0, 0.0); + glm::vec3 pos4(1.0, 1.0, 0.0); + // texture coordinates + glm::vec2 uv1(0.0, 1.0); + glm::vec2 uv2(0.0, 0.0); + glm::vec2 uv3(1.0, 0.0); + glm::vec2 uv4(1.0, 1.0); + // normal vector + glm::vec3 nm(0.0, 0.0, 1.0); + + // calculate tangent/bitangent vectors of both triangles + glm::vec3 tangent1, bitangent1; + glm::vec3 tangent2, bitangent2; + // - triangle 1 + glm::vec3 edge1 = pos2 - pos1; + glm::vec3 edge2 = pos3 - pos1; + glm::vec2 deltaUV1 = uv2 - uv1; + glm::vec2 deltaUV2 = uv3 - uv1; + + GLfloat f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y); + + tangent1.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x); + tangent1.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y); + tangent1.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z); + tangent1 = glm::normalize(tangent1); + + bitangent1.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x); + bitangent1.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y); + bitangent1.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z); + bitangent1 = glm::normalize(bitangent1); + + // - triangle 2 + edge1 = pos3 - pos1; + edge2 = pos4 - pos1; + deltaUV1 = uv3 - uv1; + deltaUV2 = uv4 - uv1; + + f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y); + + tangent2.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x); + tangent2.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y); + tangent2.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z); + tangent2 = glm::normalize(tangent2); + + + bitangent2.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x); + bitangent2.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y); + bitangent2.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z); + bitangent2 = glm::normalize(bitangent2); + + + GLfloat quadVertices[] = { + // Positions // normal // TexCoords // Tangent // Bitangent + pos1.x, pos1.y, pos1.z, nm.x, nm.y, nm.z, uv1.x, uv1.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, + pos2.x, pos2.y, pos2.z, nm.x, nm.y, nm.z, uv2.x, uv2.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, + pos3.x, pos3.y, pos3.z, nm.x, nm.y, nm.z, uv3.x, uv3.y, tangent1.x, tangent1.y, tangent1.z, bitangent1.x, bitangent1.y, bitangent1.z, + + pos1.x, pos1.y, pos1.z, nm.x, nm.y, nm.z, uv1.x, uv1.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z, + pos3.x, pos3.y, pos3.z, nm.x, nm.y, nm.z, uv3.x, uv3.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z, + pos4.x, pos4.y, pos4.z, nm.x, nm.y, nm.z, uv4.x, uv4.y, tangent2.x, tangent2.y, tangent2.z, bitangent2.x, bitangent2.y, bitangent2.z + }; + // Setup plane VAO + glGenVertexArrays(1, &quadVAO); + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat))); + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(8 * sizeof(GLfloat))); + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(GLfloat), (GLvoid*)(11 * sizeof(GLfloat))); + } + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); +} + +// This function loads a texture from file. Note: texture loading functions like these are usually +// managed by a 'Resource Manager' that manages all resources (like textures, models, audio). +// For learning purposes we'll just define it as a utility function. +GLuint loadTexture(GLchar const * path) +{ + //Generate texture ID and load texture data + GLuint textureID; + glGenTextures(1, &textureID); + int width, height; + unsigned char* image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGB); + // Assign texture to ID + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); + glGenerateMipmap(GL_TEXTURE_2D); + + // Parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + SOIL_free_image_data(image); + return textureID; +} + +#pragma region "User input" + +bool keys[1024]; +bool keysPressed[1024]; +// Moves/alters the camera positions based on user input +void Do_Movement() +{ + // Camera controls + if (keys[GLFW_KEY_W]) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (keys[GLFW_KEY_S]) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (keys[GLFW_KEY_A]) + camera.ProcessKeyboard(LEFT, deltaTime); + if (keys[GLFW_KEY_D]) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// Is called whenever a key is pressed/released via GLFW +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GL_TRUE); + + if (key >= 0 && key <= 1024) + { + if (action == GLFW_PRESS) + keys[key] = true; + else if (action == GLFW_RELEASE) + { + keys[key] = false; + keysPressed[key] = false; + } + } +} + +GLfloat lastX = 400, lastY = 300; +bool firstMouse = true; +// Moves/alters the camera positions based on user input +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + GLfloat xoffset = xpos - lastX; + GLfloat yoffset = lastY - ypos; + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +#pragma endregion \ No newline at end of file diff --git a/src/6.pbr/1.2.lighting_textured/pbr.frag b/src/6.pbr/1.2.lighting_textured/pbr.frag new file mode 100644 index 0000000..e548ac6 --- /dev/null +++ b/src/6.pbr/1.2.lighting_textured/pbr.frag @@ -0,0 +1,157 @@ +#version 330 core +out vec4 FragColor; +in vec2 TexCoords; +in vec3 WorldPos; +in vec3 Normal; + +// material parameters +uniform sampler2D albedo; +uniform sampler2D normal; +uniform sampler2D metallic; +uniform sampler2D roughness; +uniform sampler2D ao; + +// lights +uniform vec3 lightPositions[4]; +uniform vec3 lightColors[4]; + +uniform vec3 camPos; +uniform float exposure; + +const float PI = 3.14159265359; + +vec3 getNormal(vec3 worldNormal, vec3 tangentNormal) +{ + vec3 Q1 = dFdx(WorldPos); + vec3 Q2 = dFdy(WorldPos); + vec2 st1 = dFdx(TexCoords); + vec2 st2 = dFdy(TexCoords); + + vec3 normal = normalize(worldNormal); + vec3 tangent = normalize(Q1*st2.t - Q2*st1.t); + vec3 binormal = -normalize(cross(normal, tangent)); + mat3 TBN = mat3(tangent, binormal, normal); + + return normalize(TBN * tangentNormal); +} + +float DistributionGGX(vec3 N, vec3 H, float roughness) +{ + float a = roughness*roughness; + float a2 = a*a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH*NdotH; + + float nom = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return nom / denom; +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return nom / denom; +} + +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} + +vec3 fresnelSchlick(float cosTheta, vec3 F0) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} +// ---------------------------------------------------------------------------- +vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) +{ + return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); +} + +void main() +{ + vec3 albedo = pow(texture(albedo, TexCoords).rgb, vec3(2.2)); + // vec3 normal = getWorldNormalFromTangentSpace(texture(normal, TexCoords).rgb); + float metallic = texture(metallic, TexCoords).r; + float roughness = texture(roughness, TexCoords).r; + float ao = texture(ao, TexCoords).r; + + vec3 N = normalize(Normal); + vec3 V = normalize(camPos - WorldPos); + vec3 R = reflect(-V, N); + + // NOTE(Joey): calculate color/reflectance at normal incidence + // NOTE(Joey): if dia-electric (like plastic) use F0 as 0.04 and + // if it's a metal, use their albedo color as F0 (metallic workflow) + vec3 F0 = vec3(0.04); // NOTE(Joey): base reflectance at incident angle for non-metallic (dia-conductor) surfaces + F0 = mix(F0, albedo, metallic); + // NOTE(Joey): calculate reflectance w/ (modified for roughness) Fresnel + vec3 F = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); + + // NOTE(Joey): kS is equal to Fresnel + vec3 kS = F; + // NOTE(Joey): for energy conservation, the diffuse and specular light can't + // be above 1.0 (unless the surface emits light) so to preserve this + // relationship the diffuse component (kD) equals 1.0 - kS. + vec3 kD = vec3(1.0) - kS; + // multiply kD by the inverse metalness such that only non-metals + // have diffuse lighting, or a linear blend if partly metal (pure metals have + // no diffuse light). + kD *= 1.0 - metallic; + + // first do ambient lighting (note that the next IBL tutorial will replace the ambient + // lighting with environment lighting). + vec3 ambient = vec3(0.01) * albedo * ao; + + // for every light, calculate their contribution to the reflectance equation + vec3 Lo = vec3(0.0); + for(int i = 0; i < 4; ++i) + { + vec3 L = normalize(lightPositions[i] - WorldPos); + vec3 H = normalize(V + L); + float distance = length(lightPositions[i] - WorldPos); + float attenuation = 1.0 / distance * distance; + vec3 radiance = lightColors[i] * attenuation; + + // NDF + float ndf = DistributionGGX(N, H, roughness); + + // Geometry + float g = GeometrySmith(N, V, L, roughness); + + // cook-torrance brdf + vec3 nominator = ndf * g * F; + float denominator = 4 * max(dot(V, N), 0.0) * max(dot(L, N), 0.0) + 0.001; // 0.001 to prevent divide by zero. + vec3 brdf = nominator / denominator; + + // NOTE(Joey): scale light by NdotL + float NdotL = max(dot(N, L), 0.0); + + // NOTE(Joey): reflectance equation + + Lo += (kD * albedo / PI + kS * brdf) * radiance * NdotL; + } + + vec3 color = ambient + Lo; + + // NOTE(Joey): HDR tonemapping + // color = vec3(1.0) - exp(-color * exposure); + color = color / (color + vec3(1.0)); + // NOTE(Joey): gamma correct + color = pow(color, vec3(1.0/2.2)); + + + FragColor = vec4(color, 0.0, 1.0); +} diff --git a/src/6.pbr/1.2.lighting_textured/pbr.vs b/src/6.pbr/1.2.lighting_textured/pbr.vs new file mode 100644 index 0000000..2ef028a --- /dev/null +++ b/src/6.pbr/1.2.lighting_textured/pbr.vs @@ -0,0 +1,21 @@ +#version 330 core +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoords; +layout (location = 2) in vec3 normal; + +out vec2 TexCoords; +out vec3 WorldPos; +out vec3 Normal; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +void main() +{ + TexCoords = texCoords; + WorldPos = vec3(model * vec4(pos, 1.0f)); + Normal = mat3(model) * normal; + + gl_Position = projection * view * vec4(WorldPos, 1.0); +} \ No newline at end of file diff --git a/src/6.in_practice/1.debugging/debugging.cpp b/src/7.in_practice/1.debugging/debugging.cpp similarity index 100% rename from src/6.in_practice/1.debugging/debugging.cpp rename to src/7.in_practice/1.debugging/debugging.cpp diff --git a/src/6.in_practice/1.debugging/debugging.frag b/src/7.in_practice/1.debugging/debugging.frag similarity index 100% rename from src/6.in_practice/1.debugging/debugging.frag rename to src/7.in_practice/1.debugging/debugging.frag diff --git a/src/6.in_practice/1.debugging/debugging.vs b/src/7.in_practice/1.debugging/debugging.vs similarity index 100% rename from src/6.in_practice/1.debugging/debugging.vs rename to src/7.in_practice/1.debugging/debugging.vs