ATOM-14495 POM Height Bias
Added parallax "Height Offset" properties to StandardPBR and EnhancedPBR. - Refactored depth pass and shadow pass shaders to use the GetParallaxInput utility. - Updated EnhancedPBR.materialtype to match the parallax controls and functors of StandardPBR.materialtype. - Updated EnhancedPBR's shadow pass shader to apply a ShadowMapDepthBias because I noticed StandardPBR is doing this, so I made them match. - Updated StandardPBR and EnhancedPBR to both use the depth offset property, as well as heightmap clipping debug view. - Note that depth offset is not supported in StandardMultilayerPBR yet, it's hard-coded to 0 for now. - Note that I plan to rename a lot of the "depth" terms to "heightmap" or "displacement" in an upcoming commit.
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -90,30 +90,12 @@ PSDepthOutput MainPS(VSDepthOutput 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],
|
||||
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);
|
||||
}
|
||||
return OUT;
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@ 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)
|
||||
@@ -126,9 +128,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];
|
||||
@@ -186,6 +188,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)
|
||||
{
|
||||
baseColor = lerp(baseColor, float3(1.0,0.0,1.0), 0.5);
|
||||
}
|
||||
|
||||
// ------- Metallic -------
|
||||
|
||||
|
||||
@@ -98,35 +98,21 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
|
||||
if(o_parallax_feature_enabled && o_parallax_enablePixelDepthOffset)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
OUT.m_depth += ShadowMapDepthBias;
|
||||
}
|
||||
return OUT;
|
||||
}
|
||||
|
||||
+25
-13
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -126,7 +126,8 @@ 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 parallaxMainDepthOffset = 0.0;
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor, parallaxMainDepthOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
|
||||
|
||||
|
||||
+3
-2
@@ -163,8 +163,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_parallaxMainDepthFactor,
|
||||
|
||||
float parallaxMainDepthOffset = 0.0;
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor, parallaxMainDepthOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
|
||||
|
||||
|
||||
+3
-2
@@ -124,8 +124,9 @@ 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 parallaxMainDepthOffset = 0.0;
|
||||
GetParallaxInput(IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex], MaterialSrg::m_parallaxMainDepthFactor, parallaxMainDepthOffset,
|
||||
ObjectSrg::GetWorldMatrix(), uvMatrix, uvMatrixInverse,
|
||||
IN.m_uv[MaterialSrg::m_parallaxUvIndex], IN.m_worldPosition, depth);
|
||||
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -91,31 +91,15 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace)
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
return OUT;
|
||||
}
|
||||
|
||||
@@ -106,15 +106,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)
|
||||
{
|
||||
|
||||
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)
|
||||
@@ -150,6 +153,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)
|
||||
{
|
||||
baseColor = lerp(baseColor, float3(1.0,0.0,1.0), 0.5);
|
||||
}
|
||||
|
||||
// ------- 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)
|
||||
|
||||
@@ -107,31 +107,14 @@ 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;
|
||||
}
|
||||
return OUT;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ 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;
|
||||
|
||||
//! The client shader must define this function.
|
||||
@@ -34,23 +37,49 @@ float SampleDepthOrHeightMap(bool isHeightmap, Texture2D map, sampler mapSampler
|
||||
return abs((isHeightmap * 1.0) - map.SampleGrad(mapSampler, uv, uv_ddx, uv_ddy).r);
|
||||
}
|
||||
|
||||
float GetClampedDepth(float minSampledDepth, float2 uv, float2 uv_ddx, float2 uv_ddy)
|
||||
{
|
||||
float sampledDepthValue = GetDepth(uv, uv_ddx, uv_ddy);
|
||||
sampledDepthValue = max(sampledDepthValue, minSampledDepth);
|
||||
return sampledDepthValue;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
@@ -60,22 +89,41 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
|
||||
|
||||
float2 ddx_uv = ddx_fine(uv);
|
||||
float2 ddy_uv = ddy_fine(uv);
|
||||
|
||||
// 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;
|
||||
|
||||
// Note that depthOffset can raise the heightmap toward (and potentially above) the surface of the mesh.
|
||||
// We will clamp the heightmap samples to prevent displacements that lie above the surface, which would cause various
|
||||
// problems especially when PDO is enabled, like parallax surfaces clipping through foreground geometry, and parallax
|
||||
// surfaces disappearing at low angles.
|
||||
float minSampledDepth = depthOffset / depthFactor;
|
||||
minSampledDepth = clamp(minSampledDepth, 0, 1);
|
||||
|
||||
float currentSample = GetDepth(uv, ddx_uv, ddy_uv);
|
||||
// Get an initial heightmap sample to start the intersection search, starting at our initial parallaxOffset position.
|
||||
float currentSample = GetClampedDepth(minSampledDepth, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
float prevSample;
|
||||
float3 parallaxOffset = float3(0,0,0);
|
||||
|
||||
// Note that when depthOffset > 0, we could actually narrow the search so that instead of going through the entire [0,1] range
|
||||
// of the heightmap, we could go through the range [minSampledDepth,1]. This would give more accurate results and fewer artifacts
|
||||
// in case where depthOffset is significant. But for the sake of simplicity we currently search the whole range in all cases.
|
||||
|
||||
// find the intersect step
|
||||
// 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 = GetClampedDepth(minSampledDepth, 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 +156,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 = GetClampedDepth(minSampledDepth, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -136,7 +184,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
|
||||
parallaxOffset += adjustedDelta;
|
||||
prevSample = currentSample;
|
||||
|
||||
currentSample = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
currentSample = GetClampedDepth(minSampledDepth, uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -144,6 +192,23 @@ 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)
|
||||
{
|
||||
result.m_isClipped = o_parallax_highlightClipping;
|
||||
parallaxOffset = float3(0,0,0);
|
||||
}
|
||||
// Extra check to report whether the heightmap is clipped. Inaccuracies in the intersection search make it difficult to rely on
|
||||
// parallaxOffset.z to determine whether clipping has occurred. The most accurate way to report clipping is to sample the
|
||||
// heightmap one last time at the final adjusted UV. Since that's expensive, we only do it when the o_parallax_highlightClipping
|
||||
// option is set.
|
||||
else if (o_parallax_highlightClipping)
|
||||
{
|
||||
float sampledDepthValue = GetDepth(uv + parallaxOffset.xy, ddx_uv, ddy_uv);
|
||||
result.m_isClipped = sampledDepthValue < minSampledDepth;
|
||||
}
|
||||
|
||||
if(o_parallax_shadow && any(dirToLightTS))
|
||||
{
|
||||
@@ -168,7 +233,7 @@ float3 AdvancedParallaxMapping(float depthFactor, float2 uv, float3 dirToCameraT
|
||||
}
|
||||
|
||||
shadowUV += shadowDelta;
|
||||
currentSample = GetDepth(shadowUV, ddx_uv, ddy_uv);
|
||||
currentSample = GetClampedDepth(minSampledDepth, shadowUV, ddx_uv, ddy_uv);
|
||||
currentStep -= step;
|
||||
}
|
||||
|
||||
@@ -181,12 +246,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 +260,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 +309,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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user