diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index 1e36b36ae..019fd9dd8 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -190,16 +190,55 @@ Asset::LoadResult Material::load() // Load layer layer = MaterialLayer::Load(GetID(), &stream, _shaderHeader.Material.Info, name); - if (ContentDeprecated::Clear()) + const bool upgradeOldSpecular = _shaderHeader.Material.GraphVersion < 177; + if (ContentDeprecated::Clear() || upgradeOldSpecular) { // If encountered any deprecated data when loading graph then serialize it MaterialGraph graph; MemoryWriteStream writeStream(1024); stream.SetPosition(0); - if (!graph.Load(&stream, true) && !graph.Save(&writeStream, true)) + if (!graph.Load(&stream, true)) { - surfaceChunk->Data.Copy(ToSpan(writeStream)); - ContentDeprecated::Clear(); + if (upgradeOldSpecular) + { + // [Deprecated in 1.11] + // Specular calculations were changed to support up to 16% of reflectance via ^2 curve instead of linear up to 8% + // Insert Custom Code node that converts old materials into a new system to ensure they look the same + MaterialGraph::Node* rootNode = nullptr; + for (auto& e : graph.Nodes) + { + if (e.Type == ROOT_NODE_TYPE) + { + rootNode = &e; + break; + } + } + const auto& specularBoxInfo = MaterialGenerator::GetMaterialRootNodeBox(MaterialGraphBoxes::Specular); + auto specularBox = rootNode ? rootNode->GetBox(specularBoxInfo.ID) : nullptr; + if (specularBox && specularBox->HasConnection()) + { + auto& customCodeNode = graph.Nodes.AddOne(); + customCodeNode.ID = graph.Nodes.Count() + 1000; + customCodeNode.Type = GRAPH_NODE_MAKE_TYPE(1, 8); + customCodeNode.Boxes.Resize(2); + customCodeNode.Boxes[0] = MaterialGraphBox(&customCodeNode, 0, VariantType::Float4); // Input0 + customCodeNode.Boxes[1] = MaterialGraphBox(&customCodeNode, 8, VariantType::Float4); // Output0 + customCodeNode.Values.Resize(1); + customCodeNode.Values[0] = TEXT("// Convert old Specular value to a new range\nOutput0.x = min(Input0.x * 0.5f, 0.6f);"); + auto specularSourceBox = specularBox->Connections[0]; + specularBox->Connections.Clear(); + specularSourceBox->Connections.Clear(); +#define CONNECT(boxA, boxB) boxA->Connections.Add(boxB); boxB->Connections.Add(boxA) + CONNECT(specularSourceBox, (&customCodeNode.Boxes[0])); // Specular -> Input0 + CONNECT((&customCodeNode.Boxes[1]), specularBox); // Output0 -> Specular +#undef CONNECT + } + } + if (!graph.Save(&writeStream, true)) + { + surfaceChunk->Data.Copy(ToSpan(writeStream)); + ContentDeprecated::Clear(); + } } } } diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index 2d21f5b29..c45fd3748 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -10,7 +10,7 @@ /// /// Current materials shader version. /// -#define MATERIAL_GRAPH_VERSION 176 +#define MATERIAL_GRAPH_VERSION 177 class Material; class GPUShader; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index cdf5d4b35..4594446ec 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -106,7 +106,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) Value values[OutputsMax]; for (int32 i = 0; i < OutputsMax; i++) { - const auto outputBox = node->GetBox(Output0BoxID + i); + const auto outputBox = node->TryGetBox(Output0BoxID + i); if (outputBox && outputBox->HasConnection()) { values[i] = writeLocal(VariantType::Float4, node); @@ -119,7 +119,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) for (int32 i = 0; i < InputsMax; i++) { auto inputName = TEXT("Input") + StringUtils::ToString(i); - const auto inputBox = node->GetBox(Input0BoxID + i); + const auto inputBox = node->TryGetBox(Input0BoxID + i); if (inputBox && inputBox->HasConnection()) { auto inputValue = tryGetValue(inputBox, Value::Zero); @@ -131,7 +131,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) for (int32 i = 0; i < OutputsMax; i++) { auto outputName = TEXT("Output") + StringUtils::ToString(i); - const auto outputBox = node->GetBox(Output0BoxID + i); + const auto outputBox = node->TryGetBox(Output0BoxID + i); if (outputBox && outputBox->HasConnection()) { code.Replace(*outputName, *values[i].Value, StringSearchCase::CaseSensitive); @@ -146,7 +146,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Link output values to boxes for (int32 i = 0; i < OutputsMax; i++) { - const auto outputBox = node->GetBox(Output0BoxID + i); + const auto outputBox = node->TryGetBox(Output0BoxID + i); if (outputBox && outputBox->HasConnection()) { outputBox->Cache = values[i]; diff --git a/Source/Shaders/BRDF.hlsl b/Source/Shaders/BRDF.hlsl index 9eed9a670..37aa36e42 100644 --- a/Source/Shaders/BRDF.hlsl +++ b/Source/Shaders/BRDF.hlsl @@ -58,6 +58,12 @@ float3 F_Schlick(float3 specularColor, float VoH) return saturate(50.0 * specularColor.g) * fc + (1 - fc) * specularColor; } +float3 F_Schlick(float3 f0, float3 f90, float VoH) +{ + float fc = Pow5(1 - VoH); + return f90 * fc + (1 - fc) * f0; +} + #define REFLECTION_CAPTURE_NUM_MIPS 7 #define REFLECTION_CAPTURE_ROUGHEST_MIP 1 #define REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE 1.2 diff --git a/Source/Shaders/GBufferCommon.hlsl b/Source/Shaders/GBufferCommon.hlsl index 346c9c24a..678168220 100644 --- a/Source/Shaders/GBufferCommon.hlsl +++ b/Source/Shaders/GBufferCommon.hlsl @@ -27,26 +27,28 @@ bool IsSubsurfaceMode(int shadingModel) return shadingModel == SHADING_MODEL_SUBSURFACE || shadingModel == SHADING_MODEL_FOLIAGE; } -float3 GetDiffuseColor(in float3 color, in float metalness) +float3 GetDiffuseColor(float3 color, float metalness) { - return color - color * metalness; + return color * (1.0 - metalness); } -float3 GetSpecularColor(in float3 color, in float specular, in float metalness) +// [https://google.github.io/filament/Filament.md.html] +float3 GetSpecularColor(float3 color, float specular, float metalness) { - return lerp(0.08 * specular.xxx, color.rgb, metalness.xxx); + float dielectricF0 = 0.16 * specular * specular; + return lerp(dielectricF0.xxx, color, metalness.xxx); } // Calculate material diffuse color -float3 GetDiffuseColor(in GBufferSample gBuffer) +float3 GetDiffuseColor(GBufferSample gBuffer) { - return gBuffer.Color - gBuffer.Color * gBuffer.Metalness; + return GetDiffuseColor(gBuffer.Color, gBuffer.Metalness); } // Calculate material specular color -float3 GetSpecularColor(in GBufferSample gBuffer) +float3 GetSpecularColor(GBufferSample gBuffer) { - return lerp(0.08 * gBuffer.Specular.xxx, gBuffer.Color.rgb, gBuffer.Metalness.xxx); + return GetSpecularColor(gBuffer.Color, gBuffer.Specular, gBuffer.Metalness); } // Compact Normal Storage for Small G-Buffers diff --git a/Source/Shaders/Lighting.hlsl b/Source/Shaders/Lighting.hlsl index 4d6e474fa..2e3f9dc76 100644 --- a/Source/Shaders/Lighting.hlsl +++ b/Source/Shaders/Lighting.hlsl @@ -31,6 +31,7 @@ LightSample StandardShading(GBufferSample gBuffer, float energy, float3 L, float float3 F = F_Schlick(specularColor, VoH); float D = D_GGX(gBuffer.Roughness, NoH) * energy; float Vis = Vis_SmithJointApprox(gBuffer.Roughness, NoV, NoL); + // TODO: apply energy compensation to specular (1.0 + specularColor * (1.0 / PreIntegratedGF.y - 1.0)) lighting.Specular = (D * Vis) * F; #endif lighting.Transmission = 0; diff --git a/Source/Shaders/Reflections.shader b/Source/Shaders/Reflections.shader index 1ae38f0eb..dd0b695e5 100644 --- a/Source/Shaders/Reflections.shader +++ b/Source/Shaders/Reflections.shader @@ -78,8 +78,6 @@ float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0 // Calculate specular color float3 specularColor = GetSpecularColor(gBuffer); - if (gBuffer.Metalness < 0.001) - specularColor = 0.04f * gBuffer.Specular; // Calculate reflecion color float3 V = normalize(gBufferData.ViewPos - gBuffer.WorldPos); diff --git a/Source/Shaders/SSR.shader b/Source/Shaders/SSR.shader index e13ae9611..3a7f52f7c 100644 --- a/Source/Shaders/SSR.shader +++ b/Source/Shaders/SSR.shader @@ -83,8 +83,6 @@ float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0 // Calculate specular color float3 specularColor = GetSpecularColor(gBuffer); - if (gBuffer.Metalness < 0.001) - specularColor = 0.04f * gBuffer.Specular; // Calculate reflection color float3 V = normalize(gBufferData.ViewPos - gBuffer.WorldPos);