mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
110 lines
4 KiB
Text
110 lines
4 KiB
Text
<!
|
|
// Shadow.slh
|
|
// libraries/render-utils/src
|
|
//
|
|
// Created by Sam Gateau on 1/4/15.
|
|
// Copyright 2013 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
!>
|
|
<@if not SHADOW_SLH@>
|
|
<@def SHADOW_SLH@>
|
|
|
|
<@include ShadowCore.slh@>
|
|
|
|
#define SHADOW_NOISE_ENABLED 0
|
|
#define SHADOW_SCREEN_SPACE_DITHER 1
|
|
|
|
// the shadow texture
|
|
uniform sampler2DArrayShadow shadowMaps;
|
|
|
|
// Sample the shadowMap with PCF (built-in)
|
|
float fetchShadow(int cascadeIndex, vec3 shadowTexcoord) {
|
|
return texture(shadowMaps, vec4(shadowTexcoord.xy, cascadeIndex, shadowTexcoord.z));
|
|
}
|
|
|
|
vec2 PCFkernel[4] = vec2[4](
|
|
vec2(-1.5, 0.5),
|
|
vec2(0.5, 0.5),
|
|
vec2(-1.5, -1.5),
|
|
vec2(0.5, -1.5)
|
|
);
|
|
|
|
float evalShadowNoise(vec4 seed) {
|
|
float dot_product = dot(seed, vec4(12.9898,78.233,45.164,94.673));
|
|
return fract(sin(dot_product) * 43758.5453);
|
|
}
|
|
|
|
struct ShadowSampleOffsets {
|
|
vec3 points[4];
|
|
};
|
|
|
|
ShadowSampleOffsets evalShadowFilterOffsets(vec4 position) {
|
|
float shadowScale = getShadowScale();
|
|
ShadowSampleOffsets offsets;
|
|
|
|
#if SHADOW_SCREEN_SPACE_DITHER
|
|
// Pattern dithering in screen space
|
|
ivec2 coords = ivec2(gl_FragCoord.xy);
|
|
#else
|
|
// Pattern dithering in world space (mm resolution)
|
|
ivec2 coords = ivec2(position.x, position.y+position.z);
|
|
#endif
|
|
|
|
#if SHADOW_NOISE_ENABLED
|
|
// Add some noise to break dithering
|
|
int index = int(4.0*evalShadowNoise(gl_FragCoord.xyyx))%4;
|
|
coords.x += index & 1;
|
|
coords.y += (index & 2) >> 1;
|
|
#endif
|
|
|
|
// Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html
|
|
ivec2 offset = coords & ivec2(1,1);
|
|
offset.y = (offset.x+offset.y) & 1;
|
|
|
|
offsets.points[0] = shadowScale * vec3(offset + PCFkernel[0], 0.0);
|
|
offsets.points[1] = shadowScale * vec3(offset + PCFkernel[1], 0.0);
|
|
offsets.points[2] = shadowScale * vec3(offset + PCFkernel[2], 0.0);
|
|
offsets.points[3] = shadowScale * vec3(offset + PCFkernel[3], 0.0);
|
|
|
|
return offsets;
|
|
}
|
|
|
|
float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias) {
|
|
shadowTexcoord.z -= bias;
|
|
float shadowAttenuation = 0.25 * (
|
|
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[0]) +
|
|
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[1]) +
|
|
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[2]) +
|
|
fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[3])
|
|
);
|
|
return shadowAttenuation;
|
|
}
|
|
|
|
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float oneMinusNdotL) {
|
|
float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * oneMinusNdotL;
|
|
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
|
|
}
|
|
|
|
float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) {
|
|
ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
|
|
vec4 cascadeShadowCoords[2];
|
|
cascadeShadowCoords[0] = vec4(0);
|
|
cascadeShadowCoords[1] = vec4(0);
|
|
ivec2 cascadeIndices;
|
|
float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);
|
|
|
|
// Adjust bias if we are at a grazing angle with light
|
|
float oneMinusNdotL = 1.0 - clamp(dot(worldLightDir, worldNormal), 0, 1);
|
|
vec2 cascadeAttenuations = vec2(1.0, 1.0);
|
|
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], oneMinusNdotL);
|
|
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
|
|
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], oneMinusNdotL);
|
|
}
|
|
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
|
// Falloff to max distance
|
|
return mix(1.0, attenuation, evalShadowFalloff(viewDepth));
|
|
}
|
|
|
|
<@endif@>
|