I work on C++ project for Android with OpenGL ES3, so I try to implement the shadow map with directional light, I understand the theory well but I never get it successfully rendered. first I create the framebuffer which contains the depth map:
glGenFramebuffers(1, &depthMapFBO);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
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, 0);
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_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
glDrawBuffers(1, GL_NONE);
glReadBuffer(GL_NONE);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
then I create a shader program that compiles the depth shader which is the following:
#version 300 es
precision mediump float;
layout (location = 0) in vec3 position;
layout (location = 4) in ivec4 BoneIDs;
layout (location = 5) in vec4 Weights;
const int MAX_BONES = 100;
uniform mat4 lightSpaceMatrix;
uniform mat4 model;
uniform bool skinned;
uniform mat4 gBones[MAX_BONES];
void main(){
vec4 nPos;
if(skinned){
mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
BoneTransform += gBones[BoneIDs[1]] * Weights[1];
BoneTransform += gBones[BoneIDs[2]] * Weights[2];
BoneTransform += gBones[BoneIDs[3]] * Weights[3];
nPos=BoneTransform * vec4(position, 1.0);
}
else
nPos = vec4(position, 1.0);
vec4 p=model * nPos;
gl_Position = lightSpaceMatrix * p;
}
and draw the scene using this shader program with the light space matrix using the following:
glCullFace(GL_FRONT);
double delta = GetCurrentTime() - firstFrame;
glm::mat4 camInv = glm::inverse(camera->getViewMatrix());
glm::mat4 lightSpaceProjection = glm::ortho(-40.0f, 40.0f, -40.0f, 40.0f, -1.0f, 100.0f);
glm::mat4 lightSpaceView = glm::lookAt(sun->direction, glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));
lightSpaceMatrix = lightSpaceProjection * (lightSpaceView*camInv) ;
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glClear(GL_DEPTH_BUFFER_BIT);
directDepthShader.use();
glUniformMatrix4fv(glGetUniformLocation(directDepthShader.getProgramID(), "lightSpaceMatrix"), 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix));
for (mesh_it it = castShadowMeshes.begin(); it != castShadowMeshes.end(); it++) {
it->get()->renderDepth(directDepthShader, delta);
}
glCullFace(GL_BACK);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
finally I render the scene with the regular shader program and bind the depth map to the shadowMap
uniform with the following code:
glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
phongShader.use();
if (sun != nullptr)
if (sun->castShadow)
glUniformMatrix4fv(glGetUniformLocation(phongShader.getProgramID(), "lightSpaceMatrix"), 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix));
this->setLightsUniforms(phongShader);
this->setViewUniforms(phongShader);
for (mesh_it it = phongMeshes.begin(); it != phongMeshes.end(); it++) {
if (it->get()->hasNormalMap) {
glUniform1i(glGetUniformLocation(phongShader.getProgramID(), "has_normal_map"), 1);
if (directlights.size() > 0) {
for (dlight_it it = this->directlights.begin(); it != this->directlights.end(); ++it) {
GLuint directLightPosLoc = glGetUniformLocation(phongShader.getProgramID(), (const GLchar*) ("directLightPos[" + ToString((*it)->index) + "]").c_str());
glUniform3f(directLightPosLoc, (*it)->direction.x, (*it)->direction.y, (*it)->direction.z);
}
}
if (pointlights.size() > 0) {
for (plight_it it = this->pointlights.begin(); it != this->pointlights.end(); ++it) {
GLuint pointLightPosLoc = glGetUniformLocation(phongShader.getProgramID(), (const GLchar*) ("pointLightPos[" + ToString((*it)->index) + "]").c_str());
glUniform3f(pointLightPosLoc, (*it)->position.x, (*it)->position.y, (*it)->position.z);
}
}
if (spotlights.size() > 0) {
for (slight_it it = this->spotlights.begin(); it != this->spotlights.end(); ++it) {
GLuint spotLightPosLoc = glGetUniformLocation(phongShader.getProgramID(), (const GLchar*) ("spotLightPos[" + ToString((*it)->index) + "]").c_str());
glUniform3f(spotLightPosLoc, (*it)->position.x, (*it)->position.y, (*it)->position.z);
}
}
}
double first = GetCurrentTime() - firstFrame;
it->get()->textures = 0;
if (sun != nullptr)
if (sun->castShadow) {
glUniform1i(glGetUniformLocation(phongShader.getProgramID(), "shadowMap"), it->get()->textures);
glActiveTexture(GL_TEXTURE0 + it->get()->textures);
glBindTexture(GL_TEXTURE_2D, depthMap);
it->get()->textures++;
}
it->get()->Render(phongShader, first, deltaTime);
glBindTexture(GL_TEXTURE_2D, 0);
}
finally the shader vertex and fragment are the following:
Vertex:
#version 300 es
precision mediump float;
#define NR_DIRECT_LIGHTS 0
#define NR_POINT_LIGHTS 0
#define NR_SPOT_LIGHTS 0
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texCoord;
layout (location = 3) in vec3 tangent;
layout (location = 4) in ivec4 BoneIDs;
layout (location = 5) in vec4 Weights;
const int MAX_BONES = 100;
out vec2 TexCoords;
out vec3 Normal;
out vec3 tDirectLightPos[NR_DIRECT_LIGHTS];
out vec3 tPointLightPos[NR_POINT_LIGHTS];
out vec3 tSpotLightPos[NR_SPOT_LIGHTS];
out vec3 tViewPos;
out vec3 tFragPos;
out vec4 FragPosLightSpace;
// conditions //
uniform bool has_normal_map;
uniform bool skinned;
//
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform vec3 viewPos;
uniform mat4 lightSpaceMatrix;
uniform mat4 gBones[MAX_BONES];
uniform vec3 directLightPos[NR_DIRECT_LIGHTS];
uniform vec3 pointLightPos[NR_POINT_LIGHTS];
uniform vec3 spotLightPos[NR_SPOT_LIGHTS];
void main(){
TexCoords = texCoord;
vec4 nPos;
vec3 N=transpose(inverse(mat3(model))) * normal;
if(skinned){
mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
BoneTransform += gBones[BoneIDs[1]] * Weights[1];
BoneTransform += gBones[BoneIDs[2]] * Weights[2];
BoneTransform += gBones[BoneIDs[3]] * Weights[3];
nPos=BoneTransform * vec4(position, 1.0);
Normal=(BoneTransform*vec4(N,0.0)).xyz;
}
else{
nPos = vec4(position, 1.0);
Normal=N;
}
gl_Position = projection*view * model * nPos;
vec3 FragPos = vec3(model * nPos);
if(has_normal_map){
mat3 normalMatrix = transpose(inverse(mat3(model)));
vec3 T = normalize(normalMatrix * tangent);
vec3 N = normalize(N);
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N,T);
if (dot(cross(N, T), B) < 0.0)
T = T * -1.0;
mat3 TBN = transpose(mat3(T, B, N));
tViewPos=TBN*viewPos;
tFragPos=TBN*FragPos;
for(int i = 0; i < NR_DIRECT_LIGHTS-2; i++)
tDirectLightPos[i]=TBN*directLightPos[i];
for(int i = 0; i < NR_POINT_LIGHTS-2; i++)
tPointLightPos[i]=TBN*pointLightPos[i];
for(int i = 0; i < NR_SPOT_LIGHTS-2; i++)
tSpotLightPos[i]=TBN*spotLightPos[i];
}
else{
tViewPos=viewPos;
tFragPos=FragPos;
}
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos,1.0);
}
Fragment:
#version 300 es
precision mediump float;
#define NR_DIRECT_LIGHTS 0
#define NR_POINT_LIGHTS 0
#define NR_SPOT_LIGHTS 0
out vec4 glFragColor;
vec2 poissonDisk[4] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 )
);
struct SpotLight{
vec3 position;
vec3 direction;
vec3 color;
float constant;
float linear;
float quadratic;
float cutoff;
float outerCutOff;
float intensity;
int castShadow;
};
struct PointLight{
vec3 position;
vec3 color;
float constant;
float linear;
float quadratic;
float intensity;
};
struct DirectLight {
vec3 direction;
vec3 color;
float intensity;
int castShadow;
};
in vec2 TexCoords;
in vec3 Normal;
in vec4 FragPosLightSpace;
in vec3 tDirectLightPos[NR_DIRECT_LIGHTS];
in vec3 tPointLightPos[NR_POINT_LIGHTS];
in vec3 tSpotLightPos[NR_SPOT_LIGHTS];
in vec3 tViewPos;
in vec3 tFragPos;
uniform bool Has_normal_map;
uniform sampler2D mat_diffuse;
uniform sampler2D mat_specular;
uniform sampler2D mat_normal;
uniform sampler2D shadowMap;
uniform vec3 matDiffuse;
uniform vec3 matSpecular;
uniform float shininess;
uniform float far_plane;
uniform DirectLight directLights[NR_DIRECT_LIGHTS];
uniform PointLight pointLights[NR_POINT_LIGHTS];
uniform SpotLight spotLights[NR_SPOT_LIGHTS];
vec3 calcDirectLight(DirectLight,vec3,vec3,vec3,vec3);
vec3 calcPointLight(PointLight,vec3,vec3,vec3,vec3);
vec3 calcSpotLight(SpotLight,vec3,vec3,vec3,vec3);
float directShadowCalculation();
void main(){
vec3 normal;
if(Has_normal_map){
normal=texture(mat_normal, TexCoords).rgb;
normal = normalize(normal * 2.0 - 1.0); // this normal is in tangent space
}
else
normal=normalize(Normal);
vec3 diffColor= matDiffuse+vec3(texture(mat_diffuse, TexCoords));
vec3 specColor= matSpecular+vec3(texture(mat_specular,TexCoords));
vec3 result;
result=vec3(0.0);
for(int i = 0; i < NR_DIRECT_LIGHTS-2; i++)
result += calcDirectLight(directLights[i],normal,tDirectLightPos[i],diffColor,specColor);
for(int i = 0; i < NR_POINT_LIGHTS-2; i++)
result += calcPointLight(pointLights[i],normal,tPointLightPos[i],vec3(0.0,0.2,0.4),specColor);
for(int i = 0; i < NR_SPOT_LIGHTS-2; i++)
result += calcSpotLight(spotLights[i],normal,tSpotLightPos[i],diffColor,specColor);
vec4 color =vec4(result,1.0);
float gamma = 2.2;
color.rgb = pow(color.rgb, vec3(1.0/gamma));
vec4 ambient=vec4(0.2,0.2,0.2,1.0)*vec4(diffColor,1.0);
glFragColor=ambient+color;
}
vec3 calcDirectLight(DirectLight light,vec3 norm,vec3 tLightPos,vec3 diffColor,vec3 specColor){
vec3 lightDir ;
if(Has_normal_map)
lightDir= normalize(tLightPos);
else
lightDir = normalize(light.direction);
float diff = max(dot(lightDir,norm), 0.0);
vec3 diffuse = light.color * diff *diffColor;
vec3 viewDir = normalize(tViewPos- tFragPos);
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(norm, halfwayDir), 0.0), 32.0);
vec3 specular = shininess* spec *specColor* light.color;
vec3 result;
if(light.castShadow==1){
float shadow = directShadowCalculation();
result =light.intensity* ( shadow* (diffuse + specular));
}
else
result =light.intensity* (diffuse + specular);
return result;
}
vec3 calcPointLight(PointLight light,vec3 norm,vec3 tLightPos,vec3 diffColor,vec3 specColor){
vec3 lightDir ;
if(Has_normal_map)
lightDir= normalize(tLightPos-tFragPos);
else
lightDir = normalize(light.position - tFragPos);
float diff = max(dot(lightDir,norm), 0.0);
vec3 diffuse = light.color * diff * diffColor;
vec3 viewDir = normalize(tViewPos- tFragPos);
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(norm, halfwayDir), 0.0), 16.0);
vec3 specular =shininess* specColor * spec * light.color;
vec3 result;
float distance = length(light.position - tFragPos);
float attenuation = 1.0f / (light.constant + light.linear * distance +light.quadratic * (distance * distance));
diffuse *= attenuation;
specular *= attenuation;
result=light.intensity*(diffuse+specular);
return result;
}
vec3 calcSpotLight(SpotLight light,vec3 norm,vec3 tLightPos,vec3 diffColor,vec3 specColor){
vec3 lightDir ;
if(Has_normal_map)
lightDir= normalize(tLightPos-tFragPos);
else
lightDir = normalize(light.position - tFragPos);
float diff = max(dot(lightDir,norm), 0.0);
vec3 diffuse = light.color * diff * diffColor;
vec3 viewDir = normalize(tViewPos- tFragPos);
float spec =0.0;
vec3 halfwayDir = normalize(lightDir + viewDir);
spec = pow(max(dot(norm, halfwayDir), 0.0), 16.0);
vec3 specular = shininess* light.color * spec * specColor;
// 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 - tFragPos);
float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
diffuse *= attenuation;
specular *= attenuation;
vec3 result = intensity*(diffuse+specular);
return result;
}
float directShadowCalculation(){
vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
float shadow = 1.0;
for (int i=0;i<4;i++){
if ( texture( shadowMap, -projCoords.xy + poissonDisk[i]/700.0 ).z < -projCoords.z ){
shadow-=0.2;
}
}
if(projCoords.z > 1.0)
shadow = 0.0;
return shadow;
}
sorry for all that code but I don't know where is the problem, it takes a week searching and debugging with no progress.
Edit
1- the light position vector is (-3.5f, 8.0f, 1.0f)
2- I changed the directShadowCalculation()
to:
float directShadowCalculation(){
vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
float shadow = 1.0;
for (int i=0;i<4;i++){
if ( texture( shadowMap, projCoords.xy + poissonDisk[i]/700.0 ).z < projCoords.z ){
shadow-=0.2;
}
}
if(projCoords.z > 1.0)
shadow = 0.0;
return shadow;
}
this is the result