Refactor specular lighting to properly map specular as reflectance in BRDF
Reference: https://google.github.io/filament/Filament.md.html #1492
This commit is contained in:
@@ -190,16 +190,55 @@ Asset::LoadResult Material::load()
|
|||||||
|
|
||||||
// Load layer
|
// Load layer
|
||||||
layer = MaterialLayer::Load(GetID(), &stream, _shaderHeader.Material.Info, name);
|
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
|
// If encountered any deprecated data when loading graph then serialize it
|
||||||
MaterialGraph graph;
|
MaterialGraph graph;
|
||||||
MemoryWriteStream writeStream(1024);
|
MemoryWriteStream writeStream(1024);
|
||||||
stream.SetPosition(0);
|
stream.SetPosition(0);
|
||||||
if (!graph.Load(&stream, true) && !graph.Save(&writeStream, true))
|
if (!graph.Load(&stream, true))
|
||||||
{
|
{
|
||||||
surfaceChunk->Data.Copy(ToSpan(writeStream));
|
if (upgradeOldSpecular)
|
||||||
ContentDeprecated::Clear();
|
{
|
||||||
|
// [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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current materials shader version.
|
/// Current materials shader version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
#define MATERIAL_GRAPH_VERSION 176
|
#define MATERIAL_GRAPH_VERSION 177
|
||||||
|
|
||||||
class Material;
|
class Material;
|
||||||
class GPUShader;
|
class GPUShader;
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
|||||||
Value values[OutputsMax];
|
Value values[OutputsMax];
|
||||||
for (int32 i = 0; i < OutputsMax; i++)
|
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())
|
if (outputBox && outputBox->HasConnection())
|
||||||
{
|
{
|
||||||
values[i] = writeLocal(VariantType::Float4, node);
|
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++)
|
for (int32 i = 0; i < InputsMax; i++)
|
||||||
{
|
{
|
||||||
auto inputName = TEXT("Input") + StringUtils::ToString(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())
|
if (inputBox && inputBox->HasConnection())
|
||||||
{
|
{
|
||||||
auto inputValue = tryGetValue(inputBox, Value::Zero);
|
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++)
|
for (int32 i = 0; i < OutputsMax; i++)
|
||||||
{
|
{
|
||||||
auto outputName = TEXT("Output") + StringUtils::ToString(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())
|
if (outputBox && outputBox->HasConnection())
|
||||||
{
|
{
|
||||||
code.Replace(*outputName, *values[i].Value, StringSearchCase::CaseSensitive);
|
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
|
// Link output values to boxes
|
||||||
for (int32 i = 0; i < OutputsMax; i++)
|
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())
|
if (outputBox && outputBox->HasConnection())
|
||||||
{
|
{
|
||||||
outputBox->Cache = values[i];
|
outputBox->Cache = values[i];
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ float3 F_Schlick(float3 specularColor, float VoH)
|
|||||||
return saturate(50.0 * specularColor.g) * fc + (1 - fc) * specularColor;
|
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_NUM_MIPS 7
|
||||||
#define REFLECTION_CAPTURE_ROUGHEST_MIP 1
|
#define REFLECTION_CAPTURE_ROUGHEST_MIP 1
|
||||||
#define REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE 1.2
|
#define REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE 1.2
|
||||||
|
|||||||
@@ -27,26 +27,28 @@ bool IsSubsurfaceMode(int shadingModel)
|
|||||||
return shadingModel == SHADING_MODEL_SUBSURFACE || shadingModel == SHADING_MODEL_FOLIAGE;
|
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
|
// 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
|
// 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
|
// Compact Normal Storage for Small G-Buffers
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ LightSample StandardShading(GBufferSample gBuffer, float energy, float3 L, float
|
|||||||
float3 F = F_Schlick(specularColor, VoH);
|
float3 F = F_Schlick(specularColor, VoH);
|
||||||
float D = D_GGX(gBuffer.Roughness, NoH) * energy;
|
float D = D_GGX(gBuffer.Roughness, NoH) * energy;
|
||||||
float Vis = Vis_SmithJointApprox(gBuffer.Roughness, NoV, NoL);
|
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;
|
lighting.Specular = (D * Vis) * F;
|
||||||
#endif
|
#endif
|
||||||
lighting.Transmission = 0;
|
lighting.Transmission = 0;
|
||||||
|
|||||||
@@ -78,8 +78,6 @@ float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0
|
|||||||
|
|
||||||
// Calculate specular color
|
// Calculate specular color
|
||||||
float3 specularColor = GetSpecularColor(gBuffer);
|
float3 specularColor = GetSpecularColor(gBuffer);
|
||||||
if (gBuffer.Metalness < 0.001)
|
|
||||||
specularColor = 0.04f * gBuffer.Specular;
|
|
||||||
|
|
||||||
// Calculate reflecion color
|
// Calculate reflecion color
|
||||||
float3 V = normalize(gBufferData.ViewPos - gBuffer.WorldPos);
|
float3 V = normalize(gBufferData.ViewPos - gBuffer.WorldPos);
|
||||||
|
|||||||
@@ -83,8 +83,6 @@ float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0
|
|||||||
|
|
||||||
// Calculate specular color
|
// Calculate specular color
|
||||||
float3 specularColor = GetSpecularColor(gBuffer);
|
float3 specularColor = GetSpecularColor(gBuffer);
|
||||||
if (gBuffer.Metalness < 0.001)
|
|
||||||
specularColor = 0.04f * gBuffer.Specular;
|
|
||||||
|
|
||||||
// Calculate reflection color
|
// Calculate reflection color
|
||||||
float3 V = normalize(gBufferData.ViewPos - gBuffer.WorldPos);
|
float3 V = normalize(gBufferData.ViewPos - gBuffer.WorldPos);
|
||||||
|
|||||||
Reference in New Issue
Block a user