diff --git a/android/filament-android/src/main/cpp/LightManager.cpp b/android/filament-android/src/main/cpp/LightManager.cpp index 3c9d9f99e2..0e2bba147d 100644 --- a/android/filament-android/src/main/cpp/LightManager.cpp +++ b/android/filament-android/src/main/cpp/LightManager.cpp @@ -23,6 +23,13 @@ using namespace filament; using namespace utils; +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_LightManager_nGetComponentCount(JNIEnv*, jclass, + jlong nativeLightManager) { + LightManager *lm = (LightManager *) nativeLightManager; + return lm->getComponentCount(); +} + extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_LightManager_nHasComponent(JNIEnv*, jclass, jlong nativeLightManager, jint entity) { 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 d7a2d08c26..99e639d96c 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 @@ -27,6 +27,10 @@ public class LightManager { mNativeObject = nativeLightManager; } + public int getComponentCount() { + return nGetComponentCount(mNativeObject); + } + public boolean hasComponent(@Entity int entity) { return nHasComponent(mNativeObject, entity); } @@ -278,6 +282,7 @@ public class LightManager { return mNativeObject; } + private static native int nGetComponentCount(long nativeLightManager); private static native boolean nHasComponent(long nativeLightManager, int entity); private static native int nGetInstance(long nativeLightManager, int entity); private static native void nDestroy(long nativeLightManager, int entity); diff --git a/filament/include/filament/LightManager.h b/filament/include/filament/LightManager.h index a4ee84e48f..1e1421d660 100644 --- a/filament/include/filament/LightManager.h +++ b/filament/include/filament/LightManager.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -144,6 +145,21 @@ class UTILS_PUBLIC LightManager : public FilamentAPI { public: using Instance = utils::EntityInstance; + /** + * Returns the number of component in the LightManager, not that component are not + * guaranteed to be active. Use the EntityManager::isAlive() before use if needed. + * + * @return number of component in the LightManager + */ + size_t getComponentCount() const noexcept; + + /** + * Returns the list of Entity for all components. Use getComponentCount() to know the size + * of the list. + * @return a pointer to Entity + */ + utils::Entity const* getEntities() const noexcept; + /** * Returns whether a particular Entity is associated with a component of this LightManager * @param e An Entity. @@ -236,22 +252,28 @@ public: /** * Whether screen-space contact shadows are used. This applies regardless of whether a - * Renderable is a shadow caster. This setting is currently only used for directional - * lights, ignored otherwise. + * Renderable is a shadow caster. * Screen-space contact shadows are typically useful in large scenes. * (off by default) */ bool screenSpaceContactShadows = false; /** - * Number of ray-marching steps for screen-space contact shadows. - * (8 by default) + * Number of ray-marching steps for screen-space contact shadows (8 by default). + * + * CAUTION: this parameter is ignored for all lights except the directional/sun light, + * all other lights use the same value set for the directional/sun light. + * */ uint8_t stepCount = 8; /** * Maximum shadow-occluder distance for screen-space contact shadows (world units). * (30 cm by default) + * + * CAUTION: this parameter is ignored for all lights except the directional/sun light, + * all other lights use the same value set for the directional/sun light. + * */ float maxShadowDistance = 0.3; }; @@ -721,6 +743,19 @@ public: * @param i Instance of the component obtained from getInstance(). */ bool isShadowCaster(Instance i) const noexcept; + + /** + * Helper to process all components with a given function + * @tparam F a void(Entity entity, Instance instance) + * @param func a function of type F + */ + template + void forEachComponent(F func) noexcept { + utils::Entity const* const pEntity = getEntities(); + for (size_t i = 0, c = getComponentCount(); i < c; i++) { + func(pEntity[i], Instance(i)); + } + } }; } // namespace filament diff --git a/filament/src/Scene.cpp b/filament/src/Scene.cpp index 17e96707bd..fe5c4682fb 100644 --- a/filament/src/Scene.cpp +++ b/filament/src/Scene.cpp @@ -403,14 +403,23 @@ void FScene::setSkybox(FSkybox* skybox) noexcept { } bool FScene::hasContactShadows() const noexcept { - bool hasContactShadows = mHasContactShadows; + // find out if at least one light has contact-shadow enabled + // TODO: cache the the result of this Loop in the LightManager + bool hasContactShadows = false; auto& lcm = mEngine.getLightManager(); - FLightManager::Instance directionalLight = mLightData.elementAt(0); - if (directionalLight.isValid()) { - auto const& shadowOoptions = lcm.getShadowOptions(directionalLight); - hasContactShadows = hasContactShadows && shadowOoptions.screenSpaceContactShadows; + auto pFirst = mLightData.begin(); + auto pLast = mLightData.end(); + while (pFirst != pLast && !hasContactShadows) { + if (pFirst->isValid()) { + auto const& shadowOptions = lcm.getShadowOptions(*pFirst); + hasContactShadows = shadowOptions.screenSpaceContactShadows; + } + ++pFirst; } - return hasContactShadows; + + // at least some renderables in the scene must have contact-shadows enabled + // TODO: we should refine this with only the visible ones + return hasContactShadows && mHasContactShadows; } } // namespace details diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 59dcec40ac..11f1304cf9 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -172,21 +172,23 @@ bool ShadowMapManager::update(FEngine& engine, FView& view, UniformBuffer& perVi hasShadowing = true; directionalShadows |= 0x1u; } + } - // screen-space contact shadows - LightManager::ShadowOptions const& options = lcm.getShadowOptions(light); - if (options.screenSpaceContactShadows) { - hasShadowing = true; - directionalShadows |= std::min(uint8_t(255u), options.stepCount) << 8u; - screenSpaceShadowDistance = options.maxShadowDistance; - } + // screen-space contact shadows for the directional light + FLightManager::Instance directionalLight = lightData.elementAt(0); + LightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight); + screenSpaceShadowDistance = options.maxShadowDistance; + directionalShadows |= std::min(uint8_t(255u), options.stepCount) << 8u; + if (options.screenSpaceContactShadows) { + hasShadowing = true; + directionalShadows |= 0x2u; } perViewUb.setUniform(offsetof(PerViewUib, directionalShadows), directionalShadows); perViewUb.setUniform(offsetof(PerViewUib, ssContactShadowDistance), screenSpaceShadowDistance); + // shadow-map shadows for point/spot lights FScene::ShadowInfo* const shadowInfo = lightData.data(); - for (size_t i = 0, c = mSpotShadowMaps.size(); i < c; i++) { auto& entry = mSpotShadowMaps[i]; @@ -202,6 +204,8 @@ bool ShadowMapManager::update(FEngine& engine, FView& view, UniformBuffer& perVi .shadowDimension = textureDimension - 2 }; shadowMap.update(lightData, l, scene, viewingCameraInfo, visibleLayers, layout); + + FLightManager::Instance light = lightData.elementAt(l); if (shadowMap.hasVisibleShadows()) { entry.setHasVisibleShadows(true); @@ -219,7 +223,6 @@ bool ShadowMapManager::update(FEngine& engine, FView& view, UniformBuffer& perVi shadowInfo[l].index = i; shadowInfo[l].layer = mSpotShadowMaps[i].getLayout().layer; - FLightManager::Instance light = lightData.elementAt(l); const float3 dir = lightData.elementAt(l); const float texelSizeWorldSpace = shadowMap.getTexelSizeWorldSpace(); const float normalBias = lcm.getShadowNormalBias(light); @@ -230,6 +233,18 @@ bool ShadowMapManager::update(FEngine& engine, FView& view, UniformBuffer& perVi } } + // screen-space contact shadows for point/spot lights + auto pInstance = lightData.data(); + for (size_t i = 0, c = lightData.size(); i < c; i++) { + // screen-space contact shadows + LightManager::ShadowOptions const& shadowOptions = lcm.getShadowOptions(pInstance[i]); + if (shadowOptions.screenSpaceContactShadows) { + hasShadowing = true; + shadowInfo[i].contactShadows = true; + // TODO: distance/steps are always taken from the directional light currently + } + } + return hasShadowing; } diff --git a/filament/src/View.cpp b/filament/src/View.cpp index 7a39e50caa..2f7fd48e0e 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -574,7 +574,7 @@ void FView::computeVisibilityMasks( uint8_t visibleLayers, uint8_t const* UTILS_RESTRICT layers, FRenderableManager::Visibility const* UTILS_RESTRICT visibility, - uint8_t* UTILS_RESTRICT visibleMask, size_t count) const { + uint8_t* UTILS_RESTRICT visibleMask, size_t count) { // __restrict__ seems to only be taken into account as function parameters. This is very // important here, otherwise, this loop doesn't get vectorized. // This is vectorized 16x. diff --git a/filament/src/components/LightManager.cpp b/filament/src/components/LightManager.cpp index 3dc21b5509..3e309cbe8d 100644 --- a/filament/src/components/LightManager.cpp +++ b/filament/src/components/LightManager.cpp @@ -347,12 +347,19 @@ void FLightManager::setShadowCaster(Instance i, bool shadowCaster) noexcept { using namespace details; +size_t LightManager::getComponentCount() const noexcept { + return upcast(this)->getComponentCount(); +} + +utils::Entity const* LightManager::getEntities() const noexcept { + return upcast(this)->getEntities(); +} + bool LightManager::hasComponent(Entity e) const noexcept { return upcast(this)->hasComponent(e); } -LightManager::Instance -LightManager::getInstance(Entity e) const noexcept { +LightManager::Instance LightManager::getInstance(Entity e) const noexcept { return upcast(this)->getInstance(e); } diff --git a/filament/src/components/LightManager.h b/filament/src/components/LightManager.h index 41f421d759..3d30aa4680 100644 --- a/filament/src/components/LightManager.h +++ b/filament/src/components/LightManager.h @@ -45,6 +45,14 @@ public: void terminate() noexcept; + size_t getComponentCount() const noexcept { + return mManager.getComponentCount(); + } + + utils::Entity const* getEntities() const noexcept { + return mManager.getEntities(); + } + bool hasComponent(utils::Entity e) const noexcept { return mManager.hasComponent(e); } diff --git a/filament/src/details/Scene.h b/filament/src/details/Scene.h index d036582158..7407cf8397 100644 --- a/filament/src/details/Scene.h +++ b/filament/src/details/Scene.h @@ -155,20 +155,23 @@ public: // They're packed into 32 bits and stored in the Lights uniform buffer. // They're unpacked in the fragment shader and used to calculate punctual shadows. bool castsShadows = false; // whether this light casts shadows + bool contactShadows = false; // whether this light casts contact shadows uint8_t index = 0; // an index into the arrays in the Shadows uniform buffer uint8_t layer = 0; // which layer of the shadow texture array to sample from // -- LSB ------------- // castsShadows : 1 + // contactShadows : 1 // index : 4 // layer : 4 // -- MSB ------------- uint32_t pack() const { assert(index < 16); assert(layer < 16); - return uint8_t(castsShadows) << 0u | - index << 1u | - layer << 5u; + return uint8_t(castsShadows) << 0u | + uint8_t(contactShadows) << 1u | + index << 2u | + layer << 6u; } }; diff --git a/filament/src/details/View.h b/filament/src/details/View.h index e12c5e8141..24f1ec858d 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -328,10 +328,10 @@ private: FLightManager const& lcm, utils::JobSystem& js, Frustum const& frustum, FScene::LightSoa& lightData) noexcept; - void computeVisibilityMasks( + static void computeVisibilityMasks( uint8_t visibleLayers, uint8_t const* layers, FRenderableManager::Visibility const* visibility, uint8_t* visibleMask, - size_t count) const; + size_t count) ; void bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept { driver.bindUniformBuffer(BindingPoints::PER_VIEW, mPerViewUbh); diff --git a/libs/filabridge/include/private/filament/UibGenerator.h b/libs/filabridge/include/private/filament/UibGenerator.h index ceea90d834..4d0ad0ac7a 100644 --- a/libs/filabridge/include/private/filament/UibGenerator.h +++ b/libs/filabridge/include/private/filament/UibGenerator.h @@ -90,6 +90,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float refractionLodOffset; // bit 0: directional (sun) shadow enabled + // bit 1: directional (sun) screen-space contact shadow enabled // bit 8-15: screen-space contact shadows ray casting steps uint32_t directionalShadows; @@ -119,8 +120,8 @@ struct alignas(256) PerRenderableUib { filament::math::mat3f worldFromModelNormalMatrix; // this gets expanded to 48 bytes during the copy to the UBO alignas(16) filament::math::float4 morphWeights; // TODO: we can pack all the boolean bellow - uint32_t skinningEnabled; // 0=disabled, 1=enabled, ignored unless variant & SKINNING_OR_MORPHING - uint32_t morphingEnabled; // 0=disabled, 1=enabled, ignored unless variant & SKINNING_OR_MORPHING + int32_t skinningEnabled; // 0=disabled, 1=enabled, ignored unless variant & SKINNING_OR_MORPHING + int32_t morphingEnabled; // 0=disabled, 1=enabled, ignored unless variant & SKINNING_OR_MORPHING uint32_t screenSpaceContactShadows; // 0=disabled, 1=enabled, ignored unless variant & SKINNING_OR_MORPHING float padding0; }; diff --git a/libs/filabridge/src/UibGenerator.cpp b/libs/filabridge/src/UibGenerator.cpp index cd4d5bd799..411d15d6ae 100644 --- a/libs/filabridge/src/UibGenerator.cpp +++ b/libs/filabridge/src/UibGenerator.cpp @@ -108,7 +108,7 @@ UniformInterfaceBlock const& UibGenerator::getPerRenderableUib() noexcept { .add("morphWeights", 1, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH) .add("skinningEnabled", 1, UniformInterfaceBlock::Type::INT) .add("morphingEnabled", 1, UniformInterfaceBlock::Type::INT) - .add("screenSpaceContactShadows", 1, UniformInterfaceBlock::Type::INT) + .add("screenSpaceContactShadows", 1, UniformInterfaceBlock::Type::UINT) .add("padding0", 1, UniformInterfaceBlock::Type::FLOAT) .build(); return uib; diff --git a/libs/gltfio/include/gltfio/SimpleViewer.h b/libs/gltfio/include/gltfio/SimpleViewer.h index 0401fd682c..208549a8bd 100644 --- a/libs/gltfio/include/gltfio/SimpleViewer.h +++ b/libs/gltfio/include/gltfio/SimpleViewer.h @@ -517,13 +517,17 @@ void SimpleViewer::updateUserInterface() { lm.setDirection(sun, normalize(mSunlightDirection)); lm.setColor(sun, mSunlightColor); lm.setShadowCaster(sun, mEnableShadows); - auto options = lm.getShadowOptions(sun); - options.screenSpaceContactShadows = mEnableContactShadows; - lm.setShadowOptions(sun, options); } else { mScene->remove(mSunlight); } + lm.forEachComponent([this, &lm](utils::Entity e, LightManager::Instance ci) { + auto options = lm.getShadowOptions(ci); + options.screenSpaceContactShadows = mEnableContactShadows; + lm.setShadowOptions(ci, options); + lm.setShadowCaster(ci, mEnableShadows); + }); + if (mAsset != nullptr) { if (ImGui::CollapsingHeader("Model", headerFlags)) { if (mAnimator->getAnimationCount() > 0) { diff --git a/shaders/src/common_lighting.fs b/shaders/src/common_lighting.fs index 7c17d15dcd..4338114c66 100644 --- a/shaders/src/common_lighting.fs +++ b/shaders/src/common_lighting.fs @@ -4,6 +4,7 @@ struct Light { float attenuation; float NoL; bool castsShadows; + bool contactShadows; uint shadowIndex; uint shadowLayer; }; diff --git a/shaders/src/light_directional.fs b/shaders/src/light_directional.fs index c6ce1b493a..e2e03faa4d 100644 --- a/shaders/src/light_directional.fs +++ b/shaders/src/light_directional.fs @@ -43,9 +43,8 @@ void evaluateDirectionalLight(const MaterialInputs material, if ((frameUniforms.directionalShadows & 1u) != 0u) { visibility = shadow(light_shadowMap, 0u, getLightSpacePosition()); } - - if (visibility > 0.0 && frameUniforms.ssContactShadowDistance != 0.0) { - if (objectUniforms.screenSpaceContactShadows != 0) { + if ((frameUniforms.directionalShadows & 0x2u) != 0u && visibility > 0.0) { + if (objectUniforms.screenSpaceContactShadows != 0u) { ssContactShadowOcclusion = screenSpaceContactShadow(light.l); } } diff --git a/shaders/src/light_punctual.fs b/shaders/src/light_punctual.fs index 54ccb57814..71197d7f8d 100644 --- a/shaders/src/light_punctual.fs +++ b/shaders/src/light_punctual.fs @@ -147,8 +147,9 @@ Light getSpotLight(uint index) { uint shadowBits = floatBitsToUint(scaleOffsetShadow.z); light.castsShadows = bool(shadowBits & 0x1u); - light.shadowIndex = ((shadowBits & 0x01Eu) >> 1u); - light.shadowLayer = ((shadowBits & 0x1E0u) >> 5u); + light.contactShadows = bool((shadowBits >> 1u) & 0x1u); + light.shadowIndex = (shadowBits >> 2u) & 0xFu; + light.shadowLayer = (shadowBits >> 6u) & 0xFu; return light; } @@ -199,13 +200,22 @@ void evaluatePunctualLights(const PixelParams pixel, inout vec3 color) { // Iterate point lights for ( ; index < end; index++) { Light light = getPointLight(index); -#if defined(MATERIAL_CAN_SKIP_LIGHTING) - if (light.NoL > 0.0) { - color.rgb += surfaceShading(pixel, light, 1.0); + float visibility = 1.0; +#if defined(HAS_SHADOWING) + if (light.NoL > 0.0){ + if (light.contactShadows) { + if (objectUniforms.screenSpaceContactShadows != 0u) { + visibility -= screenSpaceContactShadow(light.l); + } + } } -#else - color.rgb += surfaceShading(pixel, light, 1.0); #endif +#if defined(MATERIAL_CAN_SKIP_LIGHTING) + if (light.NoL <= 0.0) { + continue; + } +#endif + color.rgb += surfaceShading(pixel, light, visibility); } end += froxel.spotCount; @@ -215,17 +225,23 @@ void evaluatePunctualLights(const PixelParams pixel, inout vec3 color) { Light light = getSpotLight(index); float visibility = 1.0; #if defined(HAS_SHADOWING) - if (light.castsShadows && light.NoL > 0.0) { - visibility = shadow(light_shadowMap, light.shadowLayer, - getSpotLightSpacePosition(light.shadowIndex)); + if (light.NoL > 0.0){ + if (light.castsShadows) { + visibility = shadow(light_shadowMap, light.shadowLayer, + getSpotLightSpacePosition(light.shadowIndex)); + } + if (light.contactShadows && visibility > 0.0) { + if (objectUniforms.screenSpaceContactShadows != 0u) { + visibility *= 1.0 - screenSpaceContactShadow(light.l); + } + } } #endif #if defined(MATERIAL_CAN_SKIP_LIGHTING) - if (light.NoL > 0.0) { - color.rgb += surfaceShading(pixel, light, visibility); + if (light.NoL <= 0.0) { + continue; } -#else - color.rgb += surfaceShading(pixel, light, visibility); #endif + color.rgb += surfaceShading(pixel, light, visibility); } } diff --git a/shaders/src/shading_unlit.fs b/shaders/src/shading_unlit.fs index fcaa677f9e..28a77a675c 100644 --- a/shaders/src/shading_unlit.fs +++ b/shaders/src/shading_unlit.fs @@ -40,8 +40,8 @@ vec4 evaluateMaterial(const MaterialInputs material) { if ((frameUniforms.directionalShadows & 1u) != 0u) { visibility = shadow(light_shadowMap, 0u, getLightSpacePosition()); } - if (visibility > 0.0 && frameUniforms.ssContactShadowDistance != 0.0) { - if (objectUniforms.screenSpaceContactShadows != 0) { + if ((frameUniforms.directionalShadows & 0x2u) != 0u && visibility > 0.0) { + if (objectUniforms.screenSpaceContactShadows != 0u) { visibility *= (1.0 - screenSpaceContactShadow(frameUniforms.lightDirection)); } }