diff --git a/android/filament-android/src/main/cpp/Material.cpp b/android/filament-android/src/main/cpp/Material.cpp index e923d210b0..027627128e 100644 --- a/android/filament-android/src/main/cpp/Material.cpp +++ b/android/filament-android/src/main/cpp/Material.cpp @@ -25,13 +25,14 @@ using namespace filament; extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass, - jlong nativeEngine, jobject buffer_, jint size, jint shBandCount) { + jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality) { Engine* engine = (Engine*) nativeEngine; AutoBuffer buffer(env, buffer_, size); auto builder = Material::Builder(); if (shBandCount) { builder.sphericalHarmonicsBandCount(shBandCount); } + builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality); Material* material = builder .package(buffer.getData(), buffer.getSize()) .build(*engine); 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 cd71bbae73..b304f8a6ab 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 @@ -344,9 +344,18 @@ public class Material { } public static class Builder { + public enum ShadowSamplingQuality { + /** 2x2 PCF */ + HARD, + /** 3x3 gaussian filter */ + LOW, + } + private Buffer mBuffer; private int mSize; private int mShBandCount = 0; + private ShadowSamplingQuality mShadowSamplingQuality = ShadowSamplingQuality.LOW; + /** * Specifies the material data. The material data is a binary blob produced by @@ -378,6 +387,18 @@ public class Material { return this; } + /** + * Set the quality of shadow sampling. This is only taken into account + * if this material is lit and in the surface domain. + * @param quality + * @return Reference to this Builder for chaining calls. + */ + @NonNull + public Builder shadowSamplingQuality(ShadowSamplingQuality quality) { + mShadowSamplingQuality = quality; + return this; + } + /** * Creates and returns the Material object. * @@ -390,7 +411,7 @@ public class Material { @NonNull public Material build(@NonNull Engine engine) { long nativeMaterial = nBuilderBuild(engine.getNativeObject(), - mBuffer, mSize, mShBandCount); + mBuffer, mSize, mShBandCount, mShadowSamplingQuality.ordinal()); if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material"); return new Material(nativeMaterial); } @@ -1041,7 +1062,7 @@ public class Material { mNativeObject = 0; } - private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount); + private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount, int shadowQuality); 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/Material.h b/filament/include/filament/Material.h index 11cbaef30d..768338a48a 100644 --- a/filament/include/filament/Material.h +++ b/filament/include/filament/Material.h @@ -103,6 +103,11 @@ public: Builder& operator=(Builder const& rhs) noexcept; Builder& operator=(Builder&& rhs) noexcept; + enum class ShadowSamplingQuality : uint8_t { + HARD, // 2x2 PCF + LOW // 3x3 gaussian filter + }; + /** * Specifies the material data. The material data is a binary blob produced by * libfilamat or by matc. @@ -152,6 +157,14 @@ public: */ Builder& sphericalHarmonicsBandCount(size_t shBandCount) noexcept; + /** + * Set the quality of shadow sampling. This is only taken into account + * if this material is lit and in the surface domain. + * @param quality + * @return + */ + Builder& shadowSamplingQuality(ShadowSamplingQuality quality) 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 5f641eb167..290f7034ea 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -123,6 +123,7 @@ struct Material::BuilderDetails { size_t mSize = 0; bool mDefaultMaterial = false; int32_t mShBandsCount = 3; + Builder::ShadowSamplingQuality mShadowSamplingQuality = Builder::ShadowSamplingQuality::LOW; std::unordered_map< utils::CString, std::variant, @@ -152,6 +153,11 @@ Material::Builder& Material::Builder::sphericalHarmonicsBandCount(size_t shBandC return *this; } +Material::Builder& Material::Builder::shadowSamplingQuality(ShadowSamplingQuality quality) noexcept { + mImpl->mShadowSamplingQuality = quality; + 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"; @@ -999,7 +1005,11 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Material::Builde +ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT, (int)engine.getConfig().stereoscopicEyeCount }); mSpecializationConstants.push_back({ - +ReservedSpecializationConstants::CONFIG_SH_BANDS_COUNT, builder->mShBandsCount }); + +ReservedSpecializationConstants::CONFIG_SH_BANDS_COUNT, + builder->mShBandsCount }); + mSpecializationConstants.push_back({ + +ReservedSpecializationConstants::CONFIG_SHADOW_SAMPLING_METHOD, + (int32_t)builder->mShadowSamplingQuality }); 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 273061a86c..c2dd12f6cb 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -81,7 +81,9 @@ 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 + CONFIG_SH_BANDS_COUNT = 9, + CONFIG_SHADOW_SAMPLING_METHOD = 10 + // check CONFIG_MAX_RESERVED_SPEC_CONSTANTS below }; enum class PushConstantIds : uint8_t { diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index 5fa9db0878..6c63697bfe 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -337,6 +337,9 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade generateSpecializationConstant(out, "CONFIG_SH_BANDS_COUNT", +ReservedSpecializationConstants::CONFIG_SH_BANDS_COUNT, 3); + generateSpecializationConstant(out, "CONFIG_SHADOW_SAMPLING_METHOD", + +ReservedSpecializationConstants::CONFIG_SHADOW_SAMPLING_METHOD, 1); + // 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/shadowing.fs b/shaders/src/shadowing.fs index 061aec25a3..cde6d8a9ee 100644 --- a/shaders/src/shadowing.fs +++ b/shaders/src/shadowing.fs @@ -8,10 +8,8 @@ #define SHADOW_SAMPLING_RUNTIME_DPCF 2u #define SHADOW_SAMPLING_RUNTIME_PCSS 3u -// TODO: this should be user-settable, maybe at the material level #define SHADOW_SAMPLING_PCF_HARD 0 #define SHADOW_SAMPLING_PCF_LOW 1 -#define SHADOW_SAMPLING_METHOD SHADOW_SAMPLING_PCF_LOW //------------------------------------------------------------------------------ // PCF Shadow Sampling @@ -29,7 +27,6 @@ float sampleDepth(const mediump sampler2DArrayShadow map, return texture(map, vec4(uv, layer, saturate(depth))); } -#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HARD // use hardware assisted PCF float ShadowSample_PCF_Hard(const mediump sampler2DArrayShadow map, const highp vec4 scissorNormalized, @@ -38,9 +35,7 @@ float ShadowSample_PCF_Hard(const mediump sampler2DArrayShadow map, // note: shadowPosition.z is in the [1, 0] range (reversed Z) return sampleDepth(map, scissorNormalized, layer, position.xy, position.z); } -#endif -#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW // use hardware assisted PCF + 3x3 gaussian filter float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, const highp vec4 scissorNormalized, @@ -77,7 +72,6 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, sum += uw.y * vw.y * sampleDepth(map, scissorNormalized, layer, base + vec2(u.y, v.y), depth); return sum * (1.0 / 16.0); } -#endif // use manual PCF float ShadowSample_PCF(const mediump sampler2DArray map, @@ -523,11 +517,12 @@ float shadow(const bool DIRECTIONAL, const int index, highp vec4 shadowPosition, highp float zLight) { highp vec4 scissorNormalized = shadowUniforms.shadows[index].scissorNormalized; uint layer = shadowUniforms.shadows[index].layer; -#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HARD - return ShadowSample_PCF_Hard(shadowMap, scissorNormalized, layer, shadowPosition); -#elif SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW - return ShadowSample_PCF_Low(shadowMap, scissorNormalized, layer, shadowPosition); -#endif + + if (CONFIG_SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HARD) { + return ShadowSample_PCF_Hard(shadowMap, scissorNormalized, layer, shadowPosition); + } else if (CONFIG_SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW) { + return ShadowSample_PCF_Low(shadowMap, scissorNormalized, layer, shadowPosition); + } } // Shadow requiring a sampler2D sampler (VSM, DPCF and PCSS)