From e4a0bb8fa0042452c72b2d4a2ba7ed2297f86a61 Mon Sep 17 00:00:00 2001 From: toddZ_CG Date: Fri, 21 Jun 2024 10:06:17 -0700 Subject: [PATCH] Add support for KHR_materials_Specular (#7564) Co-authored-by: Todd Zhang Co-authored-by: Romain Guy Co-authored-by: Mathias Agopian --- README.md | 1 + .../include/filament/MaterialEnums.h | 4 +- libs/filamat/src/Enums.cpp | 54 +++--- libs/filamat/src/shaders/CodeGenerator.cpp | 2 + libs/gltfio/CMakeLists.txt | 3 + libs/gltfio/include/gltfio/MaterialProvider.h | 11 +- libs/gltfio/materials/base.spec.in | 5 + libs/gltfio/materials/sheen.mat.in | 20 ++- libs/gltfio/materials/sheen.spec.in | 5 + libs/gltfio/materials/specular.mat.in | 168 ++++++++++++++++++ libs/gltfio/materials/specular.spec.in | 35 ++++ libs/gltfio/materials/transmission.mat.in | 31 +++- libs/gltfio/materials/transmission.spec.in | 5 + libs/gltfio/materials/volume.mat.in | 18 ++ libs/gltfio/materials/volume.spec.in | 5 + libs/gltfio/src/AssetLoader.cpp | 35 +++- libs/gltfio/src/JitShaderProvider.cpp | 49 +++++ libs/gltfio/src/MaterialProvider.cpp | 19 ++ libs/gltfio/src/UbershaderProvider.cpp | 55 ++++++ shaders/src/brdf.fs | 10 ++ shaders/src/common_lighting.fs | 5 + shaders/src/light_indirect.fs | 5 +- shaders/src/material_inputs.fs | 18 ++ shaders/src/shading_lit.fs | 25 +++ shaders/src/shading_model_standard.fs | 6 +- 25 files changed, 559 insertions(+), 35 deletions(-) create mode 100644 libs/gltfio/materials/specular.mat.in create mode 100644 libs/gltfio/materials/specular.spec.in diff --git a/README.md b/README.md index b04cd6fa55..01144d1219 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ steps: - [x] KHR_materials_unlit - [x] KHR_materials_variants - [x] KHR_materials_volume + - [x] KHR_materials_specular - [x] KHR_mesh_quantization - [x] KHR_texture_basisu - [x] KHR_texture_transform diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index c15c6c457c..cad7057fb1 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -203,7 +203,7 @@ enum class ReflectionMode : uint8_t { // can't really use std::underlying_type::type because the driver takes a uint32_t using AttributeBitset = utils::bitset32; -static constexpr size_t MATERIAL_PROPERTIES_COUNT = 27; +static constexpr size_t MATERIAL_PROPERTIES_COUNT = 29; enum class Property : uint8_t { BASE_COLOR, //!< float4, all shading models ROUGHNESS, //!< float, lit shading models only @@ -232,6 +232,8 @@ enum class Property : uint8_t { IOR, //!< float, material's index of refraction MICRO_THICKNESS, //!< float, thickness of the thin layer BENT_NORMAL, //!< float3, all shading models only, except unlit + SPECULAR_FACTOR, //!< float, lit shading models only, except subsurface and cloth + SPECULAR_COLOR_FACTOR, //!< float3, lit shading models only, except subsurface and cloth // when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT }; diff --git a/libs/filamat/src/Enums.cpp b/libs/filamat/src/Enums.cpp index 583d376f95..bb163425da 100644 --- a/libs/filamat/src/Enums.cpp +++ b/libs/filamat/src/Enums.cpp @@ -21,33 +21,35 @@ namespace filamat { std::unordered_map Enums::mStringToProperty = { - { "baseColor", Property::BASE_COLOR }, - { "roughness", Property::ROUGHNESS }, - { "metallic", Property::METALLIC }, - { "reflectance", Property::REFLECTANCE }, - { "ambientOcclusion", Property::AMBIENT_OCCLUSION }, - { "clearCoat", Property::CLEAR_COAT }, - { "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS }, - { "clearCoatNormal", Property::CLEAR_COAT_NORMAL }, - { "anisotropy", Property::ANISOTROPY }, - { "anisotropyDirection", Property::ANISOTROPY_DIRECTION }, - { "thickness", Property::THICKNESS }, - { "subsurfacePower", Property::SUBSURFACE_POWER }, - { "subsurfaceColor", Property::SUBSURFACE_COLOR }, - { "sheenColor", Property::SHEEN_COLOR }, - { "sheenRoughness", Property::SHEEN_ROUGHNESS }, - { "glossiness", Property::GLOSSINESS }, - { "specularColor", Property::SPECULAR_COLOR }, - { "emissive", Property::EMISSIVE }, - { "normal", Property::NORMAL }, - { "postLightingColor", Property::POST_LIGHTING_COLOR }, + { "baseColor", Property::BASE_COLOR }, + { "roughness", Property::ROUGHNESS }, + { "metallic", Property::METALLIC }, + { "reflectance", Property::REFLECTANCE }, + { "ambientOcclusion", Property::AMBIENT_OCCLUSION }, + { "clearCoat", Property::CLEAR_COAT }, + { "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS }, + { "clearCoatNormal", Property::CLEAR_COAT_NORMAL }, + { "anisotropy", Property::ANISOTROPY }, + { "anisotropyDirection", Property::ANISOTROPY_DIRECTION }, + { "thickness", Property::THICKNESS }, + { "subsurfacePower", Property::SUBSURFACE_POWER }, + { "subsurfaceColor", Property::SUBSURFACE_COLOR }, + { "sheenColor", Property::SHEEN_COLOR }, + { "sheenRoughness", Property::SHEEN_ROUGHNESS }, + { "glossiness", Property::GLOSSINESS }, + { "specularColor", Property::SPECULAR_COLOR }, + { "emissive", Property::EMISSIVE }, + { "normal", Property::NORMAL }, + { "postLightingColor", Property::POST_LIGHTING_COLOR }, { "postLightingMixFactor", Property::POST_LIGHTING_MIX_FACTOR }, - { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM }, - { "absorption", Property::ABSORPTION }, - { "transmission", Property::TRANSMISSION }, - { "ior", Property::IOR }, - { "microThickness", Property::MICRO_THICKNESS }, - { "bentNormal", Property::BENT_NORMAL }, + { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM }, + { "absorption", Property::ABSORPTION }, + { "transmission", Property::TRANSMISSION }, + { "ior", Property::IOR }, + { "microThickness", Property::MICRO_THICKNESS }, + { "bentNormal", Property::BENT_NORMAL }, + { "specularFactor", Property::SPECULAR_FACTOR }, + { "specularColorFactor", Property::SPECULAR_COLOR_FACTOR } }; template <> diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index 6d631e0582..1c74cbd779 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -1192,6 +1192,8 @@ char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) n case Property::IOR: return "IOR"; case Property::MICRO_THICKNESS: return "MICRO_THICKNESS"; case Property::BENT_NORMAL: return "BENT_NORMAL"; + case Property::SPECULAR_FACTOR: return "SPECULAR_FACTOR"; + case Property::SPECULAR_COLOR_FACTOR: return "SPECULAR_COLOR_FACTOR"; } } diff --git a/libs/gltfio/CMakeLists.txt b/libs/gltfio/CMakeLists.txt index c066cc2297..c7c7d6a63c 100644 --- a/libs/gltfio/CMakeLists.txt +++ b/libs/gltfio/CMakeLists.txt @@ -147,6 +147,9 @@ build_ubershader(volume_opaque volume lit opaque) build_ubershader(volume_fade volume lit fade) build_ubershader(volume_masked volume lit masked) build_ubershader(sheen sheen _ _) +build_ubershader(specular_opaque specular lit opaque) +build_ubershader(specular_fade specular lit fade) +build_ubershader(specular_masked specular lit masked) add_custom_target(uberz_file DEPENDS ${UBERZ_OUTPUT_PATH}) set_target_properties(uberz_file PROPERTIES FOLDER Libs) diff --git a/libs/gltfio/include/gltfio/MaterialProvider.h b/libs/gltfio/include/gltfio/MaterialProvider.h index f62d667b35..042f303bb6 100644 --- a/libs/gltfio/include/gltfio/MaterialProvider.h +++ b/libs/gltfio/include/gltfio/MaterialProvider.h @@ -93,10 +93,17 @@ struct alignas(4) MaterialKey { bool hasSheen : 1; bool hasIOR : 1; bool hasVolume : 1; - uint8_t padding : 5; + bool hasSpecular : 1; + bool hasSpecularTexture : 1; + bool hasSpecularColorTexture : 1; + bool padding : 2; + // -- 32 bit boundary -- + uint8_t specularTextureUV; + uint8_t specularColorTextureUV; + uint16_t padding2; }; -static_assert(sizeof(MaterialKey) == 16, "MaterialKey has unexpected size."); +static_assert(sizeof(MaterialKey) == 20, "MaterialKey has unexpected size."); UTILS_WARNING_POP diff --git a/libs/gltfio/materials/base.spec.in b/libs/gltfio/materials/base.spec.in index 6e6e9375f5..f80236f5a1 100644 --- a/libs/gltfio/materials/base.spec.in +++ b/libs/gltfio/materials/base.spec.in @@ -30,3 +30,8 @@ TransmissionTexture = unsupported # Volume Features Volume = unsupported VolumeThicknessTexture = unsupported + +# Specular Features +Specular = unsupported +SpecularTexture = unsupported +SpecularColorTexture = unsupported diff --git a/libs/gltfio/materials/sheen.mat.in b/libs/gltfio/materials/sheen.mat.in index b8682ca34f..6ac496c96d 100644 --- a/libs/gltfio/materials/sheen.mat.in +++ b/libs/gltfio/materials/sheen.mat.in @@ -55,7 +55,16 @@ material { { type : mat3, name : sheenRoughnessUvMatrix, precision: high }, // Reflectance - { type : float, name : reflectance } + { type : float, name : reflectance }, + + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : mat3, name : specularColorUvMatrix, precision: high } ${CUSTOM_PARAMS} ], @@ -136,6 +145,15 @@ fragment { material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; } + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + ${CUSTOM_FRAGMENT} } } diff --git a/libs/gltfio/materials/sheen.spec.in b/libs/gltfio/materials/sheen.spec.in index dcd41bf25c..6c72f0b3f9 100644 --- a/libs/gltfio/materials/sheen.spec.in +++ b/libs/gltfio/materials/sheen.spec.in @@ -17,3 +17,8 @@ TextureTransforms=optional Sheen=optional SheenColorTexture=optional SheenRoughnessTexture=optional + +# KHR_material_specular Features +Specular=optional +SpecularTexture=optional +SpecularColorTexture=unsupported diff --git a/libs/gltfio/materials/specular.mat.in b/libs/gltfio/materials/specular.mat.in new file mode 100644 index 0000000000..acb4f2dbd7 --- /dev/null +++ b/libs/gltfio/materials/specular.mat.in @@ -0,0 +1,168 @@ +material { + name : specular_ubershader, + requires : [ uv0, uv1, color ], + shadingModel : lit, + blending : ${BLENDING}, + doubleSided : ${DOUBLESIDED}, + transparency : ${TRANSPARENCY}, + flipUV : false, + specularAmbientOcclusion : simple, + specularAntiAliasing : true, + clearCoatIorChange : false, + reflections: screenspace, + parameters : [ + + // Base Color + { type : int, name : baseColorIndex }, + { type : float4, name : baseColorFactor }, + { type : sampler2d, name : baseColorMap }, + { type : mat3, name : baseColorUvMatrix, precision: high }, + + // Metallic-Roughness Map + { type : int, name : metallicRoughnessIndex }, + { type : float, name : metallicFactor }, + { type : float, name : roughnessFactor }, + { type : sampler2d, name : metallicRoughnessMap }, + { type : mat3, name : metallicRoughnessUvMatrix, precision: high }, + + // Normal Map + { type : int, name : normalIndex }, + { type : float, name : normalScale }, + { type : sampler2d, name : normalMap }, + { type : mat3, name : normalUvMatrix, precision: high }, + + // Ambient Occlusion + { type : int, name : aoIndex }, + { type : float, name : aoStrength }, + { type : sampler2d, name : occlusionMap }, + { type : mat3, name : occlusionUvMatrix, precision: high }, + + // Emissive Map + { type : int, name : emissiveIndex }, + { type : float3, name : emissiveFactor }, + { type : float, name : emissiveStrength }, + { type : sampler2d, name : emissiveMap }, + { type : mat3, name : emissiveUvMatrix, precision: high }, + + // Clear coat, we drop texture support for clear coat due to 8 texture limits. + { type : float, name : clearCoatFactor }, + { type : float, name : clearCoatRoughnessFactor }, + { type : int, name : clearCoatIndex }, + { type : sampler2d, name : clearCoatMap }, + { type : mat3, name : clearCoatUvMatrix, precision: high }, + { type : int, name : clearCoatRoughnessIndex }, + { type : mat3, name : clearCoatRoughnessUvMatrix, precision: high }, + { type : int, name : clearCoatNormalIndex }, + { type : mat3, name : clearCoatNormalUvMatrix, precision: high }, + { type : float, name : clearCoatNormalScale }, + + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : sampler2d, name : specularColorMap }, + { type : mat3, name : specularColorUvMatrix, precision: high }, + + // IOR + { type : float, name : ior } + + ${CUSTOM_PARAMS} + ], +} + +vertex { +void materialVertex(inout MaterialVertexInputs material) { + ${CUSTOM_VERTEX} +} +} + +fragment { +void material(inout MaterialInputs material) { + highp float2 uvs[2]; + uvs[0] = getUV0(); + uvs[1] = getUV1(); + + if (materialParams.normalIndex > -1) { + highp float2 uv = uvs[materialParams.normalIndex]; + uv = (vec3(uv, 1.0) * materialParams.normalUvMatrix).xy; + material.normal = texture(materialParams_normalMap, uv).xyz * 2.0 - 1.0; + material.normal.xy *= materialParams.normalScale; + } + + prepareMaterial(material); + material.baseColor = materialParams.baseColorFactor; + + if (materialParams.baseColorIndex > -1) { + highp float2 uv = uvs[materialParams.baseColorIndex]; + uv = (vec3(uv, 1.0) * materialParams.baseColorUvMatrix).xy; + material.baseColor *= texture(materialParams_baseColorMap, uv); + } + +#if defined(BLEND_MODE_TRANSPARENT) + material.baseColor.rgb *= material.baseColor.a; +#endif + + material.baseColor *= getColor(); + + material.roughness = materialParams.roughnessFactor; + material.metallic = materialParams.metallicFactor; + material.ior = materialParams.ior; + + material.emissive = vec4(materialParams.emissiveStrength * + materialParams.emissiveFactor.rgb, 0.0); + + // KHR_materials_clearcoat forbids clear coat from + // being applied in the specular/glossiness model + material.clearCoat = materialParams.clearCoatFactor; + material.clearCoatRoughness = materialParams.clearCoatRoughnessFactor; + + if (materialParams.clearCoatIndex > -1) { + highp float2 uv = uvs[materialParams.clearCoatIndex]; + uv = (vec3(uv, 1.0) * materialParams.clearCoatUvMatrix).xy; + material.clearCoat *= texture(materialParams_clearCoatMap, uv).r; + } + + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + + if (materialParams.specularColorIndex > -1) { + highp float2 uv = uvs[materialParams.specularColorIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularColorUvMatrix).xy; + vec4 sc = texture(materialParams_specularColorMap, uv); + material.specularColorFactor.r *= sc.r; + material.specularColorFactor.g *= sc.g; + material.specularColorFactor.b *= sc.b; + } + + if (materialParams.metallicRoughnessIndex > -1) { + highp float2 uv = uvs[materialParams.metallicRoughnessIndex]; + uv = (vec3(uv, 1.0) * materialParams.metallicRoughnessUvMatrix).xy; + vec4 mr = texture(materialParams_metallicRoughnessMap, uv); + material.roughness *= mr.g; + material.metallic *= mr.b; + } + + if (materialParams.aoIndex > -1) { + highp float2 uv = uvs[materialParams.aoIndex]; + uv = (vec3(uv, 1.0) * materialParams.occlusionUvMatrix).xy; + float occlusion = texture(materialParams_occlusionMap, uv).r; + material.ambientOcclusion = 1.0 + materialParams.aoStrength * (occlusion - 1.0); + } + if (materialParams.emissiveIndex > -1) { + highp float2 uv = uvs[materialParams.emissiveIndex]; + uv = (vec3(uv, 1.0) * materialParams.emissiveUvMatrix).xy; + material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; + } + + ${CUSTOM_FRAGMENT} +} +} diff --git a/libs/gltfio/materials/specular.spec.in b/libs/gltfio/materials/specular.spec.in new file mode 100644 index 0000000000..1b381ee8b5 --- /dev/null +++ b/libs/gltfio/materials/specular.spec.in @@ -0,0 +1,35 @@ +ShadingModel=lit + +# Required Aspects +BlendingMode = ${BLENDING} + +VertexColors = optional +BaseColorTexture = optional +NormalTexture = optional +OcclusionTexture = optional +EmissiveTexture = optional +MetallicRoughnessTexture = optional +TextureTransforms = optional + +# KHR_materials_specular Features +Specular = optional +SpecularTexture = optional +SpecularColorTexture = optional + +# ClearCoat Features +ClearCoat = optional +ClearCoatTexture = optional + +# Sheen Features +Sheen = unsupported +SheenColorTexture = unsupported +SheenRoughnessTexture = unsupported + +# Transmission and IOR Features +Transmission = unsupported +Ior = optional +TransmissionTexture = unsupported + +# Volume Features +Volume = unsupported +VolumeThicknessTexture = unsupported diff --git a/libs/gltfio/materials/transmission.mat.in b/libs/gltfio/materials/transmission.mat.in index 631ec89763..608b8576f2 100644 --- a/libs/gltfio/materials/transmission.mat.in +++ b/libs/gltfio/materials/transmission.mat.in @@ -52,7 +52,18 @@ material { { type : mat3, name : transmissionUvMatrix, precision: high }, // IOR - { type : float, name : ior } + { type : float, name : ior }, + + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : sampler2d, name : specularColorMap }, + { type : mat3, name : specularColorUvMatrix, precision: high } + ${CUSTOM_PARAMS} ], @@ -126,6 +137,24 @@ fragment { material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; } + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + + if (materialParams.specularColorIndex > -1) { + highp float2 uv = uvs[materialParams.specularColorIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularColorUvMatrix).xy; + vec4 sc = texture(materialParams_specularColorMap, uv); + material.specularColorFactor.r *= sc.r; + material.specularColorFactor.g *= sc.g; + material.specularColorFactor.b *= sc.b; + } + ${CUSTOM_FRAGMENT} } } diff --git a/libs/gltfio/materials/transmission.spec.in b/libs/gltfio/materials/transmission.spec.in index ac76ae5072..ff0c364813 100644 --- a/libs/gltfio/materials/transmission.spec.in +++ b/libs/gltfio/materials/transmission.spec.in @@ -15,6 +15,11 @@ EmissiveTexture=optional MetallicRoughnessTexture=optional TextureTransforms=optional +# KHR_materials_specular Features +Specular=optional +SpecularTexture=optional +SpecularColorTexture=optional + Transmission=optional Ior=optional TransmissionTexture=optional diff --git a/libs/gltfio/materials/volume.mat.in b/libs/gltfio/materials/volume.mat.in index 18998f5877..d7470bb257 100644 --- a/libs/gltfio/materials/volume.mat.in +++ b/libs/gltfio/materials/volume.mat.in @@ -58,6 +58,15 @@ material { { type : sampler2d, name : volumeThicknessMap }, { type : mat3, name : volumeThicknessUvMatrix, precision: high }, + // KHR_materials_specular + { type : float, name : specularStrength }, + { type : float3, name : specularColorFactor }, + { type : int, name : specularIndex }, + { type : sampler2d, name : specularMap }, + { type : mat3, name : specularUvMatrix, precision: high }, + { type : int, name : specularColorIndex }, + { type : mat3, name : specularColorUvMatrix, precision: high }, + // IOR { type : float, name : ior } @@ -142,6 +151,15 @@ fragment { material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb; } + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + + if (materialParams.specularIndex > -1) { + highp float2 uv = uvs[materialParams.specularIndex]; + uv = (vec3(uv, 1.0) * materialParams.specularUvMatrix).xy; + material.specularFactor *= texture(materialParams_specularMap, uv).a; + } + ${CUSTOM_FRAGMENT} } } diff --git a/libs/gltfio/materials/volume.spec.in b/libs/gltfio/materials/volume.spec.in index fe290ab7f1..5f65676765 100644 --- a/libs/gltfio/materials/volume.spec.in +++ b/libs/gltfio/materials/volume.spec.in @@ -26,3 +26,8 @@ TransmissionTexture=optional # Volume Features Volume=optional VolumeThicknessTexture=optional + +# KHR_materials_specular Features +Specular=optional +SpecularTexture=optional +SpecularColorTexture=unsupported diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 31bbc169f7..19aa85e3a6 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -1222,6 +1222,7 @@ MaterialKey FAssetLoader::getMaterialKey(const cgltf_data* srcAsset, auto trConfig = inputMat->transmission; auto shConfig = inputMat->sheen; auto vlConfig = inputMat->volume; + auto spConfig = inputMat->specular; *baseColorTexture = mrConfig.base_color_texture; *metallicRoughnessTexture = mrConfig.metallic_roughness_texture; @@ -1238,7 +1239,9 @@ MaterialKey FAssetLoader::getMaterialKey(const cgltf_data* srcAsset, ccConfig.clearcoat_normal_texture.has_transform || shConfig.sheen_color_texture.has_transform || shConfig.sheen_roughness_texture.has_transform || - trConfig.transmission_texture.has_transform; + trConfig.transmission_texture.has_transform || + spConfig.specular_color_texture.has_transform || + spConfig.specular_texture.has_transform; MaterialKey matkey { .doubleSided = !!inputMat->double_sided, @@ -1273,6 +1276,11 @@ MaterialKey FAssetLoader::getMaterialKey(const cgltf_data* srcAsset, .hasSheen = !!inputMat->has_sheen, .hasIOR = !!inputMat->has_ior, .hasVolume = !!inputMat->has_volume, + .hasSpecular = !!inputMat->has_specular, + .hasSpecularTexture = spConfig.specular_texture.texture != nullptr, + .hasSpecularColorTexture = spConfig.specular_color_texture.texture != nullptr, + .specularTextureUV = (uint8_t) spConfig.specular_texture.texcoord, + .specularColorTextureUV = (uint8_t) spConfig.specular_color_texture.texcoord, }; if (inputMat->has_pbr_specular_glossiness) { @@ -1361,6 +1369,7 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp auto trConfig = inputMat->transmission; auto shConfig = inputMat->sheen; auto vlConfig = inputMat->volume; + auto spConfig = inputMat->specular; // Check the material blending mode, not the cgltf blending mode, because the provider // might have selected an alternative blend mode (e.g. to support transmission). @@ -1560,6 +1569,30 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp inputMat->emissive_strength.emissive_strength : 1.0f); } + if (matkey.hasSpecular) { + const float* s = spConfig.specular_color_factor; + mi->setParameter("specularColorFactor", float3{s[0], s[1], s[2]}); + mi->setParameter("specularStrength", spConfig.specular_factor); + + if (matkey.hasSpecularColorTexture) { + fAsset->addTextureBinding(mi, "specularColorMap", spConfig.specular_color_texture.texture, sRGB); + if (matkey.hasTextureTransforms) { + const cgltf_texture_transform uvt = spConfig.specular_color_texture.transform; + auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale); + mi->setParameter("specularColorUvMatrix", uvmat); + } + } + if (matkey.hasSpecularTexture) { + bool sameTexture = spConfig.specular_color_texture.texture == spConfig.specular_texture.texture; + fAsset->addTextureBinding(mi, "specularMap", spConfig.specular_texture.texture, sameTexture ? sRGB : LINEAR); + if (matkey.hasTextureTransforms) { + const cgltf_texture_transform uvt = spConfig.specular_texture.transform; + auto uvmat = matrixFromUvTransform(uvt.offset, uvt.rotation, uvt.scale); + mi->setParameter("specularUvMatrix", uvmat); + } + } + } + *cacheEntry = { mi, *uvmap }; return mi; } diff --git a/libs/gltfio/src/JitShaderProvider.cpp b/libs/gltfio/src/JitShaderProvider.cpp index b42acda520..44651176a5 100644 --- a/libs/gltfio/src/JitShaderProvider.cpp +++ b/libs/gltfio/src/JitShaderProvider.cpp @@ -308,6 +308,35 @@ std::string shaderFromKey(const MaterialKey& config) { material.ior = materialParams.ior; )SHADER"; } + + if (config.hasSpecular) { + shader += R"SHADER( + material.specularFactor = materialParams.specularStrength; + material.specularColorFactor = materialParams.specularColorFactor; + )SHADER"; + + if (config.hasSpecularTexture) { + shader += "highp float2 specularUV = ${specular};\n"; + if (config.hasTextureTransforms) { + shader += "specularUV = (vec3(specularUV, 1.0) * " + "materialParams.specularUvMatrix).xy;\n"; + } + shader += R"SHADER( + material.specularFactor *= texture(materialParams_specularMap, specularUV).a; + )SHADER"; + } + + if (config.hasSpecularColorTexture) { + shader += "highp float2 specularColorUV = ${specularColor};\n"; + if (config.hasTextureTransforms) { + shader += "specularColorUV = (vec3(specularColorUV, 1.0) * " + "materialParams.specularColorUvMatrix).xy;\n"; + } + shader += R"SHADER( + material.specularColorFactor *= texture(materialParams_specularColorMap, specularColorUV).rgb; + )SHADER"; + } + } } shader += "}\n"; @@ -463,6 +492,26 @@ Material* createMaterial(Engine* engine, const MaterialKey& config, const UvMap& } } + // SPECULAR + if (config.hasSpecular) { + builder.parameter("specularStrength", MaterialBuilder::UniformType::FLOAT); + builder.parameter("specularColorFactor", MaterialBuilder::UniformType::FLOAT3); + if (config.hasSpecularTexture) { + builder.parameter("specularMap", MaterialBuilder::SamplerType::SAMPLER_2D); + if (config.hasTextureTransforms) { + builder.parameter("specularUvMatrix", MaterialBuilder::UniformType::MAT3, + MaterialBuilder::ParameterPrecision::HIGH); + } + } + if (config.hasSpecularColorTexture) { + builder.parameter("specularColorMap", MaterialBuilder::SamplerType::SAMPLER_2D); + if (config.hasTextureTransforms) { + builder.parameter("specularColorUvMatrix", MaterialBuilder::UniformType::MAT3, + MaterialBuilder::ParameterPrecision::HIGH); + } + } + } + // BLENDING switch (config.alphaMode) { case AlphaMode::OPAQUE: diff --git a/libs/gltfio/src/MaterialProvider.cpp b/libs/gltfio/src/MaterialProvider.cpp index b42bd9a930..7e7fa6d947 100644 --- a/libs/gltfio/src/MaterialProvider.cpp +++ b/libs/gltfio/src/MaterialProvider.cpp @@ -143,6 +143,21 @@ void constrainMaterial(MaterialKey* key, UvMap* uvmap) { retval[key->volumeThicknessUV] = (UvSet) index++; } } + if (key->hasSpecularTexture && retval[key->specularTextureUV] == UNUSED) { + if (index > MAX_INDEX) { + key->hasSpecularTexture = false; + } else { + retval[key->specularTextureUV] = (UvSet) index++; + } + } + if (key->hasSpecularColorTexture && retval[key->specularColorTextureUV] == UNUSED) { + if (index > MAX_INDEX) { + key->hasSpecularColorTexture = false; + } else { + retval[key->specularColorTextureUV] = (UvSet) index++; + } + } + // NOTE: KHR_materials_clearcoat does not provide separate UVs, we'll assume UV0 *uvmap = retval; } @@ -167,6 +182,8 @@ void processShaderString(std::string* shader, const UvMap& uvmap, const Material const auto& sheenColorUV = uvstrings[uvmap[config.sheenColorUV]]; const auto& sheenRoughnessUV = uvstrings[uvmap[config.sheenRoughnessUV]]; const auto& volumeThicknessUV = uvstrings[uvmap[config.volumeThicknessUV]]; + const auto& specularUV = uvstrings[uvmap[config.specularTextureUV]]; + const auto& specularColorUV = uvstrings[uvmap[config.specularColorTextureUV]]; replaceAll("${normal}", normalUV); replaceAll("${color}", baseColorUV); @@ -180,6 +197,8 @@ void processShaderString(std::string* shader, const UvMap& uvmap, const Material replaceAll("${sheenColor}", sheenColorUV); replaceAll("${sheenRoughness}", sheenRoughnessUV); replaceAll("${volumeThickness}", volumeThicknessUV); + replaceAll("${specular}", specularUV); + replaceAll("${specularColor}", specularColorUV); } } // namespace filament::gltfio diff --git a/libs/gltfio/src/UbershaderProvider.cpp b/libs/gltfio/src/UbershaderProvider.cpp index 7e184f648d..64451aa363 100644 --- a/libs/gltfio/src/UbershaderProvider.cpp +++ b/libs/gltfio/src/UbershaderProvider.cpp @@ -63,6 +63,42 @@ static void prepareConfig(MaterialKey* config, const char* label) { config->hasSheen = false; config->hasIOR = false; } + + if (config->useSpecularGlossiness && config->hasSpecular) { + slog.w << "SpecularGlossiness and specular are not supported together in ubershader mode," + " removing specularGlossiness (" << label << ")." << io::endl; + + config->useSpecularGlossiness = false; + } + + if (config->unlit && config->hasSpecular) { + slog.w << "Unlit and specular are not supported together in ubershader mode," + " removing unlit (" << label << ")." << io::endl; + + config->unlit = false; + } + + // Also due to sampler overload, clearcoat with specular, we can only support with specular map. + if ((config->hasClearCoatNormalTexture || config->hasClearCoatRoughnessTexture) && config->hasSpecular) { + slog.w << "Specular can only work with clearcoat that does not use clear coat normal or clear coat roughness map," + " removing clear coat normal and roughness (" << label << ")." << io::endl; + config->hasClearCoatNormalTexture = false; + config->hasClearCoatRoughnessTexture = false; + } + + // Also due to sampler overload, sheen with specular, we can only support without specular color texture. + if (config->hasSpecularColorTexture && config->hasSheen) { + slog.w << "Sheen can only work with specular that does not use specular color map," + " removing specular color (" << label << ")." << io::endl; + config->hasSpecularColorTexture = false; + } + + // Also due to sampler overload, volume with specular, we can only support without specular color texture. + if (config->hasSpecularColorTexture && config->hasVolume) { + slog.w << "Volume can only work with specular that does not use specular color map," + "removing specular color (" << label << ")." << io::endl; + config->hasSpecularColorTexture = false; + } } class UbershaderProvider : public MaterialProvider { @@ -155,6 +191,9 @@ Material* UbershaderProvider::getMaterial(const MaterialKey& config) const { requirements.features["SheenColorTexture"] = config.hasSheenColorTexture; requirements.features["SheenRoughnessTexture"] = config.hasSheenRoughnessTexture; requirements.features["VolumeThicknessTexture"] = config.hasVolumeThicknessTexture; + requirements.features["Specular"] = config.hasSpecular; + requirements.features["SpecularTexture"] = config.hasSpecularTexture; + requirements.features["SpecularColorTexture"] = config.hasSpecularColorTexture; if (Material* mat = mMaterials.getMaterial(requirements); mat != nullptr) { return mat; @@ -250,6 +289,14 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config mi->setParameter("transmissionIndex", getUvIndex(config->transmissionUV, config->hasTransmissionTexture)); } + if (config->hasSpecular) { + mi->setParameter("specularUvMatrix", identity); + mi->setParameter("specularIndex", + getUvIndex(config->specularTextureUV, config->hasSpecularTexture)); + mi->setParameter("specularColorUvMatrix", identity); + mi->setParameter("specularColorIndex", + getUvIndex(config->specularColorTextureUV, config->hasSpecularColorTexture)); + } } TextureSampler sampler; @@ -300,6 +347,14 @@ MaterialInstance* UbershaderProvider::createMaterialInstance(MaterialKey* config mi->setParameter("reflectance", 0.5f); } + if (needsTexture("SpecularTexture")) { + mi->setParameter("specularMap", mDummyTexture, sampler); + } + + if (needsTexture("SpecularColorTexture")) { + mi->setParameter("specularColorMap", mDummyTexture, sampler); + } + return mi; } diff --git a/shaders/src/brdf.fs b/shaders/src/brdf.fs index f4204cf44d..2f4d152255 100644 --- a/shaders/src/brdf.fs +++ b/shaders/src/brdf.fs @@ -181,6 +181,16 @@ vec3 fresnel(const vec3 f0, float LoH) { #endif } +vec3 fresnel(const vec3 f0, const float f90, float LoH) { +#if BRDF_SPECULAR_F == SPECULAR_F_SCHLICK +#if FILAMENT_QUALITY == FILAMENT_QUALITY_LOW + return F_Schlick(f0, LoH); // f90 = 1.0 +#else + return F_Schlick(f0, f90, LoH); +#endif +#endif +} + float distributionAnisotropic(float at, float ab, float ToH, float BoH, float NoH) { #if BRDF_ANISOTROPIC_D == SPECULAR_D_GGX_ANISOTROPIC return D_GGX_Anisotropic(at, ab, ToH, BoH, NoH); diff --git a/shaders/src/common_lighting.fs b/shaders/src/common_lighting.fs index 2f822460de..43df890838 100644 --- a/shaders/src/common_lighting.fs +++ b/shaders/src/common_lighting.fs @@ -18,6 +18,11 @@ struct PixelParams { float perceptualRoughness; float perceptualRoughnessUnclamped; vec3 f0; +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR) + float f90; + float specular; + vec3 specularColor; +#endif float roughness; vec3 dfg; vec3 energyCompensation; diff --git a/shaders/src/light_indirect.fs b/shaders/src/light_indirect.fs index 74bcaaff20..5dc3cad35d 100644 --- a/shaders/src/light_indirect.fs +++ b/shaders/src/light_indirect.fs @@ -136,9 +136,13 @@ vec3 getSpecularDominantDirection(const vec3 n, const vec3 r, float roughness) { vec3 specularDFG(const PixelParams pixel) { #if defined(SHADING_MODEL_CLOTH) return pixel.f0 * pixel.dfg.z; +#else +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR) + return mix(pixel.dfg.xxx, pixel.dfg.yyy, pixel.f0) * pixel.specular; #else return mix(pixel.dfg.xxx, pixel.dfg.yyy, pixel.f0); #endif +#endif } vec3 getReflectedVector(const PixelParams pixel, const vec3 n) { @@ -663,7 +667,6 @@ void evaluateIBL(const MaterialInputs material, const PixelParams pixel, inout v // Combine all terms // Note: iblLuminance is already premultiplied by the exposure - color.rgb += Fr + Fd; #if defined(MATERIAL_HAS_REFRACTION) color.rgb += Ft; diff --git a/shaders/src/material_inputs.fs b/shaders/src/material_inputs.fs index 53d4ade1ec..32923dc90a 100644 --- a/shaders/src/material_inputs.fs +++ b/shaders/src/material_inputs.fs @@ -89,6 +89,15 @@ struct MaterialInputs { #endif #endif #endif + +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + float specularFactor; +#endif + +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + vec3 specularColorFactor; +#endif + }; void initMaterial(out MaterialInputs material) { @@ -177,6 +186,15 @@ void initMaterial(out MaterialInputs material) { #endif #endif #endif + +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + material.specularFactor = 1.0; +#endif + +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + material.specularColorFactor = vec3(1.0); +#endif + } #if defined(MATERIAL_HAS_CUSTOM_SURFACE_SHADING) diff --git a/shaders/src/shading_lit.fs b/shaders/src/shading_lit.fs index bf80ea57cd..103005845c 100644 --- a/shaders/src/shading_lit.fs +++ b/shaders/src/shading_lit.fs @@ -75,7 +75,21 @@ void getCommonPixelParams(const MaterialInputs material, inout PixelParams pixel // Assumes an interface from air to an IOR of 1.5 for dielectrics float reflectance = computeDielectricF0(material.reflectance); #endif +#if !defined(MATERIAL_HAS_SPECULAR_FACTOR) && !defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) pixel.f0 = computeF0(baseColor, material.metallic, reflectance); +#else + vec3 dielectricSpecularF0 = vec3(0.0); + float dielectricSpecularF90 = 0.0; +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + dielectricSpecularF0 = min(reflectance * material.specularColorFactor, vec3(1.0)); +#endif +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + dielectricSpecularF0 *= material.specularFactor; + dielectricSpecularF90 = material.specularFactor; +#endif + pixel.f0 = baseColor.rgb * material.metallic + dielectricSpecularF0 * (1.0 - material.metallic); + pixel.f90 = material.metallic + dielectricSpecularF90 * (1.0 - material.metallic); +#endif #else pixel.diffuseColor = baseColor.rgb; pixel.f0 = material.sheenColor; @@ -123,6 +137,16 @@ void getCommonPixelParams(const MaterialInputs material, inout PixelParams pixel #endif } +void getSpecularPixelParams(const MaterialInputs material, inout PixelParams pixel) { +#if defined(MATERIAL_HAS_SPECULAR_FACTOR) + pixel.specular = material.specularFactor; +#endif + +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) + pixel.specularColor = material.specularColorFactor; +#endif +} + void getSheenPixelParams(const MaterialInputs material, inout PixelParams pixel) { #if defined(MATERIAL_HAS_SHEEN_COLOR) && !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) pixel.sheenColor = material.sheenColor; @@ -232,6 +256,7 @@ void getEnergyCompensationPixelParams(inout PixelParams pixel) { * testing fails. */ void getPixelParams(const MaterialInputs material, out PixelParams pixel) { + getSpecularPixelParams(material, pixel); getCommonPixelParams(material, pixel); getSheenPixelParams(material, pixel); getClearCoatPixelParams(material, pixel); diff --git a/shaders/src/shading_model_standard.fs b/shaders/src/shading_model_standard.fs index 025295ba27..ca71d1de0f 100644 --- a/shaders/src/shading_model_standard.fs +++ b/shaders/src/shading_model_standard.fs @@ -63,8 +63,11 @@ vec3 isotropicLobe(const PixelParams pixel, const Light light, const vec3 h, float D = distribution(pixel.roughness, NoH, h); float V = visibility(pixel.roughness, NoV, NoL); +#if defined(MATERIAL_HAS_SPECULAR_COLOR_FACTOR) || defined(MATERIAL_HAS_SPECULAR_FACTOR) + vec3 F = fresnel(pixel.f0, pixel.f90, LoH); +#else vec3 F = fresnel(pixel.f0, LoH); - +#endif return (D * V) * F; } @@ -144,7 +147,6 @@ vec3 surfaceShading(const PixelParams pixel, const Light light, float occlusion) color += clearCoat; #endif #endif - return (color * light.colorIntensity.rgb) * (light.colorIntensity.w * light.attenuation * NoL * occlusion); }