Добавление теней на карту окклюзии параллакса - PullRequest
1 голос
/ 10 марта 2019

Я реализовал Parallax Occlusion Mapping через LearnOpengl , а теперь Я хочу добавить собственные тени, чтобы выдавливания фрагментов отбрасывали тени на поверхность . Я прочитал несколько статей по этой теме, но признаю, что это немного продвинуто для меня. Из того, что я понимаю, это тот же процесс, что и картирование окклюзии параллакса, но с направлением света вместо направления обзора. Я попытался изменить фрагментный шейдер, но тени все еще не отображаются.

Вот как я хочу, чтобы это выглядело. enter image description here http://www.cs.utah.edu/~sujin/courses/reports/cs6610/project-report/images/pom.png

Это результат модифицированного фрагментного шейдера. Ничего не изменилось с того времени, когда это была просто карта окклюзии параллакса. What I have

Вот модифицированный фрагментный шейдер. Я отметил те части, которые я добавил к исходному коду параллакса.

#version 330 core
in vec2 o_texCoord;
in vec3 o_worldPos;
in vec3 world_normal;
in vec3 world_tangent;

out vec4 fragColor;

uniform vec3 light_pos;
uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform sampler2D heightMap;
uniform vec3 viewPosition;
uniform float heightScale;

vec2 ParallaxMapping (vec2 texCoord, vec3 viewDir)
{
    float minLayers = 0;
    float maxLayers = 32;
    float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));
    float layerDepth = 1.0 / numLayers;

    float currentLayerDepth = 0;

    vec2 P = viewDir.xy / viewDir.z * heightScale;

    vec2 deltaTexCoords = P / numLayers;

    vec2 currentTexCoords = texCoord;

    float currentDepthMapValue = texture(heightMap, currentTexCoords).r;

    while (currentLayerDepth < currentDepthMapValue)
    {
        currentTexCoords -= deltaTexCoords;
        currentDepthMapValue = texture(heightMap, currentTexCoords).r;
        currentLayerDepth += layerDepth;
    }

    vec2 prevTexCoords = currentTexCoords + deltaTexCoords;
    float afterDepth = currentDepthMapValue - currentLayerDepth;
    float beforeDepth = texture(heightMap, prevTexCoords).r - currentLayerDepth + layerDepth;

    float weight = afterDepth / (afterDepth - beforeDepth);

    vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);

    return finalTexCoords;
}

// FUNCTION I ADDED FOR SHADOW CALCULATION
float ShadowCalc(vec2 texCoord, vec3 lightDir)
{
    float minLayers = 0;
    float maxLayers = 32;
    float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), lightDir)));

    float layerDepth = 1.0 / numLayers;

    float currentLayerDepth = 0;

    vec2 P = lightDir.xy / lightDir.z * heightScale;

    vec2 deltaTexCoords = P / numLayers;

    vec2 currentTexCoords = texCoord;

    float currentDepthMapValue = texture(heightMap, currentTexCoords).r;

    while (currentLayerDepth < currentDepthMapValue)
    {
        currentTexCoords -= deltaTexCoords;
        currentDepthMapValue = texture(heightMap, currentTexCoords).r;
        currentLayerDepth += layerDepth;
    }

    float r = currentDepthMapValue > currentLayerDepth ? 0.0 : 1.0;

    return r;
}

void main()
{
    mat3 TBN_norm = transpose(mat3(normalize(world_tangent),
                                   normalize(cross(world_normal, world_tangent)),
                                   normalize(world_normal)));
    vec3 viewDir = TBN_norm * normalize(o_worldPos - viewPosition);
    vec2 currentTex = ParallaxMapping(o_texCoord, viewDir);
    if (currentTex.x > 1.0 || currentTex.y > 1.0 || currentTex.x < 0.0 || currentTex.y < 0.0)
    {
        discard;
    }
    vec3 normal = texture(normalMap, currentTex).rgb;
    normal = normalize(normal * 2.0 - 1.0);
    vec3 lightDir = normalize(TBN_norm * light_pos - TBN_norm * o_worldPos);
    float dc = max(0.0, dot(lightDir, normal));

    // STUFF I ADDED FOR SHADOWS
    float shadow = 0;
    if (dc > 0)
    {
        shadow = ShadowCalc(currentTex, lightDir);
    }

    fragColor = shadow * dc * texture(diffuseMap, currentTex);
}

1 Ответ

2 голосов
/ 10 марта 2019

Прежде всего направление источника света к фрагменту в текстурном пространстве:

vec3 lightDir = TBN_norm * normalize(o_worldPos - light_pos);
float dc = max(0.0, dot(-lightDir, normal));

Чтобы проверить, находится ли фрагмент в собственной тени, вы должны отследить луч до источника света, запустив текст "параллакс".

float shadow = dc > 0.0 ? ShadowCalc(currentTex, lightDir) : 0.0;

Начальная высота (currentLayerDepth) - высота текущего фрагмента:

float currentDepthMapValue = texture(heightMap, currentTexCoords).r;
float currentLayerDepth = currentDepthMapValue;

Поскольку глубина mao является обратной картой глубины (1.0 - низкая), фрагмент находится в тени, если какая-либо глубина слоя (currentLayerDepth) меньше или равна текущей высоте (currentDepthMapValue). Выборка должна быть прервана, если достигнута максимальная глубина (минимальное значение 0,0).
Обратите внимание, что глубина уменьшена (currentLayerDepth -= layerDepth), и образцы текстуры взяты в противоположном направлении (currentTexCoords += deltaTexCoords) для сравнения алгоритма ParallaxMapping:

while (currentLayerDepth <= currentDepthMapValue && currentLayerDepth > 0.0)
{
    currentTexCoords += deltaTexCoords;
    currentDepthMapValue = texture(heightMap, currentTexCoords).r;
    currentLayerDepth -= layerDepth;
}
float r = currentLayerDepth > currentDepthMapValue ? 0.0 : 1.0;

Из-за деления на z-компонент в (P = lightDir.xy / lightDir.z), P и, следовательно, deltaTexCoords, всегда указывает на источник света (конечно, в проекции на текстуру).
Если z-компонент lightDir больше 0,0, поверхность освещается сзади. Это приводит к состоянию раннего прерывания беременности:

if ( lightDir.z >= 0.0 )
    return 0.0;

Полная функция ShadowCalc Функция может выглядеть следующим образом:

float ShadowCalc(vec2 texCoord, vec3 lightDir)
{
    if ( lightDir.z >= 0.0 )
        return 0.0;

    float minLayers = 0;
    float maxLayers = 32;
    float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), lightDir)));

    vec2 currentTexCoords = texCoord;
    float currentDepthMapValue = texture(heightMap, currentTexCoords).r;
    float currentLayerDepth = currentDepthMapValue;

    float layerDepth = 1.0 / numLayers;
    vec2 P = lightDir.xy / lightDir.z * heightScale;
    vec2 deltaTexCoords = P / numLayers;

    while (currentLayerDepth <= currentDepthMapValue && currentLayerDepth > 0.0)
    {
        currentTexCoords += deltaTexCoords;
        currentDepthMapValue = texture(heightMap, currentTexCoords).r;
        currentLayerDepth -= layerDepth;
    }

    float r = currentLayerDepth > currentDepthMapValue ? 0.0 : 1.0;
    return r;
}

...