Merge branch 'main' into jromnoa_atom_renderer_test_fix

This commit is contained in:
jromnoa
2021-05-06 16:45:31 -07:00
27 changed files with 675 additions and 520 deletions
@@ -42,6 +42,7 @@ class TestAssetPicker(object):
@pytest.mark.test_case_id("C13751579", "C1508814")
@pytest.mark.SUITE_periodic
@pytest.mark.xfail # ATOM-15493
def test_AssetPicker_UI_UX(self, request, editor, level, launcher_platform):
expected_lines = [
"TestEntity Entity successfully created",
@@ -946,19 +946,6 @@
"type": "Bool",
"defaultValue": false
},
{
"id": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the depth values",
"type": "Float",
"defaultValue": 0.0,
"min": 0.0,
"softMax": 0.1,
"connection": {
"type": "ShaderInput",
"id": "m_depthFactor"
}
},
{
"id": "textureMap",
"displayName": "Texture Map",
@@ -981,6 +968,32 @@
"id": "m_parallaxUvIndex"
}
},
{
"id": "factor",
"displayName": "Heightmap Scale",
"description": "The total height of the heightmap in local model units.",
"type": "Float",
"defaultValue": 0.0,
"min": 0.0,
"softMax": 0.1,
"connection": {
"type": "ShaderInput",
"id": "m_depthFactor"
}
},
{
"id": "offset",
"displayName": "Offset",
"description": "Adjusts the overall displacement amount in local model units.",
"type": "Float",
"defaultValue": 0.0,
"softMin": -0.1,
"softMax": 0.1,
"connection": {
"type": "ShaderInput",
"id": "m_depthOffset"
}
},
{
"id": "invert",
"displayName": "Invert",
@@ -1026,6 +1039,17 @@
"type": "ShaderOption",
"id": "o_parallax_enablePixelDepthOffset"
}
},
{
"id": "showClipping",
"displayName": "Show Clipping",
"description": "Highlight areas where the heightmap is clipped by the mesh surface.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderOption",
"id": "o_parallax_highlightClipping"
}
}
],
"subsurfaceScattering": [
@@ -1714,22 +1738,6 @@
"shaderOption": "o_emissive_useTexture"
}
},
{
// See the comment above for details.
"type": "UseTexture",
"args": {
"textureProperty": "parallax.textureMap",
"dependentProperties": ["parallax.textureMapUv"],
"useTextureProperty": "parallax.enable",
"shaderTags": [
"ForwardPass",
"ForwardPass_EDS",
"Shadowmap_WithPS",
"DepthPass_WithPS"
],
"shaderOption": "o_parallax_feature_enabled"
}
},
{
// See the comment above for details.
"type": "UseTexture",
@@ -1813,34 +1821,6 @@
]
}
},
{
// Controls visibility for properties in the editor.
// @param actions - a list of actions that are executed in order. visibility will be set when triggerProperty hits the triggerValue.
// @param affectedProperties - the properties that are affected by actions.
"type": "UpdatePropertyVisibility",
"args": {
"actions": [
{
"triggerProperty": "parallax.enable",
"triggerValue": true,
"visibility": "Enabled"
},
{
"triggerProperty": "parallax.enable",
"triggerValue": false,
"visibility": "Hidden"
}
],
"affectedProperties": [
"parallax.factor",
"parallax.textureMap",
"parallax.invert",
"parallax.algorithm",
"parallax.quality",
"parallax.pdo"
]
}
},
{
"type": "UpdatePropertyVisibility",
"args": {
@@ -2076,6 +2056,12 @@
]
}
},
{
"type": "Lua",
"args": {
"file": "StandardPBR_ParallaxState.lua"
}
},
{
"type": "Lua",
"args": {
@@ -15,6 +15,8 @@
#include <Atom/Features/SrgSemantics.azsli>
#include <viewsrg.srgi>
#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"
@@ -104,7 +106,22 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
}
// Callback function for ParallaxMapping.azsli
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
DepthResult 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);
}
@@ -10,7 +10,6 @@
*
*/
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include "./EnhancedPBR_Common.azsli"
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/ParallaxMapping.azsli>
@@ -55,7 +54,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;
@@ -74,45 +73,28 @@ 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) };
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex];
float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex];
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor,
IN.m_uv[MaterialSrg::m_parallaxUvIndex],
ViewSrg::m_worldPosition.xyz - IN.m_worldPosition,
tangent,
bitangent,
IN.m_normal,
uvMatrix);
PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor,
tangentOffset,
IN.m_worldPosition,
tangent,
bitangent,
IN.m_normal,
uvMatrixInverse,
ObjectSrg::GetWorldMatrix(),
ViewSrg::m_viewProjectionMatrix);
OUT.m_depth = pdo.m_depth;
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
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;
}
@@ -40,8 +40,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"
@@ -102,8 +102,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;
}
@@ -132,9 +135,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
// ------- Depth & Parallax -------
depth = IN.m_position.z;
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;
@@ -143,9 +148,9 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor,
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, displacementIsClipped);
// Apply second part of the offset to the detail UV (see comment above)
IN.m_detailUv[MaterialSrg::m_parallaxUvIndex] -= IN.m_uv[MaterialSrg::m_parallaxUvIndex];
@@ -206,6 +211,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
float3 baseColor = GetDetailedBaseColorInput(
MaterialSrg::m_baseColorMap, MaterialSrg::m_sampler, baseColorUv, o_baseColor_useTexture, MaterialSrg::m_baseColor, MaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode,
MaterialSrg::m_detail_baseColor_texture, MaterialSrg::m_sampler, detailUv, o_detail_baseColor_useTexture, detailLayerBaseColorFactor);
if(o_parallax_highlightClipping && displacementIsClipped)
{
ApplyParallaxClippingHighlight(baseColor);
}
// ------- Metallic -------
@@ -12,7 +12,6 @@
#include <scenesrg.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>
@@ -54,8 +53,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;
@@ -75,57 +74,32 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
{
PSDepthOutput OUT;
OUT.m_depth = IN.m_position.z;
if(ShouldHandleParallaxInDepthShaders())
{
static const float ShadowMapDepthBias = 0.000001;
// 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) };
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth);
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);
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)
{
// 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) };
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex];
float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex];
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor,
IN.m_uv[MaterialSrg::m_parallaxUvIndex],
dirToCamera,
tangent,
bitangent,
IN.m_normal,
uvMatrix);
PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor,
tangentOffset,
IN.m_worldPosition,
tangent,
bitangent,
IN.m_normal,
uvMatrixInverse,
ObjectSrg::GetWorldMatrix(),
ViewSrg::m_viewProjectionMatrix);
OUT.m_depth = pdo.m_depth;
}
return OUT;
}
@@ -24,16 +24,15 @@
#define COMMON_SRG_INPUTS_PARALLAX(prefix) \
Texture2D prefix##m_depthMap; \
float prefix##m_depthFactor; \
float prefix##m_depthOffset; \
bool prefix##m_depthInverted;
#define COMMON_OPTIONS_PARALLAX(prefix) \
option bool prefix##o_useDepthMap;
option bool o_parallax_feature_enabled;
void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor,
void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor, float depthOffset,
float4x4 objectWorldMatrix, float3x3 uvMatrix, float3x3 uvMatrixInverse,
inout float2 uv, inout float3 worldPosition, inout float depth)
inout float2 uv, inout float3 worldPosition, inout float depth, out bool isClipped)
{
if(o_parallax_feature_enabled)
{
@@ -49,20 +48,22 @@ void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float dep
dirToCamera = ViewSrg::m_worldPosition.xyz - worldPosition;
}
float3 tangentOffset = GetParallaxOffset( depthFactor,
uv,
dirToCamera,
tangent,
bitangent,
normal,
uvMatrix);
ParallaxOffset tangentOffset = GetParallaxOffset( depthFactor,
depthOffset,
uv,
dirToCamera,
tangent,
bitangent,
normal,
uvMatrix);
uv += tangentOffset.xy;
uv += tangentOffset.m_offsetTS.xy;
isClipped = tangentOffset.m_isClipped;
if(o_parallax_enablePixelDepthOffset)
{
PixelDepthOffset pdo = CalcPixelDepthOffset(depthFactor,
tangentOffset,
tangentOffset.m_offsetTS,
worldPosition,
tangent,
bitangent,
@@ -70,9 +71,20 @@ void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float dep
uvMatrixInverse,
objectWorldMatrix,
ViewSrg::m_viewProjectionMatrix);
depth = pdo.m_depth;
worldPosition = pdo.m_worldPosition;
}
}
}
void GetParallaxInput(float3 normal, float3 tangent, float3 bitangent, float depthFactor, float depthOffset,
float4x4 objectWorldMatrix, float3x3 uvMatrix, float3x3 uvMatrixInverse,
inout float2 uv, inout float3 worldPosition, inout float depth)
{
bool isClipped;
GetParallaxInput(normal, tangent, bitangent, depthFactor, depthOffset, objectWorldMatrix, uvMatrix, uvMatrixInverse, uv, worldPosition, depth, isClipped);
}
@@ -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",
@@ -361,19 +356,6 @@
"id": "m_parallaxUvIndex"
}
},
{
"id": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the depth values for all layers.",
"type": "Float",
"defaultValue": 1.0,
"min": 0.0,
"softMax": 2.0,
"connection": {
"type": "ShaderInput",
"id": "m_parallaxMainDepthFactor"
}
},
{
"id": "algorithm",
"displayName": "Algorithm",
@@ -408,73 +390,17 @@
"type": "ShaderOption",
"id": "o_parallax_enablePixelDepthOffset"
}
}
],
"opacity": [
},
{
"id": "mode",
"displayName": "Opacity Mode",
"description": "Opacity mode for this texture.",
"type": "Enum",
"enumValues": [ "Opaque", "Cutout", "Blended" ],
"defaultValue": "Opaque",
"id": "showClipping",
"displayName": "Show Clipping",
"description": "Highlight areas where the heightmap is clipped by the mesh surface.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderOption",
"id": "o_opacity_mode"
"id": "o_parallax_highlightClipping"
}
},
{
"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": [
@@ -1343,8 +1269,8 @@
},
{
"id": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the depth values",
"displayName": "Heightmap Scale",
"description": "The total height of the heightmap in local model units.",
"type": "Float",
"defaultValue": 0.0,
"min": 0.0,
@@ -1354,6 +1280,19 @@
"id": "m_layer1_m_depthFactor"
}
},
{
"id": "offset",
"displayName": "Offset",
"description": "Adjusts the overall displacement amount in local model units.",
"type": "Float",
"defaultValue": 0.0,
"softMin": -0.1,
"softMax": 0.1,
"connection": {
"type": "ShaderInput",
"id": "m_layer1_m_depthOffset"
}
},
{
"id": "invert",
"displayName": "Invert",
@@ -2036,8 +1975,8 @@
},
{
"id": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the depth values",
"displayName": "Heightmap Scale",
"description": "The total height of the heightmap in local model units.",
"type": "Float",
"defaultValue": 0.0,
"min": 0.0,
@@ -2047,6 +1986,19 @@
"id": "m_layer2_m_depthFactor"
}
},
{
"id": "offset",
"displayName": "Offset",
"description": "Adjusts the overall displacement amount in local model units.",
"type": "Float",
"defaultValue": 0.0,
"softMin": -0.1,
"softMax": 0.1,
"connection": {
"type": "ShaderInput",
"id": "m_layer2_m_depthOffset"
}
},
{
"id": "invert",
"displayName": "Invert",
@@ -2729,8 +2681,8 @@
},
{
"id": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the depth values",
"displayName": "Heightmap Scale",
"description": "The total height of the heightmap in local model units.",
"type": "Float",
"defaultValue": 0.0,
"min": 0.0,
@@ -2740,6 +2692,19 @@
"id": "m_layer3_m_depthFactor"
}
},
{
"id": "offset",
"displayName": "Offset",
"description": "Adjusts the overall displacement amount in local model units.",
"type": "Float",
"defaultValue": 0.0,
"softMin": -0.1,
"softMax": 0.1,
"connection": {
"type": "ShaderInput",
"id": "m_layer3_m_depthOffset"
}
},
{
"id": "invert",
"displayName": "Invert",
@@ -2851,16 +2816,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 +2841,7 @@
{
"type": "Lua",
"args": {
"file": "StandardPBR_ShaderEnable.lua"
"file": "StandardMultilayerPBR_ShaderEnable.lua"
}
},
{
@@ -2925,27 +2881,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) \
@@ -57,17 +60,16 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
float4 m_pad3; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed.
uint m_parallaxUvIndex;
float m_parallaxMainDepthFactor;
// These are used to limit the heightmap intersection search range to the narrowest band possible, to give the best quality result.
float m_displacementMin; // The lowest displacement value possible from all layers combined
float m_displacementMax; // The highest displacement value possible from all layers combined
float3x3 m_uvMatrix;
float4 m_pad4; // [GFX TODO][ATOM-14595] This is a workaround for a data stomping bug. Remove once it's fixed.
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 +111,8 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
uint m_transmissionThicknessMapUvIndex;
}
// ------ Shader Options ----------------------------------------
enum class DebugDrawMode { None, BlendMaskValues, DepthMaps };
option DebugDrawMode o_debugDrawMode;
@@ -121,6 +125,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 +187,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;
@@ -192,7 +214,7 @@ void GetDepth_Setup(float3 vertexBlendMask)
}
// Callback function for ParallaxMapping.azsli
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
{
float3 layerDepthValues = float3(0,0,0);
@@ -204,8 +226,9 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
layerUv = mul(MaterialSrg::m_layer1_m_uvMatrix, float3(uv, 1.0)).xy;
}
layerDepthValues.r = SampleDepthOrHeightMap(MaterialSrg::m_layer1_m_depthInverted, MaterialSrg::m_layer1_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy);
layerDepthValues.r = SampleDepthOrHeightMap(MaterialSrg::m_layer1_m_depthInverted, MaterialSrg::m_layer1_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth;
layerDepthValues.r *= MaterialSrg::m_layer1_m_depthFactor;
layerDepthValues.r -= MaterialSrg::m_layer1_m_depthOffset;
}
if(o_layer2_o_useDepthMap)
@@ -216,8 +239,9 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
layerUv = mul(MaterialSrg::m_layer2_m_uvMatrix, float3(uv, 1.0)).xy;
}
layerDepthValues.g = SampleDepthOrHeightMap(MaterialSrg::m_layer2_m_depthInverted, MaterialSrg::m_layer2_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy);
layerDepthValues.g = SampleDepthOrHeightMap(MaterialSrg::m_layer2_m_depthInverted, MaterialSrg::m_layer2_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth;
layerDepthValues.g *= MaterialSrg::m_layer2_m_depthFactor;
layerDepthValues.g -= MaterialSrg::m_layer2_m_depthOffset;
}
if(o_layer3_o_useDepthMap)
@@ -228,8 +252,9 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
layerUv = mul(MaterialSrg::m_layer3_m_uvMatrix, float3(uv, 1.0)).xy;
}
layerDepthValues.b = SampleDepthOrHeightMap(MaterialSrg::m_layer3_m_depthInverted, MaterialSrg::m_layer3_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy);
layerDepthValues.b = SampleDepthOrHeightMap(MaterialSrg::m_layer3_m_depthInverted, MaterialSrg::m_layer3_m_depthMap, MaterialSrg::m_sampler, layerUv, uv_ddx, uv_ddy).m_depth;
layerDepthValues.b *= MaterialSrg::m_layer3_m_depthFactor;
layerDepthValues.b -= MaterialSrg::m_layer3_m_depthOffset;
}
// Note, when the blend source is BlendMaskSource::VertexColors, parallax will not be able to blend correctly between layers. It will end up using the same blend mask values
@@ -237,7 +262,6 @@ float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
// you have a small depth factor relative to the size of the blend transition.
float3 blendMaskValues = GetBlendMaskValues(uv, s_blendMaskFromVertexStream);
float3 depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendMaskValues);
return depth;
float depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendMaskValues);
return DepthResultAbsolute(depth);
}
@@ -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) };
@@ -126,7 +115,9 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor,
float parallaxOverallOffset = MaterialSrg::m_displacementMax;
float parallaxOverallFactor = MaterialSrg::m_displacementMax - MaterialSrg::m_displacementMin;
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], parallaxOverallFactor, parallaxOverallOffset,
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
@@ -55,7 +55,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"
@@ -121,9 +120,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;
@@ -167,23 +165,27 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
if(o_debugDrawMode == DebugDrawMode::DepthMaps)
{
GetDepth_Setup(IN.m_blendMask);
float depth = GetDepth(IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0));
float depth = GetNormalizedDepth(-MaterialSrg::m_displacementMax, -MaterialSrg::m_displacementMin, IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0));
return DebugOutput(float3(depth,depth,depth));
}
// ------- Parallax -------
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)
if(ShouldHandleParallax())
{
GetDepth_Setup(IN.m_blendMask);
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor,
float parallaxOverallOffset = MaterialSrg::m_displacementMax;
float parallaxOverallFactor = MaterialSrg::m_displacementMax - MaterialSrg::m_displacementMin;
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], parallaxOverallFactor, parallaxOverallOffset,
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, displacementIsClipped);
// Adjust directional light shadow coorinates for parallax correction
if(o_parallax_enablePixelDepthOffset)
@@ -218,15 +220,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;
@@ -245,6 +238,10 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
surface.normal = 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);
@@ -253,6 +250,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
float3 layer2_baseColor = BlendBaseColor(layer2_sampledColor, MaterialSrg::m_layer2_m_baseColor.rgb, MaterialSrg::m_layer2_m_baseColorFactor, o_layer2_o_baseColorTextureBlendMode, o_layer2_o_baseColor_useTexture);
float3 layer3_baseColor = BlendBaseColor(layer3_sampledColor, MaterialSrg::m_layer3_m_baseColor.rgb, MaterialSrg::m_layer3_m_baseColorFactor, o_layer3_o_baseColorTextureBlendMode, o_layer3_o_baseColor_useTexture);
float3 baseColor = BlendLayers(layer1_baseColor, layer2_baseColor, layer3_baseColor, blendMaskValues);
if(o_parallax_highlightClipping && displacementIsClipped)
{
ApplyParallaxClippingHighlight(baseColor);
}
// ------- Metallic -------
@@ -427,32 +429,13 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
lightingData.FinalizeLighting(surface.transmission.tint);
if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent)
{
alpha = FresnelSchlickWithRoughness(lightingData.NdotV, alpha, surface.roughnessLinear).x; // Increase opacity at grazing angles.
}
const float alpha = 1.0;
PbrLightingOutput lightingOutput = GetPbrLightingOutput(surface, lightingData, alpha);
// ------- 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);
}
// 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);
return lightingOutput;
@@ -20,10 +20,12 @@ function GetMaterialPropertyDependencies()
"layer1_parallax.enable",
"layer2_parallax.enable",
"layer3_parallax.enable",
"parallax.factor",
"layer1_parallax.factor",
"layer2_parallax.factor",
"layer3_parallax.factor"
"layer3_parallax.factor",
"layer1_parallax.offset",
"layer2_parallax.offset",
"layer3_parallax.offset"
}
end
@@ -31,6 +33,23 @@ function GetShaderOptionDependencies()
return {"o_parallax_feature_enabled"}
end
function MergeRange(heightMinMax, offset, factor)
top = offset
bottom = offset - factor
if(heightMinMax[1] == nil) then
heightMinMax[1] = top
else
heightMinMax[1] = math.max(heightMinMax[1], top)
end
if(heightMinMax[0] == nil) then
heightMinMax[0] = bottom
else
heightMinMax[0] = math.min(heightMinMax[0], bottom)
end
end
function Process(context)
local enableParallax = context:GetMaterialPropertyValue_bool("parallax.enable")
local enable1 = context:GetMaterialPropertyValue_bool("layer1_parallax.enable")
@@ -39,30 +58,25 @@ function Process(context)
enableParallax = enableParallax and (enable1 or enable2 or enable3)
context:SetShaderOptionValue_bool("o_parallax_feature_enabled", enableParallax)
-- Smaller values for the main parallax factor used in GetParallaxOffset() give better quality.
-- So increase the per-layer parallax factors by normalizing them, and reduce the main factor accordingly.
if(enableParallax) then
local factorLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.factor")
local factorLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.factor")
local factorLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.factor")
local mainFactor = context:GetMaterialPropertyValue_float("parallax.factor")
maxLayerFactor = 0.0
if(enable1) then maxLayerFactor = math.max(maxLayerFactor, factorLayer1) end
if(enable2) then maxLayerFactor = math.max(maxLayerFactor, factorLayer2) end
if(enable3) then maxLayerFactor = math.max(maxLayerFactor, factorLayer3) end
local offsetLayer1 = context:GetMaterialPropertyValue_float("layer1_parallax.offset")
local offsetLayer2 = context:GetMaterialPropertyValue_float("layer2_parallax.offset")
local offsetLayer3 = context:GetMaterialPropertyValue_float("layer3_parallax.offset")
if(maxLayerFactor < 0.0001) then
local heightMinMax = {nil, nil}
if(enable1) then MergeRange(heightMinMax, offsetLayer1, factorLayer1) end
if(enable2) then MergeRange(heightMinMax, offsetLayer2, factorLayer2) end
if(enable3) then MergeRange(heightMinMax, offsetLayer3, factorLayer3) end
if(heightMinMax[1] - heightMinMax[0] < 0.0001) then
context:SetShaderOptionValue_bool("o_parallax_feature_enabled", false)
else
factorLayer1 = factorLayer1 / maxLayerFactor
factorLayer2 = factorLayer2 / maxLayerFactor
factorLayer3 = factorLayer3 / maxLayerFactor
mainFactor = mainFactor * maxLayerFactor;
context:SetShaderConstant_float("m_layer1_m_depthFactor", factorLayer1)
context:SetShaderConstant_float("m_layer2_m_depthFactor", factorLayer2)
context:SetShaderConstant_float("m_layer3_m_depthFactor", factorLayer3)
context:SetShaderConstant_float("m_parallaxMainDepthFactor", mainFactor)
context:SetShaderConstant_float("m_displacementMin", heightMinMax[0])
context:SetShaderConstant_float("m_displacementMax", heightMinMax[1])
end
end
end
@@ -76,8 +90,8 @@ function ProcessEditor(context)
end
context:SetMaterialPropertyVisibility("parallax.parallaxUv", visibility)
context:SetMaterialPropertyVisibility("parallax.factor", visibility)
context:SetMaterialPropertyVisibility("parallax.algorithm", visibility)
context:SetMaterialPropertyVisibility("parallax.quality", visibility)
context:SetMaterialPropertyVisibility("parallax.pdo", visibility)
context:SetMaterialPropertyVisibility("parallax.showClipping", visibility)
end
@@ -42,7 +42,8 @@ function ProcessEditor(context)
if(not enable or textureMap == nil) then
visibility = MaterialPropertyVisibility_Hidden
end
context:SetMaterialPropertyVisibility("parallax.factor", visibility)
context:SetMaterialPropertyVisibility("parallax.offset", visibility)
context:SetMaterialPropertyVisibility("parallax.invert", visibility)
end
@@ -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) };
@@ -124,13 +113,15 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor,
float parallaxOverallOffset = MaterialSrg::m_displacementMax;
float parallaxOverallFactor = MaterialSrg::m_displacementMax - MaterialSrg::m_displacementMin;
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], parallaxOverallFactor, parallaxOverallOffset,
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
OUT.m_depth = depth;
}
return OUT;
}
@@ -910,8 +910,8 @@
},
{
"id": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the depth values",
"displayName": "Heightmap Scale",
"description": "The total height of the heightmap in local model units.",
"type": "Float",
"defaultValue": 0.0,
"min": 0.0,
@@ -921,6 +921,19 @@
"id": "m_depthFactor"
}
},
{
"id": "offset",
"displayName": "Offset",
"description": "Adjusts the overall displacement amount in local model units.",
"type": "Float",
"defaultValue": 0.0,
"softMin": -0.1,
"softMax": 0.1,
"connection": {
"type": "ShaderInput",
"id": "m_depthOffset"
}
},
{
"id": "invert",
"displayName": "Invert",
@@ -966,6 +979,17 @@
"type": "ShaderOption",
"id": "o_parallax_enablePixelDepthOffset"
}
},
{
"id": "showClipping",
"displayName": "Show Clipping",
"description": "Highlight areas where the heightmap is clipped by the mesh surface.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderOption",
"id": "o_parallax_highlightClipping"
}
}
],
"subsurfaceScattering": [
@@ -15,6 +15,8 @@
#include <Atom/Features/SrgSemantics.azsli>
#include <viewsrg.srgi>
#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"
@@ -93,8 +95,23 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
}
// Callback function for ParallaxMapping.azsli
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
DepthResult 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);
}
@@ -10,7 +10,6 @@
*
*/
#include <Atom/Features/PBR/AlphaUtils.azsli>
#include "./StandardPBR_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,6 +74,23 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
{
PSDepthOutput OUT;
OUT.m_depth = IN.m_position.z;
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) };
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
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];
@@ -82,39 +98,5 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
CheckClipping(alpha, MaterialSrg::m_opacityFactor);
OUT.m_depth = IN.m_position.z;
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
{
// 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) };
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex];
float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex];
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor,
IN.m_uv[MaterialSrg::m_parallaxUvIndex],
ViewSrg::m_worldPosition.xyz - IN.m_worldPosition,
tangent,
bitangent,
IN.m_normal,
uvMatrix);
PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor,
tangentOffset,
IN.m_worldPosition,
tangent,
bitangent,
IN.m_normal,
uvMatrixInverse,
ObjectSrg::GetWorldMatrix(),
ViewSrg::m_viewProjectionMatrix);
OUT.m_depth = pdo.m_depth;
}
return OUT;
}
@@ -40,7 +40,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"
@@ -94,7 +94,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;
}
@@ -123,15 +126,18 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
// ------- Depth & Parallax -------
depth = IN.m_position.z;
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();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor,
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth, displacementIsClipped);
// Adjust directional light shadow coorinates for parallax correction
if(o_parallax_enablePixelDepthOffset)
@@ -166,6 +172,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
float3 sampledColor = GetBaseColorInput(MaterialSrg::m_baseColorMap, MaterialSrg::m_sampler, baseColorUv, MaterialSrg::m_baseColor.rgb, o_baseColor_useTexture);
float3 baseColor = BlendBaseColor(sampledColor, MaterialSrg::m_baseColor.rgb, MaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode, o_baseColor_useTexture);
if(o_parallax_highlightClipping && displacementIsClipped)
{
ApplyParallaxClippingHighlight(baseColor);
}
// ------- Metallic -------
float metallic = 0;
@@ -41,8 +41,10 @@ function ProcessEditor(context)
if(not enable or textureMap == nil) then
visibility = MaterialPropertyVisibility_Hidden
end
context:SetMaterialPropertyVisibility("parallax.factor", visibility)
context:SetMaterialPropertyVisibility("parallax.offset", visibility)
context:SetMaterialPropertyVisibility("parallax.showClipping", visibility)
context:SetMaterialPropertyVisibility("parallax.invert", visibility)
context:SetMaterialPropertyVisibility("parallax.algorithm", visibility)
context:SetMaterialPropertyVisibility("parallax.quality", visibility)
@@ -12,7 +12,6 @@
#include <scenesrg.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>
@@ -56,7 +55,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;
@@ -76,28 +75,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;
@@ -106,31 +86,22 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) };
PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1);
float3 tangent = tangents[MaterialSrg::m_parallaxUvIndex];
float3 bitangent = bitangents[MaterialSrg::m_parallaxUvIndex];
float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3();
float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3();
float3 tangentOffset = GetParallaxOffset( MaterialSrg::m_depthFactor,
IN.m_uv[MaterialSrg::m_parallaxUvIndex],
dirToCamera,
tangent,
bitangent,
IN.m_normal,
uvMatrix);
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_depthFactor, MaterialSrg::m_depthOffset,
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, OUT.m_depth);
PixelDepthOffset pdo = CalcPixelDepthOffset(MaterialSrg::m_depthFactor,
tangentOffset,
IN.m_worldPosition,
tangent,
bitangent,
IN.m_normal,
uvMatrixInverse,
ObjectSrg::GetWorldMatrix(),
ViewSrg::m_viewProjectionMatrix);
OUT.m_depth = pdo.m_depth + ShadowMapDepthBias;
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;
}
@@ -18,39 +18,147 @@ option bool o_parallax_enablePixelDepthOffset;
option enum class ParallaxAlgorithm {Basic, Steep, POM, Relief, Contact} o_parallax_algorithm;
option enum class ParallaxQuality {Low, Medium, High, Ultra} o_parallax_quality;
option bool o_parallax_feature_enabled;
option bool o_parallax_highlightClipping;
option bool o_parallax_shadow;
// I tried to make this an enum class, but ran into some DXC bug when compiling to SPIRV.
enum DepthResultCode
{
DepthResultCode_Invalid,
DepthResultCode_Normalized, //!< The result is in range [0,1], where 0 is the top of the heightmap and 1 is the bottom of the heightmap.
DepthResultCode_Absolute //!< The result is tangent space units (the same as world units if there's no mesh scaling), where 0 is at the mesh surface and positive values are below the surface.
};
//! The return value for the GetDepth() callback function below.
struct DepthResult
{
DepthResultCode m_resultCode;
float m_depth;
};
//! Convenience function for making a DepthResult with Code::Normalized
DepthResult DepthResultNormalized(float depth)
{
DepthResult result;
result.m_resultCode = DepthResultCode_Normalized;
result.m_depth = depth;
return result;
}
//! Convenience function for making a DepthResult with Code::Absolute
DepthResult DepthResultAbsolute(float depth)
{
DepthResult result;
result.m_resultCode = DepthResultCode_Absolute;
result.m_depth = depth;
return result;
}
//! The client shader must define this function.
//! This allows the client shader to implement special depth map sampling, for example procedurally generating or blending depth maps.
//! In simple cases though, the implementation of GetDepth() can simply call SampleDepthOrHeightMap().
//! @param uv the UV coordinates to use for sampling
//! @param uv_ddx will be set to ddx_fine(uv)
//! @param uv_ddy will be set to ddy_fine(uv)
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy);
//! @return see struct DepthResult
DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy);
//! Convenience function that can be used to implement GetDepth().
//! @param isHeightmap indicates whether to sample the map is a height map rather than a depth map.
float SampleDepthOrHeightMap(bool isHeightmap, Texture2D map, sampler mapSampler, float2 uv, float2 uv_ddx, float2 uv_ddy)
//! @return see struct DepthResult. In this case it will always contain a Code::Normalized result.
DepthResult SampleDepthOrHeightMap(bool isHeightmap, Texture2D map, sampler mapSampler, float2 uv, float2 uv_ddx, float2 uv_ddy)
{
return abs((isHeightmap * 1.0) - map.SampleGrad(mapSampler, uv, uv_ddx, uv_ddy).r);
DepthResult result;
result.m_resultCode = DepthResultCode_Normalized;
result.m_depth = abs((isHeightmap * 1.0) - map.SampleGrad(mapSampler, uv, uv_ddx, uv_ddy).r);
return result;
}
//! Calls GetDepth() and then normalizes the result if it isn't normalized already.
//! @param startDepth is the high point, which corresponds to a normalized depth value of 0.
//! @param stopDepth is the low point, which corresponds to a normalized depth value of 1.
//! @param inverseDepthRange is an optimization, and must be set to "1.0 / (stopDepth - startDepth)".
//! @param uv the UV coordinates to use for sampling
//! @param uv_ddx must be set to ddx_fine(uv)
//! @param uv_ddy must be set to ddy_fine(uv)
//! @param a depth value in the range [0,1]
float GetNormalizedDepth(float startDepth, float stopDepth, float inverseDepthRange, float2 uv, float2 uv_ddx, float2 uv_ddy)
{
// startDepth can be less than 0, representing a displacement above the mesh surface.
// But since we don't currently support any vertex displacement, negative depth values would cause various
// problems especially when PDO is enabled, like parallax surfaces clipping through foreground geometry, and parallax
// surfaces disappearing at low angles. So we clamp all depth values to a minimum of 0.
float normalizedDepth = 0.0;
DepthResult depthResult = GetDepth(uv, uv_ddx, uv_ddy);
if(stopDepth - startDepth > 0.0001)
{
if(DepthResultCode_Normalized == depthResult.m_resultCode)
{
float minNormalizedDepth = -startDepth * inverseDepthRange;
normalizedDepth = max(depthResult.m_depth, minNormalizedDepth);
}
else if(DepthResultCode_Absolute == depthResult.m_resultCode)
{
float clampedAbsoluteDepth = max(depthResult.m_depth, 0.0);
normalizedDepth = (clampedAbsoluteDepth - startDepth) * inverseDepthRange;
}
}
return normalizedDepth;
}
float GetNormalizedDepth(float startDepth, float stopDepth, float2 uv, float2 uv_ddx, float2 uv_ddy)
{
float inverseDepthRange = 1.0 / (stopDepth - startDepth);
return GetNormalizedDepth(startDepth, stopDepth, inverseDepthRange, uv, uv_ddx, uv_ddy);
}
void ApplyParallaxClippingHighlight(inout float3 baseColor)
{
baseColor = lerp(baseColor, float3(1.0, 0.0, 1.0), 0.5);
}
struct ParallaxOffset
{
float3 m_offsetTS; //!< represents the intersection point relative to the geometry surface, in tangent space.
bool m_isClipped; //!< Indicates whether the result is being clipped by the geometry surface, mainly for debug rendering. Only set when o_parallax_highlightClipping is true.
};
// dirToCameraTS should be in tangent space and normalized
// From Reat-Time Rendering 3rd edition, p.192
float3 BasicParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraTS)
ParallaxOffset BasicParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraTS)
{
// the amount to shift
float2 delta = dirToCameraTS.xy * GetDepth(uv, ddx_fine(uv), ddy_fine(uv)) * depthFactor;
float2 delta = dirToCameraTS.xy * GetNormalizedDepth(0, depthFactor, uv, ddx_fine(uv), ddy_fine(uv)) * depthFactor;
float3 offset = float3(0,0,0);
offset.xy -= delta;
return offset;
ParallaxOffset result;
result.m_offsetTS = float3(0,0,0);
result.m_offsetTS.xy -= delta;
result.m_isClipped = false;
return result;
}
// dirToCameraTS and dirToLightTS should be in tangent space and normalized
// Adapt from CryEngine shader shadelib.cfi and POM function in https://github.com/a-riccardi/shader-toy
// Performs ray intersection against a surface with a heightmap.
// Adapted from CryEngine shader shadelib.cfi and POM function in https://github.com/a-riccardi/shader-toy
// check https://github.com/UPBGE/blender/issues/1009 for more details.
float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, int numSteps, inout float parallaxShadowAttenuation)
// @param depthFactor - scales the heightmap in tangent space units (which normally ends up being world units).
// @param depthOffset - offsets the heighmap up or down in tangent space units (which normally ends up being world units).
// @param uv - the UV coordinates on the surface, where the search will begin, used to sample the heightmap.
// @param dirToCameraTS - normalized direction to the camera, in tangent space.
// @param dirToLightTS - normalized direction to a light source, in tangent space, for self-shadowing (if enabled via o_parallax_shadow).
// @param numSteps - the number of steps to take when marching along the ray searching for intersection.
// @param parallaxShadowAttenuation - returns a factor for attenuating a light source, for self-shadowing (if enabled via o_parallax_shadow).
ParallaxOffset AdvancedParallaxMapping(float depthFactor, float depthOffset, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, int numSteps, inout float parallaxShadowAttenuation)
{
ParallaxOffset result;
result.m_isClipped = false;
float dirToCameraZInverse = 1.0 / dirToCameraTS.z;
float step = 1.0 / numSteps;
float currentStep = 0.0;
@@ -61,21 +169,38 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
float2 ddx_uv = ddx_fine(uv);
float2 ddy_uv = ddy_fine(uv);
float currentSample = GetDepth(uv, ddx_uv, ddy_uv);
float prevSample;
float3 parallaxOffset = float3(0,0,0);
float depthSearchStart = -depthOffset;
float depthSearchEnd = depthSearchStart + depthFactor;
float inverseDepthFactor = 1.0 / depthFactor;
// find the intersect step
// This is the relative position at which we begin searching for intersection.
// It is adjusted according to the depthOffset, raising or lowering the whole surface by depthOffset units.
float3 parallaxOffset = dirToCameraTS.xyz * dirToCameraZInverse * depthOffset;
// Get an initial heightmap sample to start the intersection search, starting at our initial parallaxOffset position.
float currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
float prevSample;
// Note that when depthOffset < 0, we could actually narrow the search so that instead of going through the entire [depthSearchStart,depthSearchEnd] range
// of the heightmap, we could go through the range [0,depthSearchEnd]. This would give more accurate results and fewer artifacts
// in case where the magnitude of depthOffset is significant. But for the sake of simplicity we currently search the whole range in all cases.
// Do a basic search for the intersect step
while(currentSample > currentStep)
{
currentStep += step;
parallaxOffset += delta;
prevSample = currentSample;
currentSample = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv);
currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
}
// Depending on the algorithm, we refine the result of the above search
switch(o_parallax_algorithm)
{
case ParallaxAlgorithm::Steep:
break; // This algorithm just relies on the course intersection test loop above
case ParallaxAlgorithm::POM:
{
if(currentStep > 0.0)
@@ -108,7 +233,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
parallaxOffset += reliefDelta * depthSign;
currentStep += reliefStep * depthSign;
currentSample = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv);
currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
}
}
break;
@@ -136,7 +261,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
parallaxOffset += adjustedDelta;
prevSample = currentSample;
currentSample = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv);
currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
}
}
break;
@@ -144,6 +269,30 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
default:
break;
}
// Even though we do a bunch of clamping above when calling GetClampedDepth(), there are still cases where the parallax offset
// can be noticeably above the surface and still needs to be clamped here. The main case is when depthFactor==0 and depthOffset>1.
if(parallaxOffset.z > 0.0)
{
parallaxOffset = float3(0,0,0);
}
if (o_parallax_highlightClipping)
{
// The most accurate way to report clipping is to sample the heightmap one last time at the final adjusted UV.
// (trying to do it based on parallaxOffset.z values just leads to too many edge cases)
DepthResult depthResult = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv);
if(DepthResultCode_Normalized == depthResult.m_resultCode)
{
result.m_isClipped = lerp(depthSearchStart, depthSearchEnd, depthResult.m_depth) < 0;
}
else if(DepthResultCode_Absolute == depthResult.m_resultCode)
{
result.m_isClipped = depthResult.m_depth < 0.0;
}
}
if(o_parallax_shadow && any(dirToLightTS))
{
@@ -168,7 +317,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
}
shadowUV += shadowDelta;
currentSample = GetDepth(shadowUV, ddx_uv, ddy_uv);
currentSample = GetNormalizedDepth(depthSearchStart, depthSearchEnd, inverseDepthFactor, shadowUV, ddx_uv, ddy_uv);
currentStep -= step;
}
@@ -181,12 +330,13 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
parallaxShadowAttenuation = 1;
}
}
return parallaxOffset;
result.m_offsetTS = parallaxOffset;
return result;
}
// return offset in tangent space
float3 CalculateParallaxOffset(float depthFactor, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, inout float parallaxShadowAttenuation)
ParallaxOffset CalculateParallaxOffset(float depthFactor, float depthOffset, float2 uv, float3 dirToCameraTS, float3 dirToLightTS, inout float parallaxShadowAttenuation)
{
if(o_parallax_algorithm == ParallaxAlgorithm::Basic)
{
@@ -194,27 +344,34 @@ float3 CalculateParallaxOffset(float depthFactor, float2 uv, float3 dirToCameraT
}
else
{
float3 parallaxOffset;
ParallaxOffset parallaxOffset;
switch(o_parallax_quality)
{
case ParallaxQuality::Low:
parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 16, parallaxShadowAttenuation);
parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 16, parallaxShadowAttenuation);
break;
case ParallaxQuality::Medium:
parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 32, parallaxShadowAttenuation);
parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 32, parallaxShadowAttenuation);
break;
case ParallaxQuality::High:
parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 64, parallaxShadowAttenuation);
parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 64, parallaxShadowAttenuation);
break;
case ParallaxQuality::Ultra:
parallaxOffset = AdvancedParallaxMapping(depthFactor, uv, dirToCameraTS, dirToLightTS, 128, parallaxShadowAttenuation);
parallaxOffset = AdvancedParallaxMapping(depthFactor, depthOffset, uv, dirToCameraTS, dirToLightTS, 128, parallaxShadowAttenuation);
break;
}
return parallaxOffset;
}
}
float3 GetParallaxOffset( float depthFactor,
// Performs ray intersection against a surface with a heightmap, to determine an offset amount required for a parallax effect.
// @param depthFactor - scales the heightmap in tangent space units (which normally ends up being world units).
// @param depthOffset - offsets the heighmap up or down in tangent space units (which normally ends up being world units).
// @param uv - the UV coordinates on the surface, where the search will begin, used to sample the heightmap.
// @param dirToCameraTS - normalized direction to the camera, in tangent space.
// @param dirToLightTS - normalized direction to a light source, in tangent space, for self-shadowing (if enabled via o_parallax_shadow).
ParallaxOffset GetParallaxOffset( float depthFactor,
float depthOffset,
float2 uv,
float3 dirToCameraWS,
float3 tangentWS,
@@ -236,7 +393,7 @@ float3 GetParallaxOffset( float depthFactor,
float4 dirToCameraTransformed = mul(uv3DTransform, float4(dirToCameraTS, 0.0));
float dummy = 1;
return CalculateParallaxOffset(depthFactor, uv, normalize(dirToCameraTransformed.xyz), float3(0,0,0), dummy);
return CalculateParallaxOffset(depthFactor, depthOffset, uv, normalize(dirToCameraTransformed.xyz), float3(0,0,0), dummy);
}
struct PixelDepthOffset
@@ -0,0 +1,26 @@
{
"description": "",
"materialType": "Materials/Types/StandardPBR.materialtype",
"parentMaterial": "",
"propertyLayoutVersion": 3,
"properties": {
"baseColor": {
"textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_bc.png"
},
"opacity": {
"alphaSource": "Split",
"mode": "Cutout",
"textureMap": "TestData/Textures/checker8x8_512.png"
},
"parallax": {
"algorithm": "POM",
"enable": true,
"factor": 0.10000000149011612,
"quality": "High",
"textureMap": "TestData/Textures/TextureHaven/4k_castle_brick_02_red/4k_castle_brick_02_red_disp.png"
},
"uv": {
"scale": 0.5
}
}
}
@@ -121,12 +121,12 @@ void GetSurfaceShape(float2 uv, out float depth, out float3 normal)
}
// Callback function for ParallaxMapping.azsli
float GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy)
{
float depth;
float3 normal;
GetSurfaceShape(uv, depth, normal);
return depth;
return DepthResultNormalized(depth);
}
ForwardPassOutput AutoBrick_ForwardPassPS(VSOutput IN)
@@ -136,15 +136,18 @@ ForwardPassOutput AutoBrick_ForwardPassPS(VSOutput IN)
0,1,0,
0,0,1 };
float3 tangentOffset = GetParallaxOffset( AutoBrickSrg::m_lineDepth,
IN.m_uv,
ViewSrg::m_worldPosition.xyz - IN.m_worldPosition,
IN.m_tangent,
IN.m_bitangent,
IN.m_normal,
identityUvMatrix);
float depthOffset = 0.0;
ParallaxOffset tangentOffset = GetParallaxOffset( AutoBrickSrg::m_lineDepth,
depthOffset,
IN.m_uv,
ViewSrg::m_worldPosition.xyz - IN.m_worldPosition,
IN.m_tangent,
IN.m_bitangent,
IN.m_normal,
identityUvMatrix);
IN.m_uv += tangentOffset.xy;
IN.m_uv += tangentOffset.m_offsetTS.xy;
float3 baseColor = float3(1,1,1);
const float noise = AutoBrickSrg::m_noise.Sample(AutoBrickSrg::m_sampler, IN.m_uv).r;
@@ -101,6 +101,11 @@ AZ::RPI::WindowContextSharedPtr AZ::FFont::GetDefaultWindowContext() const
bool AZ::FFont::InitFont(AZ::RPI::Scene* renderScene)
{
if (!renderScene)
{
return false;
}
auto initializationState = InitializationState::Uninitialized;
// Do an atomic transition to Initializing if we're in the Uninitialized state.
// Otherwise, check the current state.
@@ -111,11 +116,6 @@ bool AZ::FFont::InitFont(AZ::RPI::Scene* renderScene)
return initializationState == InitializationState::Initialized;
}
if (!renderScene)
{
return false;
}
// Create and initialize DynamicDrawContext for font draw
AZ::RPI::Ptr<AZ::RPI::DynamicDrawContext> dynamicDraw = m_atomFont->GetOrCreateDynamicDrawForScene(renderScene);
@@ -206,7 +206,7 @@ namespace LandscapeCanvasEditor
static const QStringList preferredCategories = {
"Vegetation",
"Rendering"
"Atom"
};
// There are a couple of cases where we prefer certain categories of Components
+10 -8
View File
@@ -549,15 +549,17 @@ finally {
message:"${currentBuild.currentResult}:${BUILD_URL}:${env.RECREATE_VOLUME}:${env.CLEAN_OUTPUT_DIRECTORY}:${env.CLEAN_ASSETS}"
)
}
step([
$class: 'Mailer',
notifyEveryUnstableBuild: true,
sendToIndividuals: true,
recipients: emailextrecipients([
[$class: 'CulpritsRecipientProvider'],
[$class: 'RequesterRecipientProvider']
node('controller') {
emailRecipients = [[$class: 'RequesterRecipientProvider']]
if (env.WATCHED_BRANCHES.tokenize(',').contains(branchName)) {
emailRecipients.add([$class: 'CulpritsRecipientProvider'])
}
step([
$class: 'Mailer',
notifyEveryUnstableBuild: true,
recipients: emailextrecipients(emailRecipients)
])
])
}
} catch(Exception e) {
}
}