diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index d3655dfb5..5220723fb 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -125,6 +125,10 @@ namespace SceneUtil else definesWithShadows["perspectiveShadowMaps"] = "0"; + definesWithShadows["disableNormalOffsetShadows"] = Settings::Manager::getFloat("normal offset distance", "Shadows") == 0.0 ? "1" : "0"; + + definesWithShadows["shadowNormalOffset"] = std::to_string(Settings::Manager::getFloat("normal offset distance", "Shadows")); + return definesWithShadows; } @@ -140,6 +144,10 @@ namespace SceneUtil definesWithShadows["perspectiveShadowMaps"] = "0"; + definesWithShadows["enableNormalOffsetShadows"] = "0"; + + definesWithShadows["shadowNormalOffset"] = "0.0"; + return definesWithShadows; } void ShadowManager::enableIndoorMode() diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 240530be0..345a3ee0d 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -669,6 +669,8 @@ minimum lispsm near far ratio = 0.25 polygon offset factor = 1.1 # Used as the units parameter for the polygon offset used for shadow map rendering. Higher values reduce shadow flicker, but risk increasing Peter Panning. See https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml for details. polygon offset units = 4.0 +# How far along the surface normal to project shadow coordinates. Higher values significantly reduce shadow flicker, usually with a lower increase of Peter Panning than the Polygon Offset settings. This value is in in-game units, so 1.0 is roughly 1.4 cm. +normal offset distance = 1.0 # Excludes theoretically unnecessary faces from shadow maps, slightly increasing performance. In practice, Peter Panning can be much less visible with these faces included, so if you have high polygon offset values, disabling this may help minimise the side effects. use front face culling = true # Allow actors to cast shadows. Potentially decreases performance. diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index 3d7f49eee..dcb241274 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -103,5 +103,5 @@ void main(void) passViewPos = viewPos.xyz; passNormal = gl_Normal.xyz; - setupShadowCoords(viewPos); + setupShadowCoords(viewPos, viewNormal); } diff --git a/files/shaders/shadows_vertex.glsl b/files/shaders/shadows_vertex.glsl index 4baa249c1..f96e1a673 100644 --- a/files/shaders/shadows_vertex.glsl +++ b/files/shaders/shadows_vertex.glsl @@ -10,20 +10,44 @@ varying vec4 shadowRegionCoords@shadow_texture_unit_index; #endif @endforeach + + // Enabling this may reduce peter panning. Probably unnecessary. + const bool onlyNormalOffsetUV = false; #endif // SHADOWS -void setupShadowCoords(vec4 viewPos) +void setupShadowCoords(vec4 viewPos, vec3 viewNormal) { #if SHADOWS // This matrix has the opposite handedness to the others used here, so multiplication must have the vector to the left. Alternatively it could be transposed after construction, but that's extra work for the GPU just to make the code look a tiny bit cleaner. mat4 eyePlaneMat; + vec4 shadowOffset; @foreach shadow_texture_unit_index @shadow_texture_unit_list eyePlaneMat = mat4(gl_EyePlaneS[shadowTextureUnit@shadow_texture_unit_index], gl_EyePlaneT[shadowTextureUnit@shadow_texture_unit_index], gl_EyePlaneR[shadowTextureUnit@shadow_texture_unit_index], gl_EyePlaneQ[shadowTextureUnit@shadow_texture_unit_index]); - shadowSpaceCoords@shadow_texture_unit_index = viewPos * eyePlaneMat; #if @perspectiveShadowMaps shadowRegionCoords@shadow_texture_unit_index = validRegionMatrix@shadow_texture_unit_index * viewPos; #endif + +#if @disableNormalOffsetShadows + shadowSpaceCoords@shadow_texture_unit_index = viewPos * eyePlaneMat; +#else + shadowOffset = vec4(viewNormal * @shadowNormalOffset, 0.0); + + if (onlyNormalOffsetUV) + { + shadowSpaceCoords@shadow_texture_unit_index = viewPos * eyePlaneMat; + + vec4 lightSpaceXY = viewPos + shadowOffset; + lightSpaceXY = lightSpaceXY * eyePlaneMat; + + shadowSpaceCoords@shadow_texture_unit_index.xy = lightSpaceXY.xy; + } + else + { + vec4 offsetViewPosition = viewPos + shadowOffset; + shadowSpaceCoords@shadow_texture_unit_index = offsetViewPosition * eyePlaneMat; + } +#endif @endforeach #endif // SHADOWS } \ No newline at end of file diff --git a/files/shaders/terrain_vertex.glsl b/files/shaders/terrain_vertex.glsl index 669eac012..8a9cf82cc 100644 --- a/files/shaders/terrain_vertex.glsl +++ b/files/shaders/terrain_vertex.glsl @@ -25,9 +25,10 @@ void main(void) vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; + + vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); #if !PER_PIXEL_LIGHTING - vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting); #else passColor = gl_Color; @@ -37,5 +38,5 @@ void main(void) uv = gl_MultiTexCoord0.xy; - setupShadowCoords(viewPos); + setupShadowCoords(viewPos, viewNormal); } diff --git a/files/shaders/water_vertex.glsl b/files/shaders/water_vertex.glsl index b028d90d2..575f8f3c2 100644 --- a/files/shaders/water_vertex.glsl +++ b/files/shaders/water_vertex.glsl @@ -22,5 +22,5 @@ void main(void) depthPassthrough = gl_Position.z; - setupShadowCoords(gl_ModelViewMatrix * gl_Vertex); + setupShadowCoords(gl_ModelViewMatrix * gl_Vertex, normalize((gl_NormalMatrix * gl_Normal).xyz)); }