From 775b7d048b3f740939c308e62ddde6b4c94d858a Mon Sep 17 00:00:00 2001 From: jiaweig Date: Wed, 19 May 2021 00:36:24 -0700 Subject: [PATCH 1/6] Add support for tangent stream pairing with the first UV from the model. --- .../Types/EnhancedPBR_DepthPass_WithPS.azsl | 13 ++- .../Types/EnhancedPBR_ForwardPass.azsl | 17 ++-- .../Types/EnhancedPBR_Shadowmap_WithPS.azsl | 14 +++- .../Common/Assets/Materials/Types/Skin.azsl | 15 ++-- ...tandardMultilayerPBR_DepthPass_WithPS.azsl | 16 ++-- .../StandardMultilayerPBR_ForwardPass.azsl | 15 ++-- ...tandardMultilayerPBR_Shadowmap_WithPS.azsl | 14 +++- .../Types/StandardPBR_DepthPass_WithPS.azsl | 14 +++- .../Types/StandardPBR_ForwardPass.azsl | 15 ++-- .../Types/StandardPBR_Shadowmap_WithPS.azsl | 13 ++- .../Code/Source/Mesh/MeshFeatureProcessor.cpp | 3 +- .../ShaderResourceGroups/DefaultDrawSrg.azsli | 8 +- .../ShaderLib/Atom/RPI/TangentSpace.azsli | 8 +- .../Include/Atom/RPI.Public/Model/ModelLod.h | 42 +++++++++- .../Code/Source/RPI.Public/MeshDrawPacket.cpp | 53 ++++++------ .../Code/Source/RPI.Public/Model/ModelLod.cpp | 81 ++++++++++++++++++- Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp | 28 +++++++ 17 files changed, 288 insertions(+), 81 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl index db99ee7e24..48d919ccab 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl @@ -77,10 +77,15 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) 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); + // We support two UV streams, but only a single stream of tangent/bitangent. + // By default, the first UV stream is applied and the default tangent/bitangent are used. + // If anything uses the second UV stream, and it is not a duplication of the first stream, + // generated tangent/bitangent will be applied. + // (As it implies, cases may occur where all/none of the UV steams use the default TB.) + // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl index a91e88afad..17bf7568a0 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl @@ -118,18 +118,21 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float { // ------- Tangents & Bitangets ------- - // 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) }; + // We support two UV streams, but only a single stream of tangent/bitangent. + // By default, the first UV stream is applied and the default tangent/bitangent are used. + // If anything uses the second UV stream, and it is not a duplication of the first stream, + // generated tangent/bitangent will be applied. + // (As it implies, cases may occur where all/none of the UV steams use the default TB.) + // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) || (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) || (o_clearCoat_enabled && o_clearCoat_normal_useTexture && MaterialSrg::m_clearCoatNormalMapUvIndex != 0) || (o_detail_normal_useTexture && MaterialSrg::m_detail_allMapsUvIndex != 0)) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Depth & Parallax ------- @@ -260,7 +263,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // Convert the angle from [0..1] = [0 .. 180 degrees] to radians [0 .. PI] const float anisotropyAngle = MaterialSrg::m_anisotropicAngle * PI; const float anisotropyFactor = MaterialSrg::m_anisotropicFactor; - surface.anisotropy.Init(surface.normal, tangents[0], bitangents[0], anisotropyAngle, anisotropyFactor, surface.roughnessA); + surface.anisotropy.Init(surface.normal, IN.m_tangent, IN.m_bitangent, anisotropyAngle, anisotropyFactor, surface.roughnessA); } // ------- Lighting Data ------- diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl index 80aedcd6f4..a4665ccec8 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl @@ -80,10 +80,16 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { 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); + // We support two UV streams, but only a single stream of tangent/bitangent. + // By default, the first UV stream is applied and the default tangent/bitangent are used. + // If anything uses the second UV stream, and it is not a duplication of the first stream, + // generated tangent/bitangent will be applied. + // (As it implies, cases may occur where all/none of the UV steams use the default TB.) + // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl index 84095ac163..0d0be496d6 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl @@ -181,16 +181,19 @@ PbrLightingOutput SkinPS_Common(VSOutput IN) // ------- Tangents & Bitangets ------- - // 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) }; + // We support two UV streams, but only a single stream of tangent/bitangent. + // By default, the first UV stream is applied and the default tangent/bitangent are used. + // If anything uses the second UV stream, and it is not a duplication of the first stream, + // generated tangent/bitangent will be applied. + // (As it implies, cases may occur where all/none of the UV steams use the default TB.) + // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; if ( (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) || (o_detail_normal_useTexture && MaterialSrg::m_detail_allMapsUvIndex != 0)) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } Surface surface; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl index ae156d7313..2517205bac 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl @@ -103,11 +103,17 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) 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); - + // We support two UV streams, but only a single stream of tangent/bitangent. + // By default, the first UV stream is applied and the default tangent/bitangent are used. + // If anything uses the second UV stream, and it is not a duplication of the first stream, + // generated tangent/bitangent will be applied. + // (As it implies, cases may occur where all/none of the UV steams use the default TB.) + // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); + GetDepth_Setup(IN.m_blendMask); float depth; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index bc32aa1370..9d02891fd5 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -136,9 +136,14 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Tangents & Bitangets ------- - // 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) }; + // We support two UV streams, but only a single stream of tangent/bitangent. + // By default, the first UV stream is applied and the default tangent/bitangent are used. + // If anything uses the second UV stream, and it is not a duplication of the first stream, + // generated tangent/bitangent will be applied. + // (As it implies, cases may occur where all/none of the UV steams use the default TB.) + // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) || (o_layer1_o_normal_useTexture && MaterialSrg::m_layer1_m_normalMapUvIndex != 0) @@ -149,9 +154,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float || (o_layer3_o_clearCoat_normal_useTexture && MaterialSrg::m_layer3_m_clearCoatNormalMapUvIndex != 0) ) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Debug Modes ------- diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl index c76dd15975..d0bbf0c0a1 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl @@ -102,10 +102,16 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) 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); + // We support two UV streams, but only a single stream of tangent/bitangent. + // By default, the first UV stream is applied and the default tangent/bitangent are used. + // If anything uses the second UV stream, and it is not a duplication of the first stream, + // generated tangent/bitangent will be applied. + // (As it implies, cases may occur where all/none of the UV steams use the default TB.) + // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); GetDepth_Setup(IN.m_blendMask); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl index 28708c12d5..619feab204 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl @@ -78,10 +78,16 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) 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); + // We support two UV streams, but only a single stream of tangent/bitangent. + // By default, the first UV stream is applied and the default tangent/bitangent are used. + // If anything uses the second UV stream, and it is not a duplication of the first stream, + // generated tangent/bitangent will be applied. + // (As it implies, cases may occur where all/none of the UV steams use the default TB.) + // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl index f362349a7b..7a12a5e854 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl @@ -109,18 +109,21 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float { // ------- Tangents & Bitangets ------- - // 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) }; + // We support two UV streams, but only a single stream of tangent/bitangent. + // By default, the first UV stream is applied and the default tangent/bitangent are used. + // If anything uses the second UV stream, and it is not a duplication of the first stream, + // generated tangent/bitangent will be applied. + // (As it implies, cases may occur where all/none of the UV steams use the default TB.) + // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) || (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) || (o_clearCoat_enabled && o_clearCoat_normal_useTexture && MaterialSrg::m_clearCoatNormalMapUvIndex != 0) ) { - // Generate the tangent/bitangent for UV[1+] - const int startIndex = 1; - PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, startIndex); + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } // ------- Depth & Parallax ------- diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl index 7f24b29700..51533090d0 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl @@ -81,10 +81,15 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { 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); + // We support two UV streams, but only a single stream of tangent/bitangent. + // By default, the first UV stream is applied and the default tangent/bitangent are used. + // If anything uses the second UV stream, and it is not a duplication of the first stream, + // generated tangent/bitangent will be applied. + // (As it implies, cases may occur where all/none of the UV steams use the default TB.) + // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. + float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; + float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; + PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 29f0636e9e..d3f9ea2b77 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -728,7 +728,8 @@ namespace AZ // retrieve vertex/index buffers RPI::ModelLod::StreamBufferViewList streamBufferViews; - [[maybe_unused]] bool result = modelLod->GetStreamsForMesh(inputStreamLayout, streamBufferViews, shaderInputContract, meshIndex); + AZ::RPI::UvStreamTangentIndex dummyUvStreamTangentIndex; + [[maybe_unused]] bool result = modelLod->GetStreamsForMesh(inputStreamLayout, streamBufferViews, dummyUvStreamTangentIndex, shaderInputContract, meshIndex); AZ_Assert(result, "Failed to retrieve mesh stream buffer views"); // note that the element count is the size of the entire buffer, even though this mesh may only diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli index 69381ca0fc..ab2c2078db 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli @@ -16,7 +16,13 @@ ShaderResourceGroup DrawSrg : SRG_PerDraw { - float4 m_placeholder; // [GFX-TODO] [Atom-1727] Bug in AZSLc, empty SRGs cannot be shader variant fallbacks! // This SRG is unique per draw packet + + uint m_uvStreamTangentIndex; + + uint GetTangentIndexAtUv(uint uvIndex) + { + return 0xF;//(m_uvStreamTangentIndex >> (4 * uvIndex)) & 0xF; + } } diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli index 13e3c652db..4eeb13500d 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli @@ -190,12 +190,16 @@ void SurfaceGradientNormalMapping_GenerateTB(float2 uv, out float3 tangentWS, ou } //! Utility macro to nest SGBNM setup processes. -#define PrepareGeneratedTangent(normal, worldPos, isFrontFace, uvSets, uvSetCount, outTangents, outBitangents, startIndex) \ +#define PrepareGeneratedTangent(normal, worldPos, isFrontFace, uvSets, uvSetCount, outTangents, outBitangents) \ { \ SurfaceGradientNormalMapping_Init(normal, worldPos, !isFrontFace); \ [unroll] \ - for (int i = startIndex; i < uvSetCount; ++i) \ + for (uint i = 0; i < uvSetCount; ++i) \ { \ + if (DrawSrg::GetTangentIndexAtUv(i) == 0) \ + { \ + continue; \ + } \ SurfaceGradientNormalMapping_GenerateTB(uvSets[i], outTangents[i], outBitangents[i]); \ } \ } diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h index 285cf80aca..a69aaac6ed 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h @@ -34,6 +34,8 @@ namespace AZ //! A map matches the UV shader inputs of this material to the custom UV names from the model. using MaterialModelUvOverrideMap = AZStd::unordered_map; + class UvStreamTangentIndex; + class ModelLod final : public Data::InstanceData { @@ -115,6 +117,7 @@ namespace AZ bool GetStreamsForMesh( RHI::InputStreamLayout& layoutOut, ModelLod::StreamBufferViewList& streamBufferViewsOut, + UvStreamTangentIndex& uvStreamTangentIndexOut, const ShaderInputContract& contract, size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap = {}, @@ -130,6 +133,8 @@ namespace AZ const ModelLodAsset::Mesh::StreamBufferInfo& streamBufferInfo, Mesh& meshInstance); + StreamInfoList::const_iterator FindFirstUvStreamFromMesh(size_t meshIndex) const; + StreamInfoList::const_iterator FindDefaultUvStream(size_t meshIndex, const MaterialUvNameMap& materialUvNameMap) const; // Finds a mesh vertex input stream that is the best match for a contracted stream channel. @@ -137,12 +142,16 @@ namespace AZ // @param materialModelUvMap a map of UV name overrides, which can be supplied to bind a specific mesh stream name to a different material shader stream name. // @param materialUvNameMap the UV name map that came from a MaterialTypeAsset, which defines the default set of material shader stream names. // @param defaultUv the default UV stream to use if a matching UV stream could not be found. Use FindDefaultUvStream() to populate this. + // @param firstUv the first UV stream from the mesh, which, by design, the tangent/bitangent stream belongs to. + // @param uvStreamTangentIndex a bitset indicating which tangent/bitangent stream (including generated ones) a UV stream will be using. StreamInfoList::const_iterator FindMatchingStream( size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap, const MaterialUvNameMap& materialUvNameMap, const ShaderInputContract::StreamChannelInfo& contractStreamChannel, - StreamInfoList::const_iterator defaultUv) const; + StreamInfoList::const_iterator defaultUv, + StreamInfoList::const_iterator firstUv, + UvStreamTangentIndex& uvStreamTangentIndexOut) const; // Meshes may share index/stream buffers in an LOD or they may have // unique buffers. Often the asset builder will prioritize shared buffers @@ -165,5 +174,36 @@ namespace AZ AZStd::mutex m_callbackMutex; }; + + //! An encoded bitset for tangent used by a UV stream. + //! It will be passed through DefaultDrawSrg. + class UvStreamTangentIndex + { + public: + uint32_t GetFullFlag() const; + uint32_t GetNextAvailableUvIndex() const; + uint32_t GetTangentIndexAtUv(uint32_t uvIndex) const; + + void ApplyTangentIndex(uint32_t tangentIndex); + + void Reset(); + + // The flag indicating generated tangent/bitangent will be used. + static constexpr uint32_t UnassignedTangentIndex = 0b1111u; + + private: + // Flag composition: + // The next available slot index (highest 4 bits) + tangent index (4 bits each) * 7 + // e.g. 0x200000F0 means there are 2 UV streams, + // the first UV stream uses 0th tangent stream, + // the second UV stream uses the generated tangent stream (0xF). + uint32_t m_flag = 0; + + static constexpr uint32_t BitsPerTangentIndex = 4; + static constexpr uint32_t BitsForUvIndex = 4; + + public: + static constexpr uint32_t MaxTangents = (sizeof(m_flag) * CHAR_BIT - BitsForUvIndex) / BitsPerTangentIndex; + }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp index d0a277304e..e6fe8251c7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp @@ -169,7 +169,7 @@ namespace AZ const AZ::Data::Asset& drawSrgAsset = shader->GetAsset()->GetDrawSrgAsset(); // Set all unspecified shader options to default values, so that we get the most specialized variant possible. - // (because FindVariantStableId treats unspecified options as a request specificlly for a variant that doesn't specify those options) + // (because FindVariantStableId treats unspecified options as a request specifically for a variant that doesn't specify those options) // [GFX TODO][ATOM-3883] We should consider updating the FindVariantStableId algorithm to handle default values for us, and remove this step here. RPI::ShaderOptionGroup shaderOptions = *shaderItem.GetShaderOptions(); shaderOptions.SetUnspecifiedToDefaultValues(); @@ -198,6 +198,31 @@ namespace AZ const ShaderVariantId finalVariantId = shaderOptions.GetShaderVariantId(); const ShaderVariant& variant = r_forceRootShaderVariantUsage ? shader->GetRootVariant() : shader->GetVariant(finalVariantId); + RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; + variant.ConfigurePipelineState(pipelineStateDescriptor); + + // Render states need to merge the runtime variation. + // This allows materials to customize the render states that the shader uses. + const RHI::RenderStates& renderStatesOverlay = *shaderItem.GetRenderStatesOverlay(); + RHI::MergeStateInto(renderStatesOverlay, pipelineStateDescriptor.m_renderStates); + + streamBufferViewsPerShader.push_back(); + auto& streamBufferViews = streamBufferViewsPerShader.back(); + + UvStreamTangentIndex uvStreamTangentIndex; + + if (!m_modelLod->GetStreamsForMesh( + pipelineStateDescriptor.m_inputStreamLayout, + streamBufferViews, + uvStreamTangentIndex, + variant.GetInputContract(), + m_modelLodMeshIndex, + m_materialModelUvMap, + m_material->GetAsset()->GetMaterialTypeAsset()->GetUvNameMap())) + { + return false; + } + Data::Instance drawSrg; if (drawSrgAsset) { @@ -210,31 +235,13 @@ namespace AZ drawSrg->SetShaderVariantKeyFallbackValue(shaderOptions.GetShaderVariantKeyFallbackValue()); } + RHI::ShaderInputNameIndex shaderUvStreamTangentIndex = "m_uvStreamTangentIndex"; + + drawSrg->SetConstant(shaderUvStreamTangentIndex, uvStreamTangentIndex.GetFullFlag()); + drawSrg->Compile(); } - RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor; - variant.ConfigurePipelineState(pipelineStateDescriptor); - - // Render states need to merge the runtime variation. - // This allows materials to customize the render states that the shader uses. - const RHI::RenderStates& renderStatesOverlay = *shaderItem.GetRenderStatesOverlay(); - RHI::MergeStateInto(renderStatesOverlay, pipelineStateDescriptor.m_renderStates); - - streamBufferViewsPerShader.push_back(); - auto& streamBufferViews = streamBufferViewsPerShader.back(); - - if (!m_modelLod->GetStreamsForMesh( - pipelineStateDescriptor.m_inputStreamLayout, - streamBufferViews, - variant.GetInputContract(), - m_modelLodMeshIndex, - m_materialModelUvMap, - m_material->GetAsset()->GetMaterialTypeAsset()->GetUvNameMap())) - { - return false; - } - // Use the default draw list tag from the shader variant. RHI::DrawListTag drawListTag = shader->GetDrawListTag(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp index 8747dac663..0d064142df 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp @@ -117,6 +117,17 @@ namespace AZ return RHI::ResultCode::Success; } + ModelLod::StreamInfoList::const_iterator ModelLod::FindFirstUvStreamFromMesh(size_t meshIndex) const + { + const Mesh& mesh = m_meshes[meshIndex]; + + auto firstUv = AZStd::find_if(mesh.m_streamInfo.begin(), mesh.m_streamInfo.end(), [](const StreamBufferInfo& info) { + return info.m_semantic.m_name.GetStringView().starts_with(RHI::ShaderSemantic::UvStreamSemantic); + }); + + return firstUv; + } + ModelLod::StreamInfoList::const_iterator ModelLod::FindDefaultUvStream(size_t meshIndex, const MaterialUvNameMap& materialUvNameMap) const { const Mesh& mesh = m_meshes[meshIndex]; @@ -160,7 +171,9 @@ namespace AZ const MaterialModelUvOverrideMap& materialModelUvMap, const MaterialUvNameMap& materialUvNameMap, const ShaderInputContract::StreamChannelInfo& contractStreamChannel, - StreamInfoList::const_iterator defaultUv) const + StreamInfoList::const_iterator defaultUv, + StreamInfoList::const_iterator firstUv, + UvStreamTangentIndex& uvStreamTangentIndexOut) const { const Mesh& mesh = m_meshes[meshIndex]; auto iter = mesh.m_streamInfo.end(); @@ -229,12 +242,18 @@ namespace AZ iter = defaultUv; } + if (IsUv) + { + uvStreamTangentIndexOut.ApplyTangentIndex(iter == firstUv ? 0 : UvStreamTangentIndex::UnassignedTangentIndex); + } + return iter; } bool ModelLod::GetStreamsForMesh( RHI::InputStreamLayout& layoutOut, StreamBufferViewList& streamBufferViewsOut, + UvStreamTangentIndex& uvStreamTangentIndexOut, const ShaderInputContract& contract, size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap, @@ -250,11 +269,14 @@ namespace AZ bool success = true; + // Searching for the first UV in the mesh, so it can be used to paired with tangent/bitangent stream + auto firstUv = FindFirstUvStreamFromMesh(meshIndex); auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap); + uvStreamTangentIndexOut.Reset(); for (auto& contractStreamChannel : contract.m_streamChannels) { - auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv); + auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, uvStreamTangentIndexOut); if (iter == mesh.m_streamInfo.end()) { @@ -340,6 +362,8 @@ namespace AZ const Mesh& mesh = m_meshes[meshIndex]; auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap); + auto firstUv = FindFirstUvStreamFromMesh(meshIndex); + UvStreamTangentIndex dummyUvStreamTangentIndex; for (auto& contractStreamChannel : contract.m_streamChannels) { @@ -350,7 +374,7 @@ namespace AZ AZ_Assert(contractStreamChannel.m_streamBoundIndicatorIndex.IsValid(), "m_streamBoundIndicatorIndex was invalid for an optional shader input stream"); - auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv); + auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, dummyUvStreamTangentIndex); ShaderOptionValue isStreamBound = (iter == mesh.m_streamInfo.end()) ? ShaderOptionValue{0} : ShaderOptionValue{1}; shaderOptions.SetValue(contractStreamChannel.m_streamBoundIndicatorIndex, isStreamBound); @@ -413,5 +437,56 @@ namespace AZ m_buffers.emplace_back(buffer); return static_cast(m_buffers.size() - 1); } + + uint32_t UvStreamTangentIndex::GetFullFlag() const + { + return m_flag; + } + + uint32_t UvStreamTangentIndex::GetNextAvailableUvIndex() const + { + return m_flag >> (sizeof(m_flag) * CHAR_BIT - BitsForUvIndex); + } + + uint32_t UvStreamTangentIndex::GetTangentIndexAtUv(uint32_t uvIndex) const + { + return (m_flag >> (BitsPerTangentIndex * uvIndex)) & 0b1111u; + } + + void UvStreamTangentIndex::ApplyTangentIndex(uint32_t tangentIndex) + { + uint32_t currentSlot = GetNextAvailableUvIndex(); + if (currentSlot >= MaxTangents) + { + AZ_Error("UV Stream", false, "Reaching the max of avaiblable stream slots."); + return; + } + + if (tangentIndex > UnassignedTangentIndex) + { + AZ_Warning( + "UV Stream", false, + "Tangent index must use %d bits as defined in UvStreamTangentIndex::m_flag. Unassigned index will be applied.", + BitsPerTangentIndex); + tangentIndex = UnassignedTangentIndex; + } + + uint32_t mask = 0b1111u << (BitsPerTangentIndex * currentSlot); + mask = ~mask; + + // Clear the writing bits in case + m_flag &= mask; + + // Write the bits to the slot + m_flag |= (tangentIndex << (BitsPerTangentIndex * currentSlot)); + + // Increase the index + m_flag += (1u << (sizeof(m_flag) * CHAR_BIT - BitsForUvIndex)); + } + + void UvStreamTangentIndex::Reset() + { + m_flag = 0; + } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp index 980a9ac320..640a6b406a 100644 --- a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -917,6 +918,33 @@ namespace UnitTest } } + TEST_F(ModelTests, UvStream) + { + AZ::RPI::UvStreamTangentIndex uvStreamTangentIndex; + EXPECT_EQ(uvStreamTangentIndex.GetFullFlag(), 0u); + + uvStreamTangentIndex.ApplyTangentIndex(1u); + EXPECT_EQ(uvStreamTangentIndex.GetTangentIndexAtUv(0u), 1u); + EXPECT_EQ(uvStreamTangentIndex.GetNextAvailableUvIndex(), 1u); + + uvStreamTangentIndex.ApplyTangentIndex(5u); + EXPECT_EQ(uvStreamTangentIndex.GetTangentIndexAtUv(1u), 5u); + EXPECT_EQ(uvStreamTangentIndex.GetNextAvailableUvIndex(), 2u); + + uvStreamTangentIndex.ApplyTangentIndex(100u); + EXPECT_EQ(uvStreamTangentIndex.GetTangentIndexAtUv(2u), AZ::RPI::UvStreamTangentIndex::UnassignedTangentIndex); + EXPECT_EQ(uvStreamTangentIndex.GetNextAvailableUvIndex(), 3u); + + for (uint32_t i = 3; i < AZ::RPI::UvStreamTangentIndex::MaxTangents; ++i) + { + uvStreamTangentIndex.ApplyTangentIndex(0u); + } + + AZ_TEST_START_TRACE_SUPPRESSION; + uvStreamTangentIndex.ApplyTangentIndex(0u); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + } + // This class creates a Model with one LOD, whose mesh contains 2 planes. Plane 1 is in the XY plane at Z=-0.5, and // plane 2 is in the XY plane at Z=0.5. The two planes each have 9 quads which have been triangulated. It only has // a position and index buffer. From f8608ff351a38039be111c0f61adb48945efd7d3 Mon Sep 17 00:00:00 2001 From: jiaweig Date: Wed, 19 May 2021 00:42:37 -0700 Subject: [PATCH 2/6] Remove debug code --- .../Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli index ab2c2078db..33bfc85e4d 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli @@ -22,7 +22,7 @@ ShaderResourceGroup DrawSrg : SRG_PerDraw uint GetTangentIndexAtUv(uint uvIndex) { - return 0xF;//(m_uvStreamTangentIndex >> (4 * uvIndex)) & 0xF; + return m_uvStreamTangentIndex >> (4 * uvIndex)) & 0xF; } } From c3794ca96c95ef079685f9fdc4ffde342423605b Mon Sep 17 00:00:00 2001 From: jiaweig Date: Thu, 20 May 2021 19:11:31 -0700 Subject: [PATCH 3/6] Addressed review comments. --- .../Types/EnhancedPBR_DepthPass_WithPS.azsl | 6 -- .../Types/EnhancedPBR_ForwardPass.azsl | 12 +--- .../Types/EnhancedPBR_Shadowmap_WithPS.azsl | 6 -- .../Common/Assets/Materials/Types/Skin.azsl | 10 +-- .../Assets/Materials/Types/Skin_Common.azsli | 1 - ...tandardMultilayerPBR_DepthPass_WithPS.azsl | 6 -- .../StandardMultilayerPBR_ForwardPass.azsl | 21 +++---- ...tandardMultilayerPBR_Shadowmap_WithPS.azsl | 6 -- .../Types/StandardPBR_DepthPass_WithPS.azsl | 6 -- .../Types/StandardPBR_ForwardPass.azsl | 12 +--- .../Types/StandardPBR_Shadowmap_WithPS.azsl | 6 -- .../Code/Source/Mesh/MeshFeatureProcessor.cpp | 3 +- .../Platform/Windows/platform_windows.cmake | 2 +- .../ShaderResourceGroups/DefaultDrawSrg.azsli | 7 +-- .../ShaderLib/Atom/RPI/TangentSpace.azsli | 8 ++- .../Include/Atom/RPI.Public/MeshDrawPacket.h | 2 +- .../Include/Atom/RPI.Public/Model/ModelLod.h | 59 ++++++++++++------ .../Code/Source/RPI.Public/MeshDrawPacket.cpp | 15 +++-- .../Code/Source/RPI.Public/Model/ModelLod.cpp | 62 ++++++++++--------- Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp | 38 +++++++----- 20 files changed, 129 insertions(+), 159 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl index 48d919ccab..644473fef9 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_DepthPass_WithPS.azsl @@ -77,12 +77,6 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. - // By default, the first UV stream is applied and the default tangent/bitangent are used. - // If anything uses the second UV stream, and it is not a duplication of the first stream, - // generated tangent/bitangent will be applied. - // (As it implies, cases may occur where all/none of the UV steams use the default TB.) - // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl index 17bf7568a0..440bb97c4e 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl @@ -117,20 +117,10 @@ VSOutput EnhancedPbr_ForwardPassVS(VSInput IN) PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depth) { // ------- Tangents & Bitangets ------- - - // We support two UV streams, but only a single stream of tangent/bitangent. - // By default, the first UV stream is applied and the default tangent/bitangent are used. - // If anything uses the second UV stream, and it is not a duplication of the first stream, - // generated tangent/bitangent will be applied. - // (As it implies, cases may occur where all/none of the UV steams use the default TB.) - // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; - if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) - || (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) - || (o_clearCoat_enabled && o_clearCoat_normal_useTexture && MaterialSrg::m_clearCoatNormalMapUvIndex != 0) - || (o_detail_normal_useTexture && MaterialSrg::m_detail_allMapsUvIndex != 0)) + if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering) || o_normal_useTexture || (o_clearCoat_enabled && o_clearCoat_normal_useTexture) || o_detail_normal_useTexture) { PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl index a4665ccec8..7f6be252e2 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Shadowmap_WithPS.azsl @@ -80,12 +80,6 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { static const float ShadowMapDepthBias = 0.000001; - // We support two UV streams, but only a single stream of tangent/bitangent. - // By default, the first UV stream is applied and the default tangent/bitangent are used. - // If anything uses the second UV stream, and it is not a duplication of the first stream, - // generated tangent/bitangent will be applied. - // (As it implies, cases may occur where all/none of the UV steams use the default TB.) - // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl index 0d0be496d6..46c4251a32 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl @@ -180,18 +180,10 @@ PbrLightingOutput SkinPS_Common(VSOutput IN) float3x3 uvMatrix = CreateIdentity3x3(); // ------- Tangents & Bitangets ------- - - // We support two UV streams, but only a single stream of tangent/bitangent. - // By default, the first UV stream is applied and the default tangent/bitangent are used. - // If anything uses the second UV stream, and it is not a duplication of the first stream, - // generated tangent/bitangent will be applied. - // (As it implies, cases may occur where all/none of the UV steams use the default TB.) - // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; - if ( (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) || - (o_detail_normal_useTexture && MaterialSrg::m_detail_allMapsUvIndex != 0)) + if (o_normal_useTexture || o_detail_normal_useTexture) { PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Common.azsli index 20bf7c1f2a..90546055e6 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Common.azsli @@ -14,7 +14,6 @@ #include #include -#include #include "MaterialInputs/BaseColorInput.azsli" #include "MaterialInputs/RoughnessInput.azsli" diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl index 2517205bac..1ee7532f96 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl @@ -103,12 +103,6 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. - // By default, the first UV stream is applied and the default tangent/bitangent are used. - // If anything uses the second UV stream, and it is not a duplication of the first stream, - // generated tangent/bitangent will be applied. - // (As it implies, cases may occur where all/none of the UV steams use the default TB.) - // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index 9d02891fd5..3bca68f946 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -135,23 +135,16 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depthNDC = IN.m_position.z; // ------- Tangents & Bitangets ------- - - // We support two UV streams, but only a single stream of tangent/bitangent. - // By default, the first UV stream is applied and the default tangent/bitangent are used. - // If anything uses the second UV stream, and it is not a duplication of the first stream, - // generated tangent/bitangent will be applied. - // (As it implies, cases may occur where all/none of the UV steams use the default TB.) - // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; - if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) - || (o_layer1_o_normal_useTexture && MaterialSrg::m_layer1_m_normalMapUvIndex != 0) - || (o_layer2_o_normal_useTexture && MaterialSrg::m_layer2_m_normalMapUvIndex != 0) - || (o_layer3_o_normal_useTexture && MaterialSrg::m_layer3_m_normalMapUvIndex != 0) - || (o_layer1_o_clearCoat_normal_useTexture && MaterialSrg::m_layer1_m_clearCoatNormalMapUvIndex != 0) - || (o_layer2_o_clearCoat_normal_useTexture && MaterialSrg::m_layer2_m_clearCoatNormalMapUvIndex != 0) - || (o_layer3_o_clearCoat_normal_useTexture && MaterialSrg::m_layer3_m_clearCoatNormalMapUvIndex != 0) + if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering) + || o_layer1_o_normal_useTexture + || o_layer2_o_normal_useTexture + || o_layer3_o_normal_useTexture + || o_layer1_o_clearCoat_normal_useTexture + || o_layer2_o_clearCoat_normal_useTexture + || o_layer3_o_clearCoat_normal_useTexture ) { PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl index d0bbf0c0a1..113c7ce50f 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl @@ -102,12 +102,6 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. - // By default, the first UV stream is applied and the default tangent/bitangent are used. - // If anything uses the second UV stream, and it is not a duplication of the first stream, - // generated tangent/bitangent will be applied. - // (As it implies, cases may occur where all/none of the UV steams use the default TB.) - // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl index 619feab204..afc93f060e 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_DepthPass_WithPS.azsl @@ -78,12 +78,6 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) if(ShouldHandleParallaxInDepthShaders()) { - // We support two UV streams, but only a single stream of tangent/bitangent. - // By default, the first UV stream is applied and the default tangent/bitangent are used. - // If anything uses the second UV stream, and it is not a duplication of the first stream, - // generated tangent/bitangent will be applied. - // (As it implies, cases may occur where all/none of the UV steams use the default TB.) - // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl index 7a12a5e854..cf308fb1b5 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl @@ -108,20 +108,10 @@ VSOutput StandardPbr_ForwardPassVS(VSInput IN) PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depthNDC) { // ------- Tangents & Bitangets ------- - - // We support two UV streams, but only a single stream of tangent/bitangent. - // By default, the first UV stream is applied and the default tangent/bitangent are used. - // If anything uses the second UV stream, and it is not a duplication of the first stream, - // generated tangent/bitangent will be applied. - // (As it implies, cases may occur where all/none of the UV steams use the default TB.) - // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; - if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering && MaterialSrg::m_parallaxUvIndex != 0) - || (o_normal_useTexture && MaterialSrg::m_normalMapUvIndex != 0) - || (o_clearCoat_enabled && o_clearCoat_normal_useTexture && MaterialSrg::m_clearCoatNormalMapUvIndex != 0) - ) + if ((o_parallax_feature_enabled && !o_enableSubsurfaceScattering) || o_normal_useTexture || (o_clearCoat_enabled && o_clearCoat_normal_useTexture)) { PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl index 51533090d0..533df3bb92 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Shadowmap_WithPS.azsl @@ -81,12 +81,6 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) { static const float ShadowMapDepthBias = 0.000001; - // We support two UV streams, but only a single stream of tangent/bitangent. - // By default, the first UV stream is applied and the default tangent/bitangent are used. - // If anything uses the second UV stream, and it is not a duplication of the first stream, - // generated tangent/bitangent will be applied. - // (As it implies, cases may occur where all/none of the UV steams use the default TB.) - // Whether a UV stream can use the tangent/bitangent are encoded in DrawSrg. float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz }; float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz }; PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents); diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index d3f9ea2b77..35f85d6838 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -728,8 +728,7 @@ namespace AZ // retrieve vertex/index buffers RPI::ModelLod::StreamBufferViewList streamBufferViews; - AZ::RPI::UvStreamTangentIndex dummyUvStreamTangentIndex; - [[maybe_unused]] bool result = modelLod->GetStreamsForMesh(inputStreamLayout, streamBufferViews, dummyUvStreamTangentIndex, shaderInputContract, meshIndex); + [[maybe_unused]] bool result = modelLod->GetStreamsForMesh(inputStreamLayout, streamBufferViews, nullptr, shaderInputContract, meshIndex); AZ_Assert(result, "Failed to retrieve mesh stream buffer views"); // note that the element count is the size of the entire buffer, even though this mesh may only diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake b/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake index b12b5de9ce..8baaa1ab90 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake +++ b/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake @@ -18,5 +18,5 @@ set(LY_BUILD_DEPENDENCIES # [GFX-TODO] Add macro defintion in OpenImageIO 3rd party find cmake file set(LY_COMPILE_DEFINITIONS PRIVATE - OPEN_IMAGE_IO_ENABLED + #OPEN_IMAGE_IO_ENABLED ) diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli index 33bfc85e4d..e12736dfec 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli @@ -17,12 +17,11 @@ ShaderResourceGroup DrawSrg : SRG_PerDraw { // This SRG is unique per draw packet + uint m_uvStreamTangentBitmask; - uint m_uvStreamTangentIndex; - - uint GetTangentIndexAtUv(uint uvIndex) + uint GetTangentAtUv(uint uvIndex) { - return m_uvStreamTangentIndex >> (4 * uvIndex)) & 0xF; + return (m_uvStreamTangentBitmask >> (4 * uvIndex)) & 0xF; } } diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli index 4eeb13500d..7e4dc0fd22 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli @@ -190,13 +190,19 @@ void SurfaceGradientNormalMapping_GenerateTB(float2 uv, out float3 tangentWS, ou } //! Utility macro to nest SGBNM setup processes. +//! We support two UV streams, but only a single stream of tangent/bitangent. +//! By default, the first UV stream is applied and the default tangent/bitangent are used. +//! If anything uses the second UV stream, and it is not a duplication of the first stream, +//! generated tangent/bitangent will be applied. +//! (As it implies, cases may occur where all/none of the UV steams use the default TB.) +//! What tangent/bitangent a UV stream uses is encoded in MaterialDrawSrg. #define PrepareGeneratedTangent(normal, worldPos, isFrontFace, uvSets, uvSetCount, outTangents, outBitangents) \ { \ SurfaceGradientNormalMapping_Init(normal, worldPos, !isFrontFace); \ [unroll] \ for (uint i = 0; i < uvSetCount; ++i) \ { \ - if (DrawSrg::GetTangentIndexAtUv(i) == 0) \ + if (DrawSrg::GetTangentAtUv(i) == 0) \ { \ continue; \ } \ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h index 0aa979d611..70cabca371 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/MeshDrawPacket.h @@ -98,7 +98,7 @@ namespace AZ //! List of shader options set for this specific draw packet typedef AZStd::pair ShaderOptionPair; typedef AZStd::vector ShaderOptionVector; - ShaderOptionVector m_shaderOptions; + ShaderOptionVector m_shaderOptions; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h index a69aaac6ed..bb3bfe52e7 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h @@ -34,7 +34,7 @@ namespace AZ //! A map matches the UV shader inputs of this material to the custom UV names from the model. using MaterialModelUvOverrideMap = AZStd::unordered_map; - class UvStreamTangentIndex; + class UvStreamTangentBitmask; class ModelLod final : public Data::InstanceData @@ -110,6 +110,7 @@ namespace AZ const MaterialUvNameMap& materialUvNameMap = {}) const; //! Fills a InputStreamLayout and StreamBufferViewList for the set of streams that satisfy a ShaderInputContract. + // @param uvStreamTangentBitmaskOut a mask processed during UV stream matching, and later to determine which tangent/bitangent stream to use. // @param contract the contract that defines the expected inputs for a shader, used to determine which streams are optional. // @param meshIndex the index of the mesh to search in. // @param materialModelUvMap a map of UV name overrides, which can be supplied to bind a specific mesh stream name to a different material shader stream name. @@ -117,7 +118,7 @@ namespace AZ bool GetStreamsForMesh( RHI::InputStreamLayout& layoutOut, ModelLod::StreamBufferViewList& streamBufferViewsOut, - UvStreamTangentIndex& uvStreamTangentIndexOut, + UvStreamTangentBitmask* uvStreamTangentBitmaskOut, const ShaderInputContract& contract, size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap = {}, @@ -151,7 +152,7 @@ namespace AZ const ShaderInputContract::StreamChannelInfo& contractStreamChannel, StreamInfoList::const_iterator defaultUv, StreamInfoList::const_iterator firstUv, - UvStreamTangentIndex& uvStreamTangentIndexOut) const; + UvStreamTangentBitmask* uvStreamTangentBitmaskOut) const; // Meshes may share index/stream buffers in an LOD or they may have // unique buffers. Often the asset builder will prioritize shared buffers @@ -175,35 +176,53 @@ namespace AZ AZStd::mutex m_callbackMutex; }; - //! An encoded bitset for tangent used by a UV stream. - //! It will be passed through DefaultDrawSrg. - class UvStreamTangentIndex + //! An encoded bitmask for tangent used by UV streams. + //! It contains the information about number of UV streams and which tangent/bitangent is used by each UV stream. + //! See m_mask for more details. + //! The mask will be passed through per draw SRG. + class UvStreamTangentBitmask { public: - uint32_t GetFullFlag() const; - uint32_t GetNextAvailableUvIndex() const; - uint32_t GetTangentIndexAtUv(uint32_t uvIndex) const; + //! Get the full mask including number of UVs and tangent/bitangent assignment to each UV. + uint32_t GetFullTangentBitmask() const; - void ApplyTangentIndex(uint32_t tangentIndex); + //! Get number of UVs that have tangent/bitangent assigned. + uint32_t GetUvStreamCount() const; + //! Get tangent/bitangent assignment to the specified UV in the material. + //! @param uvIndex the index of the UV from the material, in default order as in the shader code. + uint32_t GetTangentAtUv(uint32_t uvIndex) const; + + //! Apply the tangent to the next UV, whose index is the same as GetUvStreamCount. + //! @param tangent the tangent/bitangent to be assigned. Ranged in [0, 0xF) + //! It comes from the model in order, e.g. 0 means the first available tangent stream from the model. + //! Specially, value 0xF(=UnassignedTangent) means generated tangent/bitangent will be used in shader. + //! If ranged out of definition, unassigned tangent will be applied. + void ApplyTangent(uint32_t tangent); + + //! Reset the bitmask to clear state. void Reset(); - // The flag indicating generated tangent/bitangent will be used. - static constexpr uint32_t UnassignedTangentIndex = 0b1111u; + //! The bit mask indicating generated tangent/bitangent will be used. + static constexpr uint32_t UnassignedTangent = 0b1111u; + //! The variable name defined in the SRG shader code. + static constexpr const char* SrgName = "m_uvStreamTangentBitmask"; private: - // Flag composition: - // The next available slot index (highest 4 bits) + tangent index (4 bits each) * 7 - // e.g. 0x200000F0 means there are 2 UV streams, - // the first UV stream uses 0th tangent stream, - // the second UV stream uses the generated tangent stream (0xF). - uint32_t m_flag = 0; + //! Mask composition: + //! The number of UV slots (highest 4 bits) + tangent mask (4 bits each) * 7 + //! e.g. 0x200000F0 means there are 2 UV streams, + //! the first UV stream uses 0th tangent stream (0x0), + //! the second UV stream uses the generated tangent stream (0xF). + uint32_t m_mask = 0; - static constexpr uint32_t BitsPerTangentIndex = 4; + //! Bit size in the mask composition. + static constexpr uint32_t BitsPerTangent = 4; static constexpr uint32_t BitsForUvIndex = 4; public: - static constexpr uint32_t MaxTangents = (sizeof(m_flag) * CHAR_BIT - BitsForUvIndex) / BitsPerTangentIndex; + //! Max UV slots available in this bit mask. + static constexpr uint32_t MaxUvSlots = (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex) / BitsPerTangent; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp index e6fe8251c7..b1265b1228 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/MeshDrawPacket.cpp @@ -209,12 +209,12 @@ namespace AZ streamBufferViewsPerShader.push_back(); auto& streamBufferViews = streamBufferViewsPerShader.back(); - UvStreamTangentIndex uvStreamTangentIndex; + UvStreamTangentBitmask uvStreamTangentBitmask; if (!m_modelLod->GetStreamsForMesh( pipelineStateDescriptor.m_inputStreamLayout, streamBufferViews, - uvStreamTangentIndex, + &uvStreamTangentBitmask, variant.GetInputContract(), m_modelLodMeshIndex, m_materialModelUvMap, @@ -235,9 +235,16 @@ namespace AZ drawSrg->SetShaderVariantKeyFallbackValue(shaderOptions.GetShaderVariantKeyFallbackValue()); } - RHI::ShaderInputNameIndex shaderUvStreamTangentIndex = "m_uvStreamTangentIndex"; + // Pass UvStreamTangentBitmask to the shader if the draw SRG has it. + { + AZ::Name shaderUvStreamTangentBitmask = AZ::Name(UvStreamTangentBitmask::SrgName); + auto index = drawSrg->FindShaderInputConstantIndex(shaderUvStreamTangentBitmask); - drawSrg->SetConstant(shaderUvStreamTangentIndex, uvStreamTangentIndex.GetFullFlag()); + if (index.IsValid()) + { + drawSrg->SetConstant(index, uvStreamTangentBitmask.GetFullTangentBitmask()); + } + } drawSrg->Compile(); } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp index 0d064142df..1cf3866158 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp @@ -173,7 +173,7 @@ namespace AZ const ShaderInputContract::StreamChannelInfo& contractStreamChannel, StreamInfoList::const_iterator defaultUv, StreamInfoList::const_iterator firstUv, - UvStreamTangentIndex& uvStreamTangentIndexOut) const + UvStreamTangentBitmask* uvStreamTangentBitmaskOut) const { const Mesh& mesh = m_meshes[meshIndex]; auto iter = mesh.m_streamInfo.end(); @@ -197,8 +197,8 @@ namespace AZ // Cost of linear search UV names is low because the size is extremely limited. return uvNamePair.m_shaderInput == contractStreamChannel.m_semantic; }); - const bool IsUv = materialUvIter != materialUvNameMap.end(); - if (IsUv) + const bool isUv = materialUvIter != materialUvNameMap.end(); + if (isUv) { const AZ::Name& materialUvName = materialUvIter->m_uvName; auto modelUvMapIter = materialModelUvMap.find(materialUvIter->m_shaderInput); @@ -237,14 +237,14 @@ namespace AZ }); } - if (iter == mesh.m_streamInfo.end() && IsUv) + if (iter == mesh.m_streamInfo.end() && isUv) { iter = defaultUv; } - if (IsUv) + if (isUv && uvStreamTangentBitmaskOut) { - uvStreamTangentIndexOut.ApplyTangentIndex(iter == firstUv ? 0 : UvStreamTangentIndex::UnassignedTangentIndex); + uvStreamTangentBitmaskOut->ApplyTangent(iter == firstUv ? 0 : UvStreamTangentBitmask::UnassignedTangent); } return iter; @@ -253,7 +253,7 @@ namespace AZ bool ModelLod::GetStreamsForMesh( RHI::InputStreamLayout& layoutOut, StreamBufferViewList& streamBufferViewsOut, - UvStreamTangentIndex& uvStreamTangentIndexOut, + UvStreamTangentBitmask* uvStreamTangentBitmaskOut, const ShaderInputContract& contract, size_t meshIndex, const MaterialModelUvOverrideMap& materialModelUvMap, @@ -272,11 +272,14 @@ namespace AZ // Searching for the first UV in the mesh, so it can be used to paired with tangent/bitangent stream auto firstUv = FindFirstUvStreamFromMesh(meshIndex); auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap); - uvStreamTangentIndexOut.Reset(); + if (uvStreamTangentBitmaskOut) + { + uvStreamTangentBitmaskOut->Reset(); + } for (auto& contractStreamChannel : contract.m_streamChannels) { - auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, uvStreamTangentIndexOut); + auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, uvStreamTangentBitmaskOut); if (iter == mesh.m_streamInfo.end()) { @@ -363,7 +366,6 @@ namespace AZ auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap); auto firstUv = FindFirstUvStreamFromMesh(meshIndex); - UvStreamTangentIndex dummyUvStreamTangentIndex; for (auto& contractStreamChannel : contract.m_streamChannels) { @@ -374,7 +376,7 @@ namespace AZ AZ_Assert(contractStreamChannel.m_streamBoundIndicatorIndex.IsValid(), "m_streamBoundIndicatorIndex was invalid for an optional shader input stream"); - auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, dummyUvStreamTangentIndex); + auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, nullptr); ShaderOptionValue isStreamBound = (iter == mesh.m_streamInfo.end()) ? ShaderOptionValue{0} : ShaderOptionValue{1}; shaderOptions.SetValue(contractStreamChannel.m_streamBoundIndicatorIndex, isStreamBound); @@ -438,55 +440,55 @@ namespace AZ return static_cast(m_buffers.size() - 1); } - uint32_t UvStreamTangentIndex::GetFullFlag() const + uint32_t UvStreamTangentBitmask::GetFullTangentBitmask() const { - return m_flag; + return m_mask; } - uint32_t UvStreamTangentIndex::GetNextAvailableUvIndex() const + uint32_t UvStreamTangentBitmask::GetUvStreamCount() const { - return m_flag >> (sizeof(m_flag) * CHAR_BIT - BitsForUvIndex); + return m_mask >> (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex); } - uint32_t UvStreamTangentIndex::GetTangentIndexAtUv(uint32_t uvIndex) const + uint32_t UvStreamTangentBitmask::GetTangentAtUv(uint32_t uvIndex) const { - return (m_flag >> (BitsPerTangentIndex * uvIndex)) & 0b1111u; + return (m_mask >> (BitsPerTangent * uvIndex)) & 0b1111u; } - void UvStreamTangentIndex::ApplyTangentIndex(uint32_t tangentIndex) + void UvStreamTangentBitmask::ApplyTangent(uint32_t tangentIndex) { - uint32_t currentSlot = GetNextAvailableUvIndex(); - if (currentSlot >= MaxTangents) + uint32_t currentSlot = GetUvStreamCount(); + if (currentSlot >= MaxUvSlots) { AZ_Error("UV Stream", false, "Reaching the max of avaiblable stream slots."); return; } - if (tangentIndex > UnassignedTangentIndex) + if (tangentIndex > UnassignedTangent) { AZ_Warning( "UV Stream", false, "Tangent index must use %d bits as defined in UvStreamTangentIndex::m_flag. Unassigned index will be applied.", - BitsPerTangentIndex); - tangentIndex = UnassignedTangentIndex; + BitsPerTangent); + tangentIndex = UnassignedTangent; } - uint32_t mask = 0b1111u << (BitsPerTangentIndex * currentSlot); - mask = ~mask; + uint32_t clearMask = 0b1111u << (BitsPerTangent * currentSlot); + clearMask = ~clearMask; // Clear the writing bits in case - m_flag &= mask; + m_mask &= clearMask; // Write the bits to the slot - m_flag |= (tangentIndex << (BitsPerTangentIndex * currentSlot)); + m_mask |= (tangentIndex << (BitsPerTangent * currentSlot)); // Increase the index - m_flag += (1u << (sizeof(m_flag) * CHAR_BIT - BitsForUvIndex)); + m_mask += (1u << (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex)); } - void UvStreamTangentIndex::Reset() + void UvStreamTangentBitmask::Reset() { - m_flag = 0; + m_mask = 0; } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp index 640a6b406a..64f759d496 100644 --- a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp @@ -920,29 +920,39 @@ namespace UnitTest TEST_F(ModelTests, UvStream) { - AZ::RPI::UvStreamTangentIndex uvStreamTangentIndex; - EXPECT_EQ(uvStreamTangentIndex.GetFullFlag(), 0u); + AZ::RPI::UvStreamTangentBitmask uvStreamTangentBitmask; + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0u); - uvStreamTangentIndex.ApplyTangentIndex(1u); - EXPECT_EQ(uvStreamTangentIndex.GetTangentIndexAtUv(0u), 1u); - EXPECT_EQ(uvStreamTangentIndex.GetNextAvailableUvIndex(), 1u); + uvStreamTangentBitmask.ApplyTangent(1u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(0u), 1u); + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x10000001); + EXPECT_EQ(uvStreamTangentBitmask.GetUvStreamCount(), 1u); - uvStreamTangentIndex.ApplyTangentIndex(5u); - EXPECT_EQ(uvStreamTangentIndex.GetTangentIndexAtUv(1u), 5u); - EXPECT_EQ(uvStreamTangentIndex.GetNextAvailableUvIndex(), 2u); + uvStreamTangentBitmask.ApplyTangent(5u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(0u), 1u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(1u), 5u); + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x20000051); + EXPECT_EQ(uvStreamTangentBitmask.GetUvStreamCount(), 2u); - uvStreamTangentIndex.ApplyTangentIndex(100u); - EXPECT_EQ(uvStreamTangentIndex.GetTangentIndexAtUv(2u), AZ::RPI::UvStreamTangentIndex::UnassignedTangentIndex); - EXPECT_EQ(uvStreamTangentIndex.GetNextAvailableUvIndex(), 3u); + uvStreamTangentBitmask.ApplyTangent(100u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(0u), 1u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(1u), 5u); + EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(2u), AZ::RPI::UvStreamTangentBitmask::UnassignedTangent); + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x30000F51); + EXPECT_EQ(uvStreamTangentBitmask.GetUvStreamCount(), 3u); - for (uint32_t i = 3; i < AZ::RPI::UvStreamTangentIndex::MaxTangents; ++i) + for (uint32_t i = 3; i < AZ::RPI::UvStreamTangentBitmask::MaxUvSlots; ++i) { - uvStreamTangentIndex.ApplyTangentIndex(0u); + uvStreamTangentBitmask.ApplyTangent(0u); } + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x70000F51); + AZ_TEST_START_TRACE_SUPPRESSION; - uvStreamTangentIndex.ApplyTangentIndex(0u); + uvStreamTangentBitmask.ApplyTangent(0u); AZ_TEST_STOP_TRACE_SUPPRESSION(1); + + EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x70000F51); } // This class creates a Model with one LOD, whose mesh contains 2 planes. Plane 1 is in the XY plane at Z=-0.5, and From 9bad174f1cf0a8afd0478230fc59a5acded85040 Mon Sep 17 00:00:00 2001 From: jiaweig Date: Thu, 20 May 2021 19:19:10 -0700 Subject: [PATCH 4/6] Remove unrelated files --- .../Feature/Common/Assets/Materials/Types/Skin_Common.azsli | 1 + .../Common/Code/Source/Platform/Windows/platform_windows.cmake | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Common.azsli index 90546055e6..20bf7c1f2a 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Common.azsli @@ -14,6 +14,7 @@ #include #include +#include #include "MaterialInputs/BaseColorInput.azsli" #include "MaterialInputs/RoughnessInput.azsli" diff --git a/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake b/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake index 8baaa1ab90..b12b5de9ce 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake +++ b/Gems/Atom/Feature/Common/Code/Source/Platform/Windows/platform_windows.cmake @@ -18,5 +18,5 @@ set(LY_BUILD_DEPENDENCIES # [GFX-TODO] Add macro defintion in OpenImageIO 3rd party find cmake file set(LY_COMPILE_DEFINITIONS PRIVATE - #OPEN_IMAGE_IO_ENABLED + OPEN_IMAGE_IO_ENABLED ) From 3dc76d76c0b7e842a5a2b041cf8639176ab09a72 Mon Sep 17 00:00:00 2001 From: jiaweig Date: Mon, 24 May 2021 10:04:08 -0700 Subject: [PATCH 5/6] Fix comments --- Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli index 7e4dc0fd22..eb049d4676 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/TangentSpace.azsli @@ -195,7 +195,7 @@ void SurfaceGradientNormalMapping_GenerateTB(float2 uv, out float3 tangentWS, ou //! If anything uses the second UV stream, and it is not a duplication of the first stream, //! generated tangent/bitangent will be applied. //! (As it implies, cases may occur where all/none of the UV steams use the default TB.) -//! What tangent/bitangent a UV stream uses is encoded in MaterialDrawSrg. +//! What tangent/bitangent a UV stream uses is encoded in DrawSrg. #define PrepareGeneratedTangent(normal, worldPos, isFrontFace, uvSets, uvSetCount, outTangents, outBitangents) \ { \ SurfaceGradientNormalMapping_Init(normal, worldPos, !isFrontFace); \ From f64bd999e09e4b67dea8b5b974e6ea03bf67d373 Mon Sep 17 00:00:00 2001 From: jiaweig Date: Tue, 25 May 2021 18:47:04 -0700 Subject: [PATCH 6/6] Move UvStreamTangentBitmask to new files. --- .../Include/Atom/RPI.Public/Model/ModelLod.h | 52 +------------- .../RPI.Public/Model/UvStreamTangentBitmask.h | 72 +++++++++++++++++++ .../Code/Source/RPI.Public/Model/ModelLod.cpp | 51 ------------- .../Model/UvStreamTangentBitmask.cpp | 71 ++++++++++++++++++ Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp | 2 +- .../Atom/RPI/Code/atom_rpi_public_files.cmake | 2 + 6 files changed, 147 insertions(+), 103 deletions(-) create mode 100644 Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h create mode 100644 Gems/Atom/RPI/Code/Source/RPI.Public/Model/UvStreamTangentBitmask.cpp diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h index bb3bfe52e7..5a1c571a26 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/ModelLod.h @@ -14,6 +14,7 @@ #include #include +#include #include @@ -34,8 +35,6 @@ namespace AZ //! A map matches the UV shader inputs of this material to the custom UV names from the model. using MaterialModelUvOverrideMap = AZStd::unordered_map; - class UvStreamTangentBitmask; - class ModelLod final : public Data::InstanceData { @@ -175,54 +174,5 @@ namespace AZ AZStd::mutex m_callbackMutex; }; - - //! An encoded bitmask for tangent used by UV streams. - //! It contains the information about number of UV streams and which tangent/bitangent is used by each UV stream. - //! See m_mask for more details. - //! The mask will be passed through per draw SRG. - class UvStreamTangentBitmask - { - public: - //! Get the full mask including number of UVs and tangent/bitangent assignment to each UV. - uint32_t GetFullTangentBitmask() const; - - //! Get number of UVs that have tangent/bitangent assigned. - uint32_t GetUvStreamCount() const; - - //! Get tangent/bitangent assignment to the specified UV in the material. - //! @param uvIndex the index of the UV from the material, in default order as in the shader code. - uint32_t GetTangentAtUv(uint32_t uvIndex) const; - - //! Apply the tangent to the next UV, whose index is the same as GetUvStreamCount. - //! @param tangent the tangent/bitangent to be assigned. Ranged in [0, 0xF) - //! It comes from the model in order, e.g. 0 means the first available tangent stream from the model. - //! Specially, value 0xF(=UnassignedTangent) means generated tangent/bitangent will be used in shader. - //! If ranged out of definition, unassigned tangent will be applied. - void ApplyTangent(uint32_t tangent); - - //! Reset the bitmask to clear state. - void Reset(); - - //! The bit mask indicating generated tangent/bitangent will be used. - static constexpr uint32_t UnassignedTangent = 0b1111u; - - //! The variable name defined in the SRG shader code. - static constexpr const char* SrgName = "m_uvStreamTangentBitmask"; - private: - //! Mask composition: - //! The number of UV slots (highest 4 bits) + tangent mask (4 bits each) * 7 - //! e.g. 0x200000F0 means there are 2 UV streams, - //! the first UV stream uses 0th tangent stream (0x0), - //! the second UV stream uses the generated tangent stream (0xF). - uint32_t m_mask = 0; - - //! Bit size in the mask composition. - static constexpr uint32_t BitsPerTangent = 4; - static constexpr uint32_t BitsForUvIndex = 4; - - public: - //! Max UV slots available in this bit mask. - static constexpr uint32_t MaxUvSlots = (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex) / BitsPerTangent; - }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h new file mode 100644 index 0000000000..aa599bc27c --- /dev/null +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h @@ -0,0 +1,72 @@ +/* +* 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. +* +*/ + +#pragma once + +#include + +#include + +namespace AZ +{ + namespace RPI + { + //! An encoded bitmask for tangent used by UV streams. + //! It contains the information about number of UV streams and which tangent/bitangent is used by each UV stream. + //! See m_mask for more details. + //! The mask will be passed through per draw SRG. + class UvStreamTangentBitmask + { + public: + //! Get the full mask including number of UVs and tangent/bitangent assignment to each UV. + uint32_t GetFullTangentBitmask() const; + + //! Get number of UVs that have tangent/bitangent assigned. + uint32_t GetUvStreamCount() const; + + //! Get tangent/bitangent assignment to the specified UV in the material. + //! @param uvIndex the index of the UV from the material, in default order as in the shader code. + uint32_t GetTangentAtUv(uint32_t uvIndex) const; + + //! Apply the tangent to the next UV, whose index is the same as GetUvStreamCount. + //! @param tangent the tangent/bitangent to be assigned. Ranged in [0, 0xF) + //! It comes from the model in order, e.g. 0 means the first available tangent stream from the model. + //! Specially, value 0xF(=UnassignedTangent) means generated tangent/bitangent will be used in shader. + //! If ranged out of definition, unassigned tangent will be applied. + void ApplyTangent(uint32_t tangent); + + //! Reset the bitmask to clear state. + void Reset(); + + //! The bit mask indicating generated tangent/bitangent will be used. + static constexpr uint32_t UnassignedTangent = 0b1111u; + + //! The variable name defined in the SRG shader code. + static constexpr const char* SrgName = "m_uvStreamTangentBitmask"; + private: + //! Mask composition: + //! The number of UV slots (highest 4 bits) + tangent mask (4 bits each) * 7 + //! e.g. 0x200000F0 means there are 2 UV streams, + //! the first UV stream uses 0th tangent stream (0x0), + //! the second UV stream uses the generated tangent stream (0xF). + uint32_t m_mask = 0; + + //! Bit size in the mask composition. + static constexpr uint32_t BitsPerTangent = 4; + static constexpr uint32_t BitsForUvIndex = 4; + + public: + //! Max UV slots available in this bit mask. + static constexpr uint32_t MaxUvSlots = (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex) / BitsPerTangent; + }; + } +} diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp index 1cf3866158..c6a1a51f39 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/ModelLod.cpp @@ -439,56 +439,5 @@ namespace AZ m_buffers.emplace_back(buffer); return static_cast(m_buffers.size() - 1); } - - uint32_t UvStreamTangentBitmask::GetFullTangentBitmask() const - { - return m_mask; - } - - uint32_t UvStreamTangentBitmask::GetUvStreamCount() const - { - return m_mask >> (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex); - } - - uint32_t UvStreamTangentBitmask::GetTangentAtUv(uint32_t uvIndex) const - { - return (m_mask >> (BitsPerTangent * uvIndex)) & 0b1111u; - } - - void UvStreamTangentBitmask::ApplyTangent(uint32_t tangentIndex) - { - uint32_t currentSlot = GetUvStreamCount(); - if (currentSlot >= MaxUvSlots) - { - AZ_Error("UV Stream", false, "Reaching the max of avaiblable stream slots."); - return; - } - - if (tangentIndex > UnassignedTangent) - { - AZ_Warning( - "UV Stream", false, - "Tangent index must use %d bits as defined in UvStreamTangentIndex::m_flag. Unassigned index will be applied.", - BitsPerTangent); - tangentIndex = UnassignedTangent; - } - - uint32_t clearMask = 0b1111u << (BitsPerTangent * currentSlot); - clearMask = ~clearMask; - - // Clear the writing bits in case - m_mask &= clearMask; - - // Write the bits to the slot - m_mask |= (tangentIndex << (BitsPerTangent * currentSlot)); - - // Increase the index - m_mask += (1u << (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex)); - } - - void UvStreamTangentBitmask::Reset() - { - m_mask = 0; - } } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/UvStreamTangentBitmask.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/UvStreamTangentBitmask.cpp new file mode 100644 index 0000000000..829207e406 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/UvStreamTangentBitmask.cpp @@ -0,0 +1,71 @@ +/* +* 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. +* +*/ + +#include +#include + +namespace AZ +{ + namespace RPI + { + uint32_t UvStreamTangentBitmask::GetFullTangentBitmask() const + { + return m_mask; + } + + uint32_t UvStreamTangentBitmask::GetUvStreamCount() const + { + return m_mask >> (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex); + } + + uint32_t UvStreamTangentBitmask::GetTangentAtUv(uint32_t uvIndex) const + { + return (m_mask >> (BitsPerTangent * uvIndex)) & 0b1111u; + } + + void UvStreamTangentBitmask::ApplyTangent(uint32_t tangentIndex) + { + uint32_t currentSlot = GetUvStreamCount(); + if (currentSlot >= MaxUvSlots) + { + AZ_Error("UV Stream", false, "Reaching the max of avaiblable stream slots."); + return; + } + + if (tangentIndex > UnassignedTangent) + { + AZ_Warning( + "UV Stream", false, + "Tangent index must use %d bits as defined in UvStreamTangentIndex::m_flag. Unassigned index will be applied.", + BitsPerTangent); + tangentIndex = UnassignedTangent; + } + + uint32_t clearMask = 0b1111u << (BitsPerTangent * currentSlot); + clearMask = ~clearMask; + + // Clear the writing bits in case + m_mask &= clearMask; + + // Write the bits to the slot + m_mask |= (tangentIndex << (BitsPerTangent * currentSlot)); + + // Increase the index + m_mask += (1u << (sizeof(m_mask) * CHAR_BIT - BitsForUvIndex)); + } + + void UvStreamTangentBitmask::Reset() + { + m_mask = 0; + } + } +} diff --git a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp index 64f759d496..1ec14c6169 100644 --- a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include diff --git a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake index 0d5c19758b..6242f3f140 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_public_files.cmake @@ -57,6 +57,7 @@ set(FILES Include/Atom/RPI.Public/Model/ModelLod.h Include/Atom/RPI.Public/Model/ModelLodUtils.h Include/Atom/RPI.Public/Model/ModelSystem.h + Include/Atom/RPI.Public/Model/UvStreamTangentBitmask.h Include/Atom/RPI.Public/Pass/AttachmentReadback.h Include/Atom/RPI.Public/Pass/ComputePass.h Include/Atom/RPI.Public/Pass/CopyPass.h @@ -136,6 +137,7 @@ set(FILES Source/RPI.Public/Model/ModelLod.cpp Source/RPI.Public/Model/ModelLodUtils.cpp Source/RPI.Public/Model/ModelSystem.cpp + Source/RPI.Public/Model/UvStreamTangentBitmask.cpp Source/RPI.Public/Pass/AttachmentReadback.cpp Source/RPI.Public/Pass/ComputePass.cpp Source/RPI.Public/Pass/CopyPass.cpp