diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index e55d9f87a6..79fa6008ea 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -9,3 +9,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut fog: fixed fog height falloff and computation precision on mobile [⚠️ **Recompile Materials**] +materials: new alphaToCoverage property can be used to control alpha to coverage behavior diff --git a/android/filamat-android/src/main/cpp/MaterialBuilder.cpp b/android/filamat-android/src/main/cpp/MaterialBuilder.cpp index 2c70f2526a..c17505e284 100644 --- a/android/filamat-android/src/main/cpp/MaterialBuilder.cpp +++ b/android/filamat-android/src/main/cpp/MaterialBuilder.cpp @@ -250,6 +250,13 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderMaskThr builder->maskThreshold(maskThreshold); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderAlphaToCoverage(JNIEnv*, + jclass, jlong nativeBuilder, jboolean enable) { + auto builder = (MaterialBuilder*) nativeBuilder; + builder->alphaToCoverage(enable); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderShadowMultiplier( JNIEnv*, jclass, jlong nativeBuilder, jboolean shadowMultiplier) { diff --git a/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java b/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java index 435f2fe75d..000262ed95 100644 --- a/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java +++ b/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java @@ -360,6 +360,12 @@ public class MaterialBuilder { return this; } + @NonNull + public MaterialBuilder alphaToCoverage(boolean enable) { + nMaterialBuilderAlphaToCoverage(mNativeObject, enable); + return this; + } + @NonNull public MaterialBuilder shadowMultiplier(boolean shadowMultiplier) { nMaterialBuilderShadowMultiplier(mNativeObject, shadowMultiplier); @@ -584,6 +590,7 @@ public class MaterialBuilder { private static native void nMaterialBuilderDepthCulling(long nativeBuilder, boolean enable); private static native void nMaterialBuilderDoubleSided(long nativeBuilder, boolean doubleSided); private static native void nMaterialBuilderMaskThreshold(long nativeBuilder, float mode); + private static native void nMaterialBuilderAlphaToCoverage(long nativeBuilder, boolean enable); private static native void nMaterialBuilderShadowMultiplier(long mNativeObject, boolean shadowMultiplier); diff --git a/android/filament-android/src/main/cpp/Material.cpp b/android/filament-android/src/main/cpp/Material.cpp index 44b50c5d61..f42119e2db 100644 --- a/android/filament-android/src/main/cpp/Material.cpp +++ b/android/filament-android/src/main/cpp/Material.cpp @@ -153,6 +153,14 @@ Java_com_google_android_filament_Material_nIsDoubleSided(JNIEnv*, jclass, return (jboolean) material->isDoubleSided(); } +extern "C" +JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_Material_nIsAlphaToCoverageEnabled(JNIEnv*, jclass, + jlong nativeMaterial) { + Material* material = (Material*) nativeMaterial; + return (jboolean) material->isAlphaToCoverageEnabled(); +} + extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_Material_nGetMaskThreshold(JNIEnv*, jclass, diff --git a/android/filament-android/src/main/cpp/MaterialInstance.cpp b/android/filament-android/src/main/cpp/MaterialInstance.cpp index 13561e0f50..5273dc2c80 100644 --- a/android/filament-android/src/main/cpp/MaterialInstance.cpp +++ b/android/filament-android/src/main/cpp/MaterialInstance.cpp @@ -457,7 +457,6 @@ extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_MaterialInstance_nGetMaskThreshold(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nGetMaskThreshold() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->getMaskThreshold(); } @@ -466,7 +465,6 @@ extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingVariance(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nGetSpecularAntiAliasingVariance() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->getSpecularAntiAliasingVariance(); } @@ -475,7 +473,6 @@ extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingThreshold(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nGetSpecularAntiAliasingThreshold() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->getSpecularAntiAliasingThreshold(); } @@ -484,7 +481,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsDoubleSided(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nIsDoubleSided() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isDoubleSided(); } @@ -493,7 +489,6 @@ extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_MaterialInstance_nGetCullingMode(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nGetCullingMode() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return (jint)instance->getCullingMode(); } @@ -502,7 +497,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsColorWriteEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nIsColorWriteEnabled() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isColorWriteEnabled(); } @@ -511,7 +505,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsDepthWriteEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nIsDepthWriteEnabled() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isDepthWriteEnabled(); } @@ -520,7 +513,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsStencilWriteEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nIsStencilWriteEnabled() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isStencilWriteEnabled(); } @@ -529,7 +521,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsDepthCullingEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - // TODO: implement nIsDepthCullingEnabled() MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isDepthCullingEnabled(); } 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 54aeca69e7..51d6f01bd5 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 @@ -503,6 +503,17 @@ public class Material { return nIsDoubleSided(getNativeObject()); } + /** + * Indicates whether instances of this material will use alpha to coverage. + * + * @see + * + * Rasterization: alphaToCoverage + */ + public boolean isAlphaToCoverageEnabled() { + return nIsAlphaToCoverageEnabled(getNativeObject()); + } + /** * Returns the alpha mask threshold used when the blending mode is set to masked. * @@ -915,6 +926,7 @@ public class Material { private static native boolean nIsDepthWriteEnabled(long nativeMaterial); private static native boolean nIsDepthCullingEnabled(long nativeMaterial); private static native boolean nIsDoubleSided(long nativeMaterial); + private static native boolean nIsAlphaToCoverageEnabled(long nativeMaterial); private static native float nGetMaskThreshold(long nativeMaterial); private static native float nGetSpecularAntiAliasingVariance(long nativeMaterial); private static native float nGetSpecularAntiAliasingThreshold(long nativeMaterial); diff --git a/docs/Materials.md.html b/docs/Materials.md.html index 91888aa775..5bc4ea4184 100644 --- a/docs/Materials.md.html +++ b/docs/Materials.md.html @@ -1362,6 +1362,11 @@ Description ALPHA_TO_COVERAGE is enabled for non-translucent views. See the maskThreshold section for more information. +!!! Note + When `blending` is set to `masked`, alpha to coverage is automatically enabled for the material. + If this behavior is undesirable, refer to the Rasterization: alphaToCoverage section to turn + alpha to coverage off using the `alphaToCoverage` property. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON material { blending : transparent @@ -1602,6 +1607,37 @@ fragment { } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Rasterization: alphaToCoverage + +Type +: `boolean` + +Value +: `true` or `false`. Defaults to `false`. + +Description +: Enables or disables alpha to coverage. When alpha to coverage is enabled, the coverage of + fragment is derived from its alpha. This property is only meaningful when MSAA is enabled. + Note: setting `blending` to `masked` automatically enables alpha to coverage. If this is not + desired, you can override this behavior by setting alpha to coverage to false as in the + example below. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON +material { + name : "Alpha to coverage", + shadingModel : lit, + blending : masked, + alphaToCoverage : false +} + +fragment { + void material(inout MaterialInputs material) { + prepareMaterial(material); + material.baseColor = materialParams.albedo; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ### Lighting: reflections Type diff --git a/filament/include/filament/Material.h b/filament/include/filament/Material.h index 572de84454..6533577383 100644 --- a/filament/include/filament/Material.h +++ b/filament/include/filament/Material.h @@ -171,6 +171,9 @@ public: //! Indicates whether this material is double-sided. bool isDoubleSided() const noexcept; + //! Indicates whether this material uses alpha to coverage. + bool isAlphaToCoverageEnabled() const noexcept; + //! Returns the alpha mask threshold used when the blending mode is set to masked. float getMaskThreshold() const noexcept; diff --git a/filament/src/Material.cpp b/filament/src/Material.cpp index bb2bc69a38..37ffce5505 100644 --- a/filament/src/Material.cpp +++ b/filament/src/Material.cpp @@ -72,6 +72,10 @@ bool Material::isDoubleSided() const noexcept { return downcast(this)->isDoubleSided(); } +bool Material::isAlphaToCoverageEnabled() const noexcept { + return downcast(this)->isAlphaToCoverageEnabled(); +} + float Material::getMaskThreshold() const noexcept { return downcast(this)->getMaskThreshold(); } diff --git a/filament/src/MaterialParser.cpp b/filament/src/MaterialParser.cpp index d5df62932d..96e5ef73ac 100644 --- a/filament/src/MaterialParser.cpp +++ b/filament/src/MaterialParser.cpp @@ -246,6 +246,14 @@ bool MaterialParser::getMaskThreshold(float* value) const noexcept { return mImpl.getFromSimpleChunk(ChunkType::MaterialMaskThreshold, value); } +bool MaterialParser::getAlphaToCoverageSet(bool* value) const noexcept { + return mImpl.getFromSimpleChunk(ChunkType::MaterialAlphaToCoverageSet, value); +} + +bool MaterialParser::getAlphaToCoverage(bool* value) const noexcept { + return mImpl.getFromSimpleChunk(ChunkType::MaterialAlphaToCoverage, value); +} + bool MaterialParser::hasShadowMultiplier(bool* value) const noexcept { return mImpl.getFromSimpleChunk(ChunkType::MaterialShadowMultiplier, value); } diff --git a/filament/src/MaterialParser.h b/filament/src/MaterialParser.h index eff0addc23..e066e547ba 100644 --- a/filament/src/MaterialParser.h +++ b/filament/src/MaterialParser.h @@ -88,6 +88,8 @@ public: bool getShading(Shading*) const noexcept; bool getBlendingMode(BlendingMode*) const noexcept; bool getMaskThreshold(float*) const noexcept; + bool getAlphaToCoverageSet(bool*) const noexcept; + bool getAlphaToCoverage(bool*) const noexcept; bool hasShadowMultiplier(bool*) const noexcept; bool getRequiredAttributes(AttributeBitset*) const noexcept; bool getRefractionMode(RefractionMode* value) const noexcept; diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 816137649d..b53b0fb287 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -298,7 +298,16 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder) parser->getColorWrite(&colorWrite); mRasterState.colorWrite = colorWrite; mRasterState.depthFunc = depthTest ? DepthFunc::GE : DepthFunc::A; - mRasterState.alphaToCoverage = mBlendingMode == BlendingMode::MASKED; + + bool alphaToCoverageSet = false; + parser->getAlphaToCoverageSet(&alphaToCoverageSet); + if (alphaToCoverageSet) { + bool alphaToCoverage = false; + parser->getAlphaToCoverage(&alphaToCoverage); + mRasterState.alphaToCoverage = alphaToCoverage; + } else { + mRasterState.alphaToCoverage = mBlendingMode == BlendingMode::MASKED; + } parser->hasSpecularAntiAliasing(&mSpecularAntiAliasing); if (mSpecularAntiAliasing) { diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index 6aab3d48eb..4740e85b69 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -124,6 +124,7 @@ public: } bool isDoubleSided() const noexcept { return mDoubleSided; } bool hasDoubleSidedCapability() const noexcept { return mDoubleSidedCapability; } + bool isAlphaToCoverageEnabled() const noexcept { return mRasterState.alphaToCoverage; } float getMaskThreshold() const noexcept { return mMaskThreshold; } bool hasShadowMultiplier() const noexcept { return mHasShadowMultiplier; } AttributeBitset getRequiredAttributes() const noexcept { return mRequiredAttributes; } diff --git a/libs/filabridge/include/filament/MaterialChunkType.h b/libs/filabridge/include/filament/MaterialChunkType.h index c63b7d5414..eed5dea079 100644 --- a/libs/filabridge/include/filament/MaterialChunkType.h +++ b/libs/filabridge/include/filament/MaterialChunkType.h @@ -67,15 +67,17 @@ enum UTILS_PUBLIC ChunkType : uint64_t { MaterialReflectionMode = charTo64bitNum("MAT_REFL"), MaterialRequiredAttributes = charTo64bitNum("MAT_REQA"), - MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"), MaterialDoubleSidedSet = charTo64bitNum("MAT_DOSS"), MaterialDoubleSided = charTo64bitNum("MAT_DOSI"), MaterialColorWrite = charTo64bitNum("MAT_CWRIT"), + MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"), MaterialDepthWrite = charTo64bitNum("MAT_DWRIT"), MaterialDepthTest = charTo64bitNum("MAT_DTEST"), MaterialInstanced = charTo64bitNum("MAT_INSTA"), MaterialCullingMode = charTo64bitNum("MAT_CUMO"), + MaterialAlphaToCoverageSet = charTo64bitNum("MAT_A2CS"), + MaterialAlphaToCoverage = charTo64bitNum("MAT_A2CO"), MaterialHasCustomDepthShader =charTo64bitNum("MAT_CSDP"), diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 6fc3b86490..2456951a6a 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -390,7 +390,10 @@ public: MaterialBuilder& featureLevel(FeatureLevel featureLevel) noexcept; - //! Set the blending mode for this material. + /** + * Set the blending mode for this material. When set to MASKED, alpha to coverage is turned on. + * You can override this behavior using alphaToCoverage(false). + */ MaterialBuilder& blending(BlendingMode blending) noexcept; /** @@ -436,6 +439,14 @@ public: */ MaterialBuilder& maskThreshold(float threshold) noexcept; + /** + * Enables or disables alpha-to-coverage. When enabled, the coverage of a fragment is based + * on its alpha value. This parameter is only useful when MSAA is in use. Alpha to coverage + * is enabled automatically when the blend mode is set to MASKED; this behavior can be + * overridden by calling alphaToCoverage(false). + */ + MaterialBuilder& alphaToCoverage(bool enable) noexcept; + //! The material output is multiplied by the shadowing factor (UNLIT model only). MaterialBuilder& shadowMultiplier(bool shadowMultiplier) noexcept; @@ -791,6 +802,8 @@ private: bool mInstanced = false; bool mDepthWrite = true; bool mDepthWriteSet = false; + bool mAlphaToCoverage = false; + bool mAlphaToCoverageSet = false; bool mSpecularAntiAliasing = false; bool mClearCoatIorChange = true; diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 4dc6ab0634..f8eb0811f2 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -336,6 +336,12 @@ MaterialBuilder& MaterialBuilder::maskThreshold(float threshold) noexcept { return *this; } +MaterialBuilder& MaterialBuilder::alphaToCoverage(bool enable) noexcept { + mAlphaToCoverage = enable; + mAlphaToCoverageSet = true; + return *this; +} + MaterialBuilder& MaterialBuilder::shadowMultiplier(bool shadowMultiplier) noexcept { mShadowMultiplier = shadowMultiplier; return *this; @@ -1226,11 +1232,13 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo& container.addSimpleChild(ChunkType::MaterialBlendingMode, static_cast(mBlendingMode)); container.addSimpleChild(ChunkType::MaterialTransparencyMode, static_cast(mTransparencyMode)); container.addSimpleChild(ChunkType::MaterialReflectionMode, static_cast(mReflectionMode)); - container.addSimpleChild(ChunkType::MaterialDepthWriteSet, mDepthWriteSet); container.addSimpleChild(ChunkType::MaterialColorWrite, mColorWrite); + container.addSimpleChild(ChunkType::MaterialDepthWriteSet, mDepthWriteSet); container.addSimpleChild(ChunkType::MaterialDepthWrite, mDepthWrite); container.addSimpleChild(ChunkType::MaterialDepthTest, mDepthTest); container.addSimpleChild(ChunkType::MaterialInstanced, mInstanced); + container.addSimpleChild(ChunkType::MaterialAlphaToCoverageSet, mAlphaToCoverageSet); + container.addSimpleChild(ChunkType::MaterialAlphaToCoverage, mAlphaToCoverage); container.addSimpleChild(ChunkType::MaterialCullingMode, static_cast(mCullingMode)); uint64_t properties = 0; diff --git a/tools/matc/src/matc/ParametersProcessor.cpp b/tools/matc/src/matc/ParametersProcessor.cpp index f340b0ec29..f1745e4b2c 100644 --- a/tools/matc/src/matc/ParametersProcessor.cpp +++ b/tools/matc/src/matc/ParametersProcessor.cpp @@ -850,6 +850,11 @@ static bool processMaskThreshold(MaterialBuilder& builder, const JsonishValue& v return true; } +static bool processAlphaToCoverage(MaterialBuilder& builder, const JsonishValue& value) { + builder.alphaToCoverage(value.toJsonBool()->getBool()); + return true; +} + static bool processShadowMultiplier(MaterialBuilder& builder, const JsonishValue& value) { builder.shadowMultiplier(value.toJsonBool()->getBool()); return true; @@ -1070,6 +1075,7 @@ ParametersProcessor::ParametersProcessor() { mParameters["transparency"] = { &processTransparencyMode, Type::STRING }; mParameters["reflections"] = { &processReflectionMode, Type::STRING }; mParameters["maskThreshold"] = { &processMaskThreshold, Type::NUMBER }; + mParameters["alphaToCoverage"] = { &processAlphaToCoverage, Type::BOOL }; mParameters["shadowMultiplier"] = { &processShadowMultiplier, Type::BOOL }; mParameters["transparentShadow"] = { &processTransparentShadow, Type::BOOL }; mParameters["shadingModel"] = { &processShading, Type::STRING };