Merge pull request #823 from aws-lumberyard-dev/Atom/jiaweig/ATOM-15125_UV_Tangent
ATOM-15125 Support Reorder of Tangent Streams
This commit is contained in:
@@ -77,10 +77,9 @@ 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);
|
||||
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();
|
||||
|
||||
@@ -117,19 +117,12 @@ VSOutput EnhancedPbr_ForwardPassVS(VSInput IN)
|
||||
PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depth)
|
||||
{
|
||||
// ------- Tangents & Bitangets -------
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz };
|
||||
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz };
|
||||
|
||||
// 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) };
|
||||
|
||||
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)
|
||||
{
|
||||
// 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 +253,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 -------
|
||||
|
||||
@@ -80,10 +80,10 @@ 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);
|
||||
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();
|
||||
|
||||
@@ -180,17 +180,12 @@ PbrLightingOutput SkinPS_Common(VSOutput IN)
|
||||
float3x3 uvMatrix = CreateIdentity3x3();
|
||||
|
||||
// ------- Tangents & Bitangets -------
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz };
|
||||
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz };
|
||||
|
||||
// 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) };
|
||||
|
||||
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)
|
||||
{
|
||||
// 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;
|
||||
|
||||
+5
-5
@@ -103,11 +103,11 @@ 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);
|
||||
|
||||
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);
|
||||
|
||||
s_blendMaskFromVertexStream = IN.m_blendMask;
|
||||
|
||||
float depth;
|
||||
|
||||
+10
-14
@@ -311,23 +311,19 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
|
||||
s_blendMaskFromVertexStream = IN.m_blendMask;
|
||||
|
||||
// ------- Tangents & Bitangets -------
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz };
|
||||
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz };
|
||||
|
||||
// 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) };
|
||||
|
||||
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
|
||||
)
|
||||
{
|
||||
// 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 -------
|
||||
|
||||
+4
-4
@@ -102,10 +102,10 @@ 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);
|
||||
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);
|
||||
|
||||
s_blendMaskFromVertexStream = IN.m_blendMask;
|
||||
|
||||
|
||||
@@ -78,10 +78,10 @@ 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);
|
||||
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();
|
||||
|
||||
@@ -110,19 +110,12 @@ VSOutput StandardPbr_ForwardPassVS(VSInput IN)
|
||||
PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float depthNDC)
|
||||
{
|
||||
// ------- Tangents & Bitangets -------
|
||||
float3 tangents[UvSetCount] = { IN.m_tangent.xyz, IN.m_tangent.xyz };
|
||||
float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, IN.m_bitangent.xyz };
|
||||
|
||||
// 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) };
|
||||
|
||||
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))
|
||||
{
|
||||
// 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 -------
|
||||
|
||||
@@ -81,10 +81,9 @@ 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);
|
||||
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();
|
||||
|
||||
@@ -741,7 +741,7 @@ namespace AZ
|
||||
|
||||
// retrieve vertex/index buffers
|
||||
RPI::ModelLod::StreamBufferViewList streamBufferViews;
|
||||
[[maybe_unused]] bool result = modelLod->GetStreamsForMesh(inputStreamLayout, streamBufferViews, 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
|
||||
|
||||
@@ -16,7 +16,12 @@
|
||||
|
||||
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_uvStreamTangentBitmask;
|
||||
|
||||
uint GetTangentAtUv(uint uvIndex)
|
||||
{
|
||||
return (m_uvStreamTangentBitmask >> (4 * uvIndex)) & 0xF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,12 +190,22 @@ 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) \
|
||||
//! 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 DrawSrg.
|
||||
#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::GetTangentAtUv(i) == 0) \
|
||||
{ \
|
||||
continue; \
|
||||
} \
|
||||
SurfaceGradientNormalMapping_GenerateTB(uvSets[i], outTangents[i], outBitangents[i]); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace AZ
|
||||
//! List of shader options set for this specific draw packet
|
||||
typedef AZStd::pair<Name, RPI::ShaderOptionValue> ShaderOptionPair;
|
||||
typedef AZStd::vector<ShaderOptionPair> ShaderOptionVector;
|
||||
ShaderOptionVector m_shaderOptions;
|
||||
ShaderOptionVector m_shaderOptions;
|
||||
};
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <Atom/RPI.Public/Buffer/Buffer.h>
|
||||
#include <Atom/RPI.Public/Material/Material.h>
|
||||
#include <Atom/RPI.Public/Model/UvStreamTangentBitmask.h>
|
||||
|
||||
#include <Atom/RHI/DrawItem.h>
|
||||
|
||||
@@ -108,6 +109,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.
|
||||
@@ -115,6 +117,7 @@ namespace AZ
|
||||
bool GetStreamsForMesh(
|
||||
RHI::InputStreamLayout& layoutOut,
|
||||
ModelLod::StreamBufferViewList& streamBufferViewsOut,
|
||||
UvStreamTangentBitmask* uvStreamTangentBitmaskOut,
|
||||
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,
|
||||
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
|
||||
|
||||
@@ -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 <limits.h>
|
||||
|
||||
#include <AzCore/base.h>
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ namespace AZ
|
||||
const AZ::Data::Asset<ShaderResourceGroupAsset>& 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();
|
||||
|
||||
UvStreamTangentBitmask uvStreamTangentBitmask;
|
||||
|
||||
if (!m_modelLod->GetStreamsForMesh(
|
||||
pipelineStateDescriptor.m_inputStreamLayout,
|
||||
streamBufferViews,
|
||||
&uvStreamTangentBitmask,
|
||||
variant.GetInputContract(),
|
||||
m_modelLodMeshIndex,
|
||||
m_materialModelUvMap,
|
||||
m_material->GetAsset()->GetMaterialTypeAsset()->GetUvNameMap()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Data::Instance<ShaderResourceGroup> drawSrg;
|
||||
if (drawSrgAsset)
|
||||
{
|
||||
@@ -210,31 +235,20 @@ namespace AZ
|
||||
drawSrg->SetShaderVariantKeyFallbackValue(shaderOptions.GetShaderVariantKeyFallbackValue());
|
||||
}
|
||||
|
||||
// Pass UvStreamTangentBitmask to the shader if the draw SRG has it.
|
||||
{
|
||||
AZ::Name shaderUvStreamTangentBitmask = AZ::Name(UvStreamTangentBitmask::SrgName);
|
||||
auto index = drawSrg->FindShaderInputConstantIndex(shaderUvStreamTangentBitmask);
|
||||
|
||||
if (index.IsValid())
|
||||
{
|
||||
drawSrg->SetConstant(index, uvStreamTangentBitmask.GetFullTangentBitmask());
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
||||
@@ -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,
|
||||
UvStreamTangentBitmask* uvStreamTangentBitmaskOut) const
|
||||
{
|
||||
const Mesh& mesh = m_meshes[meshIndex];
|
||||
auto iter = mesh.m_streamInfo.end();
|
||||
@@ -184,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);
|
||||
@@ -224,17 +237,23 @@ namespace AZ
|
||||
});
|
||||
}
|
||||
|
||||
if (iter == mesh.m_streamInfo.end() && IsUv)
|
||||
if (iter == mesh.m_streamInfo.end() && isUv)
|
||||
{
|
||||
iter = defaultUv;
|
||||
}
|
||||
|
||||
if (isUv && uvStreamTangentBitmaskOut)
|
||||
{
|
||||
uvStreamTangentBitmaskOut->ApplyTangent(iter == firstUv ? 0 : UvStreamTangentBitmask::UnassignedTangent);
|
||||
}
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
bool ModelLod::GetStreamsForMesh(
|
||||
RHI::InputStreamLayout& layoutOut,
|
||||
StreamBufferViewList& streamBufferViewsOut,
|
||||
UvStreamTangentBitmask* uvStreamTangentBitmaskOut,
|
||||
const ShaderInputContract& contract,
|
||||
size_t meshIndex,
|
||||
const MaterialModelUvOverrideMap& materialModelUvMap,
|
||||
@@ -250,11 +269,17 @@ 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);
|
||||
if (uvStreamTangentBitmaskOut)
|
||||
{
|
||||
uvStreamTangentBitmaskOut->Reset();
|
||||
}
|
||||
|
||||
for (auto& contractStreamChannel : contract.m_streamChannels)
|
||||
{
|
||||
auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv);
|
||||
auto iter = FindMatchingStream(meshIndex, materialModelUvMap, materialUvNameMap, contractStreamChannel, defaultUv, firstUv, uvStreamTangentBitmaskOut);
|
||||
|
||||
if (iter == mesh.m_streamInfo.end())
|
||||
{
|
||||
@@ -340,6 +365,7 @@ namespace AZ
|
||||
const Mesh& mesh = m_meshes[meshIndex];
|
||||
|
||||
auto defaultUv = FindDefaultUvStream(meshIndex, materialUvNameMap);
|
||||
auto firstUv = FindFirstUvStreamFromMesh(meshIndex);
|
||||
|
||||
for (auto& contractStreamChannel : contract.m_streamChannels)
|
||||
{
|
||||
@@ -350,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);
|
||||
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);
|
||||
|
||||
@@ -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 <Atom/RPI.Public/Model/UvStreamTangentBitmask.h>
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <Atom/RPI.Reflect/Model/ModelKdTree.h>
|
||||
#include <Atom/RPI.Reflect/Model/ModelLodAsset.h>
|
||||
#include <Atom/RPI.Reflect/ResourcePoolAssetCreator.h>
|
||||
#include <Atom/RPI.Public/Model/UvStreamTangentBitmask.h>
|
||||
|
||||
#include <AzCore/std/limits.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
@@ -917,6 +918,43 @@ namespace UnitTest
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ModelTests, UvStream)
|
||||
{
|
||||
AZ::RPI::UvStreamTangentBitmask uvStreamTangentBitmask;
|
||||
EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0u);
|
||||
|
||||
uvStreamTangentBitmask.ApplyTangent(1u);
|
||||
EXPECT_EQ(uvStreamTangentBitmask.GetTangentAtUv(0u), 1u);
|
||||
EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x10000001);
|
||||
EXPECT_EQ(uvStreamTangentBitmask.GetUvStreamCount(), 1u);
|
||||
|
||||
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);
|
||||
|
||||
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::UvStreamTangentBitmask::MaxUvSlots; ++i)
|
||||
{
|
||||
uvStreamTangentBitmask.ApplyTangent(0u);
|
||||
}
|
||||
|
||||
EXPECT_EQ(uvStreamTangentBitmask.GetFullTangentBitmask(), 0x70000F51);
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
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
|
||||
// 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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user