Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Material alpha-blending modes #15557

Closed
wants to merge 4 commits into from
Closed
Changes from 3 commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -228,15 +228,15 @@ int GLTFSerializer::getAccessorType(const QString& type)
int GLTFSerializer::getMaterialAlphaMode(const QString& type)
{
if (type == "OPAQUE") {
return GLTFMaterialAlphaMode::OPAQUE;
return hfm::AlphaMode::HFM_OPAQUE;
}
if (type == "MASK") {
return GLTFMaterialAlphaMode::MASK;
return hfm::AlphaMode::HFM_MASK;
}
if (type == "BLEND") {
return GLTFMaterialAlphaMode::BLEND;
return hfm::AlphaMode::HFM_BLEND;
}
return GLTFMaterialAlphaMode::OPAQUE;
return hfm::AlphaMode::HFM_OPAQUE;

This comment has been minimized.

Copy link
@SamGondelman

SamGondelman May 31, 2019

Contributor

This defaults to opaque, but elsewhere we default to blend. Does that matter? Which matches the current behavior?

}

int GLTFSerializer::getCameraType(const QString& type)
@@ -1744,6 +1744,29 @@ HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) {
void GLTFSerializer::setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& material) {


if (material.defined["alphaMode"]) {
switch (material.alphaMode) {
case hfm::AlphaMode::HFM_OPAQUE:
fbxmat.alphaMode = hfm::AlphaMode::HFM_OPAQUE;
This conversation was marked as resolved by SamGondelman

This comment has been minimized.

Copy link
@SamGondelman

SamGondelman May 31, 2019

Contributor

Not your fault but can we rename fbxmat to hfmMat

break;
case hfm::AlphaMode::HFM_MASK:
fbxmat.alphaMode = hfm::AlphaMode::HFM_MASK;
break;
case hfm::AlphaMode::HFM_BLEND:
fbxmat.alphaMode = hfm::AlphaMode::HFM_BLEND;
break;
default:
fbxmat.alphaMode = hfm::AlphaMode::HFM_OPAQUE;
break;
}
} else {
fbxmat.alphaMode = hfm::AlphaMode::HFM_OPAQUE;
}

if (material.defined["alphaCutoff"]) {
fbxmat.alphaCutoff = material.alphaCutoff;
}

if (material.defined["emissiveFactor"] && material.emissiveFactor.size() == 3) {
glm::vec3 emissive = glm::vec3(material.emissiveFactor[0],
material.emissiveFactor[1],
@@ -2074,6 +2097,7 @@ void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) {
foreach(HFMMaterial mat, hfmModel.materials) {
qCDebug(modelformat) << "\n";
qCDebug(modelformat) << " mat.materialID =" << mat.materialID;
qCDebug(modelformat) << " alphaMode =" << hfm::AlphaMode::toString(mat.alphaMode).c_str();
qCDebug(modelformat) << " diffuseColor =" << mat.diffuseColor;
qCDebug(modelformat) << " diffuseFactor =" << mat.diffuseFactor;
qCDebug(modelformat) << " specularColor =" << mat.specularColor;
@@ -416,14 +416,6 @@ struct GLTFpbrMetallicRoughness {
}
};

namespace GLTFMaterialAlphaMode {
enum Values {
OPAQUE = 0,
MASK,
BLEND
};
};

struct GLTFMaterial {
QString name;
QVector<double> emissiveFactor;
@@ -896,8 +896,9 @@ HFMModel::Pointer OBJSerializer::read(const hifi::ByteArray& data, const hifi::V
objMaterial.specularColor,
objMaterial.emissiveColor,
objMaterial.shininess,
objMaterial.opacity);

objMaterial.opacity,
hfm::AlphaMode::HFM_BLEND,
0.5f);

This comment has been minimized.

Copy link
@SamGondelman

SamGondelman May 31, 2019

Contributor

Why always blend and 0.5?

This comment has been minimized.

Copy link
@SaracenOne

SaracenOne Jun 3, 2019

Author Contributor

The reason for this is the current implementation is built around a fallback mechanism, where it still takes the state of the albodo's opacity map into account when choosing an alpha mode: opaque, 1bit, or 8bit alpha, but setting the alpha mode basically limits the alpha mode you're allowed to fall back to, with opaque being the most restrictive. It basically allows you to force the renderer to ignore the presence or properties of an alpha channel. 0.5 is also just the default alpha cutoff range.

hfmMaterial.name = materialID;
hfmMaterial.materialID = materialID;
hfmMaterial._material = std::make_shared<graphics::Material>();
@@ -29,6 +29,8 @@ namespace scriptable {
using MeshPointer = std::shared_ptr<scriptable::Mesh>;
using WeakMeshPointer = std::weak_ptr<scriptable::Mesh>;

using Material = graphics::Material;

class ScriptableModelBase;
using ScriptableModelBasePointer = QPointer<ScriptableModelBase>;

@@ -39,6 +41,7 @@ namespace scriptable {
/**jsdoc
* @typedef {object} Graphics.Material
* @property {string} name
* @property {string} alphaMode
This conversation was marked as resolved by SamGondelman

This comment has been minimized.

Copy link
@SamGondelman

SamGondelman May 14, 2019

Contributor

To actually expose this, you'll need to add it to scriptableMaterialToScriptValue in GraphicsScriptingInterface.cpp

* @property {string} model
* @property {number|string} opacity
* @property {number|string} roughness
@@ -73,6 +76,8 @@ namespace scriptable {
ScriptableMaterial& operator=(const ScriptableMaterial& material);

QString name;
Material::MaterialAlphaMode alphaMode;
float alphaCutoff;
QString model;
float opacity;
float roughness;
@@ -363,6 +363,8 @@ namespace scriptable {
obj.setProperty("name", material.name);
obj.setProperty("model", material.model);

obj.setProperty("alphaMode", Material::alphaModeAsString(material.alphaMode));

This comment has been minimized.

Copy link
@SamGondelman

SamGondelman May 31, 2019

Contributor

I think this should also have a property fallthrough and default and everything and be down by where you did alphaCutoff

This comment has been minimized.

Copy link
@SamGondelman

SamGondelman May 31, 2019

Contributor

Hm actually I guess fallthrough doesn't make sense for this?


bool hasPropertyFallthroughs = !material.propertyFallthroughs.empty();

const QScriptValue FALLTHROUGH("fallthrough");
@@ -484,6 +486,11 @@ namespace scriptable {
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::MATERIAL_PARAMS)) {
obj.setProperty("materialParams", FALLTHROUGH);
}
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::ALPHA_CUTOFF)) {
obj.setProperty("alphaCutoff", FALLTHROUGH);
} else if (material.alphaCutoff != graphics::Material::DEFAULT_ALPHA_CUTOFF) {
obj.setProperty("alphaCutoff", material.alphaCutoff);
}

obj.setProperty("defaultFallthrough", material.defaultFallthrough);

@@ -21,6 +21,8 @@

scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const scriptable::ScriptableMaterial& material) {
name = material.name;
alphaMode = material.alphaMode;
alphaCutoff = material.alphaCutoff;

This comment has been minimized.

Copy link
@SamGondelman

SamGondelman May 31, 2019

Contributor

Can you put these in between opacity and roughness, here and everywhere else, just to group things more logically?

model = material.model;
opacity = material.opacity;
roughness = material.roughness;
@@ -53,6 +55,8 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const
scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPointer& material) {
if (material) {
name = material->getName().c_str();
alphaMode = material->getAlphaMode();
alphaCutoff = material->getAlphaCutoff();
model = material->getModel().c_str();
opacity = material->getOpacity();
roughness = material->getRoughness();
@@ -23,6 +23,7 @@ const float Material::DEFAULT_ALBEDO { 0.5f };
const float Material::DEFAULT_METALLIC { 0.0f };
const float Material::DEFAULT_ROUGHNESS { 1.0f };
const float Material::DEFAULT_SCATTERING { 0.0f };
const float Material::DEFAULT_ALPHA_CUTOFF { 0.5f };

Material::Material() {
for (int i = 0; i < NUM_TOTAL_FLAGS; i++) {
@@ -32,6 +33,8 @@ Material::Material() {

Material::Material(const Material& material) :
_name(material._name),
_alphaMode(material._alphaMode),
_alphaCutoff(material._alphaCutoff),
_model(material._model),
_key(material._key),
_emissive(material._emissive),
@@ -53,6 +56,8 @@ Material& Material::operator=(const Material& material) {
QMutexLocker locker(&_textureMapsMutex);

_name = material._name;
_alphaMode = material._alphaMode;
_alphaCutoff = material._alphaCutoff;
_model = material._model;
_key = material._key;
_emissive = material._emissive;
@@ -72,6 +77,13 @@ Material& Material::operator=(const Material& material) {
return (*this);
}

void Material::setAlphaMode(const MaterialAlphaMode alphaMode) {
if (alphaMode != _alphaMode) {
_alphaMode = alphaMode;
resetOpacityMap();
}
}

void Material::setEmissive(const glm::vec3& emissive, bool isSRGB) {
_key.setEmissive(glm::any(glm::greaterThan(emissive, glm::vec3(0.0f))));
_emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive);
@@ -144,6 +156,10 @@ void Material::resetOpacityMap() const {
_key.setOpacityMaskMap(false);
_key.setTranslucentMap(false);

if (_alphaMode == MAT_OPAQUE) {
return;
}

const auto& textureMap = getTextureMap(MaterialKey::ALBEDO_MAP);
if (textureMap &&
textureMap->useAlphaChannel() &&
@@ -152,7 +168,7 @@ void Material::resetOpacityMap() const {

auto usage = textureMap->getTextureView()._texture->getUsage();
if (usage.isAlpha()) {
if (usage.isAlphaMask()) {
if (usage.isAlphaMask() || _alphaMode == MAT_MASK) {
// Texture has alpha, but it is just a mask
_key.setOpacityMaskMap(true);
_key.setTranslucentMap(false);
@@ -320,6 +320,44 @@ class Material {
const std::string& getName() const { return _name; }
void setName(const std::string& name) { _name = name; }

enum MaterialAlphaMode
{

This comment has been minimized.

Copy link
@SamGondelman

SamGondelman May 31, 2019

Contributor

enum MaterialAlphaMode {

MAT_BLEND = 0,
MAT_MASK,
MAT_OPAQUE,
};

static MaterialAlphaMode alphaModeFromString(const QString& alphaMode) {
if (alphaMode.toLower() == "blend") {

This comment has been minimized.

Copy link
@SamGondelman

SamGondelman May 31, 2019

Contributor

Could we still make both of these methods use std::string? Also you could just compute alphaMode.toLower() once at the top here.

return MAT_BLEND;
} else if (alphaMode.toLower() == "mask") {
return MAT_MASK;
} else if (alphaMode.toLower() == "opaque") {
return MAT_OPAQUE;
} else {
return MAT_BLEND;
}
};

static QString alphaModeAsString(const MaterialAlphaMode& alphaMode) {
if (alphaMode == MAT_BLEND) {
return "blend";
} else if (alphaMode == MAT_MASK) {
return "mask";
} else if (alphaMode == MAT_OPAQUE) {
return "opaque";
} else {
return "blend";
}
};

const MaterialAlphaMode getAlphaMode() const { return _alphaMode; }
void setAlphaMode(const MaterialAlphaMode alphaMode);

static const float DEFAULT_ALPHA_CUTOFF;
void setAlphaCutoff(float alphaCutoff) { _alphaCutoff = alphaCutoff; }
float getAlphaCutoff() const { return _alphaCutoff; }

const std::string& getModel() const { return _model; }
void setModel(const std::string& model) { _model = model; }

@@ -336,6 +374,7 @@ class Material {
TEXCOORDTRANSFORM1,
LIGHTMAP_PARAMS,
MATERIAL_PARAMS,
ALPHA_CUTOFF,

NUM_TOTAL_FLAGS
};
@@ -345,7 +384,7 @@ class Material {

protected:
std::string _name { "" };

MaterialAlphaMode _alphaMode { MAT_BLEND };
private:
std::string _model { "hifi_pbr" };
mutable MaterialKey _key { 0 };
@@ -360,6 +399,7 @@ class Material {
std::array<glm::mat4, NUM_TEXCOORD_TRANSFORMS> _texcoordTransforms;
glm::vec2 _lightmapParams { 0.0, 1.0 };
glm::vec2 _materialParams { 0.0, 1.0 };
float _alphaCutoff { DEFAULT_ALPHA_CUTOFF };
TextureMaps _textureMaps;

bool _defaultFallthrough { false };
@@ -425,10 +465,12 @@ class MultiMaterial : public MaterialLayerQueue {

float _metallic { Material::DEFAULT_METALLIC }; // Not Metallic
float _scattering { Material::DEFAULT_SCATTERING }; // Scattering info

float _alphaCutoff{ Material::DEFAULT_ALPHA_CUTOFF };
#if defined(__clang__)
__attribute__((unused))
#endif
glm::vec2 _spare { 0.0f }; // Padding
float _spare { 0.0f }; // Padding

uint32_t _key { 0 }; // a copy of the materialKey
#if defined(__clang__)
@@ -49,7 +49,7 @@ struct TexMapArray {
struct Material {
vec4 _emissiveOpacity;
vec4 _albedoRoughness;
vec4 _metallicScatteringSpare2;
vec4 _metallicScatteringAlphaCutoffSpare1;
vec4 _keySpare3;
};

@@ -72,8 +72,9 @@ vec3 getMaterialAlbedo(Material m) { return m._albedoRoughness.rgb; }
float getMaterialRoughness(Material m) { return m._albedoRoughness.a; }
float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); }

float getMaterialMetallic(Material m) { return m._metallicScatteringSpare2.x; }
float getMaterialScattering(Material m) { return m._metallicScatteringSpare2.y; }
float getMaterialMetallic(Material m) { return m._metallicScatteringAlphaCutoffSpare1.x; }
float getMaterialScattering(Material m) { return m._metallicScatteringAlphaCutoffSpare1.y; }
float getMaterialAlphaCutoff(Material m) { return m._metallicScatteringAlphaCutoffSpare1.z; }

BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._keySpare3.x); }

@@ -214,12 +214,11 @@ vec3 fetchLightmapMap(vec2 uv) {
}
<@endfunc@>

<@func evalMaterialOpacity(fetchedOpacity, materialOpacity, matKey, opacity)@>
<@func evalMaterialOpacity(fetchedOpacity, materialOpacity, matKey, opacity, cutoff)@>
{
const float OPACITY_MASK_THRESHOLD = 0.5;
<$opacity$> = mix(1.0,
mix(<$fetchedOpacity$>,
step(OPACITY_MASK_THRESHOLD, <$fetchedOpacity$>),
step(<$cutoff$>, <$fetchedOpacity$>),
float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)),
float((<$matKey$> & (OPACITY_TRANSLUCENT_MAP_BIT | OPACITY_MASK_MAP_BIT)) != 0)) * <$materialOpacity$>;
}
@@ -159,20 +159,46 @@ class MeshPart {
QString materialID;
};

namespace AlphaMode {
enum Values {
HFM_BLEND = 0,
HFM_MASK,
HFM_OPAQUE,
};

This comment has been minimized.

Copy link
@SamGondelman

SamGondelman May 31, 2019

Contributor

It would still be great if we could avoid defining these in multiple places.


static std::string toString(Values alphaMode) {
switch (alphaMode) {
case HFM_BLEND:
return "BLEND";
case HFM_MASK:
return "MASK";
case HFM_OPAQUE:
return "OPAQUE";
default:
return "INVALID";
};
};
};

class Material {
public:
Material() {};
Material(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor,
float shininess, float opacity) :
float shininess, float opacity, AlphaMode::Values alphaMode, float alphaCutoff) :
diffuseColor(diffuseColor),
specularColor(specularColor),
emissiveColor(emissiveColor),
shininess(shininess),
opacity(opacity) {}
opacity(opacity),
alphaMode(alphaMode),
alphaCutoff(alphaCutoff) {}

void getTextureNames(QSet<QString>& textureList) const;
void setMaxNumPixelsPerTexture(int maxNumPixels);

AlphaMode::Values alphaMode{ AlphaMode::HFM_BLEND };
float alphaCutoff{ 0.5f };

glm::vec3 diffuseColor{ 1.0f };
float diffuseFactor{ 1.0f };
glm::vec3 specularColor{ 0.02f };
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.