diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype index c635f94d56..0263f12245 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype @@ -714,6 +714,19 @@ "displayName": "Double-sided", "description": "Whether to render back-faces or just front-faces.", "type": "Bool" + }, + { + "id": "alphaAffectsSpecular", + "displayName": "Alpha affects specular", + "description": "How much the alpha value should also affect specular reflection. This should be 0.0 for materials where light can transmit through their physical surface (like glass), but 1.0 when alpha determines the very presence of a surface (like hair or grass)", + "type": "float", + "min": 0.0, + "max": 1.0, + "defaultValue": 0.0, + "connection": { + "type": "ShaderInput", + "id": "m_opacityAffectsSpecularFactor" + } } ], "uv": [ @@ -1669,4 +1682,3 @@ "UV1": "Unwrapped" } } - diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli index b6d6439268..3016adeadf 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli @@ -55,6 +55,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial float m_anisotropicFactor; // Base layer anisotropic strength of deviation: negative = Bi-Normal direction, positive = Tangent direction float m_opacityFactor; + float m_opacityAffectsSpecularFactor; Texture2D m_opacityMap; uint m_opacityMapUvIndex; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl index a8b4075d51..b563a64b75 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl @@ -331,7 +331,8 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent) { - alpha = FresnelSchlickWithRoughness(lightingData.NdotV, alpha, surface.roughnessLinear).x; // Increase opacity at grazing angles. + float fresnelAlpha = FresnelSchlickWithRoughness(lightingData.NdotV, alpha, surface.roughnessLinear).x; // Increase opacity at grazing angles. + alpha = lerp(fresnelAlpha, alpha, MaterialSrg::m_opacityAffectsSpecularFactor); } PbrLightingOutput lightingOutput = GetPbrLightingOutput(surface, lightingData, alpha); @@ -349,8 +350,13 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // For blended mode, we do (dest * alpha) + (source * 1.0). This allows the specular // to be added on top of the diffuse, but then the diffuse must be pre-multiplied. // It's done this way because surface transparency doesn't really change specular response (eg, glass). + lightingOutput.m_diffuseColor.rgb *= lightingOutput.m_diffuseColor.w; // pre-multiply diffuse - lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular + + // Add specular. m_opacityAffectsSpecularFactor controls how much the alpha masks out specular contribution. + float3 specular = lightingOutput.m_specularColor.rgb; + specular = lerp(specular, specular * lightingOutput.m_diffuseColor.w, MaterialSrg::m_opacityAffectsSpecularFactor); + lightingOutput.m_diffuseColor.rgb += specular; } else if (o_opacity_mode == OpacityMode::TintedTransparent) { @@ -367,7 +373,12 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // m_diffuseColor.rgb (source) is added to that, and the final result is stored in render target 0. lightingOutput.m_diffuseColor.rgb *= lightingOutput.m_diffuseColor.w; // pre-multiply diffuse - lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular + + // Add specular. m_opacityAffectsSpecularFactor controls how much the alpha masks out specular contribution. + float3 specular = lightingOutput.m_specularColor.rgb; + specular = lerp(specular, specular * lightingOutput.m_diffuseColor.w, MaterialSrg::m_opacityAffectsSpecularFactor); + lightingOutput.m_diffuseColor.rgb += specular; + lightingOutput.m_specularColor.rgb = baseColor * (1.0 - lightingOutput.m_diffuseColor.w); } else @@ -415,4 +426,3 @@ ForwardPassOutput EnhancedPbr_ForwardPassPS_EDS(VSOutput IN, bool isFrontFace : return OUT; } - diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index fd2c74dae0..0d3cf831aa 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -655,6 +655,19 @@ "displayName": "Double-sided", "description": "Whether to render back-faces or just front-faces.", "type": "Bool" + }, + { + "id": "alphaAffectsSpecular", + "displayName": "Alpha affects specular", + "description": "How much the alpha value should also affect specular reflection. This should be 0.0 for materials where light can transmit through their physical surface (like glass), but 1.0 when alpha determines the very presence of a surface (like hair or grass)", + "type": "float", + "min": 0.0, + "max": 1.0, + "defaultValue": 0.0, + "connection": { + "type": "ShaderInput", + "id": "m_opacityAffectsSpecularFactor" + } } ], "uv": [ @@ -1187,4 +1200,3 @@ "UV1": "Unwrapped" } } - diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli index f339642a4d..e369042e9a 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Common.azsli @@ -50,6 +50,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial float4 m_pad2; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed. float m_opacityFactor; + float m_opacityAffectsSpecularFactor; Texture2D m_opacityMap; uint m_opacityMapUvIndex; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl index 23e4ef0c10..698e9b5c54 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl @@ -259,7 +259,8 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent) { - alpha = FresnelSchlickWithRoughness(lightingData.NdotV, alpha, surface.roughnessLinear).x; // Increase opacity at grazing angles. + float fresnelAlpha = FresnelSchlickWithRoughness(lightingData.NdotV, alpha, surface.roughnessLinear).x; // Increase opacity at grazing angles. + alpha = lerp(fresnelAlpha, alpha, MaterialSrg::m_opacityAffectsSpecularFactor); } PbrLightingOutput lightingOutput = GetPbrLightingOutput(surface, lightingData, alpha); @@ -274,8 +275,13 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // For blended mode, we do (dest * alpha) + (source * 1.0). This allows the specular // to be added on top of the diffuse, but then the diffuse must be pre-multiplied. // It's done this way because surface transparency doesn't really change specular response (eg, glass). + lightingOutput.m_diffuseColor.rgb *= lightingOutput.m_diffuseColor.w; // pre-multiply diffuse - lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular + + // Add specular. m_opacityAffectsSpecularFactor controls how much the alpha masks out specular contribution. + float3 specular = lightingOutput.m_specularColor.rgb; + specular = lerp(specular, specular * lightingOutput.m_diffuseColor.w, MaterialSrg::m_opacityAffectsSpecularFactor); + lightingOutput.m_diffuseColor.rgb += specular; } else if (o_opacity_mode == OpacityMode::TintedTransparent) { @@ -292,7 +298,12 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // m_diffuseColor.rgb (source) is added to that, and the final result is stored in render target 0. lightingOutput.m_diffuseColor.rgb *= lightingOutput.m_diffuseColor.w; // pre-multiply diffuse - lightingOutput.m_diffuseColor.rgb += lightingOutput.m_specularColor.rgb; // add specular + + // Add specular. m_opacityAffectsSpecularFactor controls how much the alpha masks out specular contribution. + float3 specular = lightingOutput.m_specularColor.rgb; + specular = lerp(specular, specular * lightingOutput.m_diffuseColor.w, MaterialSrg::m_opacityAffectsSpecularFactor); + lightingOutput.m_diffuseColor.rgb += specular; + lightingOutput.m_specularColor.rgb = baseColor * (1.0 - lightingOutput.m_diffuseColor.w); } else diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_Blended_Alpha_Affects_Specular.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_Blended_Alpha_Affects_Specular.material new file mode 100644 index 0000000000..dbaec36136 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/009_Opacity_Blended_Alpha_Affects_Specular.material @@ -0,0 +1,24 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardPBR.materialtype", + "parentMaterial": "", + "propertyLayoutVersion": 3, + "properties": { + "baseColor": { + "color": [ + 0.5906767249107361, + 1.0, + 0.11703670024871826, + 1.0 + ], + "textureMap": "Textures/Default/default_basecolor.tif" + }, + "opacity": { + "alphaSource": "Split", + "factor": 0.75, + "mode": "Blended", + "textureMap": "TestData/Textures/checker8x8_gray_512.png", + "alphaAffectsSpecular": 1.0 + } + } +}