From 71aa55acb6301d91ce6c59cf68088b853c10a555 Mon Sep 17 00:00:00 2001 From: karl Date: Sun, 11 Apr 2021 19:19:50 +0200 Subject: [PATCH] Basic Bump Mapping works --- BumpMapDemo.cpp | 140 ++++++++++++++++++++++++++++++++++++++++++++++++ BumpMapDemo.h | 29 ++++++++++ QuadMesh.h | 0 Shader/bump.fs | 92 +++++++++++++++++++++++++++++++ Shader/bump.vs | 37 +++++++++++++ main.cpp | 3 +- 6 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 BumpMapDemo.cpp create mode 100644 BumpMapDemo.h create mode 100644 QuadMesh.h create mode 100644 Shader/bump.fs create mode 100644 Shader/bump.vs diff --git a/BumpMapDemo.cpp b/BumpMapDemo.cpp new file mode 100644 index 0000000..22991b2 --- /dev/null +++ b/BumpMapDemo.cpp @@ -0,0 +1,140 @@ +#include "BumpMapDemo.h" + +BumpMapDemo::BumpMapDemo() + : height_scale(0.2), render_shader(Shader("Shader/bump.vs", "Shader/bump.fs")), + camera(Camera(90, 1920, 1080, 0.1, 1000.0)), + albedo("Resources/Textures/PavingStones/PavingStones070_2K_Color.jpg", Texture::Settings()), + bump("Resources/Textures/PavingStones/PavingStones070_2K_Displacement.jpg", + Texture::Settings()), + normal("Resources/Textures/PavingStones/PavingStones070_2K_Normal.jpg", Texture::Settings()) { + // Move and rotate the camera so we see the quad well + camera.translate(glm::vec3(0.0, -1.0, 1.0)); + camera.rotate(30, glm::vec3(1.0, 0.0, 0.0)); +} + +// renders a 1x1 quad in NDC with manually calculated tangent vectors +// ------------------------------------------------------------------ +unsigned int quadVAO = 0; +unsigned int quadVBO; +void renderQuad() { + if (quadVAO == 0) { + // positions + glm::vec3 pos1(-1.0f, 1.0f, 0.0f); + glm::vec3 pos2(-1.0f, -1.0f, 0.0f); + glm::vec3 pos3(1.0f, -1.0f, 0.0f); + glm::vec3 pos4(1.0f, 1.0f, 0.0f); + // texture coordinates + glm::vec2 uv1(0.0f, 1.0f); + glm::vec2 uv2(0.0f, 0.0f); + glm::vec2 uv3(1.0f, 0.0f); + glm::vec2 uv4(1.0f, 1.0f); + // normal vector + glm::vec3 nm(0.0f, 0.0f, 1.0f); + + // 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; + + float 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); + + float 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}; + // configure 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(float), (void *)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), + (void *)(3 * sizeof(float))); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(float), + (void *)(6 * sizeof(float))); + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), + (void *)(8 * sizeof(float))); + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), + (void *)(11 * sizeof(float))); + } + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); +} + +void BumpMapDemo::render(float delta) { + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glm::mat4 projection = camera.get_projection(); + glm::mat4 view = camera.get_view(); + + render_shader.use(); + render_shader.setMat4("projection", projection); + render_shader.setMat4("view", view); + + glm::mat4 quad_model = glm::mat4(1.0f); + quad_model = glm::rotate( + quad_model, glm::radians((float)glfwGetTime() * 20.0f), + glm::normalize(glm::vec3( + 0.0, 0.0, 1.0))); // rotate the quad to show parallax mapping from multiple directions + render_shader.setMat4("model", quad_model); + render_shader.setVec3("viewPos", camera.get_translation()); + render_shader.setVec3("lightPos", glm::vec3(0.0, 1.0, 5.0)); + render_shader.setFloat("height_scale", height_scale); + albedo.bind_to(0); + normal.bind_to(1); + bump.bind_to(2); + renderQuad(); +} diff --git a/BumpMapDemo.h b/BumpMapDemo.h new file mode 100644 index 0000000..6d5f778 --- /dev/null +++ b/BumpMapDemo.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Camera.h" +#include "Framebuffer3D.h" +#include "Shader.h" +#include "Texture.h" +#include "VertexBuffer.h" + +class BumpMapDemo { + public: + BumpMapDemo(); + + void render(float delta); + + private: + unsigned int step_count; + unsigned int refinement_step_count; + float height_scale; + + Shader render_shader; + + VertexBuffer vertex_rectangle; + + Camera camera; + + Texture albedo; + Texture bump; + Texture normal; +}; \ No newline at end of file diff --git a/QuadMesh.h b/QuadMesh.h new file mode 100644 index 0000000..e69de29 diff --git a/Shader/bump.fs b/Shader/bump.fs new file mode 100644 index 0000000..243f59a --- /dev/null +++ b/Shader/bump.fs @@ -0,0 +1,92 @@ + + +#version 430 core +out vec4 FragColor; + +in VS_OUT { + vec3 FragPos; + vec2 TexCoords; + vec3 TangentLightPos; + vec3 TangentViewPos; + vec3 TangentFragPos; +} fs_in; + +layout (binding = 0) uniform sampler2D diffuseMap; +layout (binding = 1) uniform sampler2D normalMap; +layout (binding = 2) uniform sampler2D depthMap; + +uniform float height_scale; + +vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir) +{ + // number of depth layers + const float minLayers = 8; + const float maxLayers = 32; + float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir))); + // calculate the size of each layer + float layerDepth = 1.0 / numLayers; + // depth of current layer + float currentLayerDepth = 0.0; + // the amount to shift the texture coordinates per layer (from vector P) + vec2 P = viewDir.xy / viewDir.z * height_scale; + vec2 deltaTexCoords = P / numLayers; + + // get initial values + vec2 currentTexCoords = texCoords; + float currentDepthMapValue = 1.0 - texture(depthMap, currentTexCoords).r; + + while(currentLayerDepth < currentDepthMapValue) + { + // shift texture coordinates along direction of P + currentTexCoords -= deltaTexCoords; + // get depthmap value at current texture coordinates + currentDepthMapValue = 1.0 - texture(depthMap, currentTexCoords).r; + // get depth of next layer + currentLayerDepth += layerDepth; + } + + // get texture coordinates before collision (reverse operations) + vec2 prevTexCoords = currentTexCoords + deltaTexCoords; + + // get depth after and before collision for linear interpolation + float afterDepth = currentDepthMapValue - currentLayerDepth; + float beforeDepth = 1.0 - texture(depthMap, prevTexCoords).r - currentLayerDepth + layerDepth; + + // interpolation of texture coordinates + float weight = afterDepth / (afterDepth - beforeDepth); + vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); + + return finalTexCoords; +} + +void main() +{ + // offset texture coordinates with Parallax Mapping + vec3 viewDir = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos); + vec2 texCoords = fs_in.TexCoords; + + texCoords = ParallaxMapping(fs_in.TexCoords, viewDir); + if(texCoords.x > 1.0 || texCoords.y > 1.0 || texCoords.x < 0.0 || texCoords.y < 0.0) + discard; + + // obtain normal from normal map + vec3 normal = texture(normalMap, texCoords).rgb; + normal = normalize(normal * 2.0 - 1.0); + + // get diffuse color + vec3 color = texture(diffuseMap, texCoords).rgb; + // ambient + vec3 ambient = 0.1 * color; + // diffuse + vec3 lightDir = normalize(fs_in.TangentLightPos - fs_in.TangentFragPos); + float diff = max(dot(lightDir, normal), 0.0); + vec3 diffuse = diff * color; + // specular + vec3 reflectDir = reflect(-lightDir, normal); + vec3 halfwayDir = normalize(lightDir + viewDir); + float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0); + + vec3 specular = vec3(0.2) * spec; + FragColor = vec4(ambient + diffuse + specular, 1.0); +} + diff --git a/Shader/bump.vs b/Shader/bump.vs new file mode 100644 index 0000000..9e25901 --- /dev/null +++ b/Shader/bump.vs @@ -0,0 +1,37 @@ +#version 430 +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTexCoords; +layout (location = 3) in vec3 aTangent; +layout (location = 4) in vec3 aBitangent; + +out VS_OUT { + vec3 FragPos; + vec2 TexCoords; + vec3 TangentLightPos; + vec3 TangentViewPos; + vec3 TangentFragPos; +} vs_out; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; + +uniform vec3 lightPos; +uniform vec3 viewPos; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); + vs_out.FragPos = vec3(model * vec4(aPos, 1.0)); + vs_out.TexCoords = aTexCoords; + + vec3 T = normalize(mat3(model) * aTangent); + vec3 B = normalize(mat3(model) * aBitangent); + vec3 N = normalize(mat3(model) * aNormal); + mat3 TBN = transpose(mat3(T, B, N)); + + vs_out.TangentLightPos = TBN * lightPos; + vs_out.TangentViewPos = TBN * viewPos; + vs_out.TangentFragPos = TBN * vs_out.FragPos; +} diff --git a/main.cpp b/main.cpp index fa1de86..74c0145 100644 --- a/main.cpp +++ b/main.cpp @@ -5,6 +5,7 @@ #include #include +#include "BumpMapDemo.h" #include "Framebuffer3D.h" #include "Input.h" #include "MCRenderer.h" @@ -59,7 +60,7 @@ int main() { glEnable(GL_DEPTH_TEST); // Setup the Marching Cubes renderer - MCRenderer renderer = MCRenderer(128, 128, 128); + BumpMapDemo renderer; // render loop double timeInLastFrame = glfwGetTime();