具有阴影映射和相机移动的点照明

Point lighting with shadow mapping and camera moving

尝试在我的场景中创建聚光灯时遇到问题。问题是我的相机在场景中移动,因此,照明出现了问题。此外,我只看到黑屏。我知道我错过了某个地方的转变,或者做了一些额外的,但是在哪里 - 我真的不知道。

下面是我的着色器代码。

片段着色器:

#version 330 core

precision mediump float;                        // Set the default precision to medium. We don't need as high of a
                                            // precision in the fragment shader.

#define MAX_LAMPS_COUNT 8                   // Max lamps count.

uniform vec3 u_ViewPos;                     // Camera position
uniform int u_LampsCount;                   // Lamps count
uniform int u_ShadowMapWidth = 1024;        // shadow map width / default is 1024
uniform int u_ShadowMapHeight = 1024;       // shadow map height / default is 1024
uniform float brightnessThreshold = 0.5;        // brightness threshold variable
uniform float far_plane = 16;

varying mat4 v_MVMatrix;                    // Model View matrix
varying mat3 v_TBN;                         // Tangent Bitangent Normal matrix
varying vec4 v_Position;                    // Position for this fragment.
varying vec3 v_Normal;                      // Interpolated normal for this fragment.
varying vec2 v_Texture;                     // Texture coordinates.
varying float v_NormalMapping;              // Is normal mapping enabled 0 - false, 1 - true

struct Lamp {
    float ambientStrength;
    float diffuseStrength;
    float specularStrength;
    float kc; // constant term
    float kl; // linear term
    float kq; // quadratic term
    int shininess;
    vec3 lampPos; // in eye space, cameraViewMatrix * lamp world coordinates
    vec3 lampColor;
};

uniform samplerCube shadowMaps[MAX_LAMPS_COUNT];

uniform struct Mapping {
    sampler2D ambient;
    sampler2D diffuse;
    sampler2D specular;
    sampler2D normal;
} u_Mapping;

uniform Lamp u_Lamps[MAX_LAMPS_COUNT];

vec3 norm;
vec3 fragPos;
float shadow;

// output colors
layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec4 fragBrightColor;

float calculateShadow(int textureIndex, vec3 lightPos) {
    // get vector between fragment position and light position
    vec3 fragToLight = fragPos - lightPos;
    // use the light to fragment vector to sample from the depth map
    float closestDepth = texture(shadowMaps[textureIndex], fragToLight).r;
    // it is currently in linear range between [0,1]. Re-transform back to original value
    closestDepth *= far_plane;
    // now get current linear depth as the length between the fragment and light position
    float currentDepth = length(fragToLight);
    // now test for shadows
    float bias = 0.05;
    float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
    //fragColor = vec4(vec3(closestDepth / far_plane), 1.0);  // visualization
    return shadow;
}

float calculateAttenuation(Lamp lamp) {
    float distance = length(lamp.lampPos - fragPos);
    return 1.0 / (
                    lamp.kc +
                    lamp.kl * distance +
                    lamp.kq * (distance * distance)
            );
}

vec4 toVec4(vec3 v) {
    return vec4(v, 1);
}

// The entry point for our fragment shader.
void main() {
     // Transform the vertex into eye space.
    fragPos = vec3(v_MVMatrix * v_Position);

    vec3 viewDir = normalize(u_ViewPos - fragPos);
    if (v_NormalMapping == 0) norm = vec3(normalize(v_MVMatrix * vec4(v_Normal, 0)));
    else { // using normal map if normal mapping enabled
        norm = texture2D(u_Mapping.normal, v_Texture).rgb;
        norm = normalize(norm * 2.0 - 1.0); // from [0; 1] to [-1; -1]
        norm = normalize(v_TBN * norm);
    }

    vec3 ambientResult = vec3(0, 0, 0); // result of ambient lighting for all lamps
    vec3 diffuseResult = vec3(0, 0, 0); // result of diffuse lighting for all lamps
    vec3 specularResult = vec3(0, 0, 0); // result of specular lighting for all lamps

    for (int i = 0; i<u_LampsCount; i++) {
        // attenuation
        float attenuation = calculateAttenuation(u_Lamps[i]);

        // ambient
        vec3 ambient = u_Lamps[i].ambientStrength * u_Lamps[i].lampColor * attenuation;

        // diffuse
        vec3 lightDir = normalize(u_Lamps[i].lampPos - fragPos);
        float diff = max(dot(norm, lightDir), 0.0);
        vec3 diffuse = u_Lamps[i].diffuseStrength * diff * u_Lamps[i].lampColor * attenuation;

        // specular
        vec3 reflectDir = reflect(-lightDir, norm);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), u_Lamps[i].shininess);
        vec3 specular = u_Lamps[i].specularStrength * spec * u_Lamps[i].lampColor * attenuation;

        // fragment position in light space
        //fragLightSpacePos = u_Lamps[i].lightSpaceMatrix * u_Lamps[i].lightModelMatrix * v_Position;
        // calculate shadow
        shadow = calculateShadow(i, u_Lamps[i].lampPos);

        // result for this(i) lamp
        ambientResult += ambient;
        diffuseResult += diffuse * (1-shadow);
        specularResult += specular * (1-shadow);
    }

    fragColor =
            toVec4(ambientResult) * texture2D(u_Mapping.ambient, v_Texture) +
            toVec4(diffuseResult) * texture2D(u_Mapping.diffuse, v_Texture) +
            toVec4(specularResult) * texture2D(u_Mapping.specular, v_Texture);

    // brightness calculation
    //float brightness = dot(fragColor.rgb, vec3(0.2126, 0.7152, 0.0722));
    //if (brightness > brightnessThreshold) fragBrightColor = vec4(fragColor.rgb, 1.0);
    fragBrightColor = vec4(0, 0, 0, 1);
}

