Точечное освещение с теневым отображением и перемещением камеры - PullRequest
0 голосов
/ 28 июня 2018

Столкнулся с проблемой при попытке создать прожектор в моей сцене. Проблема в том, что моя камера движется по сцене, и из-за этого что-то не так с освещением. Кроме того, я вижу только черный экран. Я понимаю, что где-то пропустил преобразование или сделал что-то дополнительное, но где - я действительно не знаю.

Ниже приведен код для моих шейдеров.

Фрагмент шейдера:

#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

Ответы [ 2 ]

0 голосов
/ 30 июня 2018

Проблема была решена. Это было связано с тем, что я рассматривал карту теней в пространстве камеры (view 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 - положение лампы в мировом пространстве

0 голосов
/ 28 июня 2018

Я понимаю, что где-то пропустил преобразование или сделал что-то дополнительное, но где - я действительно не знаю.

Содержимое shadowMaps[textureIndex], вероятно, является картой глубины, взятой в «световом пространстве». Это означает, что это карта глубины, видимая из источника света.

Но

fragPos = vec3(v_MVMatrix * v_Position);

и

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

находятся в пространстве координат. Это приводит к тому, что

vec3 fragToLight = fragPos - lightPos;

- это направление в пространстве обзора, если смотреть с камеры.

Если вы делаете

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

затем к карте «светлое пространство» обращается вектор «пространство просмотра». Преобразование координат координат вида в координаты «светлого пространства» отсутствует.

Чтобы решить эту проблему, вам нужна матрица, которая преобразуется из мировых координат в координаты "светлого пространства" Это обратная матрица той матрицы проекции вида, которую вы использовали при создании shadowMaps.

mat4 inverse_light_vp_mat[MAX_LAMPS_COUNT];

Положение фрагмента должно быть преобразовано в мировые координаты, затем оно должно быть преобразовано в координаты «светлого пространства», с 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;

В «световом пространстве» позиция света равна vec3 (0,0, 0,0, 0,0), потому что позиция источника света является источником «светового пространства». Таким образом, поиск в shadowMaps можно выполнить непосредственно с помощью fragLightPos:

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