Add support for tangent stream pairing with the first UV from the model.

This commit is contained in:
jiaweig
2021-05-19 00:36:24 -07:00
parent 3182dc37c3
commit 775b7d048b
17 changed files with 288 additions and 81 deletions
@@ -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();
@@ -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 -------
@@ -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();
@@ -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;
@@ -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;
@@ -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 -------
@@ -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);
@@ -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();
@@ -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 -------
@@ -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();
@@ -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
@@ -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;
}
}
@@ -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]); \
} \
}
@@ -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<RHI::ShaderSemantic, AZ::Name>;
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
@@ -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();
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<ShaderResourceGroup> 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();
@@ -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<uint32_t>(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
@@ -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/ModelLod.h>
#include <AzCore/std/limits.h>
#include <AzCore/Component/Entity.h>
@@ -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.