Skip to content

Commit

Permalink
Trade: rework metallic/roughness packing.
Browse files Browse the repository at this point in the history
Turns out glTF doesn't actually put metalness into R and roughness into
G, even though the naming suggests that. This was done originally, but
then they changed that in order to be compatible with UE4 and allow for
a more efficient storage of an occlusion map.

Because this feels extremely arbitrary, the docs have added rationale
for each of the packing variants, and I'm also renaming the packed
attribute and checks to imply the red channel isn't used.
  • Loading branch information
mosra committed Aug 11, 2020
1 parent d73a661 commit 7d8fc34
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 131 deletions.
22 changes: 12 additions & 10 deletions doc/snippets/MagnumTrade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,24 +310,26 @@ if(data.types() & Trade::MaterialType::PbrSpecularGlossiness) {

{
/* [MaterialData-usage-packing] */
Trade::PbrMetallicRoughnessMaterialData data = DOXYGEN_IGNORE(Trade::PbrMetallicRoughnessMaterialData{{}, {}});
Trade::PbrSpecularGlossinessMaterialData data = DOXYGEN_IGNORE(Trade::PbrSpecularGlossinessMaterialData{{}, {}});

/* Use a shader that accepts a single packed metallic/roughness texture.
Querying any texture attributes will give the same values for both metalness
and roughness. */
if(data.hasMetallicRoughnessTexture()) {
UnsignedInt metallicRoughness = data.metalnessTexture();
if(data.hasSpecularGlossinessTexture()) {
UnsignedInt specularGlossiness = data.specularTexture();

DOXYGEN_IGNORE(static_cast<void>(metallicRoughness);)
DOXYGEN_IGNORE(static_cast<void>(specularGlossiness);)

/* Supply texture channels separately */
} else {
UnsignedInt metalness = data.metalnessTexture();
UnsignedInt roughness = data.roughnessTexture();
Trade::MaterialTextureSwizzle metalnessSwizzle = data.metalnessTextureSwizzle();
Trade::MaterialTextureSwizzle roughnessSwizzle = data.roughnessTextureSwizzle();
UnsignedInt specular = data.specularTexture();
UnsignedInt glossiness = data.glossinessTexture();
Trade::MaterialTextureSwizzle specularSwizzle =
data.specularTextureSwizzle();
Trade::MaterialTextureSwizzle glossinessSwizzle =
data.glossinessTextureSwizzle();

DOXYGEN_IGNORE(static_cast<void>(metalness), static_cast<void>(roughness), static_cast<void>(metalnessSwizzle), static_cast<void>(roughnessSwizzle);)
DOXYGEN_IGNORE(static_cast<void>(specular), static_cast<void>(glossiness), static_cast<void>(specularSwizzle), static_cast<void>(glossinessSwizzle);)
}
/* [MaterialData-usage-packing] */
}
Expand Down Expand Up @@ -413,7 +415,7 @@ Trade::MaterialData data{
Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat,
{
{Trade::MaterialAttribute::BaseColor, 0xffcc33_rgbf},
{Trade::MaterialAttribute::MetallicRoughnessTexture, 0u},
{Trade::MaterialAttribute::NoneRoughnessMetallicTexture, 0u},

{Trade::MaterialLayer::ClearCoat},
{Trade::MaterialAttribute::LayerFactorTexture, 1u},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ _c(RoughnessTexture,UnsignedInt)
_ct(RoughnessTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle)
_c(RoughnessTextureMatrix,Matrix3x3)
_c(RoughnessTextureCoordinates,UnsignedInt)
_c(MetallicRoughnessTexture,UnsignedInt)
_c(NoneRoughnessMetallicTexture,UnsignedInt)
_c(Glossiness,Float)
_c(GlossinessTexture,UnsignedInt)
_ct(GlossinessTextureSwizzle,TextureSwizzle,MaterialTextureSwizzle)
Expand Down
61 changes: 33 additions & 28 deletions src/Magnum/Trade/MaterialData.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ enum class MaterialAttribute: UnsignedInt {
* @ref MaterialAttributeType::Float.
*
* If @ref MaterialAttribute::MetalnessTexture or
* @ref MaterialAttribute::MetallicRoughnessTexture is present as well,
* @ref MaterialAttribute::NoneRoughnessMetallicTexture is present as well,
* these two are multiplied together.
* @see @ref PbrMetallicRoughnessMaterialData::metalness()
*/
Expand All @@ -366,9 +366,9 @@ enum class MaterialAttribute: UnsignedInt {
*
* If @ref MaterialAttribute::Metalness is present as well, these two are
* multiplied together. Can be alternatively supplied as a packed
* @ref MaterialAttribute::MetallicRoughnessTexture.
* @ref MaterialAttribute::NoneRoughnessMetallicTexture.
* @see @ref PbrMetallicRoughnessMaterialData::hasMetalnessTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture()
* @ref PbrMetallicRoughnessMaterialData::metalnessTexture()
Expand All @@ -383,9 +383,10 @@ enum class MaterialAttribute: UnsignedInt {
* @ref MaterialAttribute::MetalnessTexture together with other maps in a
* single texture. A single-channel swizzle value is expected. If not
* present, @ref MaterialTextureSwizzle::R is assumed. Does not apply to
* @ref MaterialAttribute::MetallicRoughnessTexture --- in that case, the
* metalness is implicitly in the red channel regardless of this attribute.
* @see @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(),
* @ref MaterialAttribute::NoneRoughnessMetallicTexture --- in that case,
* the metalness is implicitly in the red channel regardless of this
* attribute.
* @see @ref PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture()
* @ref PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle()
Expand Down Expand Up @@ -417,7 +418,7 @@ enum class MaterialAttribute: UnsignedInt {
* @ref MaterialAttributeType::Float.
*
* If @ref MaterialAttribute::RoughnessTexture or
* @ref MaterialAttribute::MetallicRoughnessTexture is present as well,
* @ref MaterialAttribute::NoneRoughnessMetallicTexture is present as well,
* these two are multiplied together.
* @see @ref PbrMetallicRoughnessMaterialData::roughness()
*/
Expand All @@ -429,9 +430,9 @@ enum class MaterialAttribute: UnsignedInt {
*
* If @ref MaterialAttribute::Roughness is present as well, these two are
* multiplied together. Can be alternatively supplied as a packed
* @ref MaterialAttribute::MetallicRoughnessTexture.
* @ref MaterialAttribute::NoneRoughnessMetallicTexture.
* @see @ref PbrMetallicRoughnessMaterialData::hasRoughnessTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture()
* @ref PbrMetallicRoughnessMaterialData::roughnessTexture()
Expand All @@ -446,10 +447,10 @@ enum class MaterialAttribute: UnsignedInt {
* @ref MaterialAttribute::RoughnessTexture together with other maps in a
* single texture. A single-channel swizzle value is expected. If not
* present, @ref MaterialTextureSwizzle::R is assumed. Does not apply to
* @ref MaterialAttribute::MetallicRoughnessTexture --- in that case, the
* metalness is implicitly in the green channel regardless of this
* @ref MaterialAttribute::NoneRoughnessMetallicTexture --- in that case,
* the metalness is implicitly in the green channel regardless of this
* attribute.
* @see @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(),
* @see @ref PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture()
* @ref PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle()
Expand Down Expand Up @@ -477,32 +478,36 @@ enum class MaterialAttribute: UnsignedInt {
RoughnessTextureCoordinates,

/**
* Combined metallic/roughness texture index for PBR metallic/roughness
* materials with metalness in the red channel and roughness in the green
* Combined roughness/metallic texture index for PBR metallic/roughness
* materials with metalness in the blue channel and roughness in the green
* channel, @ref MaterialAttributeType::UnsignedInt.
*
* If @ref MaterialAttribute::Metalness / @ref MaterialAttribute::Roughness
* is present as well, these two are multiplied together. Can be
* alternatively specified as a pair of @ref MaterialAttribute::MetalnessTexture
* / @ref MaterialAttribute::RoughnessTexture attributes together with
* @ref MaterialAttribute::MetalnessTextureSwizzle set to
* @ref MaterialTextureSwizzle::R (or omitted, since that's the default)
* and @ref MaterialAttribute::RoughnessTextureSwizzle set to
* @ref MaterialTextureSwizzle::G. Texture transformation and coordinate
* is present as well, these two are multiplied together.
*
* This is a convenience alias to simplify representation of glTF and UE4
* materials, which is where this packing is used ([rationale](https://github.com/KhronosGroup/glTF/issues/857)).
* This packing (and other variants) can be alternatively specified as a
* pair of @ref MaterialAttribute::RoughnessTexture /
* @ref MaterialAttribute::MetalnessTexture attributes together with
* @ref MaterialAttribute::RoughnessTextureSwizzle set to
* @ref MaterialTextureSwizzle::G
* and @ref MaterialAttribute::MetalnessTextureSwizzle set to
* @ref MaterialTextureSwizzle::B. Texture transformation and coordinate
* set, if needed, have to be specified either using the global
* @ref MaterialAttribute::TextureMatrix and
* @ref MaterialAttribute::TextureCoordinates attributes or the per-texture
* @ref MaterialAttribute::MetalnessTextureMatrix,
* @ref MaterialAttribute::RoughnessTextureMatrix,
* @ref MaterialAttribute::MetalnessTextureCoordinates and
* @ref MaterialAttribute::RoughnessTextureCoordinates variants.
* @see @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture(),
* @ref MaterialAttribute::MetalnessTextureMatrix,
* @ref MaterialAttribute::RoughnessTextureCoordinates and
* @ref MaterialAttribute::MetalnessTextureCoordinates variants.
* @see @ref PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasOcclusionRoughnessMetallicTexture(),
* @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture()
* @ref PbrMetallicRoughnessMaterialData::metalnessTexture(),
* @ref PbrMetallicRoughnessMaterialData::roughnessTexture()
*/
MetallicRoughnessTexture,
NoneRoughnessMetallicTexture,

/* DiffuseColor, DiffuseTexture, DiffuseTextureMatrix,
DiffuseTextureCoordinates, SpecularColor, SpecularTexture,
Expand Down Expand Up @@ -1439,8 +1444,8 @@ various @ref MaterialTextureSwizzle attributes such as
@ref MaterialAttribute::MetalnessTextureSwizzle. While this provides an almost
endless variability, real-world textures are in just a few common packing
schemes. For convenience these have dedicated attributes such as
@ref MaterialAttribute::MetallicRoughnessTexture and can also be checked for
with for example @ref PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture():
@ref MaterialAttribute::SpecularGlossinessTexture and can also be checked
for with for example @ref PbrSpecularGlossinessMaterialData::hasSpecularGlossinessTexture():
@snippet MagnumTrade.cpp MaterialData-usage-packing
Expand Down
26 changes: 13 additions & 13 deletions src/Magnum/Trade/PbrMetallicRoughnessMaterialData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,22 @@ using namespace Math::Literals;

bool PbrMetallicRoughnessMaterialData::hasMetalnessTexture() const {
return hasAttribute(MaterialAttribute::MetalnessTexture) ||
hasAttribute(MaterialAttribute::MetallicRoughnessTexture);
hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture);
}

bool PbrMetallicRoughnessMaterialData::hasRoughnessTexture() const {
return hasAttribute(MaterialAttribute::RoughnessTexture) ||
hasAttribute(MaterialAttribute::MetallicRoughnessTexture);
hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture);
}

bool PbrMetallicRoughnessMaterialData::hasMetallicRoughnessTexture() const {
return (hasAttribute(MaterialAttribute::MetallicRoughnessTexture) ||
bool PbrMetallicRoughnessMaterialData::hasNoneRoughnessMetallicTexture() const {
return (hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture) ||
(hasAttribute(MaterialAttribute::MetalnessTexture) &&
hasAttribute(MaterialAttribute::RoughnessTexture) &&
metalnessTextureSwizzle() == MaterialTextureSwizzle::R &&
roughnessTextureSwizzle() == MaterialTextureSwizzle::G)) &&
metalnessTextureMatrix() == roughnessTextureMatrix() &&
metalnessTextureCoordinates() == roughnessTextureCoordinates();
roughnessTextureSwizzle() == MaterialTextureSwizzle::G &&
metalnessTextureSwizzle() == MaterialTextureSwizzle::B)) &&
roughnessTextureMatrix() == metalnessTextureMatrix() &&
roughnessTextureCoordinates() == metalnessTextureCoordinates();
}

bool PbrMetallicRoughnessMaterialData::hasRoughnessMetallicOcclusionTexture() const {
Expand Down Expand Up @@ -156,16 +156,16 @@ UnsignedInt PbrMetallicRoughnessMaterialData::metalnessTexture() const {
would be misleading as it can be also MetallicRoughnessTexture */
CORRADE_ASSERT(hasMetalnessTexture(),
"Trade::PbrMetallicRoughnessMaterialData::metalnessTexture(): the material doesn't have a metalness texture", {});
if(Containers::Optional<UnsignedInt> value = tryAttribute<UnsignedInt>(MaterialAttribute::MetallicRoughnessTexture))
if(Containers::Optional<UnsignedInt> value = tryAttribute<UnsignedInt>(MaterialAttribute::NoneRoughnessMetallicTexture))
return *value;
return attribute<UnsignedInt>(MaterialAttribute::MetalnessTexture);
}

MaterialTextureSwizzle PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle() const {
CORRADE_ASSERT(hasMetalnessTexture(),
"Trade::PbrMetallicRoughnessMaterialData::metalnessTextureSwizzle(): the material doesn't have a metalness texture", {});
if(hasAttribute(MaterialAttribute::MetallicRoughnessTexture))
return MaterialTextureSwizzle::R;
if(hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture))
return MaterialTextureSwizzle::B;
return attributeOr(MaterialAttribute::MetalnessTextureSwizzle, MaterialTextureSwizzle::R);
}

Expand Down Expand Up @@ -194,15 +194,15 @@ UnsignedInt PbrMetallicRoughnessMaterialData::roughnessTexture() const {
would be misleading as it can be also MetallicRoughnessTexture */
CORRADE_ASSERT(hasRoughnessTexture(),
"Trade::PbrMetallicRoughnessMaterialData::roughnessTexture(): the material doesn't have a roughness texture", {});
if(Containers::Optional<UnsignedInt> value = tryAttribute<UnsignedInt>(MaterialAttribute::MetallicRoughnessTexture))
if(Containers::Optional<UnsignedInt> value = tryAttribute<UnsignedInt>(MaterialAttribute::NoneRoughnessMetallicTexture))
return *value;
return attribute<UnsignedInt>(MaterialAttribute::RoughnessTexture);
}

MaterialTextureSwizzle PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle() const {
CORRADE_ASSERT(hasRoughnessTexture(),
"Trade::PbrMetallicRoughnessMaterialData::roughnessTextureSwizzle(): the material doesn't have a roughness texture", {});
if(hasAttribute(MaterialAttribute::MetallicRoughnessTexture))
if(hasAttribute(MaterialAttribute::NoneRoughnessMetallicTexture))
return MaterialTextureSwizzle::G;
return attributeOr(MaterialAttribute::RoughnessTextureSwizzle, MaterialTextureSwizzle::R);
}
Expand Down
Loading

0 comments on commit 7d8fc34

Please sign in to comment.