From 9c00efa7e01a77a9e4133134dca75d7e5f702965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 11 Aug 2020 12:33:22 +0200 Subject: [PATCH] Trade: MaterialTextureSwizzle::GA for normal maps and related utils. Things are getting complicated, heh. --- doc/snippets/MagnumTrade.glsl | 38 ++++++++++++++++++++++ src/Magnum/Trade/MaterialData.cpp | 7 ++++ src/Magnum/Trade/MaterialData.h | 35 ++++++++++++++++++-- src/Magnum/Trade/Test/MaterialDataTest.cpp | 13 +++++++- 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 doc/snippets/MagnumTrade.glsl diff --git a/doc/snippets/MagnumTrade.glsl b/doc/snippets/MagnumTrade.glsl new file mode 100644 index 000000000..d1cef7a37 --- /dev/null +++ b/doc/snippets/MagnumTrade.glsl @@ -0,0 +1,38 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/* [unpackTwoChannelNormal] */ +/* Assumes (1, y, 1, x) or (x, y, , 1) */ +vec3 unpackTwoChannelNormal(vec4 packed) { + packed.x *= packed.w; + + /* reconstruct Z */ + vec3 normal; + normal.xy = packed.xy*2.0 - 1.0; + normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, + normal.xy))); + return normal; +} +/* [unpackTwoChannelNormal] */ diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 88f08df4f..a888e2d27 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -65,6 +65,13 @@ constexpr struct { } +UnsignedInt materialTextureSwizzleComponentCount(const MaterialTextureSwizzle swizzle) { + return (UnsignedInt(swizzle) & 0xff000000u ? 1 : 0) + + (UnsignedInt(swizzle) & 0x00ff0000u ? 1 : 0) + + (UnsignedInt(swizzle) & 0x0000ff00u ? 1 : 0) + + (UnsignedInt(swizzle) & 0x000000ffu ? 1 : 0); +} + std::size_t materialAttributeTypeSize(const MaterialAttributeType type) { switch(type) { case MaterialAttributeType::Bool: diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 5f6e99148..990cae23e 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -613,10 +613,22 @@ enum class MaterialAttribute: UnsignedInt { * not present, @ref MaterialTextureSwizzle::RGB is assumed. * * If the texture is just two-component, the remaining component is - * implicit and calculated as @f$ z = \sqrt{1 - x^2 - y^2} @f$. + * implicit and calculated as @f$ z = \sqrt{1 - x^2 - y^2} @f$. In order to + * account for numeric issues and avoid negative values under the square + * root, it's commonly done as @f$ z = \sqrt{\max(0, 1 - x^2 - y^2)} @f$. + * Additionally, to mitigate artifacts when storing normal texture in a + * compressed format, @ref MaterialTextureSwizzle::GA may get used instead + * of @ref MaterialTextureSwizzle::RG. + * + * Shader code that is able to reconstruct a XYZ normal from both RG and GA + * variants assuming constant values in other channels ([source](https://github.com/KhronosGroup/glTF/issues/1682#issuecomment-557880407)): + * + * @snippet MagnumTrade.glsl unpackTwoChannelNormal + * * @see @ref PbrMetallicRoughnessMaterialData::hasNormalRoughnessMetallicTexture(), * @ref PbrMetallicRoughnessMaterialData::normalTextureSwizzle(), - * @ref PbrSpecularGlossinessMaterialData::normalTextureSwizzle() + * @ref PbrSpecularGlossinessMaterialData::normalTextureSwizzle(), + * @ref materialTextureSwizzleComponentCount() */ NormalTextureSwizzle, @@ -842,6 +854,7 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAttribute value); @m_since_latest See @ref MaterialData for more information. +@see @ref materialTextureSwizzleComponentCount() */ enum class MaterialTextureSwizzle: UnsignedInt { /** Red component */ @@ -862,6 +875,16 @@ enum class MaterialTextureSwizzle: UnsignedInt { /** Green and blue component */ GB = Utility::Endianness::fourCC('G', 'B', '\0', '\0'), + /** + * Green and alpha component. May get used to mitigate artifacts when + * storing two independent channels (such as two-channel normal maps) in + * compressed texture formats --- these commonly have separately compressed + * RGB and alpha, with green channel having the most precision of the RGB + * triplet. + * @see @ref MaterialAttribute::NormalTextureSwizzle + */ + GA = Utility::Endianness::fourCC('G', 'A', '\0', '\0'), + /** Blue and alpha component */ BA = Utility::Endianness::fourCC('B', 'A', '\0', '\0'), @@ -875,6 +898,14 @@ enum class MaterialTextureSwizzle: UnsignedInt { RGBA = Utility::Endianness::fourCC('R', 'G', 'B', 'A'), }; +/** +@brief Component count in a material texture swizzle +@m_since_latest + +Returns for example @cpp 2 @ce for @ref MaterialTextureSwizzle::GA. +*/ +MAGNUM_TRADE_EXPORT UnsignedInt materialTextureSwizzleComponentCount(MaterialTextureSwizzle swizzle); + /** @debugoperatorenum{MaterialTextureSwizzle} @m_since_latest diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index ee5703c71..3a50546b4 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -46,6 +46,7 @@ class MaterialDataTest: public TestSuite::Tester { public: explicit MaterialDataTest(); + void textureSwizzleComponentCount(); void attributeTypeSize(); void attributeTypeSizeInvalid(); @@ -217,7 +218,9 @@ class MaterialDataTest: public TestSuite::Tester { }; MaterialDataTest::MaterialDataTest() { - addTests({&MaterialDataTest::attributeTypeSize, + addTests({&MaterialDataTest::textureSwizzleComponentCount, + + &MaterialDataTest::attributeTypeSize, &MaterialDataTest::attributeTypeSizeInvalid, &MaterialDataTest::attributeMap, &MaterialDataTest::layerMap, @@ -417,6 +420,14 @@ MaterialDataTest::MaterialDataTest() { using namespace Containers::Literals; using namespace Math::Literals; +void MaterialDataTest::textureSwizzleComponentCount() { + CORRADE_COMPARE(materialTextureSwizzleComponentCount(MaterialTextureSwizzle::B), 1); + CORRADE_COMPARE(materialTextureSwizzleComponentCount(MaterialTextureSwizzle::RG), 2); + CORRADE_COMPARE(materialTextureSwizzleComponentCount(MaterialTextureSwizzle::GA), 2); + CORRADE_COMPARE(materialTextureSwizzleComponentCount(MaterialTextureSwizzle::GBA), 3); + CORRADE_COMPARE(materialTextureSwizzleComponentCount(MaterialTextureSwizzle::RGBA), 4); +} + void MaterialDataTest::attributeTypeSize() { CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Bool), 1); CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Deg), 4);