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.
monroegm-disable-blank-issue-2
Ken Pruiksma 4 years ago committed by GitHub
parent d77403f9df
commit ff4412db7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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);
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);
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);
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,

Loading…
Cancel
Save