From 8a81486f5d03897d65233dd543a21269b24d10f3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 29 Jul 2021 11:59:21 -0700 Subject: [PATCH] Implement light channels Currently there are 8 light channels, both renderables and lights can be associated to one or several light channel, lighting occurs when at least one channel is active on both the light and the renderable. Fixes #4275 --- RELEASE_NOTES.md | 2 + .../src/main/cpp/LightManager.cpp | 39 +++++++++--- .../src/main/cpp/RenderableManager.cpp | 23 +++++++ .../google/android/filament/LightManager.java | 39 ++++++++++++ .../android/filament/RenderableManager.java | 39 ++++++++++++ filament/include/filament/LightManager.h | 24 ++++++++ filament/include/filament/RenderableManager.h | 24 ++++++++ filament/src/Scene.cpp | 11 +++- filament/src/View.cpp | 1 + filament/src/components/LightManager.cpp | 60 ++++++++++++++++--- filament/src/components/LightManager.h | 12 +++- filament/src/components/RenderableManager.cpp | 41 ++++++++++++- filament/src/components/RenderableManager.h | 11 ++++ filament/src/details/Scene.h | 2 + .../include/private/filament/UibStructs.h | 5 +- libs/filamat/src/UibGenerator.cpp | 5 +- shaders/src/common_lighting.fs | 1 + shaders/src/light_directional.fs | 6 ++ shaders/src/light_punctual.fs | 5 ++ web/filament-js/jsbindings.cpp | 13 +++- 20 files changed, 340 insertions(+), 23 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index edf23fe7fc..65e8ae9876 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,8 @@ A new header is inserted each time a *tag* is created. - engine: Option to automatically compute bent normals from SSAO & apply to specular AO [⚠️ **Material breakage**]. +- engine: New APIs: Light channels. Geometry and lights now have a channel associated to them, at + least one channel must match for lighting to occur [⚠️ **Material breakage**]. ## v1.11.2 diff --git a/android/filament-android/src/main/cpp/LightManager.cpp b/android/filament-android/src/main/cpp/LightManager.cpp index db19de6f2d..84df8ecd7f 100644 --- a/android/filament-android/src/main/cpp/LightManager.cpp +++ b/android/filament-android/src/main/cpp/LightManager.cpp @@ -189,6 +189,23 @@ Java_com_google_android_filament_LightManager_nBuilderHaloFalloff(JNIEnv*, jclas builder->sunHaloFalloff(haloFalloff); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_LightManager_nBuilderLightChannel(JNIEnv*, jclass, + jlong nativeBuilder, jint channel, jboolean enable) { + LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder; + builder->lightChannel(channel, (bool)enable); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_LightManager_nBuilderBuild(JNIEnv*, jclass, + jlong nativeBuilder, jlong nativeEngine, jint entity) { + LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder; + Engine *engine = (Engine *) nativeEngine; + return jboolean(builder->build(*engine, (Entity &) entity) == LightManager::Builder::Success); +} + +// ------------------------------------------------------------------------------------------------ + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_LightManager_nComputeUniformSplits(JNIEnv* env, jclass, jfloatArray splitPositions, jint cascades) { @@ -213,13 +230,7 @@ Java_com_google_android_filament_LightManager_nComputePracticalSplits(JNIEnv* en env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0); } -extern "C" JNIEXPORT jboolean JNICALL -Java_com_google_android_filament_LightManager_nBuilderBuild(JNIEnv*, jclass, - jlong nativeBuilder, jlong nativeEngine, jint entity) { - LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder; - Engine *engine = (Engine *) nativeEngine; - return jboolean(builder->build(*engine, (Entity &) entity) == LightManager::Builder::Success); -} +// ------------------------------------------------------------------------------------------------ extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_LightManager_nGetType(JNIEnv* env, @@ -394,3 +405,17 @@ Java_com_google_android_filament_LightManager_nGetInnerConeAngle(JNIEnv*, jclass LightManager *lm = (LightManager *) nativeLightManager; return (jfloat)lm->getSpotLightInnerCone((LightManager::Instance) i); } + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_LightManager_nSetLightChannel(JNIEnv*, jclass, + jlong nativeLightManager, jint i, jint channel, jboolean enable) { + LightManager *lm = (LightManager *) nativeLightManager; + lm->setLightChannel((LightManager::Instance) i, channel, (bool)enable); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_LightManager_nGetLightChannel(JNIEnv*, jclass, + jlong nativeLightManager, jint i, jint channel) { + LightManager const *lm = (LightManager const *) nativeLightManager; + return lm->getLightChannel((LightManager::Instance) i, channel); +} diff --git a/android/filament-android/src/main/cpp/RenderableManager.cpp b/android/filament-android/src/main/cpp/RenderableManager.cpp index fb77f0ed47..cba710637c 100644 --- a/android/filament-android/src/main/cpp/RenderableManager.cpp +++ b/android/filament-android/src/main/cpp/RenderableManager.cpp @@ -214,6 +214,15 @@ Java_com_google_android_filament_RenderableManager_nBuilderMorphing(JNIEnv*, jcl builder->morphing(enabled); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_RenderableManager_nBuilderLightChannel(JNIEnv*, jclass, + jlong nativeBuilder, jint channel, jboolean enable) { + RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder; + builder->lightChannel(channel, (bool)enable); +} + +// ------------------------------------------------------------------------------------------------ + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_RenderableManager_nSetSkinningBuffer(JNIEnv*, jclass, jlong nativeRenderableManager, jint i, jlong nativeSkinningBuffer, jint count, jint offset) { @@ -404,3 +413,17 @@ Java_com_google_android_filament_RenderableManager_nGetEnabledAttributesAt(JNIEn AttributeBitset enabled = rm->getEnabledAttributesAt((RenderableManager::Instance) i, (size_t) primitiveIndex); return enabled.getValue(); } + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_RenderableManager_nSetLightChannel(JNIEnv*, jclass, + jlong nativeRenderableManager, jint i, jint channel, jboolean enable) { + RenderableManager *rm = (RenderableManager *) nativeRenderableManager; + rm->setLightChannel((RenderableManager::Instance) i, channel, (bool)enable); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_RenderableManager_nGetLightChannel(JNIEnv*, jclass, + jlong nativeRenderableManager, jint i, jint channel) { + RenderableManager const *rm = (RenderableManager const *) nativeRenderableManager; + return rm->getLightChannel((RenderableManager::Instance) i, channel); +} 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 4409345ce2..f3c4c5cb4a 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 @@ -430,6 +430,18 @@ public class LightManager { mFinalizer = new BuilderFinalizer(mNativeBuilder); } + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * + * @param channel Light channel to enable or disable, between 0 and 7. + * @param enable Whether to enable or disable the light channel. + */ + @NonNull + public Builder lightChannel(@IntRange(from = 0, to = 7) int channel, boolean enable) { + nBuilderLightChannel(mNativeBuilder, channel, enable); + return this; + } + /** * Whether this Light casts shadows (disabled by default) * @@ -785,6 +797,30 @@ public class LightManager { return type == Type.SPOT || type == Type.FOCUSED_SPOT; } + /** + * Enables or disables a light channel. + * Light channel 0 is enabled by default. + * + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to set + * @param enable true to enable, false to disable + * + * @see Builder#lightChannel + */ + public void setLightChannel(@EntityInstance int i, @IntRange(from = 0, to = 7) int channel, boolean enable) { + nSetLightChannel(mNativeObject, i, channel, enable); + } + + /** + * Returns whether a light channel is enabled on a specified renderable. + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to query + * @return true if the light channel is enabled, false otherwise + */ + public boolean getLightChannel(@EntityInstance int i, @IntRange(from = 0, to = 7) int channel) { + return nGetLightChannel(mNativeObject, i, channel); + } + /** * Dynamically updates the light's position. * @@ -1108,6 +1144,7 @@ public class LightManager { private static native void nBuilderAngularRadius(long nativeBuilder, float angularRadius); private static native void nBuilderHaloSize(long nativeBuilder, float haloSize); private static native void nBuilderHaloFalloff(long nativeBuilder, float haloFalloff); + private static native void nBuilderLightChannel(long nativeBuilder, int channel, boolean enable); private static native void nComputeUniformSplits(float[] splitPositions, int cascades); private static native void nComputeLogSplits(float[] splitPositions, int cascades, float near, float far); @@ -1137,4 +1174,6 @@ public class LightManager { private static native boolean nIsShadowCaster(long nativeLightManager, int i); private static native float nGetOuterConeAngle(long nativeLightManager, int i); private static native float nGetInnerConeAngle(long nativeLightManager, int i); + private static native void nSetLightChannel(long nativeLightManager, int i, int channel, boolean enable); + private static native boolean nGetLightChannel(long nativeLightManager, int i, int channel); } diff --git a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java index 49e1570719..be9c6c6559 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java @@ -266,6 +266,18 @@ public class RenderableManager { return this; } + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * + * @param channel Light channel to enable or disable, between 0 and 7. + * @param enable Whether to enable or disable the light channel. + */ + @NonNull + public Builder lightChannel(@IntRange(from = 0, to = 7) int channel, boolean enable) { + nBuilderLightChannel(mNativeBuilder, channel, enable); + return this; + } + /** * Controls if this renderable casts shadows, false by default. * @@ -531,6 +543,30 @@ public class RenderableManager { nSetCulling(mNativeObject, i, enabled); } + /** + * Enables or disables a light channel. + * Light channel 0 is enabled by default. + * + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to set + * @param enable true to enable, false to disable + * + * @see Builder#lightChannel + */ + public void setLightChannel(@EntityInstance int i, @IntRange(from = 0, to = 7) int channel, boolean enable) { + nSetLightChannel(mNativeObject, i, channel, enable); + } + + /** + * Returns whether a light channel is enabled on a specified renderable. + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to query + * @return true if the light channel is enabled, false otherwise + */ + public boolean getLightChannel(@EntityInstance int i, @IntRange(from = 0, to = 7) int channel) { + return nGetLightChannel(mNativeObject, i, channel); + } + /** * Changes whether or not the renderable casts shadows. * @@ -718,6 +754,7 @@ public class RenderableManager { private static native void nBuilderSkinningBuffer(long nativeBuilder, long nativeSkinningBuffer, int boneCount, int offset); private static native void nBuilderMorphing(long nativeBuilder, boolean enabled); private static native void nEnableSkinningBuffers(long nativeBuilder, boolean enabled); + private static native void nBuilderLightChannel(long nativeRenderableManager, int channel, boolean enable); private static native void nSetSkinningBuffer(long nativeObject, int i, long nativeSkinningBuffer, int count, int offset); private static native int nSetBonesAsMatrices(long nativeObject, int i, Buffer matrices, int remaining, int boneCount, int offset); @@ -727,6 +764,8 @@ public class RenderableManager { private static native void nSetLayerMask(long nativeRenderableManager, int i, int select, int value); private static native void nSetPriority(long nativeRenderableManager, int i, int priority); private static native void nSetCulling(long nativeRenderableManager, int i, boolean enabled); + private static native void nSetLightChannel(long nativeRenderableManager, int i, int channel, boolean enable); + private static native boolean nGetLightChannel(long nativeRenderableManager, int i, int channel); private static native void nSetCastShadows(long nativeRenderableManager, int i, boolean enabled); private static native void nSetReceiveShadows(long nativeRenderableManager, int i, boolean enabled); private static native void nSetScreenSpaceContactShadows(long nativeRenderableManager, int i, boolean enabled); diff --git a/filament/include/filament/LightManager.h b/filament/include/filament/LightManager.h index 83d1da3c2e..a0e0441dd2 100644 --- a/filament/include/filament/LightManager.h +++ b/filament/include/filament/LightManager.h @@ -395,6 +395,15 @@ public: Builder& operator=(Builder const& rhs) noexcept; Builder& operator=(Builder&& rhs) noexcept; + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * + * @param channel Light channel to enable or disable, between 0 and 7. + * @param enable Whether to enable or disable the light channel. + * @return This Builder, for chaining calls. + */ + Builder& lightChannel(unsigned int channel, bool enable = true) noexcept; + /** * Whether this Light casts shadows (disabled by default) * @@ -661,6 +670,21 @@ public: return type == Type::SPOT || type == Type::FOCUSED_SPOT; } + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * @param channel light channel to enable or disable, between 0 and 7. + * @param enable whether to enable (true) or disable (false) the specified light channel. + */ + void setLightChannel(Instance i, unsigned int channel, bool enable = true) noexcept; + + /** + * Returns whether a light channel is enabled on a specified light. + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to query + * @return true if the light channel is enabled, false otherwise + */ + bool getLightChannel(Instance i, unsigned int channel) const noexcept; + /** * Dynamically updates the light's position. * diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index 8f2b6775bb..b9e0577124 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -218,6 +218,14 @@ public: */ Builder& culling(bool enable) noexcept; + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * + * @param channel Light channel to enable or disable, between 0 and 7. + * @param enable Whether to enable or disable the light channel. + */ + Builder& lightChannel(unsigned int channel, bool enable = true) noexcept; + /** * Controls if this renderable casts shadows, false by default. * @@ -381,6 +389,22 @@ public: */ void setCulling(Instance instance, bool enable) noexcept; + /** + * Enables or disables a light channel. + * Light channel 0 is enabled by default. + * + * \see Builder::lightChannel() + */ + void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept; + + /** + * Returns whether a light channel is enabled on a specified renderable. + * @param instance Instance of the component obtained from getInstance(). + * @param channel Light channel to query + * @return true if the light channel is enabled, false otherwise + */ + bool getLightChannel(Instance instance, unsigned int channel) const noexcept; + /** * Changes whether or not the renderable casts shadows. * diff --git a/filament/src/Scene.cpp b/filament/src/Scene.cpp index daef9d2584..6ea8144b67 100644 --- a/filament/src/Scene.cpp +++ b/filament/src/Scene.cpp @@ -140,6 +140,7 @@ void FScene::prepare(const mat4f& worldOriginTransform, bool shadowReceiversAreC worldAABB.center, // WORLD_AABB_CENTER 0, // VISIBLE_MASK rcm.getMorphWeights(ri), // MORPH_WEIGHTS + rcm.getChannels(ri), // CHANNELS rcm.getLayerMask(ri), // LAYERS worldAABB.halfExtent, // WORLD_AABB_EXTENT {}, // PRIMITIVES @@ -239,6 +240,7 @@ void FScene::updateUBOs(utils::Range visibleRenderables, backend::Hand FRenderableManager::Visibility visibility = sceneData.elementAt(i); hasContactShadows = hasContactShadows || visibility.screenSpaceContactShadows; + UniformBuffer::setUniform(buffer, offset + offsetof(PerRenderableUib, flags), PerRenderableUib::packFlags( @@ -250,9 +252,14 @@ void FScene::updateUBOs(utils::Range visibleRenderables, backend::Hand offset + offsetof(PerRenderableUib, morphWeights), sceneData.elementAt(i)); + UniformBuffer::setUniform(buffer, + offset + offsetof(PerRenderableUib, channels), + (uint32_t)sceneData.elementAt(i)); + // TODO: We need to find a better way to provide the scale information per object UniformBuffer::setUniform(buffer, - offset + offsetof(PerRenderableUib, userData), sceneData.elementAt(i)); + offset + offsetof(PerRenderableUib, userData), + sceneData.elementAt(i)); } // TODO: handle static objects separately @@ -309,7 +316,7 @@ void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootAren shadowInfo[i].contactShadows, shadowInfo[i].index, shadowInfo[i].layer); - lp[gpuIndex].channels = LightsUib::packChannels(1, shadowInfo[i].castsShadows); + lp[gpuIndex].channels = LightsUib::packChannels(lcm.getLightChannels(li), shadowInfo[i].castsShadows); lp[gpuIndex].reserved = {}; } diff --git a/filament/src/View.cpp b/filament/src/View.cpp index 99e2bd8bbe..d77bcfd8e3 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -355,6 +355,7 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc s.lightDirection = l; s.lightColorIntensity = colorIntensity; + s.lightChannels = lcm.getLightChannels(directionalLight); const bool isSun = lcm.isSunLight(directionalLight); // The last parameter must be < 0.0f for regular directional lights diff --git a/filament/src/components/LightManager.cpp b/filament/src/components/LightManager.cpp index 5972eb2a64..51d36baf51 100644 --- a/filament/src/components/LightManager.cpp +++ b/filament/src/components/LightManager.cpp @@ -24,6 +24,8 @@ #include #include +#include + using namespace filament::math; using namespace utils; @@ -36,6 +38,7 @@ struct LightManager::BuilderDetails { Type mType = Type::DIRECTIONAL; bool mCastShadows = false; bool mCastLight = true; + uint8_t mChannels = 1u; float3 mPosition = {}; float mFalloff = 1.0f; LinearColor mColor = LinearColor{ 1.0f }; @@ -134,6 +137,15 @@ LightManager::Builder& LightManager::Builder::sunHaloFalloff(float haloFalloff) return *this; } +LightManager::Builder& LightManager::Builder::lightChannel(unsigned int channel, bool enable) noexcept { + if (channel < 8) { + const uint8_t mask = 1u << channel; + mImpl->mChannels &= ~mask; + mImpl->mChannels |= enable ? mask : 0u; + } + return *this; +} + LightManager::Builder::Result LightManager::Builder::build(Engine& engine, Entity entity) { upcast(engine).createLight(*this, entity); return Success; @@ -171,6 +183,8 @@ void FLightManager::create(const FLightManager::Builder& builder, utils::Entity lightType.shadowCaster = builder->mCastShadows; lightType.lightCaster = builder->mCastLight; + mManager[i].channels = builder->mChannels; + // set default values by calling the setters setShadowOptions(i, builder->mShadowOptions); setLocalPosition(i, builder->mPosition); @@ -227,16 +241,40 @@ void FLightManager::setShadowOptions(Instance i, ShadowOptions const& options) n params.options.vsm.blurWidth = std::max(0.0f, options.vsm.blurWidth); } +void FLightManager::setLightChannel(Instance i, unsigned int channel, bool enable) noexcept { + if (i) { + if (channel < 8) { + auto& manager = mManager; + const uint8_t mask = 1u << channel; + manager[i].channels &= ~mask; + manager[i].channels |= enable ? mask : 0u; + } + } +} + +bool FLightManager::getLightChannel(Instance i, unsigned int channel) const noexcept { + if (i) { + if (channel < 8) { + auto& manager = mManager; + const uint8_t mask = 1u << channel; + return bool(manager[i].channels & mask); + } + } + return false; +} + void FLightManager::setLocalPosition(Instance i, const float3& position) noexcept { - assert_invariant(i); - auto& manager = mManager; - manager[i].position = position; + if (i) { + auto& manager = mManager; + manager[i].position = position; + } } void FLightManager::setLocalDirection(Instance i, float3 direction) noexcept { - assert_invariant(i); - auto& manager = mManager; - manager[i].direction = direction; + if (i) { + auto& manager = mManager; + manager[i].direction = direction; + } } void FLightManager::setColor(Instance i, const LinearColor& color) noexcept { @@ -438,8 +476,16 @@ void LightManager::destroy(Entity e) noexcept { return upcast(this)->destroy(e); } +void LightManager::setLightChannel(Instance i, unsigned int channel, bool enable) noexcept { + upcast(this)->setLightChannel(i, channel, enable); +} + +bool LightManager::getLightChannel(LightManager::Instance i, unsigned int channel) const noexcept { + return upcast(this)->getLightChannel(i, channel); +} + void LightManager::setPosition(Instance i, const float3& position) noexcept { - return upcast(this)->setLocalPosition(i, position); + upcast(this)->setLocalPosition(i, position); } const float3& LightManager::getPosition(Instance i) const noexcept { diff --git a/filament/src/components/LightManager.h b/filament/src/components/LightManager.h index 9fb4b1b4f6..2081e5f008 100644 --- a/filament/src/components/LightManager.h +++ b/filament/src/components/LightManager.h @@ -96,6 +96,7 @@ public: UTILS_NOINLINE void setLocalPosition(Instance i, const math::float3& position) noexcept; UTILS_NOINLINE void setLocalDirection(Instance i, math::float3 direction) noexcept; + UTILS_NOINLINE void setLightChannel(Instance i, unsigned int channel, bool enable) noexcept; UTILS_NOINLINE void setColor(Instance i, const LinearColor& color) noexcept; UTILS_NOINLINE void setSpotLightCone(Instance i, float inner, float outer) noexcept; UTILS_NOINLINE void setIntensity(Instance i, float intensity, IntensityUnit unit) noexcept; @@ -105,6 +106,8 @@ public: UTILS_NOINLINE void setSunHaloSize(Instance i, float haloSize) noexcept; UTILS_NOINLINE void setSunHaloFalloff(Instance i, float haloFalloff) noexcept; + UTILS_NOINLINE bool getLightChannel(Instance i, unsigned int channel) const noexcept; + LightType const& getLightType(Instance i) const noexcept { return mManager[i].lightType; } @@ -209,6 +212,10 @@ public: return getSpotParams(i).radius; } + uint8_t getLightChannels(Instance i) const noexcept { + return mManager[i].channels; + } + const math::float3& getLocalPosition(Instance i) const noexcept { return mManager[i].position; } @@ -238,6 +245,7 @@ private: SUN_HALO_FALLOFF, // state for the directional light sun INTENSITY, FALLOFF, + CHANNELS, }; using Base = utils::SingleInstanceComponentManager< // 120 bytes @@ -251,7 +259,8 @@ private: float, // 4 float, // 4 float, // 4 - float // 4 + float, // 4 + uint8_t // 1 >; struct Sim : public Base { @@ -277,6 +286,7 @@ private: Field sunHaloFalloff; Field intensity; Field squaredFallOffInv; + Field channels; }; }; diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index b6353a8157..0c9e92fe59 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -45,6 +45,7 @@ struct RenderableManager::BuilderDetails { Box mAABB; uint8_t mLayerMask = 0x1; uint8_t mPriority = 0x4; + uint8_t mChannels = 1; bool mCulling : 1; bool mCastShadows : 1; bool mReceiveShadows : 1; @@ -133,6 +134,15 @@ RenderableManager::Builder& RenderableManager::Builder::culling(bool enable) noe return *this; } +RenderableManager::Builder& RenderableManager::Builder::lightChannel(unsigned int channel, bool enable) noexcept { + if (channel < 8) { + const uint8_t mask = 1u << channel; + mImpl->mChannels &= ~mask; + mImpl->mChannels |= enable ? mask : 0u; + } + return *this; +} + RenderableManager::Builder& RenderableManager::Builder::castShadows(bool enable) noexcept { mImpl->mCastShadows = enable; return *this; @@ -307,6 +317,7 @@ void FRenderableManager::create( setSkinning(ci, false); setMorphing(ci, builder->mMorphingEnabled); setMorphWeights(ci, {0, 0, 0, 0}); + mManager[ci].channels = builder->mChannels; const uint32_t count = builder->mSkinningBoneCount; if (builder->mSkinningBufferMode) { @@ -557,6 +568,26 @@ void FRenderableManager::setMorphWeights(Instance ci, const float4& weights) noe } } +void FRenderableManager::setLightChannel(Instance ci, unsigned int channel, bool enable) noexcept { + if (ci) { + if (channel < 8) { + const uint8_t mask = 1u << channel; + mManager[ci].channels &= ~mask; + mManager[ci].channels |= enable ? mask : 0u; + } + } +} + +bool FRenderableManager::getLightChannel(Instance ci, unsigned int channel) const noexcept { + if (ci) { + if (channel < 8) { + const uint8_t mask = 1u << channel; + return bool(mManager[ci].channels & mask); + } + } + return false; +} + // ------------------------------------------------------------------------------------------------ // Trampoline calling into private implementation // ------------------------------------------------------------------------------------------------ @@ -666,9 +697,17 @@ void RenderableManager::setMorphWeights(Instance instance, float4 const& weights upcast(this)->setMorphWeights(instance, weights); } -void RenderableManager::setSkinningBuffer(RenderableManager::Instance instance, +void RenderableManager::setSkinningBuffer(Instance instance, SkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept { upcast(this)->setSkinningBuffer(instance, upcast(skinningBuffer), count, offset); } +void RenderableManager::setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept { + upcast(this)->setLightChannel(instance, channel, enable); +} + +bool RenderableManager::getLightChannel(Instance instance, unsigned int channel) const noexcept { + return upcast(this)->getLightChannel(instance, channel); +} + } // namespace filament diff --git a/filament/src/components/RenderableManager.h b/filament/src/components/RenderableManager.h index 97ce2789c4..6104a621be 100644 --- a/filament/src/components/RenderableManager.h +++ b/filament/src/components/RenderableManager.h @@ -108,6 +108,9 @@ public: inline void setMorphWeights(Instance instance, const math::float4& weights) noexcept; inline void setSkinningBuffer(Instance instance, FSkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept; + inline void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept; + + inline bool getLightChannel(Instance instance, unsigned int channel) const noexcept; inline bool isShadowCaster(Instance instance) const noexcept; inline bool isShadowReceiver(Instance instance) const noexcept; @@ -120,6 +123,7 @@ public: inline uint8_t getLayerMask(Instance instance) const noexcept; inline uint8_t getPriority(Instance instance) const noexcept; inline math::float4 getMorphWeights(Instance instance) const noexcept; + inline uint8_t getChannels(Instance instance) const noexcept; struct SkinningBindingInfo { backend::Handle handle; @@ -163,6 +167,7 @@ private: AABB, // user data LAYERS, // user data MORPH_WEIGHTS, // user data + CHANNELS, // user data VISIBILITY, // user data PRIMITIVES, // user data BONES, // filament data, UBO storing a pointer to the bones information @@ -172,6 +177,7 @@ private: Box, // AABB uint8_t, // LAYERS math::float4, // MORPH_WEIGHTS + uint8_t, // CHANNELS Visibility, // VISIBILITY utils::Slice, // PRIMITIVES Bones // BONES @@ -192,6 +198,7 @@ private: Field aabb; Field layers; Field morphWeights; + Field channels; Field visibility; Field primitives; Field bones; @@ -317,6 +324,10 @@ math::float4 FRenderableManager::getMorphWeights(Instance instance) const noexce return mManager[instance].morphWeights; } +uint8_t FRenderableManager::getChannels(Instance instance) const noexcept { + return mManager[instance].channels; +} + Box const& FRenderableManager::getAABB(Instance instance) const noexcept { return mManager[instance].aabb; } diff --git a/filament/src/details/Scene.h b/filament/src/details/Scene.h index 018c001285..33895af75c 100644 --- a/filament/src/details/Scene.h +++ b/filament/src/details/Scene.h @@ -108,6 +108,7 @@ public: WORLD_AABB_CENTER, // 12 | world-space bounding box center of the renderable VISIBLE_MASK, // 1 | each bit represents a visibility in a pass MORPH_WEIGHTS, // 4 | floats for morphing + CHANNELS, // 1 | currently light channels only // These are not needed anymore after culling LAYERS, // 1 | layers @@ -130,6 +131,7 @@ public: math::float3, // WORLD_AABB_CENTER VisibleMaskType, // VISIBLE_MASK math::float4, // MORPH_WEIGHTS + uint8_t, // CHANNELS uint8_t, // LAYERS math::float3, // WORLD_AABB_EXTENT utils::Slice, // PRIMITIVES diff --git a/libs/filabridge/include/private/filament/UibStructs.h b/libs/filabridge/include/private/filament/UibStructs.h index 2972f465e3..7277090bec 100644 --- a/libs/filabridge/include/private/filament/UibStructs.h +++ b/libs/filabridge/include/private/filament/UibStructs.h @@ -64,7 +64,8 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) filament::math::float4 sun; // cos(sunAngle), sin(sunAngle), 1/(sunAngle*HALO_SIZE-sunAngle), HALO_EXP - filament::math::float4 padding0; + filament::math::float3 padding0; + uint32_t lightChannels; filament::math::float3 lightDirection; uint32_t fParamsX; // stride-x @@ -142,7 +143,7 @@ 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; // morph weights (we could easily have 8 using half) uint32_t flags; // see packFlags() below - uint32_t reserved0; // 0 + uint32_t channels; // 0x000000ll uint32_t reserved1; // 0 // TODO: We need a better solution, this currently holds the average local scale for the renderable float userData; diff --git a/libs/filamat/src/UibGenerator.cpp b/libs/filamat/src/UibGenerator.cpp index d651820758..02521b4f3a 100644 --- a/libs/filamat/src/UibGenerator.cpp +++ b/libs/filamat/src/UibGenerator.cpp @@ -51,7 +51,8 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { // directional light .add("lightColorIntensity", 1, UniformInterfaceBlock::Type::FLOAT4) .add("sun", 1, UniformInterfaceBlock::Type::FLOAT4) - .add("padding0", 1, UniformInterfaceBlock::Type::FLOAT4) + .add("padding0", 1, UniformInterfaceBlock::Type::FLOAT3) + .add("lightChannels", 1, UniformInterfaceBlock::Type::UINT) .add("lightDirection", 1, UniformInterfaceBlock::Type::FLOAT3) .add("fParamsX", 1, UniformInterfaceBlock::Type::UINT) // shadow @@ -121,7 +122,7 @@ UniformInterfaceBlock const& UibGenerator::getPerRenderableUib() noexcept { .add("worldFromModelNormalMatrix", 1, UniformInterfaceBlock::Type::MAT3, Precision::HIGH) .add("morphWeights", 1, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH) .add("flags", 1, UniformInterfaceBlock::Type::UINT) - .add("reserved0", 1, UniformInterfaceBlock::Type::UINT) + .add("channels", 1, UniformInterfaceBlock::Type::UINT) .add("reserved1", 1, UniformInterfaceBlock::Type::UINT) .add("userData", 1, UniformInterfaceBlock::Type::FLOAT) .build(); diff --git a/shaders/src/common_lighting.fs b/shaders/src/common_lighting.fs index 52cde9ec42..44b686c195 100644 --- a/shaders/src/common_lighting.fs +++ b/shaders/src/common_lighting.fs @@ -8,6 +8,7 @@ struct Light { bool contactShadows; uint shadowIndex; uint shadowLayer; + uint channels; }; struct PixelParams { diff --git a/shaders/src/light_directional.fs b/shaders/src/light_directional.fs index ec58092b2b..841cc02824 100644 --- a/shaders/src/light_directional.fs +++ b/shaders/src/light_directional.fs @@ -27,6 +27,7 @@ Light getDirectionalLight() { light.l = sampleSunAreaLight(frameUniforms.lightDirection); light.attenuation = 1.0; light.NoL = saturate(dot(shading_normal, light.l)); + light.channels = frameUniforms.lightChannels & 0xFFu; return light; } @@ -35,6 +36,11 @@ void evaluateDirectionalLight(const MaterialInputs material, Light light = getDirectionalLight(); + uint channels = objectUniforms.channels & 0xFFu; + if ((light.channels & channels) == 0u) { + return; + } + #if defined(MATERIAL_CAN_SKIP_LIGHTING) if (light.NoL <= 0.0) { return; diff --git a/shaders/src/light_punctual.fs b/shaders/src/light_punctual.fs index 646fbd8706..9aff62713d 100644 --- a/shaders/src/light_punctual.fs +++ b/shaders/src/light_punctual.fs @@ -160,6 +160,7 @@ Light getLight(const uint index) { light.contactShadows = false; light.shadowIndex = 0u; light.shadowLayer = 0u; + light.channels = channels; uint type = typeShadow & 0x1u; if (type == LIGHT_TYPE_SPOT) { @@ -198,6 +199,10 @@ void evaluatePunctualLights(const MaterialInputs material, // Iterate point lights for ( ; index < end; index++) { Light light = getLight(index); + if ((light.channels & channels) == 0u) { + continue; + } + #if defined(MATERIAL_CAN_SKIP_LIGHTING) if (light.NoL <= 0.0 || light.attenuation <= 0.0) { continue; diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 1a956185a7..febd79b244 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -980,6 +980,10 @@ class_("RenderableManager$Builder") (RenderableBuilder* builder, size_t index, uint16_t order), { return &builder->blendOrder(index, order); }) + .BUILDER_FUNCTION("lightChannel", RenderableBuilder, + (RenderableBuilder* builder, unsigned int channel, bool enable), { + return &builder->lightChannel(channel, enable); }) + .function("_build", EMBIND_LAMBDA(int, (RenderableBuilder* builder, Engine* engine, utils::Entity entity), { return (int) builder->build(*engine, entity); @@ -1006,6 +1010,8 @@ class_("RenderableManager") .function("setReceiveShadows", &RenderableManager::setReceiveShadows) .function("isShadowCaster", &RenderableManager::isShadowCaster) .function("isShadowReceiver", &RenderableManager::isShadowReceiver) + .function("setLightChannel", &RenderableManager::setLightChannel) + .function("getLightChannel", &RenderableManager::getLightChannel) .function("setBones", EMBIND_LAMBDA(void, (RenderableManager* self, RenderableManager::Instance instance, emscripten::val transforms, size_t offset), { @@ -1144,7 +1150,10 @@ class_("LightManager$Builder") .BUILDER_FUNCTION("sunHaloSize", LightBuilder, (LightBuilder* builder, float value), { return &builder->sunHaloSize(value); }) .BUILDER_FUNCTION("sunHaloFalloff", LightBuilder, - (LightBuilder* builder, float value), { return &builder->sunHaloFalloff(value); }); + (LightBuilder* builder, float value), { return &builder->sunHaloFalloff(value); }) + .BUILDER_FUNCTION("lightChannel", LightBuilder, + (LightBuilder* builder, unsigned int channel, bool enable), { + return &builder->lightChannel(channel, enable); }); class_("LightManager") .function("hasComponent", &LightManager::hasComponent) @@ -1191,6 +1200,8 @@ class_("LightManager") .function("getSunHaloFalloff", &LightManager::getSunHaloFalloff) .function("setShadowCaster", &LightManager::setShadowCaster) .function("isShadowCaster", &LightManager::isShadowCaster) + .function("setLightChannel", &LightManager::setLightChannel) + .function("getLightChannel", &LightManager::getLightChannel) ; /// LightManager$Instance ::class:: Component instance returned by [LightManager]