Fixed ATOM-15434 "Decal + POM (Grey outline issue)"

Made sure the depth and shadow shaders enable parallax calculations when alpha clipping is enabled (instead of only when parallax POM is enabled).
Moved the alpha calculations to be *after* the parallax calculations.
Factored out ShouldHandleParallax() and ShouldHandleParallaxInDepthShaders() utility functions, which help us ensure consistent application of parallax calculations across the various shaders in each material type.
Removed some dead code in a couple shaders where dirToCamera was calculated but not used.
Also did ATOM-15034 "Remove Opacity From Multi-Layer Material Types For Now" rather than addressing whatever additional alpha cutout issues might be present on multilayer materials.

Testing:
AtomSampleViewer full test suite.
Added a new AtomSampleViewer screenshot test for alpha clipping with parallax (separate repo).
Addional testing of relevant properties in Matirial Editor.
Tested updated StandardMultilayerPBR in Editor.exe where I had shadows and clipping against other geometry.
This commit is contained in:
Chris Santora
2021-05-03 11:37:08 -07:00
parent d1801bbe47
commit f6479aca98
14 changed files with 167 additions and 245 deletions
@@ -14,6 +14,8 @@
#include <Atom/Features/SrgSemantics.azsli>
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/LightingOptions.azsli>
#include "MaterialInputs/BaseColorInput.azsli"
#include "MaterialInputs/RoughnessInput.azsli"
@@ -107,3 +109,18 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
{
return SampleDepthOrHeightMap(MaterialSrg::m_depthInverted, MaterialSrg::m_depthMap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy);
}
COMMON_OPTIONS_PARALLAX()
bool ShouldHandleParallax()
{
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled.
return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap;
}
bool ShouldHandleParallaxInDepthShaders()
{
// The depth pass shaders need to calculate parallax when the result could affect the depth buffer, or when
// parallax could affect texel clipping.
return ShouldHandleParallax() && (o_parallax_enablePixelDepthOffset || o_opacity_mode == OpacityMode::Cutout);
}
@@ -11,7 +11,6 @@
*/
#include <viewsrg.srgi>
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include "./EnhancedPBR_Common.azsli"
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/ParallaxMapping.azsli>
@@ -56,7 +55,7 @@ VSDepthOutput MainVS(VSInput IN)
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
OUT.m_uv[1] = IN.m_uv1;
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
OUT.m_worldPosition = worldPosition.xyz;
@@ -75,15 +74,9 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
{
PSDepthOutput OUT;
// Clip Alpha
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
OUT.m_depth = IN.m_position.z;
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
@@ -97,5 +90,12 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth);
}
// Clip Alpha
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
return OUT;
}
@@ -26,8 +26,8 @@ COMMON_OPTIONS_NORMAL()
COMMON_OPTIONS_CLEAR_COAT()
COMMON_OPTIONS_OCCLUSION()
COMMON_OPTIONS_EMISSIVE()
COMMON_OPTIONS_PARALLAX()
COMMON_OPTIONS_DETAIL_MAPS()
// Note COMMON_OPTIONS_PARALLAX is in StandardPBR_Common.azsli because it's needed by all StandardPBR shaders.
// Alpha
#include "MaterialInputs/AlphaInput.azsli"
@@ -67,7 +67,6 @@ struct VSOutput
float2 m_detailUv[UvSetCount] : UV3;
};
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/LightingModel.azsli>
#include <Atom/Features/Vertex/VertexHelper.azsli>
@@ -88,8 +87,11 @@ VSOutput EnhancedPbr_ForwardPassVS(VSInput IN)
// but we would need to address how it works with the parallax code below that indexes into the m_detailUV array.
OUT.m_detailUv[0] = mul(MaterialSrg::m_detailUvMatrix, float3(IN.m_uv0, 1.0)).xy;
OUT.m_detailUv[1] = mul(MaterialSrg::m_detailUvMatrix, float3(IN.m_uv1, 1.0)).xy;
// Shadow coords will be calculated in the pixel shader in this case
bool skipShadowCoords = ShouldHandleParallax() && o_parallax_enablePixelDepthOffset;
VertexHelper(IN, OUT, worldPosition, o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset);
VertexHelper(IN, OUT, worldPosition, skipShadowCoords);
return OUT;
}
@@ -119,7 +121,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
bool displacementIsClipped = false;
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled
if(!o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap)
if(ShouldHandleParallax())
{
// GetParallaxInput applies an tangent offset to the UV. We want to apply the same offset to the detailUv (note: this needs to be tested with content)
// The math is: offset = newUv - oldUv; detailUv += offset;
@@ -13,7 +13,6 @@
#include <scenesrg.srgi>
#include <viewsrg.srgi>
#include "EnhancedPBR_Common.azsli"
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/ParallaxMapping.azsli>
#include <Atom/Features/MatrixUtility.azsli>
@@ -55,8 +54,8 @@ VertexOutput MainVS(VertexInput IN)
// By design, only UV0 is allowed to apply transforms.
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
OUT.m_uv[1] = IN.m_uv1;
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
OUT.m_worldPosition = worldPosition.xyz;
@@ -76,27 +75,9 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
{
PSDepthOutput OUT;
// Clip Alpha
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
OUT.m_depth = IN.m_position.z;
float3 dirToCamera;
if(ViewSrg::m_projectionMatrix[0].w)
{
// orthographic projection (directional light)
// No view position, use light direction
dirToCamera = ViewSrg::m_viewMatrix[2].xyz;
}
else
{
dirToCamera = ViewSrg::m_worldPosition.xyz - IN.m_worldPosition;
}
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
static const float ShadowMapDepthBias = 0.000001;
@@ -114,5 +95,12 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
OUT.m_depth += ShadowMapDepthBias;
}
// Clip Alpha
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
return OUT;
}
@@ -18,11 +18,6 @@
"displayName": "Parallax Settings",
"description": "Properties for configuring the parallax effect, applied to all layers."
},
{
"id": "opacity",
"displayName": "Opacity",
"description": "Properties for configuring the materials transparency."
},
{
"id": "uv",
"displayName": "UVs",
@@ -410,73 +405,6 @@
}
}
],
"opacity": [
{
"id": "mode",
"displayName": "Opacity Mode",
"description": "Opacity mode for this texture.",
"type": "Enum",
"enumValues": [ "Opaque", "Cutout", "Blended" ],
"defaultValue": "Opaque",
"connection": {
"type": "ShaderOption",
"id": "o_opacity_mode"
}
},
{
"id": "alphaSource",
"displayName": "Alpha Source",
"description": "Source texture of alpha value.",
"type": "Enum",
"enumValues": [ "Packed", "Split", "None" ],
"defaultValue": "Packed",
"connection": {
"type": "ShaderOption",
"id": "o_opacity_source"
}
},
{
"id": "textureMap",
"displayName": "Texture Map",
"description": "Texture map for defining surface opacity.",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_opacityMap"
}
},
{
"id": "textureMapUv",
"displayName": "UV",
"description": "Opacity texture map UV set",
"type": "Enum",
"enumIsUv": true,
"defaultValue": "Tiled",
"connection": {
"type": "ShaderInput",
"id": "m_opacityMapUvIndex"
}
},
{
"id": "factor",
"displayName": "Factor",
"description": "Factor for cutout threshold and blending",
"type": "Float",
"min": 0.0,
"max": 1.0,
"defaultValue": 0.5,
"connection": {
"type": "ShaderInput",
"id": "m_opacityFactor"
}
},
{
"id": "doubleSided",
"displayName": "Double-sided",
"description": "Whether to render back-faces or just front-faces.",
"type": "Bool"
}
],
"uv": [
{
"id": "center",
@@ -2851,16 +2779,7 @@
{
"file": "Shaders/MotionVector/SkinnedMeshMotionVector.shader",
"tag": "SkinnedMeshMotionVector"
},
// Used by the light culling system to produce accurate depth bounds for this object when it uses blended transparency
{
"file": "Shaders/Depth/DepthPassTransparentMin.shader",
"tag": "DepthPassTransparentMin"
},
{
"file": "Shaders/Depth/DepthPassTransparentMax.shader",
"tag": "DepthPassTransparentMax"
}
}
],
"functors": [
//##############################################################################################
@@ -2885,7 +2804,7 @@
{
"type": "Lua",
"args": {
"file": "StandardPBR_ShaderEnable.lua"
"file": "StandardMultilayerPBR_ShaderEnable.lua"
}
},
{
@@ -2925,27 +2844,6 @@
"file": "StandardPBR_SubsurfaceState.lua"
}
},
{
"type": "Lua",
"args": {
"file": "StandardPBR_HandleOpacityDoubleSided.lua"
}
},
{
"type": "OverrideDrawList",
"args": {
"triggerProperty": "opacity.mode",
"triggerValue": "Blended",
"shaderIndex": 1,
"drawList": "transparent"
}
},
{
"type": "Lua",
"args": {
"file": "StandardPBR_HandleOpacityMode.lua"
}
},
//##############################################################################################
// Layer 1 Functors
//##############################################################################################
@@ -14,6 +14,7 @@
#include <Atom/Features/SrgSemantics.azsli>
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
#include <Atom/Features/PBR/LightingOptions.azsli>
#include "MaterialInputs/BaseColorInput.azsli"
#include "MaterialInputs/RoughnessInput.azsli"
@@ -26,6 +27,8 @@
#include "MaterialInputs/ParallaxInput.azsli"
#include "MaterialInputs/UvSetCount.azsli"
// ------ ShaderResourceGroup ----------------------------------------
#define DEFINE_LAYER_SRG_INPUTS(prefix) \
COMMON_SRG_INPUTS_BASE_COLOR(prefix) \
COMMON_SRG_INPUTS_ROUGHNESS(prefix) \
@@ -64,10 +67,6 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
float3x3 m_uvMatrixInverse;
float4 m_pad5; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed.
float m_opacityFactor;
Texture2D m_opacityMap;
uint m_opacityMapUvIndex;
Sampler m_sampler
{
AddressU = Wrap;
@@ -109,6 +108,8 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
uint m_transmissionThicknessMapUvIndex;
}
// ------ Shader Options ----------------------------------------
enum class DebugDrawMode { None, BlendMaskValues, DepthMaps };
option DebugDrawMode o_debugDrawMode;
@@ -121,6 +122,8 @@ option BlendMaskSource o_blendSource;
// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream.
option bool o_blendMask_isBound;
// ------ Blend Utilities ----------------------------------------
//! Returns the BlendMaskSource that will actually be used when rendering (not necessarily the same BlendMaskSource specified by the user)
BlendMaskSource GetFinalBlendMaskSource()
{
@@ -181,6 +184,22 @@ float3 BlendLayers(float3 layer1, float3 layer2, float3 layer3, float3 blendMask
return layer1 * blendMaskValues.r + layer2 * blendMaskValues.g + layer3 * blendMaskValues.b;
}
// ------ Parallax Utilities ----------------------------------------
bool ShouldHandleParallax()
{
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled.
// Also, all the debug draw modes avoid parallax (they early-return before parallax code actually) so you can see exactly where the various maps appear on the surface UV space.
return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_debugDrawMode == DebugDrawMode::None;
}
bool ShouldHandleParallaxInDepthShaders()
{
// The depth pass shaders need to calculate parallax when the result could affect the depth buffer (or when
// parallax could affect texel clipping but we don't have alpha/clipping support in multilayer PBR).
return ShouldHandleParallax() && o_parallax_enablePixelDepthOffset;
}
// These static values are used to pass extra data to the GetDepth callback function during the parallax depth search.
static float3 s_blendMaskFromVertexStream;
@@ -11,12 +11,10 @@
*/
#include <viewsrg.srgi>
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/ParallaxMapping.azsli>
#include <Atom/Features/MatrixUtility.azsli>
#include "MaterialInputs/AlphaInput.azsli"
#include "MaterialInputs/ParallaxInput.azsli"
@@ -72,7 +70,7 @@ VSDepthOutput MainVS(VSInput IN)
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
OUT.m_uv[1] = IN.m_uv1;
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
OUT.m_worldPosition = worldPosition.xyz;
@@ -101,18 +99,9 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
{
PSDepthOutput OUT;
// Alpha
float2 layer1_baseColorUV = IN.m_uv[MaterialSrg::m_layer1_m_baseColorMapUvIndex];
float2 layer2_baseColorUV = IN.m_uv[MaterialSrg::m_layer2_m_baseColorMapUvIndex];
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
// [GFX TODO][ATOM-14589] Figure out how to deal with opacity, instead of just hard-coding to layer1
float alpha = SampleAlpha(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_opacityMap, layer1_baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
OUT.m_depth = IN.m_position.z;
if(o_debugDrawMode == DebugDrawMode::None && o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
@@ -42,7 +42,6 @@ DEFINE_LAYER_OPTIONS(o_layer1_)
DEFINE_LAYER_OPTIONS(o_layer2_)
DEFINE_LAYER_OPTIONS(o_layer3_)
#include "MaterialInputs/AlphaInput.azsli"
#include "MaterialInputs/SubsurfaceInput.azsli"
#include "MaterialInputs/TransmissionInput.azsli"
#include "StandardMultilayerPBR_Common.azsli"
@@ -83,7 +82,7 @@ struct VSOutput
float3 m_blendMask : UV7;
};
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/AlphaUtils.azsli> // TODO: Remove this after OpacityMode is removed from LightingModel
#include <Atom/Features/PBR/LightingModel.azsli>
#include <Atom/Features/Vertex/VertexHelper.azsli>
@@ -107,9 +106,8 @@ VSOutput ForwardPassVS(VSInput IN)
OUT.m_blendMask = float3(1,1,1);
}
// We can skip per-vertex shadow coords when parallax is enabled because we need to calculate per-pixel shadow coords anyway.
// We cannot skip shadow coords when o_debugDrawMode is on because some debug draw modes return before parallax.
bool skipShadowCoords = o_debugDrawMode == DebugDrawMode::None && o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset;
// Shadow coords will be calculated in the pixel shader in this case
bool skipShadowCoords = ShouldHandleParallax() && o_parallax_enablePixelDepthOffset;
VertexHelper(IN, OUT, worldPosition, skipShadowCoords);
return OUT;
@@ -157,7 +155,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
// ------- Parallax -------
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled
if(!o_enableSubsurfaceScattering && o_parallax_feature_enabled)
if(ShouldHandleParallax())
{
GetDepth_Setup(IN.m_blendMask);
@@ -199,15 +197,6 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
// Now that any parallax has been calculated, we calculate the blend factors for any layers that are impacted by the parallax.
float3 blendMaskValues = GetBlendMaskValues(IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask);
// ------- Alpha & Clip -------
float2 layer1_baseColorUv = uvLayer1[MaterialSrg::m_layer1_m_baseColorMapUvIndex];
float2 layer2_baseColorUv = uvLayer2[MaterialSrg::m_layer2_m_baseColorMapUvIndex];
float2 layer3_baseColorUv = uvLayer3[MaterialSrg::m_layer3_m_baseColorMapUvIndex];
float2 opacityUv = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
// [GFX TODO][ATOM-14589] Figure out how to deal with opacity, instead of just hard-coding to layer1
float alpha = GetAlphaInputAndClip(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_opacityMap, layer1_baseColorUv, opacityUv, MaterialSrg::m_sampler, MaterialSrg::m_opacityFactor, o_opacity_source);
// ------- Normal -------
float3 layer1_normalFactor = MaterialSrg::m_layer1_m_normalFactor * blendMaskValues.r;
@@ -226,6 +215,10 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
float3 normalWS = normalize(TangentSpaceToWorld(normalTS, IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex]));
// ------- Base Color -------
float2 layer1_baseColorUv = uvLayer1[MaterialSrg::m_layer1_m_baseColorMapUvIndex];
float2 layer2_baseColorUv = uvLayer2[MaterialSrg::m_layer2_m_baseColorMapUvIndex];
float2 layer3_baseColorUv = uvLayer3[MaterialSrg::m_layer3_m_baseColorMapUvIndex];
float3 layer1_sampledColor = GetBaseColorInput(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_sampler, layer1_baseColorUv, MaterialSrg::m_layer1_m_baseColor.rgb, o_layer1_o_baseColor_useTexture);
float3 layer2_sampledColor = GetBaseColorInput(MaterialSrg::m_layer2_m_baseColorMap, MaterialSrg::m_sampler, layer2_baseColorUv, MaterialSrg::m_layer2_m_baseColor.rgb, o_layer2_o_baseColor_useTexture);
@@ -352,34 +345,18 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
// ------- Lighting Calculation -------
const float2 anisotropy = 0.0; // Does not affect calculations unless 'o_enableAnisotropy' is enabled
const float alpha = 1.0;
PbrLightingOutput lightingOutput = PbrLighting(IN,
baseColor, metallic, roughness, specularF0Factor,
normalWS, tangents[0], bitangents[0], anisotropy,
emissive, diffuseAmbientOcclusion, specularOcclusion, transmissionTintThickness, MaterialSrg::m_transmissionParams, clearCoatFactor, clearCoatRoughness, clearCoatNormal, alpha, o_opacity_mode);
emissive, diffuseAmbientOcclusion, specularOcclusion, transmissionTintThickness, MaterialSrg::m_transmissionParams, clearCoatFactor, clearCoatRoughness, clearCoatNormal, alpha, OpacityMode::Opaque);
// ------- Opacity -------
if (o_opacity_mode == OpacityMode::Blended)
{
// [GFX_TODO ATOM-13187] PbrLighting shouldn't be writing directly to render targets. It's confusing when
// specular is being added to diffuse just because we're calling render target 0 "diffuse".
// 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
}
else
{
// Pack factor and quality, drawback: because of precision limit of float16 cannot represent exact 1, maximum representable value is 0.9961
uint factorAndQuality = dot(round(float2(saturate(surfaceScatteringFactor), MaterialSrg::m_subsurfaceScatteringQuality) * 255), float2(256, 1));
lightingOutput.m_diffuseColor.w = factorAndQuality * (o_enableSubsurfaceScattering ? 1.0 : -1.0);
lightingOutput.m_scatterDistance = MaterialSrg::m_scatterDistance;
}
// Pack factor and quality, drawback: because of precision limit of float16 cannot represent exact 1, maximum representable value is 0.9961
uint factorAndQuality = dot(round(float2(saturate(surfaceScatteringFactor), MaterialSrg::m_subsurfaceScatteringQuality) * 255), float2(256, 1));
lightingOutput.m_diffuseColor.w = factorAndQuality * (o_enableSubsurfaceScattering ? 1.0 : -1.0);
lightingOutput.m_scatterDistance = MaterialSrg::m_scatterDistance;
return lightingOutput;
}
@@ -0,0 +1,39 @@
--------------------------------------------------------------------------------------
--
-- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
-- its licensors.
--
-- For complete copyright and license terms please see the LICENSE at the root of this
-- distribution (the "License"). All use of this software is governed by the License,
-- or, if provided, by the license below or the license accompanying this file. Do not
-- remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--
--
----------------------------------------------------------------------------------------------------
function GetMaterialPropertyDependencies()
return {"parallax.enable", "parallax.pdo"}
end
function Process(context)
local parallaxEnabled = context:GetMaterialPropertyValue_bool("parallax.enable")
local parallaxPdoEnabled = context:GetMaterialPropertyValue_bool("parallax.pdo")
local depthPass = context:GetShaderByTag("DepthPass")
local shadowMap = context:GetShaderByTag("Shadowmap")
local forwardPassEDS = context:GetShaderByTag("ForwardPass_EDS")
local depthPassWithPS = context:GetShaderByTag("DepthPass_WithPS")
local shadowMapWitPS = context:GetShaderByTag("Shadowmap_WithPS")
local forwardPass = context:GetShaderByTag("ForwardPass")
local shadingAffectsDepth = parallaxEnabled and parallaxPdoEnabled;
depthPass:SetEnabled(not shadingAffectsDepth)
shadowMap:SetEnabled(not shadingAffectsDepth)
forwardPassEDS:SetEnabled(not shadingAffectsDepth)
depthPassWithPS:SetEnabled(shadingAffectsDepth)
shadowMapWitPS:SetEnabled(shadingAffectsDepth)
forwardPass:SetEnabled(shadingAffectsDepth)
end
@@ -12,12 +12,10 @@
#include <scenesrg.srgi>
#include <viewsrg.srgi>
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/ParallaxMapping.azsli>
#include <Atom/Features/MatrixUtility.azsli>
#include "MaterialInputs/AlphaInput.azsli"
#include "MaterialInputs/ParallaxInput.azsli"
#include "MaterialInputs/ParallaxInput.azsli"
@@ -71,7 +69,7 @@ VertexOutput MainVS(VertexInput IN)
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
OUT.m_uv[1] = IN.m_uv1;
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
OUT.m_worldPosition = worldPosition.xyz;
@@ -100,18 +98,9 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
{
PSDepthOutput OUT;
// Alpha
float2 layer1_baseColorUV = IN.m_uv[MaterialSrg::m_layer1_m_baseColorMapUvIndex];
float2 layer2_baseColorUV = IN.m_uv[MaterialSrg::m_layer2_m_baseColorMapUvIndex];
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
// [GFX TODO][ATOM-14589] Figure out how to deal with opacity, instead of just hard-coding to layer1
float alpha = SampleAlpha(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_opacityMap, layer1_baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
OUT.m_depth = IN.m_position.z;
if(o_debugDrawMode == DebugDrawMode::None && o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
@@ -132,6 +121,6 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
OUT.m_depth = depth;
}
return OUT;
}
@@ -14,6 +14,8 @@
#include <Atom/Features/SrgSemantics.azsli>
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
#include <Atom/Features/PBR/LightingOptions.azsli>
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include "MaterialInputs/BaseColorInput.azsli"
#include "MaterialInputs/RoughnessInput.azsli"
@@ -97,3 +99,17 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
return SampleDepthOrHeightMap(MaterialSrg::m_depthInverted, MaterialSrg::m_depthMap, MaterialSrg::m_sampler, uv, uv_ddx, uv_ddy);
}
COMMON_OPTIONS_PARALLAX()
bool ShouldHandleParallax()
{
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scattering is enabled.
return !o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap;
}
bool ShouldHandleParallaxInDepthShaders()
{
// The depth pass shaders need to calculate parallax when the result could affect the depth buffer, or when
// parallax could affect texel clipping.
return ShouldHandleParallax() && (o_parallax_enablePixelDepthOffset || o_opacity_mode == OpacityMode::Cutout);
}
@@ -11,7 +11,6 @@
*/
#include <viewsrg.srgi>
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include "./StandardPBR_Common.azsli"
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/ParallaxMapping.azsli>
@@ -57,7 +56,7 @@ VSDepthOutput MainVS(VSInput IN)
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
OUT.m_uv[1] = IN.m_uv1;
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
OUT.m_worldPosition = worldPosition.xyz;
@@ -76,16 +75,9 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
{
PSDepthOutput OUT;
// Alpha
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
OUT.m_depth = IN.m_position.z;
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
// We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space.
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, float3(0, 0, 0) };
@@ -99,7 +91,13 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth);
}
// Alpha
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
return OUT;
}
@@ -26,7 +26,7 @@ COMMON_OPTIONS_NORMAL()
COMMON_OPTIONS_CLEAR_COAT()
COMMON_OPTIONS_OCCLUSION()
COMMON_OPTIONS_EMISSIVE()
COMMON_OPTIONS_PARALLAX()
// Note COMMON_OPTIONS_PARALLAX is in StandardPBR_Common.azsli because it's needed by all StandardPBR shaders.
// Alpha
#include "MaterialInputs/AlphaInput.azsli"
@@ -66,7 +66,6 @@ struct VSOutput
float2 m_uv[UvSetCount] : UV1;
};
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/LightingModel.azsli>
#include <Atom/Features/Vertex/VertexHelper.azsli>
@@ -80,7 +79,10 @@ VSOutput StandardPbr_ForwardPassVS(VSInput IN)
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
OUT.m_uv[1] = IN.m_uv1;
VertexHelper(IN, OUT, worldPosition, o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset);
// Shadow coords will be calculated in the pixel shader in this case
bool skipShadowCoords = ShouldHandleParallax() && o_parallax_enablePixelDepthOffset;
VertexHelper(IN, OUT, worldPosition, skipShadowCoords);
return OUT;
}
@@ -110,7 +112,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
bool displacementIsClipped = false;
// Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled
if(!o_enableSubsurfaceScattering && o_parallax_feature_enabled && o_useDepthMap)
if(ShouldHandleParallax())
{
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
@@ -13,7 +13,6 @@
#include <scenesrg.srgi>
#include <viewsrg.srgi>
#include "StandardPBR_Common.azsli"
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/ParallaxMapping.azsli>
#include <Atom/Features/MatrixUtility.azsli>
@@ -57,7 +56,7 @@ VertexOutput MainVS(VertexInput IN)
OUT.m_uv[0] = mul(MaterialSrg::m_uvMatrix, float3(IN.m_uv0, 1.0)).xy;
OUT.m_uv[1] = IN.m_uv1;
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
OUT.m_worldPosition = worldPosition.xyz;
@@ -77,28 +76,9 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
{
PSDepthOutput OUT;
// Alpha
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
OUT.m_depth = IN.m_position.z;
float3 dirToCamera;
if(ViewSrg::m_projectionMatrix[0].w)
{
// orthographic projection (directional light)
// No view position, use light direction
dirToCamera = ViewSrg::m_viewMatrix[2].xyz;
}
else
{
dirToCamera = ViewSrg::m_worldPosition.xyz - IN.m_worldPosition;
}
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
if(ShouldHandleParallaxInDepthShaders())
{
static const float ShadowMapDepthBias = 0.000001;
@@ -116,5 +96,13 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
OUT.m_depth += ShadowMapDepthBias;
}
// Alpha
float2 baseColorUV = IN.m_uv[MaterialSrg::m_baseColorMapUvIndex];
float2 opacityUV = IN.m_uv[MaterialSrg::m_opacityMapUvIndex];
float alpha = SampleAlpha(MaterialSrg::m_baseColorMap, MaterialSrg::m_opacityMap, baseColorUV, opacityUV, MaterialSrg::m_sampler, o_opacity_source);
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
return OUT;
}