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.
This commit is contained in:
Philip Rideout
2019-04-08 11:43:01 -07:00
committed by GitHub
parent 1634a8a2db
commit dcc17b9b7e
19 changed files with 122 additions and 38 deletions

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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;
}

View File

@@ -26,9 +26,12 @@
#include "details/Material.h"
#include "details/Texture.h"
#include <utils/Log.h>
#include <string.h>
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<MaterialInstance*>(this)->setParameter(
"maskThreshold", material->getMaskThreshold());
"_maskThreshold", material->getMaskThreshold());
}
if (material->hasDoubleSidedCapability()) {
static_cast<MaterialInstance*>(this)->setParameter(
"_doubleSided", material->isDoubleSided());
}
}
@@ -79,7 +87,12 @@ void FMaterialInstance::initDefaultInstance(FEngine& engine, FMaterial const* ma
if (material->getBlendingMode() == BlendingMode::MASKED) {
static_cast<MaterialInstance*>(this)->setParameter(
"maskThreshold", material->getMaskThreshold());
"_maskThreshold", material->getMaskThreshold());
}
if (material->hasDoubleSidedCapability()) {
static_cast<MaterialInstance*>(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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<bool> matDepthWriteSet(ChunkType::MaterialDepthWriteSet, mDepthWriteSet);
container.addChild(&matDepthWriteSet);
SimpleFieldChunk<bool> matDoubleSidedSet(ChunkType::MaterialDoubleSidedSet, mDoubleSidedSet);
SimpleFieldChunk<bool> matDoubleSidedSet(ChunkType::MaterialDoubleSidedSet, mDoubleSidedCapability);
container.addChild(&matDoubleSidedSet);
SimpleFieldChunk<bool> matDoubleSided(ChunkType::MaterialDoubleSided, mDoubleSided);

View File

@@ -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;

View File

@@ -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) {

View File

@@ -4,7 +4,7 @@ material {
shadingModel : ${SHADINGMODEL},
blending : ${BLENDING},
depthWrite : true,
doubleSided : true,
doubleSided : false,
flipUV : false,
parameters : [
// Base Color

View File

@@ -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()

View File

@@ -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);

View File

@@ -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

View File

@@ -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)

View File

@@ -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 {

View File

@@ -821,7 +821,9 @@ class_<MaterialInstance>("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>("TextureSampler")
.constructor<backend::SamplerMinFilter, backend::SamplerMagFilter, backend::SamplerWrapMode>();