I’m studying Shaders and FBOs and found some interesting article which shows
- Blur with Bloom
- Shadow
Now I’m trying to mix them and apply it to my project, but looks like it is not working properly.
When I apply Shadow map, it become too dark, tried to adjust light color but it is still dark.
[With Shadow mapping]
[Without Shadow mapping]
What I want is something similar to above (bright / warm) lighting but if I apply shadow.
I’m sharing my code here, what’s the problem with my source code?
Main
// Settings
const unsigned int SCR_WIDTH = 1920;
const unsigned int SCR_HEIGHT = 1080;
// Camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = (float)SCR_WIDTH / 2.0;
float lastY = (float)SCR_HEIGHT / 2.0;
bool firstMouse = true;
unsigned int amount = 10;
unsigned int pingpongFBO[2];
unsigned int pingpongColorBuffers[2];
unsigned int colorBuffers[2];
unsigned int rboDepth;
unsigned int hdrFBO;
unsigned int depthMapFBO;
unsigned int depthMap;
unsigned int attachments[2];
bool bloom = false;
bool bloomKeyPressed = false;
float exposure = 30.0f;
bool horizontal = true;
bool first_iteration = true;
unsigned int woodTexture, containerTexture, whiteTexture;
// Lighting position and color
lights.position = glm::vec3(2.0f, 4.0f, -1.0f);
lights.color = glm::vec3(0.5);
int main() {
// glfw: initialize and configure
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfw window creation
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
return -1;
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetKeyCallback(window, keyboard_callback);
// tell GLFW to capture our mouse
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// glad: load all OpenGL function pointers
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
return -1;
// configure global opengl state
glEnable(GL_DEPTH_TEST);
// build and compile shaders
shader = Shader("shaders/shadow_mapping.vs", "shaders/shadow_mapping.fs");
shader.use();
shader.setInt("diffuseTexture", 0);
shader.setInt("shadowMap", 1);
simpleDepthShader = Shader("shaders/shadow_mapping_depth.vs", "shaders/shadow_mapping_depth.fs");
debugDepthQuad = Shader("shaders/debug_quad_depth.vs", "shaders/debug_quad_depth.fs");
debugDepthQuad.use();
debugDepthQuad.setInt("depthMap", 0);
sLighting = Shader("shaders/shadow_mapping.vs", "shaders/light.fs");
sBlur = Shader("shaders/blur.vs", "shaders/blur.fs");
sBlur.use();
sBlur.setInt("image", 0);
sBloom = Shader("shaders/bloom.vs", "shaders/bloom.fs");
sBloom.use();
sBloom.setInt("scene", 0);
sBloom.setInt("blur", 1);
// Set up vertex data (and buffer(s)) and configure vertex attributes
float planeVertices[] = {
// positions // normals // texcoords
25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 0.0f,
-25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 25.0f,
25.0f, -0.5f, 25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 0.0f,
-25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 0.0f, 25.0f,
25.0f, -0.5f, -25.0f, 0.0f, 1.0f, 0.0f, 25.0f, 25.0f
};
// Plane VAO
unsigned int planeVBO;
glGenVertexArrays(1, &planeVAO);
glGenBuffers(1, &planeVBO);
glBindVertexArray(planeVAO);
glBindBuffer(GL_ARRAY_BUFFER, planeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), planeVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glBindVertexArray(0);
// Load textures
woodTexture = eventManager.textureManager.LoadTexture("resources/textures/wood.png", true);
containerTexture = eventManager.textureManager.LoadTexture("resources/textures/container2.png", true);
// Configure depth map FBO
const unsigned int SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024;
unsigned int depthMapFBO;
glGenFramebuffers(1, &depthMapFBO);
// Create depth texture
unsigned int depthMap;
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float borderColor[] = { 1.0, 1.0, 1.0, 1.0 };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
// Attach depth texture as FBO's depth buffer
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Configure (floating point) framebuffers
if (hdrFBO == 0)
glGenFramebuffers(1, &hdrFBO);
glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO);
// Create 2 floating point color buffers (1 for normal rendering, other for brightness threshold values)
if (colorBuffers[0] == 0)
glGenTextures(2, colorBuffers);
for (unsigned int i = 0; i < 2; i++) {
glBindTexture(GL_TEXTURE_2D, colorBuffers[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Clamp to the edge as the blur filter would otherwise sample repeated texture values!
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Attach texture to framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, colorBuffers[i], 0);
}
// Create and attach depth buffer (render buffer)
if (rboDepth == 0)
glGenRenderbuffers(1, &rboDepth);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth);
// Tell OpenGL which color attachments of this framebuffer will be used for rendering
if (attachments[0] == 0) {
attachments[0] = GL_COLOR_ATTACHMENT0;
attachments[1] = GL_COLOR_ATTACHMENT1;
}
glDrawBuffers(2, attachments);
// Finally check if framebuffer is complete
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "Framebuffer not complete!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Ping-pong-framebuffer for blurring
if (pingpongFBO[0] == 0)
glGenFramebuffers(2, pingpongFBO);
if (pingpongColorBuffers[0] == 0)
glGenTextures(2, pingpongColorBuffers);
for (unsigned int i = 0; i < 2; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[i]);
glBindTexture(GL_TEXTURE_2D, pingpongColorBuffers[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// We clamp to the edge as the blur filter would otherwise sample repeated texture values!
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpongColorBuffers[i], 0);
// Also check if framebuffers are complete (no need for depth buffer)
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "Framebuffer not complete!" << std::endl;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Render loop
IsApplicationActive = true;
while (!glfwWindowShouldClose(window)) {
glm::mat4 lightProjection, lightView;
glm::mat4 lightSpaceMatrix;
float near_plane = 1.0f, far_plane = 7.5f;
// Render
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render depth of scene to texture (from light's perspective)
lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);
lightView = glm::lookAt(lights.position, glm::vec3(0.0f), glm::vec3(0.0, 1.0, 0.0));
lightSpaceMatrix = lightProjection * lightView;
// Show the light source as bright cubes
sLighting.use();
sLighting.setMat4("projection", lightProjection);
sLighting.setMat4("view", lightView);
// Render scene from light's point of view
simpleDepthShader.use();
simpleDepthShader.setMat4("lightSpaceMatrix", lightSpaceMatrix);
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glClear(GL_DEPTH_BUFFER_BIT);
renderScene(simpleDepthShader, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Render scene into floating point framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Blur bright fragments with two-pass Gaussian Blur
sBlur.use();
for (unsigned int i = 0; i < amount; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[horizontal]);
sBlur.setInt("horizontal", horizontal);
// Bind texture of other framebuffer (or scene if first iteration)
glBindTexture(GL_TEXTURE_2D, first_iteration ? colorBuffers[1] : pingpongColorBuffers[!horizontal]);
renderQuad();
horizontal = !horizontal;
if (first_iteration)
first_iteration = false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Render floating point color buffer to 2D quad and tonemap HDR colors to default framebuffer's (clamped) color range
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
sBloom.use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, colorBuffers[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, pingpongColorBuffers[!horizontal]);
sBloom.setInt("bloom", bloom);
sBloom.setFloat("exposure", exposure);
renderQuad();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Reset viewport
glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render scene as normal using the generated depth/shadow map
glm::mat4 projection, model, view;
glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.use();
projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
view = camera.GetViewMatrix();
shader.setMat4("projection", projection);
shader.setMat4("view", view);
// set light uniforms
shader.setBool("withLight", true);
shader.setVec3("customAmbient", vec3(0.0));
shader.setVec3("viewPos", camera.Position);
shader.setVec3("lights.Position", lights.position);
shader.setVec3("lights.Color", lights.color);
shader.setMat4("lightSpaceMatrix", lightSpaceMatrix);
renderScene(shader, depthMap);
// Render Depth map to quad for visual debugging
debugDepthQuad.use();
debugDepthQuad.setFloat("near_plane", near_plane);
debugDepthQuad.setFloat("far_plane", far_plane);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, depthMap);
// glfw: swap buffers and poll IO events
glfwSwapBuffers(window);
glfwPollEvents();
}
}
Shadow_Mapping.vs
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
out VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
vec4 FragPosLightSpace;
} vs_out;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform mat4 lightSpaceMatrix;
void main() {
vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
vs_out.TexCoords = aTexCoords;
mat3 normalMatrix = transpose(inverse(mat3(model)));
vs_out.Normal = normalize(normalMatrix * aNormal);
for (int i = 0; i < 4; i++)
vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0);
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
Shadow_Mapping.fs
#version 450 core
layout (location = 0) out vec4 FragColor;
layout (location = 1) out vec4 BrightColor;
out vec4 FragColor;
in VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
vec4 FragPosLightSpace;
} fs_in;
struct Light {
vec3 Position;
vec3 Color;
};
uniform Light lights;
uniform sampler2D diffuseTexture;
uniform vec3 viewPos;
uniform sampler2D shadowMap;
float ShadowCalculation(vec4 fragPosLightSpace) {
// Perform perspective divide
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
// Transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
// Get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
float closestDepth = texture(shadowMap, projCoords.xy).r;
// Get depth of current fragment from light's perspective
float currentDepth = projCoords.z;
// Calculate bias (based on depth map resolution and slope)
vec3 normal = normalize(fs_in.Normal);
vec3 lightDir = normalize(lights.Position - fs_in.FragPos);
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
// Check whether current frag pos is in shadow
// float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
// PCF
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(shadowMap, 0);
for(int x = -1; x <= 1; ++x) {
for(int y = -1; y <= 1; ++y) {
float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;
// Keep the shadow at 0.0 when outside the far_plane region of the light's frustum.
if (projCoords.z > 1.0)
shadow = 0.0;
return shadow;
}
void main() {
vec3 ambient;
vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb;
vec3 normal = normalize(fs_in.Normal);
// Ambient
ambient = vec3(0.3) * color;
// Diffuse
vec3 lightDir = normalize(lights.Position - fs_in.FragPos);
float diff = max(dot(lightDir, normal), 0.0);
vec3 diffuse = diff * lights.Color;
// Specular
vec3 viewDir = normalize(viewPos - fs_in.FragPos);
vec3 reflectDir = reflect(-lightDir, normal);
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0);
vec3 specular = spec * lights.Color * customAmbient;
// Calculate shadow
float shadow = ShadowCalculation(fs_in.FragPosLightSpace);
vec3 lighting = (ambient + (3.0 - shadow) * (diffuse + specular)) * color;
// Attenuation (use quadratic as we have gamma correction)
float distance = length(fs_in.FragPos - lights.Position);
diffuse *= 1.0 / (distance * distance);
lighting += diffuse;
// Check whether diffuse is higher than some threshold, if so, output as threshold color
float brightness = dot(lighting, vec3(0.2126, 0.7152, 0.0722));
if (brightness > 1.0)
BrightColor = vec4(lighting, 1.0);
else
BrightColor = vec4(0.0, 0.0, 0.0, 1.0);
FragColor = vec4(vec3(0.015, 0.015, 0.015), 1.0);
}
Shadow_Mapping_Depth.vs
#version 450 core
layout (location = 0) in vec3 aPos;
uniform mat4 lightSpaceMatrix;
uniform mat4 model;
void main() {
gl_Position = lightSpaceMatrix * model * vec4(aPos, 1.0);
}
lighting.fs
#version 450 core
layout (location = 0) out vec4 FragColor;
layout (location = 1) out vec4 BrightColor;
in VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
vec4 FragPosLightSpace;
} fs_in;
uniform vec3 lightColor;
void main() {
FragColor = vec4(lightColor, 1.0);
float brightness = dot(FragColor.rgb, vec3(0.2126, 0.7152, 0.0722));
if (brightness > 1.0)
BrightColor = vec4(FragColor.rgb, 1.0);
else
BrightColor = vec4(0.0, 0.0, 0.0, 1.0);
}
blur.vs
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main() {
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}
blur.fs
#version 450 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D image;
uniform bool horizontal;
uniform float weight[5] = float[] (0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162);
void main() {
// Get size of single texel
vec2 tex_offset = 1.0 / textureSize(image, 0);
vec3 result = texture(image, TexCoords).rgb * weight[0];
if (horizontal) {
for(int i = 1; i < 5; ++i) {
result += texture(image, TexCoords + vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
result += texture(image, TexCoords - vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
}
}
else {
for (int i = 1; i < 5; ++i) {
result += texture(image, TexCoords + vec2(0.0, tex_offset.y * i)).rgb * weight[i];
result += texture(image, TexCoords - vec2(0.0, tex_offset.y * i)).rgb * weight[i];
}
}
FragColor = vec4(result, 1.0);
}
bloom.vs
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main() {
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}
bloom.fs
#version 450 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D scene;
uniform sampler2D blur;
uniform bool bloom;
uniform float exposure;
void main() {
const float gamma = 2.2;
vec3 hdrColor = texture(scene, TexCoords).rgb;
vec3 bloomColor = texture(blur, TexCoords).rgb;
// Additive blending
if (bloom)
hdrColor += bloomColor;
// Tone mapping
vec3 result = vec3(1.0) - exp(-hdrColor * exposure);
// Also gamma correct while we're at it
result = pow(result, vec3(1.0 / gamma));
FragColor = vec4(result, 1.0);
}