From dcc17b9b7ef022205bd3a6bfd6af03c95e6edcc2 Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Mon, 8 Apr 2019 11:43:01 -0700 Subject: [PATCH] Allow dynamic doubleSided and materialThreshold. (#1072) * MaterialInstance now has setMaskThreshold for convenience. * Materials now support dynamic doubleSided property. This does not change the format of material packages because they already have both getDoubleSided() and getDoubleSidedSet(). The only way in which this change could impact existing applications is that materials that explicity set doubleSided to "false" will now respect the material's culling mode, rather than forcing it to NONE. Fixes gltf_viewer with littlest_tokyo in ubershader mode. Fixes #963. * JNI for dynamic material properties. * Add underscore prefix to internal material params. * Remove un-needed mat info field. --- .../src/main/cpp/MaterialInstance.cpp | 16 +++++++++ .../android/filament/MaterialInstance.java | 12 +++++++ docs/Materials.md.html | 7 ++-- filament/include/filament/MaterialInstance.h | 12 +++++++ filament/src/Material.cpp | 15 ++------- filament/src/MaterialInstance.cpp | 33 +++++++++++++++++-- filament/src/details/Material.h | 2 ++ filament/src/details/MaterialInstance.h | 6 ++++ .../filamat/include/filamat/MaterialBuilder.h | 3 +- libs/filamat/src/MaterialBuilder.cpp | 12 ++++--- libs/filamat/src/shaders/MaterialInfo.h | 2 +- libs/filamat/src/shaders/ShaderGenerator.cpp | 7 +--- libs/gltfio/materials/ubershader.mat.in | 2 +- libs/gltfio/src/MaterialGenerator.cpp | 1 - libs/gltfio/src/UbershaderLoader.cpp | 4 +-- shaders/src/getters.fs | 14 +++++++- shaders/src/shading_parameters.fs | 6 ++-- web/filament-js/filament.d.ts | 2 ++ web/filament-js/jsbindings.cpp | 4 ++- 19 files changed, 122 insertions(+), 38 deletions(-) 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();