From 213eb6af9edfc890c228b35bfeef8249f772ddd2 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 18 Jun 2024 17:06:35 -0700 Subject: [PATCH] Lit Materials can now specify the quality of SH computations The number of SH bands used for the indirect light irradiance computations can be set to 1, 2 or 3 (default) in Material::Builder. For e.g. in lower-end devices w/ non HDR content, it might be beneficial to set this value to 2. BUGS=[341971013] --- .../src/main/cpp/Material.cpp | 9 ++++- .../com/google/android/filament/Material.java | 22 ++++++++++- filament/include/filament/IndirectLight.h | 2 + filament/include/filament/Material.h | 12 ++++++ filament/src/details/Material.cpp | 8 ++++ .../include/private/filament/EngineEnums.h | 1 + libs/filamat/src/shaders/CodeGenerator.cpp | 3 ++ shaders/src/light_indirect.fs | 37 ++++++++++--------- 8 files changed, 72 insertions(+), 22 deletions(-) diff --git a/android/filament-android/src/main/cpp/Material.cpp b/android/filament-android/src/main/cpp/Material.cpp index 7ef3b0b5be..e923d210b0 100644 --- a/android/filament-android/src/main/cpp/Material.cpp +++ b/android/filament-android/src/main/cpp/Material.cpp @@ -25,12 +25,17 @@ using namespace filament; extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass, - jlong nativeEngine, jobject buffer_, jint size) { + jlong nativeEngine, jobject buffer_, jint size, jint shBandCount) { Engine* engine = (Engine*) nativeEngine; AutoBuffer buffer(env, buffer_, size); - Material* material = Material::Builder() + auto builder = Material::Builder(); + if (shBandCount) { + builder.sphericalHarmonicsBandCount(shBandCount); + } + Material* material = builder .package(buffer.getData(), buffer.getSize()) .build(*engine); + return (jlong) material; } diff --git a/android/filament-android/src/main/java/com/google/android/filament/Material.java b/android/filament-android/src/main/java/com/google/android/filament/Material.java index 80b143ee80..cd71bbae73 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Material.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Material.java @@ -346,6 +346,7 @@ public class Material { public static class Builder { private Buffer mBuffer; private int mSize; + private int mShBandCount = 0; /** * Specifies the material data. The material data is a binary blob produced by @@ -361,6 +362,22 @@ public class Material { return this; } + /** + * Sets the quality of the indirect lights computations. This is only taken into account + * if this material is lit and in the surface domain. This setting will affect the + * IndirectLight computation if one is specified on the Scene and Spherical Harmonics + * are used for the irradiance. + * + * @param shBandCount Number of spherical harmonic bands. Must be 1, 2 or 3 (default). + * @return Reference to this Builder for chaining calls. + * @see IndirectLight + */ + @NonNull + public Builder sphericalHarmonicsBandCount(@IntRange(from = 0) int shBandCount) { + mShBandCount = shBandCount; + return this; + } + /** * Creates and returns the Material object. * @@ -372,7 +389,8 @@ public class Material { */ @NonNull public Material build(@NonNull Engine engine) { - long nativeMaterial = nBuilderBuild(engine.getNativeObject(), mBuffer, mSize); + long nativeMaterial = nBuilderBuild(engine.getNativeObject(), + mBuffer, mSize, mShBandCount); if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material"); return new Material(nativeMaterial); } @@ -1023,7 +1041,7 @@ public class Material { mNativeObject = 0; } - private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size); + private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount); private static native long nCreateInstance(long nativeMaterial); private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name); private static native long nGetDefaultInstance(long nativeMaterial); diff --git a/filament/include/filament/IndirectLight.h b/filament/include/filament/IndirectLight.h index c230dac8fe..c4cc62ecd7 100644 --- a/filament/include/filament/IndirectLight.h +++ b/filament/include/filament/IndirectLight.h @@ -158,6 +158,8 @@ public: * * @return This Builder, for chaining calls. * + * @see Material::Builder::sphericalHarmonicsBandCount() + * * @note * Because the coefficients are pre-scaled, `sh[0]` is the environment's * average irradiance. diff --git a/filament/include/filament/Material.h b/filament/include/filament/Material.h index b4b9bbed3e..11cbaef30d 100644 --- a/filament/include/filament/Material.h +++ b/filament/include/filament/Material.h @@ -140,6 +140,18 @@ public: return constant(name, strlen(name), value); } + /** + * Sets the quality of the indirect lights computations. This is only taken into account + * if this material is lit and in the surface domain. This setting will affect the + * IndirectLight computation if one is specified on the Scene and Spherical Harmonics + * are used for the irradiance. + * + * @param shBandCount Number of spherical harmonic bands. Must be 1, 2 or 3 (default). + * @return Reference to this Builder for chaining calls. + * @see IndirectLight + */ + Builder& sphericalHarmonicsBandCount(size_t shBandCount) noexcept; + /** * Creates the Material object and returns a pointer to it. * diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index b8fa8d2999..30f240c315 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -119,6 +119,7 @@ struct Material::BuilderDetails { const void* mPayload = nullptr; size_t mSize = 0; bool mDefaultMaterial = false; + bool mShBandsCount = 3; std::unordered_map< utils::CString, std::variant, @@ -143,6 +144,11 @@ Material::Builder& Material::Builder::package(const void* payload, size_t size) return *this; } +Material::Builder& Material::Builder::sphericalHarmonicsBandCount(size_t shBandCount) noexcept { + mImpl->mShBandsCount = math::clamp(shBandCount, size_t(1), size_t(3)); + return *this; +} + template Material::Builder& Material::Builder::constant(const char* name, size_t nameLength, T value) { FILAMENT_CHECK_PRECONDITION(name != nullptr) << "name cannot be null"; @@ -897,6 +903,8 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Material::Builde mSpecializationConstants.push_back({ +ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT, (int)engine.getConfig().stereoscopicEyeCount }); + mSpecializationConstants.push_back({ + +ReservedSpecializationConstants::CONFIG_SH_BANDS_COUNT, builder->mShBandsCount }); if (UTILS_UNLIKELY(parser->getShaderLanguage() == ShaderLanguage::ESSL1)) { // The actual value of this spec-constant is set in the OpenGLDriver backend. mSpecializationConstants.push_back({ diff --git a/libs/filabridge/include/private/filament/EngineEnums.h b/libs/filabridge/include/private/filament/EngineEnums.h index 4a8c042e75..9491f2e118 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -70,6 +70,7 @@ enum class ReservedSpecializationConstants : uint8_t { CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP = 6, CONFIG_DEBUG_FROXEL_VISUALIZATION = 7, CONFIG_STEREO_EYE_COUNT = 8, // don't change (hardcoded in ShaderCompilerService.cpp) + CONFIG_SH_BANDS_COUNT = 9 }; enum class PushConstantIds : uint8_t { diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index 75d2b940d8..6d631e0582 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -332,6 +332,9 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade generateSpecializationConstant(out, "CONFIG_STEREO_EYE_COUNT", +ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT, material.stereoscopicEyeCount); + generateSpecializationConstant(out, "CONFIG_SH_BANDS_COUNT", + +ReservedSpecializationConstants::CONFIG_SH_BANDS_COUNT, 3); + // CONFIG_MAX_STEREOSCOPIC_EYES is used to size arrays and on Adreno GPUs + vulkan, this has to // be explicitly, statically defined (as in #define). Otherwise (using const int for // example), we'd run into a GPU crash. diff --git a/shaders/src/light_indirect.fs b/shaders/src/light_indirect.fs index 5084b10b64..74bcaaff20 100644 --- a/shaders/src/light_indirect.fs +++ b/shaders/src/light_indirect.fs @@ -2,9 +2,6 @@ // Image based lighting configuration //------------------------------------------------------------------------------ -// Number of spherical harmonics bands (1, 2 or 3) -#define SPHERICAL_HARMONICS_BANDS 3 - // IBL integration algorithm #define IBL_INTEGRATION_PREFILTERED_CUBEMAP 0 #define IBL_INTEGRATION_IMPORTANCE_SAMPLING 1 @@ -44,21 +41,25 @@ vec3 prefilteredDFG(float perceptualRoughness, float NoV) { //------------------------------------------------------------------------------ vec3 Irradiance_SphericalHarmonics(const vec3 n) { - return max( - frameUniforms.iblSH[0] -#if SPHERICAL_HARMONICS_BANDS >= 2 - + frameUniforms.iblSH[1] * (n.y) - + frameUniforms.iblSH[2] * (n.z) - + frameUniforms.iblSH[3] * (n.x) -#endif -#if SPHERICAL_HARMONICS_BANDS >= 3 - + frameUniforms.iblSH[4] * (n.y * n.x) - + frameUniforms.iblSH[5] * (n.y * n.z) - + frameUniforms.iblSH[6] * (3.0 * n.z * n.z - 1.0) - + frameUniforms.iblSH[7] * (n.z * n.x) - + frameUniforms.iblSH[8] * (n.x * n.x - n.y * n.y) -#endif - , 0.0); + vec3 sphericalHarmonics = frameUniforms.iblSH[0]; + + if (CONFIG_SH_BANDS_COUNT >= 2) { + sphericalHarmonics += + frameUniforms.iblSH[1] * (n.y) + + frameUniforms.iblSH[2] * (n.z) + + frameUniforms.iblSH[3] * (n.x); + } + + if (CONFIG_SH_BANDS_COUNT >= 3) { + sphericalHarmonics += + frameUniforms.iblSH[4] * (n.y * n.x) + + frameUniforms.iblSH[5] * (n.y * n.z) + + frameUniforms.iblSH[6] * (3.0 * n.z * n.z - 1.0) + + frameUniforms.iblSH[7] * (n.z * n.x) + + frameUniforms.iblSH[8] * (n.x * n.x - n.y * n.y); + } + + return max(sphericalHarmonics, 0.0); } vec3 Irradiance_RoughnessOne(const vec3 n) {