Search code examples
c++openglglslshaderglm-math

Omnidirectional shadows for directional light


when using a shadow map, the light projection (orthogonal) is used in the following way (similarly for other planets):

    const glm::mat4 lightProjection = glm::ortho(-saturn->GetRadius() * 3.0f, saturn->GetRadius() * 3.0f, -saturn->GetRadius() * 3.0f, saturn->GetRadius() * 3.0f, camera.GetNear(), camera.GetFar());
    const glm::mat4 lightView = glm::lookAt(_sun->GetPosition(), saturn->GetPosition(), glm::vec3(0.0, 1.0, 0.0));
    const glm::mat4 lightSpaceMatrix = lightProjection * lightView;
    saturn->SetLightSpaceMatrix(lightSpaceMatrix);

While rendering the planets, I change the lightSpaceMatrix, trying to recreate the pseudo omnidirectional light (in certain directions) in the following way:

void Application::RenderPlanetsAndSatellites(const Shader& shader) {
    shader.Use();

    for (const auto& renderableComponentPS : _renderableComponentsPS) {
        shader.SetMat4("lightSpaceMatrix", renderableComponentPS.planet->GetLightSpaceMatrix());

        ...

        renderableComponentPS.planet->SetShader(shader);
        renderableComponentPS.planet->AdjustToParent(isTimeRun);
        renderableComponentPS.planet->Render();

        for (const auto& satellite : renderableComponentPS.satellites) {
            satellite->SetShader(shader);
            satellite->AdjustToParent(isTimeRun);
            satellite->Render();
        }
    }
}

Vertex shader:

#version 460 core

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;
    vec4 FragPosLightSpace;
} vs_out;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform mat4 lightSpaceMatrix;

uniform vec3 lightPos;
uniform vec3 viewPos;

uniform float zCoef; // For log z-buffer (2.0 / log2(farPlane + 1.0))

void main() {
    vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
    vs_out.TexCoords = aTexCoords;

    mat3 normalMatrix = mat3(transpose(inverse(model)));
    vec3 T = normalize(normalMatrix * aTangent);
    vec3 N = normalize(normalMatrix * aNormal);
    T = normalize(T - dot(T, N) * N);
    vec3 B = cross(N, T);

    mat3 TBN = transpose(mat3(T, B, N));
    vs_out.TangentLightPos = TBN * lightPos;
    vs_out.TangentViewPos  = TBN * viewPos;
    vs_out.TangentFragPos  = TBN * vs_out.FragPos;
    vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0);

    gl_Position = projection * view * vec4(vs_out.FragPos, 1.0f);

    // Log z-buffer [логарифмический z-буфер]
    gl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * zCoef - 1.0;
    gl_Position.z *= gl_Position.w;
}

Shadow calculation in fragment shader:

float CalculateShadow(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;
    vec3 lightDir = lightPos - fs_in.FragPos;
    vec3 lightDirNorm = normalize(lightDir);

    float shadow;

    ApplyPCF(shadow, projCoords, currentDepth);
    return shadow;
}

But for some reason this does not work. The planet that is closest to the sun begins to seem to cover all other planets:

enter image description here

enter image description here

enter image description here


Solution

  • So guys, I solved the problem...

    The idea is to render a scene component (planet, its satellites, rings, etc.) into a shadow map, then immediately into a regular buffer, and then clear the shadow map (depth buffer) so that the planet closest to the sun does not cover all the others, etc.

    Cleaning is necessary because only one shadow map is used, and without cleaning, the depth will be overlapped by objects from all over the scene.

    Thus, by changing the lightSpaceMatrix, it can be created the impression that an omnidirectional light source is used in the scene.

    enter image description here