Прежде всего направление источника света к фрагменту в текстурном пространстве:
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;
}