顶点着色器:

#version 130

uniform mat4 u_MVPMatrix;      // A constant representing the combined model/view/projection matrix.
uniform mat4 u_MVMatrix;       // A constant representing the combined model/view matrix.
uniform float u_NormalMapping;  // Normal mapping; 0 - false, 1 - true

attribute vec4 a_Position;     // Per-vertex position information we will pass in.
attribute vec3 a_Normal;       // Per-vertex normal information we will pass in.
attribute vec3 a_Tangent;      // Per-vertex tangent information we will pass in.
attribute vec3 a_Bitangent;    // Per-vertex bitangent information we will pass in.
attribute vec2 a_Texture;      // Per-vertex texture information we will pass in.

varying mat4 v_MVMatrix;       // This will be passed into the fragment shader.
varying mat3 v_TBN;            // This will be passed into the fragment shader.
varying vec4 v_Position;       // This will be passed into the fragment shader.
varying vec3 v_Normal;         // This will be passed into the fragment shader.
varying vec2 v_Texture;        // This will be passed into the fragment shader.
varying float v_NormalMapping;  // This will be passed into the fragment shader.

void main() {
    // creating TBN (tangent-bitangent-normal) matrix if normal mapping enabled
    if (u_NormalMapping == 1) {
        vec3 T = normalize(vec3(u_MVMatrix * vec4(a_Tangent, 0.0)));
        vec3 B = normalize(vec3(u_MVMatrix * vec4(a_Bitangent, 0.0)));
        vec3 N = normalize(vec3(u_MVMatrix * vec4(a_Normal, 0.0)));
        mat3 TBN = mat3(T, B, N);
        v_TBN = TBN;
    }

    // gl_Position is a special variable used to store the final position.
    // Multiply the vertex by the matrix to get the final point in normalized screen coordinates.
    gl_Position = u_MVPMatrix * a_Position;

    // sending all needed variables to fragment shader
    v_Position = a_Position;
    v_Texture = a_Texture;
    v_NormalMapping = u_NormalMapping;
    v_MVMatrix = u_MVMatrix;
    v_Normal = a_Normal;
}

顶点阴影着色器:

#version 130

attribute vec3 a_Position;
uniform mat4 u_ModelMatrix;

void main() {
    gl_Position = u_ModelMatrix * vec4(a_Position, 1.0);
}

片段阴影着色器:

#version 330 core
in vec4 fragPos;

uniform vec3 lightPos; // cameraViewMatrix * lamp world coordinates
uniform float far_plane = 16;

void main()
{
    float lightDistance = length(fragPos.xyz - lightPos);

    // map to [0;1] range by dividing by far_plane
    lightDistance = lightDistance / far_plane;

    // write this as modified depth
    gl_FragDepth = lightDistance;
}

几何阴影着色器:

#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices=18) out;

uniform mat4 shadowMatrices[6];

out vec4 fragPos; // FragPos from GS (output per emitvertex)

void main() {
    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();
    }
}

以及演示可视化阴影贴图的视频: https://youtu.be/zaNXGG1qLaw

I understand that I missed the transformation somewhere, or did some extra, but where - I really do not know.

shadowMaps[textureIndex]的内容很可能是"light space"拍摄的深度图。这意味着它是从光源看到的深度图。

但是

fragPos = vec3(v_MVMatrix * v_Position);

struct Lamp {
    .....
    vec3 lampPos; // in eye space, cameraViewMatrix * lamp world coordinates
    .....
};

在 space 坐标中。这导致

vec3 fragToLight = fragPos - lightPos;

是从相机看到的 space 中的一个方向。

如果你这样做

float closestDepth = texture(shadowMaps[textureIndex], fragToLight).r;

然后 "light space" 地图由 "view space" 向量访问。缺少从视图 space 坐标到 "light space" 坐标的转换。

要解决此问题,您需要一个从世界坐标转换为 "light space" 坐标的矩阵。这是您在创建 shadowMaps.

时使用的视图投影矩阵的 矩阵
mat4 inverse_light_vp_mat[MAX_LAMPS_COUNT];

片段位置必须转换为世界坐标,然后必须转换为"light space"坐标,inverse_light_vp_mat:

varying mat4 v_ModelMatrix;  // Model matrix

vec4 fragLightPos = inverse_light_vp_mat[textureIndex] * v_ModelMatrix * v_Position;
fragLightPos.xyz /= fragLightPos.w;

在"light space"中光源位置是vec3(0.0,0.0,0.0),因为光源的位置是"light space"的原点。所以在 shadowMaps 中查找可以直接用 fragLightPos:

float closestDepth = texture(shadowMaps[textureIndex], fragLightPos.xyz).r;

问题已解决。这是因为我考虑了相机space(视图space)中的阴影贴图,但在世界space中是必需的。还有,在计算影子本身的时候,也需要计算世界上的一切space.

片段着色器:

vec3 fragToLight = vec3(model * v_Position) - lightPosWorldSpace;

vec3 fragToLight = vec3(model * v_Position) - vec3(inverse(view) * lightPos); (lightPos - vec4)

片段阴影着色器:

float lightDistance = length(fragPos.xyz - lightPos);,lightPos - lamp 在世界中的位置 space