I was wondering how I would go about programming point light shadows with deferred rendering??
The point light shadows just dont show up for me. I think it is to do with the following line: float shadow = calculate_shadows(FragPos); as for directional shadows I multiple the fragpos with a lightSpaceMatrix (lightView * lightProj) and that worked, but for point shadows I dont have a lightSpaceMatrix to use.
light fragment shader
#version 420 core
out vec4 FragColor;
in vec2 _texcoord;
uniform vec3 camera_pos;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedo;
uniform sampler2D gSpecular;
uniform sampler2D gMetalness;
uniform samplerCube gPointShadowmap;
uniform mat4 viewMatrix;
uniform vec3 lightPos;
vec3 FragPos;
vec3 Normal;
float calculate_shadows(vec3 light_space_pos)
{
// perform perspective divide
vec3 fragToLight = light_space_pos - vec3(0.0f, 0.0f, 0.0f);
// get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
float closestDepth = texture(gPointShadowmap, fragToLight).r;
// it is currently in linear range between [0,1], let's re-transform it back to original depth value
closestDepth *= 25.0f;
// now get current linear depth as the length between the fragment and light position
float currentDepth = length(fragToLight);
// test for shadows
float bias = 0.05; // we use a much larger bias since depth is now in [near_plane, far_plane] range
float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
//FragColor = vec4(vec3(closestDepth / 25.0f), 1.0);
return shadow;
}
void main(void)
{
FragPos = texture(gPosition, _texcoord).rgb;
Normal = texture(gNormal, _texcoord).rgb;
vec3 Diffuse = texture(gAlbedo, _texcoord).rgb;
float Emissive = texture(gAlbedo, _texcoord).a;
vec3 Specular = texture(gAlbedo, _texcoord).rgb;
vec3 Metalness = texture(gMetalness, _texcoord).rgb; // Reflection pass
float AmbientOcclusion = texture(gSsao, _texcoord).r;
vec3 lightColor = vec3(0.3);
// ambient
vec3 ambient = 0.3 * Diffuse;
// diffuse
vec3 lightDir = normalize(vec3(0.0, 0.0, 0.0) - FragPos);
float diff = max(dot(lightDir, Normal), 0.0);
vec3 diffuse = diff * lightColor;
// specular
vec3 viewDir = normalize(camera_pos - FragPos);
vec3 reflectDir = reflect(-lightDir, Normal);
float spec = 0.0;
vec3 halfwayDir = normalize(lightDir + viewDir);
spec = pow(max(dot(Normal, halfwayDir), 0.0), 64.0);
vec3 specular = spec * lightColor;
// calculate shadow
float shadow = calculate_shadows(FragPos);
vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular));
FragColor = vec4(lighting, 1.0);
}
pointshadows vertex shader
#version 330 core
layout(location = 0) in vec3 position;
uniform mat4 model;
void main(void)
{
gl_Position = model * vec4(position, 1.0);
}
pointshadows fragment shader
#version 330 core
in vec4 FragPos;
void main(void)
{
float lightDistance = length(FragPos.xyz - vec3(0.0, 3.0, 0.0));
// map to [0;1] range by dividing by far_plane
lightDistance = lightDistance / 25.0;
// write this as modified depth
gl_FragDepth = lightDistance;
}
pointshadows geometry shader
#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 18) out;
uniform mat4 shadowMatrices[6];
out vec4 FragPos;
void main(void)
{
for(int face = 0; face < 6; ++face)
{
gl_Layer = face; // built-in variable that specifies to which face we render.
for(int i = 0; i < 3; ++i) // for each triangle's vertices
{
FragPos = gl_in[i].gl_Position;
gl_Position = shadowMatrices[face] * FragPos;
EmitVertex();
}
EndPrimitive();
}
}
Temp PointShadow class
#ifndef __POINTSHADOWPASS
#define __POINTSHADOWPASS
#include "Content.h"
class PointShadowPass
{
private:
static unsigned int _shadow_fbo;
public:
static unsigned int _shadow_texture;
static glm::vec3 lightPos;
static std::vector<glm::mat4> shadowTransforms;
PointShadowPass() {}
~PointShadowPass() {}
inline static void Initialise()
{
lightPos = glm::vec3(0.0f, 0.0f, 0.0f);
glGenFramebuffers(1, &_shadow_fbo);
glGenTextures(1, &_shadow_texture);
glBindTexture(GL_TEXTURE_2D, _shadow_texture);
for (unsigned int i = 0; i < 6; i++)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glBindFramebuffer(GL_FRAMEBUFFER, _shadow_fbo);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, _shadow_texture, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
inline static void Render(unsigned int pointshadow_program, Camera* camera, std::vector<Actor*> _actors)
{
glDisable(GL_BLEND); // Disable blending for opique materials
glEnable(GL_DEPTH_TEST); // Enable depth test
glm::mat4 model;
glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), (float)1024 / (float)1024, 1.0f, 25.0f);
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)));
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)));
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
glViewport(0, 0, 1024, 1024);
glBindFramebuffer(GL_FRAMEBUFFER, _shadow_fbo);
glClear(GL_DEPTH_BUFFER_BIT);
glUseProgram(pointshadow_program);
for (unsigned int i = 0; i < 6; ++i)
glUniformMatrix4fv(glGetUniformLocation(pointshadow_program, ("shadowMatrices[" + std::to_string(i) + "]").c_str()), 1, GL_FALSE, glm::value_ptr(shadowTransforms[i]));
for (unsigned int i = 0; i < _actors.size(); i++)
{
model = _actors[i]->GetModelMatrix() * camera->GetViewMatrix();
glUniformMatrix4fv(glGetUniformLocation(pointshadow_program, "model"), 1, GL_FALSE, glm::value_ptr(model)); // set the model matrix uniform
_actors[i]->Render();
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
};
std::vector<glm::mat4> PointShadowPass::shadowTransforms;
unsigned int PointShadowPass::_shadow_fbo;
unsigned int PointShadowPass::_shadow_texture;
glm::vec3 PointShadowPass::lightPos;
#endif
I managed to get something showing (shadows move with camera rotation)
Reading your comments it seems you have some misconceptions about what informations you can have in defered rendering?
You said that all the coordinates have to be in screenspace which isn't true. For deffered rendering you have a G-Buffer and in it you can put whatever kind of information you want. To get world position information you have two choices, either you have a world position buffer, so you know where each of your fragment is in the world. Or you can compute this information back from the depth buffer and camera projection matrix.
If you have a point shadow calculation that works in forward rendering you can do the same in deferred rendering, in the shader that does all the light calculation you need the shadow cubemap, light position and you do the calculation like you used to.
EDIT:
looking at your code for calculate_shadows(vec3 light_space_pos)
, in deferred rendering you don't send it your position in lightspace, but the position in world space. So the function should be:
calculate_shadows(vec3 frag_world_pos)
you have for the first line vec3 fragToLight = light_space_pos - vec3(0.0f, 0.0f, 0.0f);
which should be vec3 fragToLight = frag_world_pos- lightPos
.
Or you do this calculation before using the function. Eitherway, you need the position of your point light to calculate the direction and distance between your fragment and the light.