From 11aed7474eba3aeca00cebf77ccece3c126cfa74 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 20 Mar 2013 18:34:54 +0100 Subject: [PATCH 1/4] Shadow cast on water --- files/materials/water.mat | 19 +++++++++ files/materials/water.shader | 76 +++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/files/materials/water.mat b/files/materials/water.mat index 0ec71d2df7..1e5f8c8e04 100644 --- a/files/materials/water.mat +++ b/files/materials/water.mat @@ -54,5 +54,24 @@ material Water scale 0.1 0.1 alpha_op_ex source1 src_manual src_current 0.7 } + + texture_unit shadowMap0 + { + content_type shadow + tex_address_mode clamp + filtering none + } + texture_unit shadowMap1 + { + content_type shadow + tex_address_mode clamp + filtering none + } + texture_unit shadowMap2 + { + content_type shadow + tex_address_mode clamp + filtering none + } } } diff --git a/files/materials/water.shader b/files/materials/water.shader index 793cdc95e3..a6d0a47e6e 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -50,6 +50,13 @@ // Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html ) +#define SHADOWS_PSSM @shGlobalSettingBool(shadows_pssm) +#define SHADOWS @shGlobalSettingBool(shadows) + +#if SHADOWS || SHADOWS_PSSM + #include "shadows.h" +#endif + #define RIPPLES 1 #define REFRACTION @shGlobalSettingBool(refraction) @@ -64,6 +71,23 @@ shOutput(float4, position) shOutput(float, depthPassthrough) + +#if SHADOWS + shOutput(float4, lightSpacePos0) + shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) +#endif + +#if SHADOWS_PSSM + @shForeach(3) + shOutput(float4, lightSpacePos@shIterator) + shUniform(float4x4, texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator) + @shEndForeach +#endif + +#if SHADOWS || SHADOWS_PSSM + shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix) +#endif + SH_START_PROGRAM { shOutputPosition = shMatrixMult(wvp, shInputPosition); @@ -88,6 +112,17 @@ position = shInputPosition; depthPassthrough = shOutputPosition.z; + + +#if SHADOWS + lightSpacePos0 = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition)); +#endif +#if SHADOWS_PSSM + float4 wPos = shMatrixMult(worldMatrix, shInputPosition); + @shForeach(3) + lightSpacePos@shIterator = shMatrixMult(texViewProjMatrix@shIterator, wPos); + @shEndForeach +#endif } #else @@ -186,10 +221,47 @@ shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params) shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position_object_space) + + +#if SHADOWS + shInput(float4, lightSpacePos0) + shSampler2D(shadowMap0) + shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 1) +#endif +#if SHADOWS_PSSM + @shForeach(3) + shInput(float4, lightSpacePos@shIterator) + shSampler2D(shadowMap@shIterator) + shUniform(float2, invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(1)) + @shEndForeach + shUniform(float3, pssmSplitPoints) @shSharedParameter(pssmSplitPoints) +#endif + +#if SHADOWS || SHADOWS_PSSM + shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart) +#endif SH_START_PROGRAM { +#if SHADOWS + float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0); +#endif +#if SHADOWS_PSSM + float shadow = pssmDepthShadow (lightSpacePos0, invShadowmapSize0, shadowMap0, lightSpacePos1, invShadowmapSize1, shadowMap1, lightSpacePos2, invShadowmapSize2, shadowMap2, depthPassthrough, pssmSplitPoints); +#endif + +#if SHADOWS || SHADOWS_PSSM + float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; + float fade = 1-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange); + shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow); +#endif + +#if !SHADOWS && !SHADOWS_PSSM + float shadow = 1.0; +#endif + + float2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z; screenCoords.y = (1-shSaturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y; @@ -244,7 +316,7 @@ float3 llR = reflect(lVec, pNormal); float s = shSaturate(dot(lR, vVec)*2.0-1.2); - float lightScatter = shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y)); + float lightScatter = shadow * shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y)); float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*float3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); // fresnel @@ -267,7 +339,7 @@ #endif // specular - float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS); + float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow; #if REFRACTION shOutputColour(0).xyz = shLerp( shLerp(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpecular.xyz; From 7beda509fe711190fb952ad33c784c1d1557eba3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 23 Mar 2013 09:53:38 +0100 Subject: [PATCH 2/4] Lighting should be done in view space instead of object space, so that attenuation also works correctly for scaled objects --- files/materials/objects.shader | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 46e3abeb3c..ae589a05be 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -54,10 +54,11 @@ #if VERTEX_LIGHTING shUniform(float, lightCount) @shAutoConstant(lightCount, light_count) - shUniform(float4, lightPosition[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightPosition, light_position_object_space_array, @shGlobalSettingString(num_lights)) + shUniform(float4, lightPosition[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightPosition, light_position_view_space_array, @shGlobalSettingString(num_lights)) shUniform(float4, lightDiffuse[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightDiffuse, light_diffuse_colour_array, @shGlobalSettingString(num_lights)) shUniform(float4, lightAttenuation[@shGlobalSettingString(num_lights)]) @shAutoConstant(lightAttenuation, light_attenuation_array, @shGlobalSettingString(num_lights)) shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) + shUniform(float4x4, worldView) @shAutoConstant(worldView, worldview_matrix) #if VERTEXCOLOR_MODE != 2 shUniform(float4, materialAmbient) @shAutoConstant(materialAmbient, surface_ambient_colour) #endif @@ -131,11 +132,14 @@ #if VERTEX_LIGHTING + float3 viewPos = shMatrixMult(worldView, shInputPosition).xyz; + float3 viewNormal = normalize(shMatrixMult(worldView, float4(normal.xyz, 0)).xyz); + float3 lightDir; float d; lightResult = float4(0,0,0,1); @shForeach(@shGlobalSettingString(num_lights)) - lightDir = lightPosition[@shIterator].xyz - (shInputPosition.xyz * lightPosition[@shIterator].w); + lightDir = lightPosition[@shIterator].xyz - (viewPos * lightPosition[@shIterator].w); d = length(lightDir); lightDir = normalize(lightDir); @@ -143,11 +147,11 @@ #if VERTEXCOLOR_MODE == 2 lightResult.xyz += colour.xyz * lightDiffuse[@shIterator].xyz * (1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(normalize(normal.xyz), normalize(lightDir)), 0); + * max(dot(viewNormal.xyz, lightDir), 0); #else lightResult.xyz += materialDiffuse.xyz * lightDiffuse[@shIterator].xyz * (1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) - * max(dot(normalize(normal.xyz), normalize(lightDir)), 0); + * max(dot(viewNormal.xyz, lightDir), 0); #endif #if @shIterator == 0 From a39c129954ea7cfb318f32d14644212e648a1aed Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 29 Mar 2013 19:53:52 +0100 Subject: [PATCH 3/4] Clamp individual light attenuation at 1 --- files/materials/objects.shader | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/files/materials/objects.shader b/files/materials/objects.shader index ae589a05be..c8616e9d1e 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -146,11 +146,11 @@ #if VERTEXCOLOR_MODE == 2 lightResult.xyz += colour.xyz * lightDiffuse[@shIterator].xyz - * (1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) + * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) * max(dot(viewNormal.xyz, lightDir), 0); #else lightResult.xyz += materialDiffuse.xyz * lightDiffuse[@shIterator].xyz - * (1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) + * shSaturate(1.0 / ((lightAttenuation[@shIterator].y) + (lightAttenuation[@shIterator].z * d) + (lightAttenuation[@shIterator].w * d * d))) * max(dot(viewNormal.xyz, lightDir), 0); #endif From 5e7e9fd26aa2f48687e843d47ab287161b1ed82a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 29 Mar 2013 20:21:37 +0100 Subject: [PATCH 4/4] Properly calculate light activation range --- apps/openmw/mwrender/objects.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 5cf4abdf77..50c0210649 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -275,17 +275,23 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre bool quadratic = lightOutQuadInLin() ? !info.interior : lightQuadratic(); + // with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero, + // so we ignore lights if their attenuation falls below this factor. + const float threshold = 0.03; + if (!quadratic) { float r = radius * lightLinearRadiusMult(); float attenuation = lightLinearValue() / r; - light->setAttenuation(r*10, 0, attenuation, 0); + float activationRange = 1 / (threshold * attenuation); + light->setAttenuation(activationRange, 0, attenuation, 0); } else { float r = radius * lightQuadraticRadiusMult(); float attenuation = lightQuadraticValue() / std::pow(r, 2); - light->setAttenuation(r*10, 0, 0, attenuation); + float activationRange = std::sqrt(1 / (threshold * attenuation)); + light->setAttenuation(activationRange, 0, 0, attenuation); } // If there's an AttachLight bone, attach the light to that, otherwise attach it to the base scene node