Terrain detail and macro texture support (#4403)

* Terrain feature processor improvements regarding material, mesh, and lod
- Now using a material with pbr lighting for terrain. Removed some of the old shader code that wasn't needed anymore
- Move heightmap image declaration to the material so it's not needed in the object SRG anymore
- Added prototype code (commented out) for smoothing the terrain with a b-spline weighting function
- Mesh data is all made with a proper mesh asset now.
- Added basic LOD support (no continuous LOD yet, but it does pop between lod levels)
- Moved RenderCommon to be accessible publicly. It contains stencil refs that should be public.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Adding more material options to terrain shader

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Fixing terrain's per object srg because of changes in the default per object srg.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Added more features to the terrain material. Shader & Material reloads are now handled.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Added stub for terrain render max area setting in the renderer component. Added detail texture tiling setable from the material

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Missing change to material type from last commit

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Adding macro material in material type and support detail roughness.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Fixing merge issues in terrain material type

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Adding more features to the terrain material
- Macro color and normals
- Detail layer fade out
- Reoriented detail normals

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Remove line that unintentionally was added back during a rebase & merge.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Fixing previously deleted unused static that came back after a merge. Fixed an issue with macro normals in the terrain shader.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>
This commit is contained in:
Ken Pruiksma
2021-10-04 16:35:22 -05:00
committed by GitHub
parent 093f321b68
commit 7052ccfa52
9 changed files with 519 additions and 54 deletions
@@ -4,8 +4,27 @@
"parentMaterial": "",
"propertyLayoutVersion": 1,
"properties": {
"settings": {
"baseColor": [ 0.78, 0.59, 0.48 ]
"macroColor": {
"useTexture": false
},
"macroNormal": {
"useTexture": false
},
"baseColor": {
"color": [ 0.18, 0.18, 0.18 ],
"useTexture": false
},
"normal":
{
"useTexture": false
},
"roughness":
{
"useTexture": false
},
"specularF0":
{
"useTexture": false
}
}
}
@@ -100,25 +100,269 @@
}
},
{
"id": "baseColor",
"displayName": "Base Color",
"id": "detailTextureMultiplier",
"displayName": "Detail Texture UV Multiplier",
"description": "How many times to repeat the detail texture per sector",
"type": "Float",
"defaultValue": 8.0,
"connection": {
"type": "ShaderInput",
"id": "m_detailTextureMultiplier"
}
},
{
"id": "detailFadeDistance",
"displayName": "Detail Fade Distance (meters)",
"description": "The distance in meters that the detail texture starts to fade",
"type": "Float",
"defaultValue": 100.0,
"connection": {
"type": "ShaderInput",
"id": "m_detailFadeDistance"
}
},
{
"id": "detailFadeLength",
"displayName": "Detail Fade Length",
"description": "How far beyond Detail Fade Distance where the detail texture no longer applies.",
"type": "Float",
"defaultValue": 50.0,
"connection": {
"type": "ShaderInput",
"id": "m_detailFadeLength"
}
}
],
"macroColor": [
{
"id": "textureMap",
"displayName": "Texture",
"description": "Macro color texture map",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_macroColorMap"
}
},
{
"id": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture.",
"type": "Bool",
"defaultValue": true
}
],
"macroNormal": [
{
"id": "textureMap",
"displayName": "Texture",
"description": "Macro normal texture map",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_macroNormalMap"
}
},
{
"id": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture.",
"type": "Bool",
"defaultValue": true
},
{
"id": "flipX",
"displayName": "Flip X Channel",
"description": "Flip tangent direction for this normal map.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderInput",
"id": "m_flipMacroNormalX"
}
},
{
"id": "flipY",
"displayName": "Flip Y Channel",
"description": "Flip bitangent direction for this normal map.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderInput",
"id": "m_flipMacroNormalY"
}
}
],
"baseColor": [
{
"id": "color",
"displayName": "Color",
"description": "Color is displayed as sRGB but the values are stored as linear color.",
"type": "Color",
"defaultValue": [ 0.18, 0.18, 0.18 ],
"defaultValue": [ 1.0, 1.0, 1.0 ],
"connection": {
"type": "ShaderInput",
"id": "m_baseColor"
}
},
{
"id": "roughness",
"displayName": "Roughness",
"id": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the base color values. Zero (0.0) is black, white (1.0) is full color.",
"type": "Float",
"defaultValue": 1.0,
"min": 0.0,
"max": 1.0,
"connection": {
"type": "ShaderInput",
"id": "m_roughness"
"id": "m_baseColorFactor"
}
},
{
"id": "textureMap",
"displayName": "Texture",
"description": "Base color texture map",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_baseColorMap"
}
},
{
"id": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture.",
"type": "Bool",
"defaultValue": true
},
{
"id": "textureBlendMode",
"displayName": "Texture Blend Mode",
"description": "Selects the equation to use when combining Color, Factor, and Texture.",
"type": "Enum",
"enumValues": [ "Multiply", "LinearLight", "Lerp", "Overlay" ],
"defaultValue": "Overlay",
"connection": {
"type": "ShaderOption",
"id": "o_baseColorTextureBlendMode"
}
}
],
"normal": [
{
"id": "textureMap",
"displayName": "Texture",
"description": "Texture for defining surface normal direction.",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_normalMap"
}
},
{
"id": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture, or just rely on vertex normals.",
"type": "Bool",
"defaultValue": true
},
{
"id": "flipX",
"displayName": "Flip X Channel",
"description": "Flip tangent direction for this normal map.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderInput",
"id": "m_flipNormalX"
}
},
{
"id": "flipY",
"displayName": "Flip Y Channel",
"description": "Flip bitangent direction for this normal map.",
"type": "Bool",
"defaultValue": false,
"connection": {
"type": "ShaderInput",
"id": "m_flipNormalY"
}
},
{
"id": "factor",
"displayName": "Factor",
"description": "Strength factor for scaling the values",
"type": "Float",
"defaultValue": 1.0,
"min": 0.0,
"softMax": 2.0,
"connection": {
"type": "ShaderInput",
"id": "m_normalFactor"
}
}
],
"roughness": [
{
"id": "textureMap",
"displayName": "Texture",
"description": "Texture for defining surface roughness.",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_roughnessMap"
}
},
{
"id": "useTexture",
"description": "Whether to use the texture, or just default to the Factor value.",
"type": "Bool",
"defaultValue": true
},
{
"id": "factor",
"displayName": "Factor",
"description": "Controls the roughness value",
"type": "Float",
"defaultValue": 1.0,
"min": 0.0,
"max": 1.0,
"connection": {
"type": "ShaderInput",
"id": "m_roughnessFactor"
}
}
],
"specularF0": [
{
"id": "textureMap",
"displayName": "Texture",
"description": "Texture for defining surface reflectance.",
"type": "Image",
"connection": {
"type": "ShaderInput",
"id": "m_specularF0Map"
}
},
{
"id": "useTexture",
"displayName": "Use Texture",
"description": "Whether to use the texture, or just default to the Factor value.",
"type": "Bool",
"defaultValue": true
},
{
"id": "factor",
"displayName": "Factor",
"description": "The default IOR is 1.5, which gives you 0.04 (4% of light reflected at 0 degree angle for dielectric materials). F0 values lie in the range 0-0.08, so that is why the default F0 slider is set on 0.5.",
"type": "Float",
"defaultValue": 0.5,
"min": 0.0,
"max": 1.0,
"connection": {
"type": "ShaderInput",
"id": "m_specularF0Factor"
}
}
]
@@ -136,5 +380,55 @@
}
],
"functors": [
{
"type": "UseTexture",
"args": {
"textureProperty": "macroColor.textureMap",
"useTextureProperty": "macroColor.useTexture",
"shaderOption": "o_macroColor_useTexture"
}
},
{
"type": "UseTexture",
"args": {
"textureProperty": "macroNormal.textureMap",
"useTextureProperty": "macroNormal.useTexture",
"shaderOption": "o_macroNormal_useTexture"
}
},
{
"type": "UseTexture",
"args": {
"textureProperty": "baseColor.textureMap",
"useTextureProperty": "baseColor.useTexture",
"dependentProperties": ["baseColor.textureBlendMode"],
"shaderOption": "o_baseColor_useTexture"
}
},
{
"type": "UseTexture",
"args": {
"textureProperty": "specularF0.textureMap",
"useTextureProperty": "specularF0.useTexture",
"shaderOption": "o_specularF0_useTexture"
}
},
{
"type": "UseTexture",
"args": {
"textureProperty": "normal.textureMap",
"useTextureProperty": "normal.useTexture",
"dependentProperties": ["normal.factor", "normal.flipX", "normal.flipY"],
"shaderOption": "o_normal_useTexture"
}
},
{
"type": "UseTexture",
"args": {
"textureProperty": "roughness.textureMap",
"useTextureProperty": "roughness.useTexture",
"shaderOption": "o_roughness_useTexture"
}
}
]
}
@@ -7,6 +7,12 @@
#pragma once
#include <../Materials/Types/MaterialInputs/BaseColorInput.azsli>
#include <../Materials/Types/MaterialInputs/RoughnessInput.azsli>
#include <../Materials/Types/MaterialInputs/MetallicInput.azsli>
#include <../Materials/Types/MaterialInputs/SpecularInput.azsli>
#include <../Materials/Types/MaterialInputs/NormalInput.azsli>
ShaderResourceGroup ObjectSrg : SRG_PerObject
{
row_major float3x4 m_modelToWorld;
@@ -70,7 +76,10 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial
{
Texture2D<float> m_heightmapImage;
Texture2D m_heightmapImage;
float m_detailTextureMultiplier;
float m_detailFadeDistance;
float m_detailFadeLength;
Sampler HeightmapSampler
{
@@ -82,11 +91,52 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial
AddressW = Clamp;
};
Sampler m_sampler
{
AddressU = Wrap;
AddressV = Wrap;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
MaxAnisotropy = 16;
};
// Macro Color
Texture2D m_macroColorMap;
// Macro normal
Texture2D m_macroNormalMap;
bool m_flipMacroNormalX;
bool m_flipMacroNormalY;
// Base Color
float3 m_baseColor;
float m_roughness;
float m_baseColorFactor;
Texture2D m_baseColorMap;
// Normal
Texture2D m_normalMap;
bool m_flipNormalX;
bool m_flipNormalY;
float m_normalFactor;
// Roughness
Texture2D m_roughnessMap;
float m_roughnessFactor;
// Specular
Texture2D m_specularF0Map;
float m_specularF0Factor;
}
option bool o_useTerrainSmoothing = false;
option bool o_macroColor_useTexture = true;
option bool o_macroNormal_useTexture = true;
option bool o_baseColor_useTexture = true;
option bool o_specularF0_useTexture = true;
option bool o_normal_useTexture = true;
option bool o_roughness_useTexture = true;
option TextureBlendMode o_baseColorTextureBlendMode = TextureBlendMode::Multiply;
struct VertexInput
{
@@ -98,7 +148,7 @@ struct VertexInput
// This function samples a 4x4 neighborhood around the uv. Normally this would take 16 samples, but by taking
// advantage of bilinear filtering this can be done with 9 taps on the edges between pixels. The cost is further
// reduced by dropping the diagonals.
float SampleBSpline5Tap(Texture2D<float> texture, SamplerState textureSampler, float2 uv, float2 textureSize, float2 rcpTextureSize)
float SampleBSpline5Tap(Texture2D texture, SamplerState textureSampler, float2 uv, float2 textureSize, float2 rcpTextureSize)
{
// Think of sample locations in the 4x4 neighborhood as having a top left coordinate of 0,0 and
// a bottom right coordinate of 3,3.
@@ -71,18 +71,49 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN)
Surface surface;
// Position, Normal, Roughness
// Position
surface.position = IN.m_worldPosition.xyz;
surface.normal = normalize(IN.m_normal);
surface.roughnessLinear = TerrainMaterialSrg::m_roughness;
float viewDistance = length(ViewSrg::m_worldPosition - surface.position);
float detailFactor = saturate((viewDistance - TerrainMaterialSrg::m_detailFadeDistance) / max(TerrainMaterialSrg::m_detailFadeLength, EPSILON));
ObjectSrg::TerrainData terrainData = ObjectSrg::m_terrainData;
float2 origUv = lerp(terrainData.m_uvMin, terrainData.m_uvMax, IN.m_uv);
origUv.y = 1.0 - origUv.y;
float2 detailUv = IN.m_uv * TerrainMaterialSrg::m_detailTextureMultiplier;
// ------- Normal -------
float3 macroNormal = IN.m_normal;
if (o_macroNormal_useTexture)
{
macroNormal = GetNormalInputTS(TerrainMaterialSrg::m_macroNormalMap, TerrainMaterialSrg::m_sampler,
origUv, TerrainMaterialSrg::m_flipMacroNormalX, TerrainMaterialSrg::m_flipMacroNormalY, CreateIdentity3x3(), true, 1.0);
}
float3 detailNormal = GetNormalInputTS(TerrainMaterialSrg::m_normalMap, TerrainMaterialSrg::m_sampler,
detailUv, TerrainMaterialSrg::m_flipNormalX, TerrainMaterialSrg::m_flipNormalY, CreateIdentity3x3(), o_normal_useTexture, TerrainMaterialSrg::m_normalFactor);
detailNormal = ReorientTangentSpaceNormal(macroNormal, detailNormal);
surface.normal = lerp(detailNormal, macroNormal, detailFactor);
surface.normal = normalize(surface.normal);
// ------- Macro Color -------
float3 macroColor = GetBaseColorInput(TerrainMaterialSrg::m_macroColorMap, TerrainMaterialSrg::m_sampler, origUv, TerrainMaterialSrg::m_baseColor.rgb, o_baseColor_useTexture);
// ------- Base Color -------
float3 detailColor = GetBaseColorInput(TerrainMaterialSrg::m_baseColorMap, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_baseColor.rgb, o_baseColor_useTexture);
float3 blendedColor = BlendBaseColor(lerp(detailColor, TerrainMaterialSrg::m_baseColor.rgb, detailFactor), macroColor, TerrainMaterialSrg::m_baseColorFactor, o_baseColorTextureBlendMode, o_baseColor_useTexture);
// ------- Specular -------
float specularF0Factor = GetSpecularInput(TerrainMaterialSrg::m_specularF0Map, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_specularF0Factor, o_specularF0_useTexture);
specularF0Factor = lerp(specularF0Factor, 0.5, detailFactor);
surface.SetAlbedoAndSpecularF0(blendedColor, specularF0Factor, 0.0);
// ------- Roughness -------
surface.roughnessLinear = GetRoughnessInput(TerrainMaterialSrg::m_roughnessMap, TerrainMaterialSrg::m_sampler, detailUv, TerrainMaterialSrg::m_roughnessFactor, 0.0, 1.0, o_roughness_useTexture);
surface.roughnessLinear = lerp(surface.roughnessLinear, 1.0, detailFactor);
surface.CalculateRoughnessA();
// Albedo, SpecularF0
const float specularF0Factor = 0.5f;
float3 color = TerrainMaterialSrg::m_baseColor;
surface.SetAlbedoAndSpecularF0(color, specularF0Factor, 0.0);
// Clear Coat, Transmission
// Clear Coat, Transmission (Not used for terrain)
surface.clearCoat.InitializeToZero();
surface.transmission.InitializeToZero();
@@ -28,16 +28,27 @@ namespace Terrain
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<TerrainWorldRendererConfig, AZ::ComponentConfig>()->Version(1);
serialize->Class<TerrainWorldRendererConfig, AZ::ComponentConfig>()
->Version(1)
->Field("WorldSize", &TerrainWorldRendererConfig::m_worldSize)
;
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
edit->Class<TerrainWorldRendererConfig>("Terrain World Renderer Component", "Enables terrain rendering")
editContext->Class<TerrainWorldRendererConfig>("Terrain World Renderer Component", "Enables terrain rendering")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector<AZ::Crc32>({ AZ_CRC_CE("Level") }))
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true);
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector<AZ::Crc32>({ AZ_CRC_CE("Level") }))
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &TerrainWorldRendererConfig::m_worldSize, "Rendered world size", "The maximum amount of terrain that's rendered")
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_512Meters, "512 Meters")
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_1024Meters, "1 Kilometer")
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_2048Meters, "2 Kilometers")
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_4096Meters, "4 Kilometers")
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_8192Meters, "8 Kilometers")
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_16384Meters, "16 Kilometers")
;
}
}
}
@@ -72,6 +83,28 @@ namespace Terrain
TerrainWorldRendererComponent::TerrainWorldRendererComponent(const TerrainWorldRendererConfig& configuration)
: m_configuration(configuration)
{
switch (configuration.m_worldSize)
{
case TerrainWorldRendererConfig::WorldSize::_512Meters:
m_terrainFeatureProcessor->SetWorldSize(AZ::Vector2(512.0f, 512.0f));
break;
case TerrainWorldRendererConfig::WorldSize::_1024Meters:
m_terrainFeatureProcessor->SetWorldSize(AZ::Vector2(1024.0f, 1024.0f));
break;
case TerrainWorldRendererConfig::WorldSize::_2048Meters:
m_terrainFeatureProcessor->SetWorldSize(AZ::Vector2(2048.0f, 2048.0f));
break;
case TerrainWorldRendererConfig::WorldSize::_4096Meters:
m_terrainFeatureProcessor->SetWorldSize(AZ::Vector2(4096.0f, 4096.0f));
break;
case TerrainWorldRendererConfig::WorldSize::_8192Meters:
m_terrainFeatureProcessor->SetWorldSize(AZ::Vector2(8192.0f, 8192.0f));
break;
case TerrainWorldRendererConfig::WorldSize::_16384Meters:
m_terrainFeatureProcessor->SetWorldSize(AZ::Vector2(16384.0f, 16384.0f));
break;
}
}
TerrainWorldRendererComponent::~TerrainWorldRendererComponent()
@@ -27,13 +27,28 @@ namespace Terrain
{
class TerrainFeatureProcessor;
class TerrainWorldRendererConfig
struct TerrainWorldRendererConfig final
: public AZ::ComponentConfig
{
public:
AZ_CLASS_ALLOCATOR(TerrainWorldRendererConfig, AZ::SystemAllocator, 0);
AZ_RTTI(TerrainWorldRendererConfig, "{08C5863C-092D-4A69-8226-4978E4F6E343}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context);
enum class WorldSize : uint8_t
{
Unknown,
_512Meters,
_1024Meters,
_2048Meters,
_4096Meters,
_8192Meters,
_16384Meters,
WorldSizeCount,
};
WorldSize m_worldSize;
};
@@ -29,10 +29,10 @@ namespace Terrain
editContext->Class<EditorTerrainWorldRendererComponent>(
"Terrain World Renderer", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Terrain")
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/TerrainWorldRenderer.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/TerrainWorldRenderer.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector<AZ::Crc32>({ AZ_CRC_CE("Level") }))
->Attribute(AZ::Edit::Attributes::Category, "Terrain")
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/TerrainWorldRenderer.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/TerrainWorldRenderer.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector<AZ::Crc32>({ AZ_CRC_CE("Level") }))
;
}
}
@@ -76,26 +76,24 @@ namespace Terrain
void TerrainFeatureProcessor::Initialize()
{
{
// Load the terrain material asynchronously
const AZStd::string materialFilePath = "Materials/Terrain/DefaultPbrTerrain.azmaterial";
m_materialAssetLoader = AZStd::make_unique<AZ::RPI::AssetUtils::AsyncAssetLoader>();
*m_materialAssetLoader = AZ::RPI::AssetUtils::AsyncAssetLoader::Create<AZ::RPI::MaterialAsset>(materialFilePath, 0u,
[&](AZ::Data::Asset<AZ::Data::AssetData> assetData, bool success) -> void
// Load the terrain material asynchronously
const AZStd::string materialFilePath = "Materials/Terrain/DefaultPbrTerrain.azmaterial";
m_materialAssetLoader = AZStd::make_unique<AZ::RPI::AssetUtils::AsyncAssetLoader>();
*m_materialAssetLoader = AZ::RPI::AssetUtils::AsyncAssetLoader::Create<AZ::RPI::MaterialAsset>(materialFilePath, 0u,
[&](AZ::Data::Asset<AZ::Data::AssetData> assetData, bool success) -> void
{
const AZ::Data::Asset<AZ::RPI::MaterialAsset>& materialAsset = static_cast<AZ::Data::Asset<AZ::RPI::MaterialAsset>>(assetData);
if (success)
{
const AZ::Data::Asset<AZ::RPI::MaterialAsset>& materialAsset = static_cast<AZ::Data::Asset<AZ::RPI::MaterialAsset>>(assetData);
if (success)
m_materialInstance = AZ::RPI::Material::FindOrCreate(assetData);
AZ::RPI::MaterialReloadNotificationBus::Handler::BusConnect(materialAsset->GetId());
if (!materialAsset->GetObjectSrgLayout())
{
m_materialInstance = AZ::RPI::Material::FindOrCreate(assetData);
if (!materialAsset->GetObjectSrgLayout())
{
AZ_Error("TerrainFeatureProcessor", false, "No per-object ShaderResourceGroup found on terrain material.");
}
AZ_Error("TerrainFeatureProcessor", false, "No per-object ShaderResourceGroup found on terrain material.");
}
}
);
}
}
);
if (!InitializePatchModel())
{
AZ_Error(TerrainFPName, false, "Failed to create Terrain render buffers!");
@@ -105,10 +103,9 @@ namespace Terrain
void TerrainFeatureProcessor::Deactivate()
{
DisableSceneNotification();
m_patchModel = {};
m_areaData = {};
AZ::RPI::MaterialReloadNotificationBus::Handler::BusDisconnect();
}
void TerrainFeatureProcessor::Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet)
@@ -291,7 +288,7 @@ namespace Terrain
AZ::Vector2 sectorCenterXY = AZ::Vector2(sectorData.m_aabb.GetCenter().GetX(), sectorData.m_aabb.GetCenter().GetY());
float sectorDistance = sectorCenterXY.GetDistance(cameraPositionXY);
float lodForCamera = ceilf(AZ::GetMax(0.0f, log2f(sectorDistance / (GridMeters * 4.0f))));
float lodForCamera = floorf(AZ::GetMax(0.0f, log2f(sectorDistance / (GridMeters * 4.0f))));
lodChoice = AZ::GetMin(lodChoice, aznumeric_cast<uint8_t>(lodForCamera));
}
}
@@ -317,6 +314,7 @@ namespace Terrain
uint16_t gridVertices = gridSize + 1; // For m_gridSize quads, (m_gridSize + 1) vertices are needed.
size_t size = gridVertices * gridVertices;
size *= size;
patchdata.m_positions.reserve(size);
patchdata.m_uvs.reserve(size);
@@ -432,4 +430,21 @@ namespace Terrain
return success;
}
void TerrainFeatureProcessor::OnMaterialReinitialized([[maybe_unused]] const AZ::Data::Instance<AZ::RPI::Material>& material)
{
for (auto& sectorData : m_sectorData)
{
for (auto& drawPacket : sectorData.m_drawPackets)
{
drawPacket.Update(*GetParentScene());
}
}
}
void TerrainFeatureProcessor::SetWorldSize([[maybe_unused]] AZ::Vector2 sizeInMeters)
{
// This will control the max rendering size. Actual terrain size can be much
// larger but this will limit how much is rendered.
}
}
@@ -27,6 +27,7 @@
#include <Atom/RHI/StreamBufferView.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
#include <Atom/RPI.Public/Material/MaterialReloadNotificationBus.h>
namespace AZ::RPI
{
@@ -41,6 +42,7 @@ namespace Terrain
{
class TerrainFeatureProcessor final
: public AZ::RPI::FeatureProcessor
, private AZ::RPI::MaterialReloadNotificationBus::Handler
{
public:
AZ_RTTI(TerrainFeatureProcessor, "{D7DAC1F9-4A9F-4D3C-80AE-99579BF8AB1C}", AZ::RPI::FeatureProcessor);
@@ -52,12 +54,18 @@ namespace Terrain
TerrainFeatureProcessor() = default;
~TerrainFeatureProcessor() = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
// AZ::Component overrides...
void Activate() override;
void Deactivate() override;
// AZ::RPI::FeatureProcessor overrides...
void Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet) override;
// AZ::RPI::MaterialReloadNotificationBus::Handler overrides...
void OnMaterialReinitialized(const AZ::Data::Instance<AZ::RPI::Material>& material) override;
void SetWorldSize(AZ::Vector2 sizeInMeters);
void UpdateTerrainData(const AZ::Transform& transform, const AZ::Aabb& worldBounds, float sampleSpacing,
uint32_t width, uint32_t height, const AZStd::vector<float>& heightData);