diff --git a/android/filament-android/src/main/cpp/MaterialInstance.cpp b/android/filament-android/src/main/cpp/MaterialInstance.cpp index 2c192e0377..785ba08f02 100644 --- a/android/filament-android/src/main/cpp/MaterialInstance.cpp +++ b/android/filament-android/src/main/cpp/MaterialInstance.cpp @@ -217,3 +217,19 @@ Java_com_google_android_filament_MaterialInstance_nSetPolygonOffset(JNIEnv*, MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; instance->setPolygonOffset(scale, constant); } + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_MaterialInstance_nSetMaskThreshold(JNIEnv*, + jclass, jlong nativeMaterialInstance, jfloat threshold) { + MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + instance->setMaskThreshold(threshold); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_MaterialInstance_nSetDoubleSided(JNIEnv*, + jclass, jlong nativeMaterialInstance, jboolean doubleSided) { + MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + instance->setDoubleSided(doubleSided); +} diff --git a/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java b/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java index a163bb4ecb..1460f47bb3 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java +++ b/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java @@ -151,6 +151,14 @@ public class MaterialInstance { nSetPolygonOffset(getNativeObject(), scale, constant); } + public void setMaskThreshold(float threshold) { + nSetMaskThreshold(getNativeObject(), threshold); + } + + public void setDoubleSided(boolean doubleSided) { + nSetDoubleSided(getNativeObject(), doubleSided); + } + long getNativeObject() { if (mNativeObject == 0) { throw new IllegalStateException("Calling method on destroyed MaterialInstance"); @@ -211,4 +219,8 @@ public class MaterialInstance { private static native void nSetPolygonOffset(long nativeMaterialInstance, float scale, float constant); + + private static native void nSetMaskThreshold(long nativeMaterialInstance, float threshold); + + private static native void nSetDoubleSided(long nativeMaterialInstance, boolean doubleSided); } diff --git a/docs/Materials.md.html b/docs/Materials.md.html index 97f01c66d3..42b20ff219 100644 --- a/docs/Materials.md.html +++ b/docs/Materials.md.html @@ -1029,9 +1029,10 @@ Value : `true` or `false`. Defaults to `false`. Description -: Enables or disables two-sided rendering. When set to `true`, `culling` is automatically set to - `none`; if the triangle is back-facing, the triangle's normal is automatically flipped to - become front-facing. +: Enables two-sided rendering and its capability to be toggled at run time. When set to `true`, + `culling` is automatically set to `none`; if the triangle is back-facing, the triangle's + normal is flipped to become front-facing. When explicitly set to `false`, this allows the + doubleSided property to be toggled at run time. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON material { diff --git a/filament/include/filament/MaterialInstance.h b/filament/include/filament/MaterialInstance.h index 76f1493e22..f2a74dcb88 100644 --- a/filament/include/filament/MaterialInstance.h +++ b/filament/include/filament/MaterialInstance.h @@ -123,6 +123,18 @@ public: * @param constant scale factore used to create a constant depth offset for each triangle */ void setPolygonOffset(float scale, float constant) noexcept; + + /** + * Overrides the minimum alpha value a fragment must have to not be discarded when the blend + * mode is MASKED. Defaults to 0.4 if it has not been set in the parent Material. + */ + void setMaskThreshold(float threshold) noexcept; + + /** + * Enables or disables double-sided lighting if the parent Material has double-sided capability, + * otherwise prints a warning. + */ + void setDoubleSided(bool doubleSided) noexcept; }; } // namespace filament diff --git a/filament/src/Material.cpp b/filament/src/Material.cpp index 205da57d4a..2b35d7715e 100644 --- a/filament/src/Material.cpp +++ b/filament/src/Material.cpp @@ -204,19 +204,8 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder) parser->getDepthTest(&depthTest); if (doubleSideSet) { - // double sided disables face culling - if (mDoubleSided) { - mRasterState.culling = CullingMode::NONE; - } else { - // the cull mode is double sided but we set the double-sided bit to false, - // revert culling to default - if (mCullingMode == CullingMode::NONE) { - mRasterState.culling = CullingMode::BACK; - } else { - // use the front/back/front&back mode set by the user - mRasterState.culling = mCullingMode; - } - } + mDoubleSidedCapability = true; + mRasterState.culling = mDoubleSided ? CullingMode::NONE : mCullingMode; } else { mRasterState.culling = mCullingMode; } diff --git a/filament/src/MaterialInstance.cpp b/filament/src/MaterialInstance.cpp index 34c29a63e7..eb2e33c0f7 100644 --- a/filament/src/MaterialInstance.cpp +++ b/filament/src/MaterialInstance.cpp @@ -26,9 +26,12 @@ #include "details/Material.h" #include "details/Texture.h" +#include + #include using namespace filament::math; +using namespace utils; namespace filament { @@ -56,7 +59,12 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material) if (material->getBlendingMode() == BlendingMode::MASKED) { static_cast(this)->setParameter( - "maskThreshold", material->getMaskThreshold()); + "_maskThreshold", material->getMaskThreshold()); + } + + if (material->hasDoubleSidedCapability()) { + static_cast(this)->setParameter( + "_doubleSided", material->isDoubleSided()); } } @@ -79,7 +87,12 @@ void FMaterialInstance::initDefaultInstance(FEngine& engine, FMaterial const* ma if (material->getBlendingMode() == BlendingMode::MASKED) { static_cast(this)->setParameter( - "maskThreshold", material->getMaskThreshold()); + "_maskThreshold", material->getMaskThreshold()); + } + + if (material->hasDoubleSidedCapability()) { + static_cast(this)->setParameter( + "_doubleSided", material->isDoubleSided()); } } @@ -125,6 +138,14 @@ void FMaterialInstance::setParameter(const char* name, mSamplers.setSampler(index, { upcast(texture)->getHwHandle(), sampler.getSamplerParams() }); } +void FMaterialInstance::setDoubleSided(bool doubleSided) noexcept { + if (!mMaterial->hasDoubleSidedCapability()) { + slog.w << "Parent material does not have double-sided capability." << io::endl; + return; + } + setParameter("_doubleSided", doubleSided); +} + } // namespace details using namespace details; @@ -209,4 +230,12 @@ void MaterialInstance::setPolygonOffset(float scale, float constant) noexcept { upcast(this)->setPolygonOffset(scale, constant); } +void MaterialInstance::setMaskThreshold(float threshold) noexcept { + upcast(this)->setMaskThreshold(threshold); +} + +void MaterialInstance::setDoubleSided(bool doubleSided) noexcept { + upcast(this)->setDoubleSided(doubleSided); +} + } // namespace filament diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index 2873523cd4..353bc061ab 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -102,6 +102,7 @@ public: return mRasterState.depthFunc != backend::RasterState::DepthFunc::A; } bool isDoubleSided() const noexcept { return mDoubleSided; } + bool hasDoubleSidedCapability() const noexcept { return mDoubleSidedCapability; } float getMaskThreshold() const noexcept { return mMaskThreshold; } bool hasShadowMultiplier() const noexcept { return mHasShadowMultiplier; } AttributeBitset getRequiredAttributes() const noexcept { return mRequiredAttributes; } @@ -131,6 +132,7 @@ private: AttributeBitset mRequiredAttributes; float mMaskThreshold; bool mDoubleSided; + bool mDoubleSidedCapability = false; bool mHasShadowMultiplier = false; bool mHasCustomDepthShader = false; bool mIsDefaultMaterial = false; diff --git a/filament/src/details/MaterialInstance.h b/filament/src/details/MaterialInstance.h index 499b778f74..4d3962156a 100644 --- a/filament/src/details/MaterialInstance.h +++ b/filament/src/details/MaterialInstance.h @@ -97,6 +97,12 @@ public: backend::PolygonOffset getPolygonOffset() const noexcept { return mPolygonOffset; } + void setMaskThreshold(float threshold) noexcept { + setParameter("_maskThreshold", threshold); + } + + void setDoubleSided(bool doubleSided) noexcept; + private: friend class FMaterial; friend class MaterialInstance; diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index c06af90fdc..aff51bbfc4 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -211,6 +211,7 @@ public: // double-sided materials don't cull faces, equivalent to culling(CullingMode::NONE) // doubleSided() overrides culling() if called + // when called with "false", this enables the capability for a run-time toggle MaterialBuilder& doubleSided(bool doubleSided) noexcept; // any fragment with an alpha below this threshold is clipped (MASKED blending mode only) @@ -342,7 +343,7 @@ private: uint8_t mParameterCount = 0; bool mDoubleSided = false; - bool mDoubleSidedSet = false; + bool mDoubleSidedCapability = false; bool mColorWrite = true; bool mDepthTest = true; bool mDepthWrite = true; diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index b87f86ef7c..fc135e8b30 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -223,7 +223,7 @@ MaterialBuilder& MaterialBuilder::depthCulling(bool enable) noexcept { MaterialBuilder& MaterialBuilder::doubleSided(bool doubleSided) noexcept { mDoubleSided = doubleSided; - mDoubleSidedSet = true; + mDoubleSidedCapability = true; return *this; } @@ -313,7 +313,11 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept { } if (mBlendingMode == BlendingMode::MASKED) { - ibb.add("maskThreshold", 1, UniformType::FLOAT); + ibb.add("_maskThreshold", 1, UniformType::FLOAT); + } + + if (mDoubleSidedCapability) { + ibb.add("_doubleSided", 1, UniformType::BOOL); } mRequiredAttributes.set(filament::VertexAttribute::POSITION); @@ -325,7 +329,7 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept { info.uib = ibb.name("MaterialParams").build(); info.isLit = isLit(); - info.isDoubleSided = mDoubleSided; + info.hasDoubleSidedCapability = mDoubleSidedCapability; info.hasExternalSamplers = hasExternalSampler(); info.curvatureToRoughness = mCurvatureToRoughness; info.limitOverInterpolation = mLimitOverInterpolation; @@ -458,7 +462,7 @@ Package MaterialBuilder::build() noexcept { SimpleFieldChunk matDepthWriteSet(ChunkType::MaterialDepthWriteSet, mDepthWriteSet); container.addChild(&matDepthWriteSet); - SimpleFieldChunk matDoubleSidedSet(ChunkType::MaterialDoubleSidedSet, mDoubleSidedSet); + SimpleFieldChunk matDoubleSidedSet(ChunkType::MaterialDoubleSidedSet, mDoubleSidedCapability); container.addChild(&matDoubleSidedSet); SimpleFieldChunk matDoubleSided(ChunkType::MaterialDoubleSided, mDoubleSided); diff --git a/libs/filamat/src/shaders/MaterialInfo.h b/libs/filamat/src/shaders/MaterialInfo.h index 697a6b1d5a..cc6b2c825c 100644 --- a/libs/filamat/src/shaders/MaterialInfo.h +++ b/libs/filamat/src/shaders/MaterialInfo.h @@ -33,7 +33,7 @@ using CullingMode = filament::backend::CullingMode; struct UTILS_PUBLIC MaterialInfo { bool isLit; - bool isDoubleSided; + bool hasDoubleSidedCapability; bool hasExternalSamplers; bool hasShadowMultiplier; bool curvatureToRoughness; diff --git a/libs/filamat/src/shaders/ShaderGenerator.cpp b/libs/filamat/src/shaders/ShaderGenerator.cpp index 0cc0d6249d..53883335eb 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.cpp +++ b/libs/filamat/src/shaders/ShaderGenerator.cpp @@ -246,7 +246,7 @@ const std::string ShaderGenerator::createFragmentProgram(filament::backend::Shad cg.generateDefine(fs, "HAS_SHADOW_MULTIPLIER", material.hasShadowMultiplier); // material defines - cg.generateDefine(fs, "MATERIAL_IS_DOUBLE_SIDED", material.isDoubleSided); + cg.generateDefine(fs, "MATERIAL_HAS_DOUBLE_SIDED_CAPABILITY", material.hasDoubleSidedCapability); switch (material.blendingMode) { case BlendingMode::OPAQUE: cg.generateDefine(fs, "BLEND_MODE_OPAQUE", true); @@ -311,11 +311,6 @@ const std::string ShaderGenerator::createFragmentProgram(filament::backend::Shad cg.generateCommonMaterial(fs, ShaderType::FRAGMENT); cg.generateParameters(fs, ShaderType::FRAGMENT); - if (material.blendingMode == BlendingMode::MASKED) { - cg.generateFunction(fs, "float", "getMaskThreshold", - " return materialParams.maskThreshold;"); - } - // shading model if (variant.isDepthPass()) { if (material.blendingMode == BlendingMode::MASKED) { diff --git a/libs/gltfio/materials/ubershader.mat.in b/libs/gltfio/materials/ubershader.mat.in index 61cb6ac2ff..e7f05e00c5 100644 --- a/libs/gltfio/materials/ubershader.mat.in +++ b/libs/gltfio/materials/ubershader.mat.in @@ -4,7 +4,7 @@ material { shadingModel : ${SHADINGMODEL}, blending : ${BLENDING}, depthWrite : true, - doubleSided : true, + doubleSided : false, flipUV : false, parameters : [ // Base Color diff --git a/libs/gltfio/src/MaterialGenerator.cpp b/libs/gltfio/src/MaterialGenerator.cpp index 0db64d10f7..b0d8f1040a 100644 --- a/libs/gltfio/src/MaterialGenerator.cpp +++ b/libs/gltfio/src/MaterialGenerator.cpp @@ -161,7 +161,6 @@ static std::string shaderFromKey(const MaterialKey& config) { static Material* createMaterial(Engine* engine, const MaterialKey& config, const UvMap& uvmap, const char* name) { - using CullingMode = MaterialBuilder::CullingMode; std::string shader = shaderFromKey(config); gltfio::details::processShaderString(&shader, uvmap, config); MaterialBuilder builder = MaterialBuilder() diff --git a/libs/gltfio/src/UbershaderLoader.cpp b/libs/gltfio/src/UbershaderLoader.cpp index 0eec9895a5..61be47649f 100644 --- a/libs/gltfio/src/UbershaderLoader.cpp +++ b/libs/gltfio/src/UbershaderLoader.cpp @@ -115,10 +115,10 @@ MaterialInstance* UbershaderLoader::createMaterialInstance(MaterialKey* config, mi->setParameter("emissiveIndex", getUvIndex(config->emissiveUV, config->hasEmissiveTexture)); if (config->alphaMode == AlphaMode::MASK) { - mi->setParameter("maskThreshold", config->alphaMaskThreshold); + mi->setMaskThreshold(config->alphaMaskThreshold); } - // TODO: honor config->doubleSided + mi->setDoubleSided(config->doubleSided); mat3f identity; mi->setParameter("baseColorUvMatrix", identity); diff --git a/shaders/src/getters.fs b/shaders/src/getters.fs index 6615219412..2ddb33177a 100644 --- a/shaders/src/getters.fs +++ b/shaders/src/getters.fs @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// Input access (varyings) +// Input access //------------------------------------------------------------------------------ #if defined(HAS_ATTRIBUTE_COLOR) @@ -28,3 +28,15 @@ HIGHP vec3 getLightSpacePosition() { return vertex_lightSpacePosition.xyz * (1.0 / vertex_lightSpacePosition.w); } #endif + +#if defined(BLEND_MODE_MASKED) +float getMaskThreshold() { + return materialParams._maskThreshold; +} +#endif + +#if defined(MATERIAL_HAS_DOUBLE_SIDED_CAPABILITY) +bool isDoubleSided() { + return materialParams._doubleSided; +} +#endif diff --git a/shaders/src/shading_parameters.fs b/shaders/src/shading_parameters.fs index 61fc02b380..f4c68c11d0 100644 --- a/shaders/src/shading_parameters.fs +++ b/shaders/src/shading_parameters.fs @@ -17,8 +17,10 @@ void computeShadingParams() { HIGHP vec3 n = vertex_worldNormal; #endif -#if defined(MATERIAL_IS_DOUBLE_SIDED) - n = gl_FrontFacing ? n : -n; +#if defined(MATERIAL_HAS_DOUBLE_SIDED_CAPABILITY) + if (isDoubleSided()) { + n = gl_FrontFacing ? n : -n; + } #endif #if defined(MATERIAL_HAS_ANISOTROPY) || defined(MATERIAL_HAS_NORMAL) || defined(MATERIAL_HAS_CLEAR_COAT_NORMAL) diff --git a/web/filament-js/filament.d.ts b/web/filament-js/filament.d.ts index a8f974a588..32bbf66f4f 100644 --- a/web/filament-js/filament.d.ts +++ b/web/filament-js/filament.d.ts @@ -72,6 +72,8 @@ export class MaterialInstance { public setTextureParameter(name: string, value: Texture, sampler: TextureSampler): void; public setColorParameter(name: string, ctype: RgbType, value: float3): void; public setPolygonOffset(scale: number, constant: number): void; + public setMaskThreshold(threshold: number): void; + public setDoubleSided(doubleSided: boolean): void; } export class EntityManager { diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index d34d4329c2..eea193d90c 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -821,7 +821,9 @@ class_("MaterialInstance") .function("setColorParameter", EMBIND_LAMBDA(void, (MaterialInstance* self, std::string name, RgbType type, filament::math::float3 value), { self->setParameter(name.c_str(), type, value); }), allow_raw_pointers()) - .function("setPolygonOffset", &MaterialInstance::setPolygonOffset); + .function("setPolygonOffset", &MaterialInstance::setPolygonOffset) + .function("setMaskThreshold", &MaterialInstance::setMaskThreshold) + .function("setDoubleSided", &MaterialInstance::setDoubleSided); class_("TextureSampler") .constructor();