Calculating uv transforms for terrain detail materials (#7375)
Calculating uv transforms for terrain detail materials This adds support for uv transforms in terrain detail materials. To support this work, the code for generating a matrix from material properties was pulled out of the material functor and put into a new MaterialUtils file in Atom Utils.
This commit is contained in:
@@ -39,59 +39,21 @@ namespace AZ
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Transform2DFunctor::Process(RuntimeContext& context)
|
||||
{
|
||||
using namespace RPI;
|
||||
|
||||
auto center = context.GetMaterialPropertyValue<Vector2>(m_center);
|
||||
auto scale = context.GetMaterialPropertyValue<float>(m_scale);
|
||||
auto scaleX = context.GetMaterialPropertyValue<float>(m_scaleX);
|
||||
auto scaleY = context.GetMaterialPropertyValue<float>(m_scaleY);
|
||||
auto translateX = context.GetMaterialPropertyValue<float>(m_translateX);
|
||||
auto translateY = context.GetMaterialPropertyValue<float>(m_translateY);
|
||||
auto rotateDegrees = context.GetMaterialPropertyValue<float>(m_rotateDegrees);
|
||||
UvTransformDescriptor desc;
|
||||
desc.m_center = context.GetMaterialPropertyValue<Vector2>(m_center);
|
||||
desc.m_scale = context.GetMaterialPropertyValue<float>(m_scale);
|
||||
desc.m_scaleX = context.GetMaterialPropertyValue<float>(m_scaleX);
|
||||
desc.m_scaleY = context.GetMaterialPropertyValue<float>(m_scaleY);
|
||||
desc.m_translateX = context.GetMaterialPropertyValue<float>(m_translateX);
|
||||
desc.m_translateY = context.GetMaterialPropertyValue<float>(m_translateY);
|
||||
desc.m_rotateDegrees = context.GetMaterialPropertyValue<float>(m_rotateDegrees);
|
||||
|
||||
if (scaleX != 0.0f)
|
||||
{
|
||||
translateX *= (1.0f / scaleX);
|
||||
}
|
||||
|
||||
if (scaleY != 0.0f)
|
||||
{
|
||||
translateY *= (1.0f / scaleY);
|
||||
}
|
||||
|
||||
Matrix3x3 translateCenter2D = Matrix3x3::CreateIdentity();
|
||||
translateCenter2D.SetBasisZ(-center.GetX(), -center.GetY(), 1.0f);
|
||||
|
||||
Matrix3x3 translateCenterInv2D = Matrix3x3::CreateIdentity();
|
||||
translateCenterInv2D.SetBasisZ(center.GetX(), center.GetY(), 1.0f);
|
||||
|
||||
Matrix3x3 scale2D = Matrix3x3::CreateDiagonal(AZ::Vector3(scaleX * scale, scaleY * scale, 1.0f));
|
||||
|
||||
Matrix3x3 translate2D = Matrix3x3::CreateIdentity();
|
||||
translate2D.SetBasisZ(translateX, translateY, 1.0f);
|
||||
|
||||
Matrix3x3 rotate2D = Matrix3x3::CreateRotationZ(AZ::DegToRad(rotateDegrees));
|
||||
|
||||
Matrix3x3 transform = translateCenter2D;
|
||||
for (auto transformType : m_transformOrder)
|
||||
{
|
||||
switch (transformType)
|
||||
{
|
||||
case TransformType::Scale:
|
||||
transform = scale2D * transform;
|
||||
break;
|
||||
case TransformType::Rotate:
|
||||
transform = rotate2D * transform;
|
||||
break;
|
||||
case TransformType::Translate:
|
||||
transform = translate2D * transform;
|
||||
break;
|
||||
}
|
||||
}
|
||||
transform = translateCenterInv2D * transform;
|
||||
Matrix3x3 transform = CreateUvTransformMatrix(desc, m_transformOrder);
|
||||
|
||||
context.GetShaderResourceGroup()->SetConstant(m_transformMatrix, transform);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h>
|
||||
#include <Atom/RHI.Reflect/Limits.h>
|
||||
#include <Atom/Utils/MaterialUtils.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -24,14 +25,6 @@ namespace AZ
|
||||
public:
|
||||
AZ_RTTI(Transform2DFunctor, "{3E9C4357-6B2D-4A22-89DB-462441C9D8CD}", RPI::MaterialFunctor);
|
||||
|
||||
enum class TransformType
|
||||
{
|
||||
Invalid,
|
||||
Scale,
|
||||
Rotate,
|
||||
Translate
|
||||
};
|
||||
|
||||
static void Reflect(ReflectContext* context);
|
||||
|
||||
using RPI::MaterialFunctor::Process;
|
||||
@@ -57,6 +50,4 @@ namespace AZ
|
||||
|
||||
} // namespace Render
|
||||
|
||||
AZ_TYPE_INFO_SPECIALIZE(Render::Transform2DFunctor::TransformType, "{D8C15D33-CE3D-4297-A646-030B0625BF84}");
|
||||
|
||||
} // namespace AZ
|
||||
|
||||
@@ -85,13 +85,13 @@ namespace AZ
|
||||
|
||||
functor->m_transformOrder = m_transformOrder;
|
||||
|
||||
AZStd::set<Transform2DFunctor::TransformType> transformSet{m_transformOrder.begin(), m_transformOrder.end()};
|
||||
AZStd::set<TransformType> transformSet{m_transformOrder.begin(), m_transformOrder.end()};
|
||||
if (m_transformOrder.size() != transformSet.size())
|
||||
{
|
||||
AZ_Warning("Transform2DFunctor", false, "transformOrder field contains duplicate entries");
|
||||
}
|
||||
|
||||
if (transformSet.find(Transform2DFunctor::TransformType::Invalid) != transformSet.end())
|
||||
if (transformSet.find(TransformType::Invalid) != transformSet.end())
|
||||
{
|
||||
AZ_Warning("Transform2DFunctor", false, "transformOrder contains invalid entries");
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "./Transform2DFunctor.h"
|
||||
#include <Atom/RPI.Edit/Material/MaterialFunctorSourceData.h>
|
||||
#include <Atom/Utils/MaterialUtils.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -30,7 +31,7 @@ namespace AZ
|
||||
|
||||
private:
|
||||
|
||||
AZStd::vector<Transform2DFunctor::TransformType> m_transformOrder; //!< Controls the order in which Scale, Translate, Rotate are performed
|
||||
AZStd::vector<TransformType> m_transformOrder; //!< Controls the order in which Scale, Translate, Rotate are performed
|
||||
|
||||
// Material property inputs...
|
||||
AZStd::string m_center; //!< material property for center of scaling and rotation
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/span.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Matrix3x3.h>
|
||||
#include <AzCore/RTTI/TypeInfo.h>
|
||||
|
||||
//! This file holds useful material related utility functions.
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
enum class TransformType
|
||||
{
|
||||
Invalid,
|
||||
Scale,
|
||||
Rotate,
|
||||
Translate
|
||||
};
|
||||
|
||||
struct UvTransformDescriptor
|
||||
{
|
||||
Vector2 m_center{ Vector2::CreateZero() };
|
||||
float m_scale{ 1.0f };
|
||||
float m_scaleX{ 1.0f };
|
||||
float m_scaleY{ 1.0f };
|
||||
float m_translateX{ 0.0f };
|
||||
float m_translateY{ 0.0f };
|
||||
float m_rotateDegrees{ 0.0f };
|
||||
};
|
||||
|
||||
// Create a 3x3 uv transform matrix from a set of input properties.
|
||||
Matrix3x3 CreateUvTransformMatrix(const UvTransformDescriptor& desc, const AZStd::span<const TransformType> transformOrder);
|
||||
}
|
||||
|
||||
AZ_TYPE_INFO_SPECIALIZE(Render::TransformType, "{D8C15D33-CE3D-4297-A646-030B0625BF84}");
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Atom/Utils/MaterialUtils.h>
|
||||
|
||||
namespace AZ::Render
|
||||
{
|
||||
|
||||
Matrix3x3 CreateUvTransformMatrix(const UvTransformDescriptor& desc, AZStd::span<const TransformType> transformOrder)
|
||||
{
|
||||
float translateX = desc.m_translateX;
|
||||
float translateY = desc.m_translateY;
|
||||
|
||||
if (desc.m_scaleX != 0.0f)
|
||||
{
|
||||
translateX *= (1.0f / desc.m_scaleX);
|
||||
}
|
||||
|
||||
if (desc.m_scaleY != 0.0f)
|
||||
{
|
||||
translateY *= (1.0f / desc.m_scaleY);
|
||||
}
|
||||
|
||||
Matrix3x3 translateCenter2D = Matrix3x3::CreateIdentity();
|
||||
translateCenter2D.SetBasisZ(-desc.m_center.GetX(), -desc.m_center.GetY(), 1.0f);
|
||||
|
||||
Matrix3x3 translateCenterInv2D = Matrix3x3::CreateIdentity();
|
||||
translateCenterInv2D.SetBasisZ(desc.m_center.GetX(), desc.m_center.GetY(), 1.0f);
|
||||
|
||||
Matrix3x3 scale2D = Matrix3x3::CreateDiagonal(AZ::Vector3(desc.m_scaleX * desc.m_scale, desc.m_scaleY * desc.m_scale, 1.0f));
|
||||
|
||||
Matrix3x3 translate2D = Matrix3x3::CreateIdentity();
|
||||
translate2D.SetBasisZ(translateX, translateY, 1.0f);
|
||||
|
||||
Matrix3x3 rotate2D = Matrix3x3::CreateRotationZ(AZ::DegToRad(desc.m_rotateDegrees));
|
||||
|
||||
Matrix3x3 transform = translateCenter2D;
|
||||
for (auto transformType : transformOrder)
|
||||
{
|
||||
switch (transformType)
|
||||
{
|
||||
case TransformType::Scale:
|
||||
transform = scale2D * transform;
|
||||
break;
|
||||
case TransformType::Rotate:
|
||||
transform = rotate2D * transform;
|
||||
break;
|
||||
case TransformType::Translate:
|
||||
transform = translate2D * transform;
|
||||
break;
|
||||
}
|
||||
}
|
||||
transform = translateCenterInv2D * transform;
|
||||
return transform;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ set(FILES
|
||||
Include/Atom/Utils/ImGuiShaderMetrics.inl
|
||||
Include/Atom/Utils/ImGuiTransientAttachmentProfiler.h
|
||||
Include/Atom/Utils/ImGuiTransientAttachmentProfiler.inl
|
||||
Include/Atom/Utils/MaterialUtils.h
|
||||
Include/Atom/Utils/PngFile.h
|
||||
Include/Atom/Utils/PpmFile.h
|
||||
Include/Atom/Utils/StableDynamicArray.h
|
||||
@@ -30,6 +31,7 @@ set(FILES
|
||||
Include/Atom/Utils/AssetCollectionAsyncLoader.h
|
||||
Source/DdsFile.cpp
|
||||
Source/ImageComparison.cpp
|
||||
Source/MaterialUtils.cpp
|
||||
Source/PngFile.cpp
|
||||
Source/PpmFile.cpp
|
||||
Source/Utils.cpp
|
||||
|
||||
@@ -204,16 +204,33 @@ void GetDetailSurfaceForMaterial(inout DetailSurface surface, uint materialId, f
|
||||
{
|
||||
TerrainSrg::DetailMaterialData detailMaterialData = TerrainSrg::m_detailMaterialData[materialId];
|
||||
|
||||
float3x3 uvTransform = (float3x3)detailMaterialData.m_uvTransform;
|
||||
float2 transformedUv = mul(uvTransform, float3(uv, 1.0)).xy;
|
||||
|
||||
// With different materials in the quad, we can't rely on ddx/ddy of the transformed uv because
|
||||
// the materials may have different uv transforms. This would create visible seams where the wrong
|
||||
// mip was being used. Instead, manually calculate the transformed ddx/ddy using the ddx/ddy of the
|
||||
// original uv.
|
||||
|
||||
float2 uvDdx = ddx(uv);
|
||||
float2 uvDdy = ddy(uv);
|
||||
|
||||
surface.m_color = GetDetailColor(detailMaterialData, uv, uvDdx, uvDdy);
|
||||
surface.m_normal = GetDetailNormal(detailMaterialData, uv, uvDdx, uvDdy);
|
||||
surface.m_roughness = GetDetailRoughness(detailMaterialData, uv, uvDdx, uvDdy);
|
||||
surface.m_specularF0 = GetDetailSpecularF0(detailMaterialData, uv, uvDdx, uvDdy);
|
||||
surface.m_metalness = GetDetailMetalness(detailMaterialData, uv, uvDdx, uvDdy);
|
||||
surface.m_occlusion = GetDetailOcclusion(detailMaterialData, uv, uvDdx, uvDdy);
|
||||
surface.m_height = GetDetailHeight(detailMaterialData, uv, uvDdx, uvDdy);
|
||||
float2 uvX = uv + uvDdx;
|
||||
float2 uvY = uv + uvDdy;
|
||||
|
||||
float2 transformedUvX = mul(uvTransform, float3(uvX, 1.0)).xy;
|
||||
float2 transformedUvY = mul(uvTransform, float3(uvY, 1.0)).xy;
|
||||
|
||||
float2 transformedUvDdx = transformedUvX - transformedUv;
|
||||
float2 transformedUvDdy = transformedUvY - transformedUv;
|
||||
|
||||
surface.m_color = GetDetailColor(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy);
|
||||
surface.m_normal = GetDetailNormal(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy);
|
||||
surface.m_roughness = GetDetailRoughness(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy);
|
||||
surface.m_specularF0 = GetDetailSpecularF0(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy);
|
||||
surface.m_metalness = GetDetailMetalness(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy);
|
||||
surface.m_occlusion = GetDetailOcclusion(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy);
|
||||
surface.m_height = GetDetailHeight(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy);
|
||||
}
|
||||
|
||||
// Debugs the detail material by choosing a random color per material ID and rendering it without blending.
|
||||
@@ -279,7 +296,7 @@ bool GetDetailSurface(inout DetailSurface surface, float2 detailMaterialIdCoord,
|
||||
float2 textureSize;
|
||||
TerrainSrg::m_detailMaterialIdImage.GetDimensions(textureSize.x, textureSize.y);
|
||||
|
||||
// The detail material id texture wraps since the "center" point can be anywhere in the texture, so mod by texturesize
|
||||
// The detail material id texture wraps since the "center" point can be anywhere in the texture, so mod by textureSize
|
||||
int2 detailMaterialIdTopLeft = ((int2(detailMaterialIdCoord) % textureSize) + textureSize) % textureSize;
|
||||
int2 detailMaterialIdBottomRight = (detailMaterialIdTopLeft + 1) % textureSize;
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderSystemInterface.h>
|
||||
|
||||
#include <Atom/Utils/MaterialUtils.h>
|
||||
|
||||
#include <SurfaceData/SurfaceDataSystemRequestBus.h>
|
||||
|
||||
namespace Terrain
|
||||
@@ -56,6 +58,13 @@ namespace Terrain
|
||||
static const char* const HeightFactor("parallax.factor");
|
||||
static const char* const HeightOffset("parallax.offset");
|
||||
static const char* const HeightBlendFactor("parallax.blendFactor");
|
||||
static const char* const UvCenter("uv.center");
|
||||
static const char* const UvScale("uv.scale");
|
||||
static const char* const UvTileU("uv.tileU");
|
||||
static const char* const UvTileV("uv.tileV");
|
||||
static const char* const UvOffsetU("uv.offsetU");
|
||||
static const char* const UvOffsetV("uv.offsetV");
|
||||
static const char* const UvRotateDegrees("uv.rotateDegrees");
|
||||
}
|
||||
|
||||
namespace TerrainSrgInputs
|
||||
@@ -548,6 +557,36 @@ namespace Terrain
|
||||
applyProperty(HeightOffset, shaderData.m_heightOffset);
|
||||
applyProperty(HeightBlendFactor, shaderData.m_heightBlendFactor);
|
||||
|
||||
AZ::Render::UvTransformDescriptor transformDescriptor;
|
||||
applyProperty(UvCenter, transformDescriptor.m_center);
|
||||
applyProperty(UvScale, transformDescriptor.m_scale);
|
||||
applyProperty(UvTileU, transformDescriptor.m_scaleX);
|
||||
applyProperty(UvTileV, transformDescriptor.m_scaleY);
|
||||
applyProperty(UvOffsetU, transformDescriptor.m_translateX);
|
||||
applyProperty(UvOffsetV, transformDescriptor.m_translateY);
|
||||
applyProperty(UvRotateDegrees, transformDescriptor.m_rotateDegrees);
|
||||
|
||||
AZStd::array<AZ::Render::TransformType, 3> order =
|
||||
{
|
||||
AZ::Render::TransformType::Rotate,
|
||||
AZ::Render::TransformType::Translate,
|
||||
AZ::Render::TransformType::Scale,
|
||||
};
|
||||
|
||||
AZ::Matrix3x3 uvTransformMatrix = AZ::Render::CreateUvTransformMatrix(transformDescriptor, order);
|
||||
uvTransformMatrix.GetRow(0).StoreToFloat3(&shaderData.m_uvTransform[0]);
|
||||
uvTransformMatrix.GetRow(1).StoreToFloat3(&shaderData.m_uvTransform[4]);
|
||||
uvTransformMatrix.GetRow(2).StoreToFloat3(&shaderData.m_uvTransform[8]);
|
||||
|
||||
// Store a hash of the matrix in element in an unused portion for quick comparisons in the shader
|
||||
size_t hash64 = 0;
|
||||
for (float value : shaderData.m_uvTransform)
|
||||
{
|
||||
AZStd::hash_combine(hash64, value);
|
||||
}
|
||||
uint32_t hash32 = uint32_t((hash64 ^ (hash64 >> 32)) & 0xFFFFFFFF);
|
||||
shaderData.m_uvTransform[3] = *reinterpret_cast<float*>(&hash32);
|
||||
|
||||
m_detailMaterialBufferNeedsUpdate = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Terrain
|
||||
|
||||
struct DetailMaterialShaderData
|
||||
{
|
||||
// Uv
|
||||
// Uv (data is 3x3, padding each row for explicit alignment)
|
||||
AZStd::array<float, 12> m_uvTransform
|
||||
{
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
|
||||
Reference in New Issue
Block a user