diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a3494cbacd..d242d0aa51 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,8 @@ A new header is inserted each time a *tag* is created. ## main branch +- engine: LiSPSM is now a user settable option + ## v1.27.2 - gltfio: punctual lights are now duplicated when adding new asset instances diff --git a/android/filament-android/src/main/cpp/LightManager.cpp b/android/filament-android/src/main/cpp/LightManager.cpp index d467fec4ac..c506b5d294 100644 --- a/android/filament-android/src/main/cpp/LightManager.cpp +++ b/android/filament-android/src/main/cpp/LightManager.cpp @@ -76,7 +76,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env, jclass, jlong nativeBuilder, jint mapSize, jint cascades, jfloatArray splitPositions, jfloat constantBias, jfloat normalBias, jfloat shadowFar, jfloat shadowNearHint, - jfloat shadowFarHint, jboolean stable, + jfloat shadowFarHint, jboolean stable, jboolean lispsm, jfloat polygonOffsetConstant, jfloat polygonOffsetSlope, jboolean screenSpaceContactShadows, jint stepCount, jfloat maxShadowDistance, jint vsmMsaaSamples, jfloat blurWidth, jfloat shadowBulbRadius) { @@ -90,6 +90,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env, .shadowNearHint = shadowNearHint, .shadowFarHint = shadowFarHint, .stable = (bool)stable, + .lispsm = (bool)lispsm, .polygonOffsetConstant = polygonOffsetConstant, .polygonOffsetSlope = polygonOffsetConstant, .screenSpaceContactShadows = (bool)screenSpaceContactShadows, diff --git a/android/filament-android/src/main/java/com/google/android/filament/LightManager.java b/android/filament-android/src/main/java/com/google/android/filament/LightManager.java index d83c891c50..d5c8c91f0b 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/LightManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/LightManager.java @@ -280,9 +280,25 @@ public class LightManager { * Controls whether the shadow map should be optimized for resolution or stability. * When set to true, all resolution enhancing features that can affect stability are * disabling, resulting in significantly lower resolution shadows, albeit stable ones. + * + * Setting this flag to true always disables LiSPSM (see below). */ public boolean stable = false; + /** + * LiSPSM, or light-space perspective shadow-mapping is a technique allowing to better + * optimize the use of the shadow-map texture. When enabled the effective resolution of + * shadows is greatly improved and yields result similar to using cascades without the + * extra cost. LiSPSM comes with some drawbacks however, in particular it is incompatible + * with blurring because it effectively affects the blur kernel size. + * + * Blurring is only an issue when using ShadowType.VSM with a large blur or with + * ShadowType.PCSS however. + * + * If these blurring artifacts become problematic, this flag can be used to disable LiSPSM. + */ + public boolean lispsm = false; + /** * Constant bias in depth-resolution units by which shadows are moved away from the * light. The default value of 0.5 is used to round depth values up. @@ -496,7 +512,7 @@ public class LightManager { nBuilderShadowOptions(mNativeBuilder, options.mapSize, options.shadowCascades, options.cascadeSplitPositions, options.constantBias, options.normalBias, options.shadowFar, options.shadowNearHint, - options.shadowFarHint, options.stable, + options.shadowFarHint, options.stable, options.lispsm, options.polygonOffsetConstant, options.polygonOffsetSlope, options.screenSpaceContactShadows, options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples, @@ -1158,7 +1174,14 @@ public class LightManager { private static native void nDestroyBuilder(long nativeBuilder); private static native boolean nBuilderBuild(long nativeBuilder, long nativeEngine, int entity); private static native void nBuilderCastShadows(long nativeBuilder, boolean enable); - private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize, int cascades, float[] splitPositions, float constantBias, float normalBias, float shadowFar, float shadowNearHint, float shadowFarhint, boolean stable, float polygonOffsetConstant, float polygonOffsetSlope, boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, int vsmMsaaSamples, float blurWidth, float shadowBulbRadius); + private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize, + int cascades, float[] splitPositions, + float constantBias, float normalBias, + float shadowFar, float shadowNearHint, float shadowFarhint, + boolean stable, boolean lispsm, + float polygonOffsetConstant, float polygonOffsetSlope, + boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, + int vsmMsaaSamples, float blurWidth, float shadowBulbRadius); private static native void nBuilderCastLight(long nativeBuilder, boolean enabled); private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z); private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z); diff --git a/filament/include/filament/LightManager.h b/filament/include/filament/LightManager.h index fad7d09df3..128e7f81de 100644 --- a/filament/include/filament/LightManager.h +++ b/filament/include/filament/LightManager.h @@ -266,9 +266,29 @@ public: * Controls whether the shadow map should be optimized for resolution or stability. * When set to true, all resolution enhancing features that can affect stability are * disabling, resulting in significantly lower resolution shadows, albeit stable ones. + * + * Setting this flag to true always disables LiSPSM (see below). + * + * @see lispsm */ bool stable = false; + /** + * LiSPSM, or light-space perspective shadow-mapping is a technique allowing to better + * optimize the use of the shadow-map texture. When enabled the effective resolution of + * shadows is greatly improved and yields result similar to using cascades without the + * extra cost. LiSPSM comes with some drawbacks however, in particular it is incompatible + * with blurring because it effectively affects the blur kernel size. + * + * Blurring is only an issue when using ShadowType::VSM with a large blur or with + * ShadowType::PCSS however. + * + * If these blurring artifacts become problematic, this flag can be used to disable LiSPSM. + * + * @see stable + */ + bool lispsm = true; + /** * Constant bias in depth-resolution units by which shadows are moved away from the * light. The default value of 0.5 is used to round depth values up. diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index 5578e718d8..c59b9203d0 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -40,8 +40,6 @@ using namespace backend; // do this only if depth-clamp is available static constexpr bool USE_DEPTH_CLAMP = false; -static constexpr bool ENABLE_LISPSM = true; - ShadowMap::ShadowMap(FEngine& engine) noexcept : mEngine(engine), mClipSpaceFlipped(engine.getBackend() == Backend::VULKAN), @@ -53,11 +51,8 @@ ShadowMap::ShadowMap(FEngine& engine) noexcept : FDebugRegistry& debugRegistry = engine.getDebugRegistry(); debugRegistry.registerProperty("d.shadowmap.focus_shadowcasters", &engine.debug.shadowmap.focus_shadowcasters); debugRegistry.registerProperty("d.shadowmap.far_uses_shadowcasters", &engine.debug.shadowmap.far_uses_shadowcasters); - if constexpr (ENABLE_LISPSM) { - debugRegistry.registerProperty("d.shadowmap.lispsm", &engine.debug.shadowmap.lispsm); - debugRegistry.registerProperty("d.shadowmap.dzn", &engine.debug.shadowmap.dzn); - debugRegistry.registerProperty("d.shadowmap.dzf", &engine.debug.shadowmap.dzf); - } + debugRegistry.registerProperty("d.shadowmap.dzn", &engine.debug.shadowmap.dzn); + debugRegistry.registerProperty("d.shadowmap.dzf", &engine.debug.shadowmap.dzf); } void ShadowMap::terminate(FEngine& engine) { @@ -231,7 +226,7 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde mHasVisibleShadows = vertexCount >= 4; if (mHasVisibleShadows) { // We can't use LISPSM in stable mode - const bool USE_LISPSM = ENABLE_LISPSM && mEngine.debug.shadowmap.lispsm && !params.options.stable; + const bool useLispsm = params.options.lispsm && !params.options.stable; /* * Compute the light's projection matrix @@ -252,7 +247,7 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde // Compute the LiSPSM warping mat4f W, Wp; mat4f L; // Rotation matrix in light space - if (USE_LISPSM) { + if (useLispsm) { // Orient the shadow map in the direction of the view vector by constructing a // rotation matrix in light space around the z-axis, that aligns the y-axis with the camera's // forward vector (V) -- this gives the wrap direction, vp, for LiSPSM. @@ -351,7 +346,7 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde // note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because // L * Mp * Mv is a rigid transform for directional lights, and doesn't matter. // if Wp[3][1] is 0, then LISPSM was cancelled. - if (USE_LISPSM && Wp[3][1] != 0.0f) { + if (useLispsm && Wp[3][1] != 0.0f) { mTexelSizeAtOneMeterWs = texelSizeWorldSpace(Wp, mat4f(MbMt * F)); } else { // We know we're using an ortho projection diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 0dd7ab2c57..db663d892e 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -497,7 +497,6 @@ public: struct { bool far_uses_shadowcasters = true; bool focus_shadowcasters = true; - bool lispsm = true; bool visualize_cascades = false; bool tightly_bound_scene = true; float dzn = -1.0f; diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index d1c31f5834..96daeb0377 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -346,6 +346,10 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, CHECK_KEY(tok); if (compare(tok, jsonChunk, "mapSize") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->mapSize); + } else if (compare(tok, jsonChunk, "stable") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->stable); + } else if (compare(tok, jsonChunk, "lispsm") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->lispsm); } else if (compare(tok, jsonChunk, "screenSpaceContactShadows") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->screenSpaceContactShadows); } else if (compare(tok, jsonChunk, "shadowCascades") == 0) { @@ -676,6 +680,8 @@ static std::ostream& operator<<(std::ostream& out, const LightManager::ShadowOpt << "\"blurWidth\": " << in.vsm.blurWidth << "\n" << "},\n" << "\"mapSize\": " << in.mapSize << ",\n" + << "\"stable\": " << to_string(in.stable) << ",\n" + << "\"lispsm\": " << to_string(in.lispsm) << ",\n" << "\"screenSpaceContactShadows\": " << to_string(in.screenSpaceContactShadows) << ",\n" << "\"shadowCascades\": " << int(in.shadowCascades) << ",\n" << "\"cascadeSplitPositions\": " << (splitsVector) << "\n" diff --git a/libs/viewer/src/ViewerGui.cpp b/libs/viewer/src/ViewerGui.cpp index d7f5fd2e34..22036e74b4 100644 --- a/libs/viewer/src/ViewerGui.cpp +++ b/libs/viewer/src/ViewerGui.cpp @@ -826,7 +826,8 @@ void ViewerGui::updateUserInterface() { int mapSize = light.shadowOptions.mapSize; ImGui::SliderInt("Shadow map size", &mapSize, 32, 1024); light.shadowOptions.mapSize = mapSize; - + ImGui::Checkbox("Stable Shadows", &light.shadowOptions.stable); + ImGui::Checkbox("Enable LiSPSM", &light.shadowOptions.lispsm); int shadowType = (int)mSettings.view.shadowType; ImGui::Combo("Shadow type", &shadowType, "PCF\0VSM\0DPCF\0PCSS\0\0"); diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 2036dd2862..c03184b9df 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -757,11 +757,6 @@ int main(int argc, char** argv) { ImGui::SliderFloat("Ki", debug.getPropertyAddress("d.view.pid.ki"), 0, 10); ImGui::SliderFloat("Kd", debug.getPropertyAddress("d.view.pid.kd"), 0, 10); #endif - bool* lispsm; - if (debug.getPropertyAddress("d.shadowmap.lispsm", &lispsm)) { - ImGui::Checkbox("Enable LiSPSM", lispsm); - } - const auto overdrawVisibilityBit = (1u << App::Scene::OVERDRAW_VISIBILITY_LAYER); bool visualizeOverdraw = view->getVisibleLayers() & overdrawVisibilityBit; // TODO: enable after stencil buffer supported is added for Vulkan. diff --git a/samples/material_sandbox.cpp b/samples/material_sandbox.cpp index cb25f79f1e..7df322fc1d 100644 --- a/samples/material_sandbox.cpp +++ b/samples/material_sandbox.cpp @@ -885,15 +885,12 @@ static void gui(filament::Engine* engine, filament::View*) { ImGui::SliderFloat("Polygon Offset Scale", ¶ms.polygonOffsetSlope, 0.0f, 10.0f); ImGui::SliderFloat("Polygon Offset Constant", ¶ms.polygonOffsetConstant, 0.0f, 10.0f); - bool* lispsm; - if (debug.getPropertyAddress("d.shadowmap.lispsm", &lispsm)) { - ImGui::Checkbox("Enable LiSPSM", lispsm); - if (*lispsm) { - ImGui::SliderFloat("dzn", - debug.getPropertyAddress("d.shadowmap.dzn"), 0.0f, 1.0f); - ImGui::SliderFloat("dzf", - debug.getPropertyAddress("d.shadowmap.dzf"),-1.0f, 0.0f); - } + ImGui::Checkbox("Enable LiSPSM", ¶ms.lispsm); + if (params.lispsm) { + ImGui::SliderFloat("dzn", + debug.getPropertyAddress("d.shadowmap.dzn"), 0.0f, 1.0f); + ImGui::SliderFloat("dzf", + debug.getPropertyAddress("d.shadowmap.dzf"),-1.0f, 0.0f); } ImGui::Unindent(); } @@ -942,6 +939,7 @@ static void gui(filament::Engine* engine, filament::View*) { LightManager::ShadowOptions options = lcm.getShadowOptions(lightInstance); options.stable = params.stableShadowMap; + options.lispsm = params.lispsm; options.normalBias = params.normalBias; options.constantBias = params.constantBias; options.polygonOffsetConstant = params.polygonOffsetConstant; diff --git a/samples/material_sandbox.h b/samples/material_sandbox.h index 8edef39f7c..273ba788fc 100644 --- a/samples/material_sandbox.h +++ b/samples/material_sandbox.h @@ -169,6 +169,7 @@ struct SandboxParameters { bool msaa = false; bool dithering = true; bool stableShadowMap = false; + bool lispsm = true; float normalBias = 1.0; float constantBias = 0.001; float polygonOffsetConstant = 0.5; diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 0ce003b771..7507efc6c8 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -325,6 +325,7 @@ value_object("LightManager$ShadowOptions") .field("shadowNearHint", &LightManager::ShadowOptions::shadowNearHint) .field("shadowFarHint", &LightManager::ShadowOptions::shadowFarHint) .field("stable", &LightManager::ShadowOptions::stable) + .field("lispsm", &LightManager::ShadowOptions::lispsm) .field("polygonOffsetConstant", &LightManager::ShadowOptions::polygonOffsetConstant) .field("polygonOffsetSlope", &LightManager::ShadowOptions::polygonOffsetSlope) .field("screenSpaceContactShadows", &LightManager::ShadowOptions::screenSpaceContactShadows)