From 9f9d77f882efc445f46139ed2188ce21fc857032 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Thu, 10 Jun 2021 18:13:46 +0100 Subject: [PATCH] First pass at simplifying glTFv2 PBR Removed 'core' set of GLTF-specific properties --- code/AssetLib/glTF2/glTF2Exporter.cpp | 60 ++++++++++++++------------- code/AssetLib/glTF2/glTF2Importer.cpp | 21 ++++++---- include/assimp/material.h | 40 +++++++++++++++++- include/assimp/pbrmaterial.h | 14 +++---- test/unit/utglTF2ImportExport.cpp | 55 ++++++++++++++++++++---- 5 files changed, 138 insertions(+), 52 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 751508225..dfa62372a 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -645,7 +645,7 @@ void glTF2Exporter::ExportMaterials() m->name = name; - GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR); if (!m->pbrMetallicRoughness.baseColorTexture.texture) { //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture @@ -654,19 +654,19 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR) != AI_SUCCESS) { + if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) { // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material. //a fallback to any diffuse color should be used instead GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat->Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; @@ -712,36 +712,38 @@ void glTF2Exporter::ExportMaterials() } } - bool hasPbrSpecularGlossiness = false; - mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness); - - if (hasPbrSpecularGlossiness) { - - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - + { + // If has a Specular color, use the KHR_materials_pbrSpecularGlossiness extension PbrSpecularGlossiness pbrSG; - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR); - - if (mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - - if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; } + + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + + // If don't have explicit glossiness then convert from roughness or shininess + if (mat->Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { + float shininess; + if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + // Add any appropriate textures + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + m->pbrSpecularGlossiness = Nullable(pbrSG); } - - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - - m->pbrSpecularGlossiness = Nullable(pbrSG); } - bool unlit; - if (mat->Get(AI_MATKEY_GLTF_UNLIT, unlit) == AI_SUCCESS && unlit) { + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; + mat->Get(AI_MATKEY_SHADING_MODEL, shadingMode); + if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index c62989c3b..b5ba65857 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -238,16 +238,18 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&str, AI_MATKEY_NAME); } + // Set Assimp DIFFUSE and BASE COLOR to the pbrMetallicRoughness base color and texture for backwards compatibility + // Technically should not load any pbrMetallicRoughness if extensionsRequired contains KHR_materials_pbrSpecularGlossiness SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR); + SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR); - aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR); float roughnessAsShininess = 1 - mat.pbrMetallicRoughness.roughnessFactor; roughnessAsShininess *= roughnessAsShininess * 1000; @@ -268,22 +270,27 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M if (mat.pbrSpecularGlossiness.isPresent) { PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; - aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR); float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f; aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS); - aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); + aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLOSSINESS_FACTOR); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR); } + + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; if (mat.unlit) { - aimat->AddProperty(&mat.unlit, 1, AI_MATKEY_GLTF_UNLIT); + shadingMode = aiShadingMode_Unlit; } + aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); + + //KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; diff --git a/include/assimp/material.h b/include/assimp/material.h index 08c0491c0..11cdef1f4 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -202,11 +202,15 @@ enum aiTextureType { /** The texture is combined with the result of the diffuse * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_DIFFUSE = 1, /** The texture is combined with the result of the specular * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_SPECULAR = 2, @@ -309,7 +313,9 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library - * + * + * #AI_MATKEY_SHADING_MODEL + * * The list of shading modes has been taken from Blender. * See Blender documentation for more information. The API does * not distinguish between "specular" and "diffuse" shaders (thus the @@ -364,13 +370,27 @@ enum aiShadingMode { aiShadingMode_CookTorrance = 0x8, /** No shading at all. Constant light influence of 1.0. + * Also known as "Unlit" */ aiShadingMode_NoShading = 0x9, + aiShadingMode_Unlit = aiShadingMode_NoShading, // Alias /** Fresnel shading */ aiShadingMode_Fresnel = 0xa, + /** Physically-Based Rendering (PBR) shading using + * Bidirectional scattering/reflectance distribution function (BSDF/BRDF) + * There are multiple methods under this banner, and model files may provide + * data for more than one PBR-BRDF method. + * Applications should use the set of provided properties to determine which + * of their preferred PBDR methods are available + * eg: + * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available + * - If AI_MATKEY_COLOR_SPECULAR is set, then a Specular/Glossiness is available + */ + aiShadingMode_PBR_BRDF = 0xb, + #ifndef SWIG _aiShadingMode_Force32Bit = INT_MAX #endif @@ -923,11 +943,29 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 + +// Metallic/Roughness Workflow +// --------------------------- +// Base color factor. Will be multiplied by final base color texture values if extant #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 +// Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 +// Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 + +// Specular/Glossiness Workflow +// --------------------------- +// Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} +// AI_MATKEY_COLOR_DIFFUSE +// Specular Color +// AI_MATKEY_COLOR_SPECULAR +// Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth +#define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 + +// Emissive +// -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 #define AI_MATKEY_EMISSIVE_INTENSITY "$mat.emissiveIntensity", 0, 0 #define AI_MATKEY_USE_AO_MAP "$mat.useAOMap", 0, 0 diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index 2e41b8b6d..fd904e4fc 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -50,16 +50,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # pragma GCC system_header #endif -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 -#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 -#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 -#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 +//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 +//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 +//#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 4110edcfc..e0ac10ad5 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -57,10 +57,9 @@ using namespace Assimp; class utglTF2ImportExport : public AbstractImportExportBase { public: - virtual bool importerTest() { + virtual bool importerMatTest(const char *file, bool spec_gloss, std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure); EXPECT_NE(scene, nullptr); if (!scene) { return false; @@ -72,13 +71,49 @@ public: } const aiMaterial *material = scene->mMaterials[0]; + // This Material should be a PBR + aiShadingMode shadingMode; + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_SHADING_MODEL, shadingMode)); + EXPECT_EQ(aiShadingMode_PBR_BRDF, shadingMode); + + // Should import the texture as diffuse and as base color aiString path; - aiTextureMapMode modes[2]; + std::array modes; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + nullptr, nullptr, modes.data())); EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); - EXPECT_EQ(modes[0], aiTextureMapMode_Mirror); - EXPECT_EQ(modes[1], aiTextureMapMode_Clamp); + EXPECT_EQ(exp_modes, modes); + + // Also as Base Color + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_BASE_COLOR, 0, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); + EXPECT_EQ(exp_modes, modes); + + // Should have a MetallicFactor (default is 1.0) + ai_real metal_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_METALLIC_FACTOR, metal_factor)); + EXPECT_EQ(ai_real(0.0), metal_factor); + + // And a roughness factor (default is 1.0) + ai_real roughness_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness_factor)); + EXPECT_EQ(ai_real(1.0), roughness_factor); + + aiColor3D spec_color = { 0, 0, 0 }; + ai_real glossiness = ai_real(0.5); + if (spec_gloss) { + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + constexpr ai_real spec_val(0.20000000298023225); // From the file + EXPECT_EQ(spec_val, spec_color.r); + EXPECT_EQ(spec_val, spec_color.g); + EXPECT_EQ(spec_val, spec_color.b); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + EXPECT_EQ(ai_real(1.0), glossiness); + } else { + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + } return true; } @@ -105,13 +140,17 @@ public: }; TEST_F(utglTF2ImportExport, importglTF2FromFileTest) { - EXPECT_TRUE(importerTest()); + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp})); } TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) { EXPECT_TRUE(binaryImporterTest()); } +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); +} + #ifndef ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer;