Various terrain improvements: (#3942)

* Various terrain improvements:
- Height now stored in a R16_unorm to decrease the amount of memory / memory bandwidth needed for the height field
- Many simplifications to the feature processor
- Added a shader to render to the depth pre pass
- Pulled common functionality needed by depth and forward to a common include shader
- Forward shader now outputs to all the expected render targets
- Adjusted the way normals and lighting are being calculated

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

* Adding missing shader files. Updated terrain shader to alter the color slightly with height.

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

* Removed pixel shader code from terrain depth pass

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

* Renamed the depth pass shaders to no longer indicate they include a pixel shader.

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

* Removing unneeded code from TerrainCommon.azsli

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>
This commit is contained in:
Ken Pruiksma
2021-09-09 12:56:16 -05:00
committed by GitHub
parent 6cb2222da8
commit d17ac748ac
11 changed files with 357 additions and 237 deletions
@@ -41,12 +41,9 @@ namespace Terrain
namespace ShaderInputs
{
static const char* const HeightmapImage("HeightmapImage");
static const char* const HeightmapImage("m_heightmapImage");
static const char* const ModelToWorld("m_modelToWorld");
static const char* const HeightScale("m_heightScale");
static const char* const UvMin("m_uvMin");
static const char* const UvMax("m_uvMax");
static const char* const UvStep("m_uvStep");
static const char* const TerrainData("m_terrainData");
}
@@ -68,76 +65,76 @@ namespace Terrain
EnableSceneNotification();
}
void TerrainFeatureProcessor::ConfigurePipelineState(ShaderState& shaderState, bool assertOnFail)
{
bool success = GetParentScene()->ConfigurePipelineState(shaderState.m_shader->GetDrawListTag(), shaderState.m_pipelineStateDescriptor);
AZ_Assert(success || !assertOnFail, "Couldn't configure the pipeline state.");
if (success)
{
shaderState.m_pipelineState = shaderState.m_shader->AcquirePipelineState(shaderState.m_pipelineStateDescriptor);
AZ_Assert(shaderState.m_pipelineState, "Failed to acquire default pipeline state.");
}
}
void TerrainFeatureProcessor::InitializeAtomStuff()
{
m_rhiSystem = AZ::RHI::RHISystemInterface::Get();
{
// Load the shader
constexpr const char* TerrainShaderFilePath = "Shaders/Terrain/Terrain.azshader";
m_shader = AZ::RPI::LoadShader(TerrainShaderFilePath);
if (!m_shader)
{
auto LoadShader = [this](const char* filePath, ShaderState& shaderState)
{
AZ_Error(TerrainFPName, false, "Failed to find or create a shader instance from shader asset '%s'", TerrainShaderFilePath);
shaderState.m_shader = AZ::RPI::LoadShader(filePath);
if (!shaderState.m_shader)
{
AZ_Error(TerrainFPName, false, "Failed to find or create a shader instance from shader asset '%s'", filePath);
return;
}
// Create the data layout
shaderState.m_pipelineStateDescriptor = AZ::RHI::PipelineStateDescriptorForDraw{};
{
AZ::RHI::InputStreamLayoutBuilder layoutBuilder;
layoutBuilder.AddBuffer()
->Channel("POSITION", AZ::RHI::Format::R32G32_FLOAT)
->Channel("UV", AZ::RHI::Format::R32G32_FLOAT)
;
shaderState.m_pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
}
auto shaderVariant = shaderState.m_shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId);
shaderVariant.ConfigurePipelineState(shaderState.m_pipelineStateDescriptor);
// If this fails to run now, it's ok, we'll initialize it in OnRenderPipelineAdded later.
ConfigurePipelineState(shaderState, false);
};
LoadShader("Shaders/Terrain/Terrain.azshader", m_shaderStates[ShaderType::Forward]);
LoadShader("Shaders/Terrain/Terrain_DepthPass_WithPS.azshader", m_shaderStates[ShaderType::Depth]);
// Forward and depth shader use same srg layout.
AZ::RHI::Ptr<AZ::RHI::ShaderResourceGroupLayout> perObjectSrgLayout =
m_shaderStates[ShaderType::Forward].m_shader->FindShaderResourceGroupLayout(AZ::Name{"ObjectSrg"});
if (!perObjectSrgLayout)
{
AZ_Error(TerrainFPName, false, "Failed to get shader resource group layout");
return;
}
else if (!perObjectSrgLayout->IsFinalized())
{
AZ_Error(TerrainFPName, false, "Shader resource group layout is not loaded");
return;
}
// Create the data layout
m_pipelineStateDescriptor = AZ::RHI::PipelineStateDescriptorForDraw{};
{
AZ::RHI::InputStreamLayoutBuilder layoutBuilder;
layoutBuilder.AddBuffer()
->Channel("POSITION", AZ::RHI::Format::R32G32_FLOAT)
->Channel("UV", AZ::RHI::Format::R32G32_FLOAT)
;
m_pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
}
auto shaderVariant = m_shader->GetVariant(AZ::RPI::ShaderAsset::RootShaderVariantStableId);
shaderVariant.ConfigurePipelineState(m_pipelineStateDescriptor);
m_drawListTag = m_shader->GetDrawListTag();
m_perObjectSrgAsset = m_shader->FindShaderResourceGroupLayout(AZ::Name{"ObjectSrg"});
if (!m_perObjectSrgAsset)
{
AZ_Error(TerrainFPName, false, "Failed to get shader resource group asset");
return;
}
else if (!m_perObjectSrgAsset->IsFinalized())
{
AZ_Error(TerrainFPName, false, "Shader resource group asset is not loaded");
return;
}
const AZ::RHI::ShaderResourceGroupLayout* shaderResourceGroupLayout = &(*m_perObjectSrgAsset);
m_heightmapImageIndex = shaderResourceGroupLayout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::HeightmapImage));
m_heightmapImageIndex = perObjectSrgLayout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::HeightmapImage));
AZ_Error(TerrainFPName, m_heightmapImageIndex.IsValid(), "Failed to find shader input image %s.", ShaderInputs::HeightmapImage);
m_modelToWorldIndex = shaderResourceGroupLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld));
m_modelToWorldIndex = perObjectSrgLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld));
AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld);
m_heightScaleIndex = shaderResourceGroupLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::HeightScale));
AZ_Error(TerrainFPName, m_heightScaleIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::HeightScale);
m_uvMinIndex = shaderResourceGroupLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::UvMin));
AZ_Error(TerrainFPName, m_uvMinIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::UvMin);
m_uvMaxIndex = shaderResourceGroupLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::UvMax));
AZ_Error(TerrainFPName, m_uvMaxIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::UvMax);
m_uvStepIndex = shaderResourceGroupLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::UvStep));
AZ_Error(TerrainFPName, m_uvStepIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::UvStep);
// If this fails to run now, it's ok, we'll initialize it in OnRenderPipelineAdded later.
bool success = GetParentScene()->ConfigurePipelineState(m_shader->GetDrawListTag(), m_pipelineStateDescriptor);
if (success)
{
m_pipelineState = m_shader->AcquirePipelineState(m_pipelineStateDescriptor);
AZ_Assert(m_pipelineState, "Failed to acquire default pipeline state for shader '%s'", TerrainShaderFilePath);
}
m_terrainDataIndex = perObjectSrgLayout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData));
AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData);
}
AZ::RHI::BufferPoolDescriptor dmaPoolDescriptor;
@@ -165,12 +162,9 @@ namespace Terrain
void TerrainFeatureProcessor::OnRenderPipelineAdded([[maybe_unused]] AZ::RPI::RenderPipelinePtr pipeline)
{
bool success = GetParentScene()->ConfigurePipelineState(m_drawListTag, m_pipelineStateDescriptor);
AZ_Assert(success, "Couldn't configure the pipeline state.");
if (success)
for (ShaderState& shaderState: m_shaderStates)
{
m_pipelineState = m_shader->AcquirePipelineState(m_pipelineStateDescriptor);
AZ_Assert(m_pipelineState, "Failed to acquire default pipeline state.");
ConfigurePipelineState(shaderState, true);
}
}
@@ -206,7 +200,7 @@ namespace Terrain
void TerrainFeatureProcessor::UpdateTerrainData(
const AZ::Transform& transform,
const AZ::Aabb& worldBounds,
[[maybe_unused]] float sampleSpacing,
float sampleSpacing,
uint32_t width, uint32_t height, const AZStd::vector<float>& heightData)
{
if (!worldBounds.IsValid())
@@ -219,6 +213,7 @@ namespace Terrain
m_areaData.m_terrainBounds = worldBounds;
m_areaData.m_heightmapImageHeight = height;
m_areaData.m_heightmapImageWidth = width;
m_areaData.m_sampleSpacing = sampleSpacing;
// Create heightmap image data
{
@@ -228,13 +223,22 @@ namespace Terrain
imageSize.m_width = width;
imageSize.m_height = height;
AZStd::vector<uint16_t> uint16Heights;
uint16Heights.reserve(heightData.size());
for (float sampleHeight : heightData)
{
float clampedSample = AZ::GetClamp(sampleHeight, 0.0f, 1.0f);
constexpr uint16_t MaxUint16 = 0xFFFF;
uint16Heights.push_back(aznumeric_cast<uint16_t>(clampedSample * MaxUint16));
}
AZ::Data::Instance<AZ::RPI::StreamingImagePool> streamingImagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemStreamingPool();
m_areaData.m_heightmapImage = AZ::RPI::StreamingImage::CreateFromCpuData(*streamingImagePool,
AZ::RHI::ImageDimension::Image2D,
imageSize,
AZ::RHI::Format::R32_FLOAT,
(uint8_t*)heightData.data(),
heightData.size() * sizeof(float));
AZ::RHI::Format::R16_UNORM,
(uint8_t*)uint16Heights.data(),
heightData.size() * sizeof(uint16_t));
AZ_Error(TerrainFPName, m_areaData.m_heightmapImage, "Failed to initialize the heightmap image!");
}
@@ -244,7 +248,8 @@ namespace Terrain
{
AZ_PROFILE_FUNCTION(AzRender);
if (m_drawListTag.IsNull())
if (m_shaderStates[ShaderType::Forward].m_shader->GetDrawListTag().IsNull() ||
m_shaderStates[ShaderType::Depth].m_shader->GetDrawListTag().IsNull())
{
return;
}
@@ -256,6 +261,7 @@ namespace Terrain
if (m_areaData.m_propertiesDirty)
{
m_areaData.m_propertiesDirty = false;
m_sectorData.clear();
AZ::RHI::DrawPacketBuilder drawPacketBuilder;
@@ -281,17 +287,17 @@ namespace Terrain
drawPacketBuilder.Begin(nullptr);
drawPacketBuilder.SetDrawArguments(drawIndexed);
drawPacketBuilder.SetIndexBufferView(m_indexBufferView);
auto& forwardShader = m_shaderStates[ShaderType::Forward].m_shader;
auto resourceGroup = AZ::RPI::ShaderResourceGroup::Create(m_shader->GetAsset(), m_shader->GetSupervariantIndex(), AZ::Name("ObjectSrg"));
//auto m_resourceGroup = AZ::RPI::ShaderResourceGroup::Create(m_shader->GetAsset(), AZ::Name("ObjectSrg"));
auto resourceGroup = AZ::RPI::ShaderResourceGroup::Create(forwardShader->GetAsset(), forwardShader->GetSupervariantIndex(), AZ::Name("ObjectSrg"));
if (!resourceGroup)
{
AZ_Error(TerrainFPName, false, "Failed to create shader resource group");
return;
}
float uvMin[2] = { 0.0f, 0.0f };
float uvMax[2] = { 1.0f, 1.0f };
AZStd::array<float, 2> uvMin = { 0.0f, 0.0f };
AZStd::array<float, 2> uvMax = { 1.0f, 1.0f };
uvMin[0] = (float)((xPatch - m_areaData.m_terrainBounds.GetMin().GetX()) / m_areaData.m_terrainBounds.GetXExtent());
uvMin[1] = (float)((yPatch - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent());
@@ -301,7 +307,7 @@ namespace Terrain
uvMax[1] =
(float)(((yPatch + m_gridMeters) - m_areaData.m_terrainBounds.GetMin().GetY()) / m_areaData.m_terrainBounds.GetYExtent());
float uvStep[2] =
AZStd::array<float, 2> uvStep =
{
1.0f / m_areaData.m_heightmapImageWidth, 1.0f / m_areaData.m_heightmapImageHeight,
};
@@ -313,18 +319,32 @@ namespace Terrain
resourceGroup->SetImage(m_heightmapImageIndex, m_areaData.m_heightmapImage);
resourceGroup->SetConstant(m_modelToWorldIndex, matrix3x4);
resourceGroup->SetConstant(m_heightScaleIndex, m_areaData.m_heightScale);
resourceGroup->SetConstant(m_uvMinIndex, uvMin);
resourceGroup->SetConstant(m_uvMaxIndex, uvMax);
resourceGroup->SetConstant(m_uvStepIndex, uvStep);
ShaderTerrainData terrainDataForSrg;
terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing;
terrainDataForSrg.m_heightScale = m_areaData.m_heightScale;
terrainDataForSrg.m_uvMin = uvMin;
terrainDataForSrg.m_uvMax = uvMax;
terrainDataForSrg.m_uvStep = uvStep;
resourceGroup->SetConstant(m_terrainDataIndex, terrainDataForSrg);
resourceGroup->Compile();
drawPacketBuilder.AddShaderResourceGroup(resourceGroup->GetRHIShaderResourceGroup());
AZ::RHI::DrawPacketBuilder::DrawRequest drawRequest;
drawRequest.m_listTag = m_drawListTag;
drawRequest.m_pipelineState = m_pipelineState.get();
drawRequest.m_streamBufferViews = AZStd::array_view<AZ::RHI::StreamBufferView>(&m_vertexBufferView, 1);
drawPacketBuilder.AddDrawItem(drawRequest);
auto addDrawItem = [&](ShaderState& shaderState)
{
AZ::RHI::DrawPacketBuilder::DrawRequest drawRequest;
drawRequest.m_listTag = shaderState.m_shader->GetDrawListTag();
drawRequest.m_pipelineState = shaderState.m_pipelineState.get();
drawRequest.m_streamBufferViews = AZStd::array_view<AZ::RHI::StreamBufferView>(&m_vertexBufferView, 1);
drawPacketBuilder.AddDrawItem(drawRequest);
};
for (ShaderState& shaderState : m_shaderStates)
{
addDrawItem(shaderState);
}
//addDrawItem(m_shaderStates[ShaderType::Forward]);
m_sectorData.emplace_back(
drawPacketBuilder.End(),
@@ -368,16 +388,16 @@ namespace Terrain
uint16_t startIndex = (uint16_t)(m_gridVertices.size());
m_gridVertices.emplace_back(x0, y0, x0 / m_gridMeters, y0 / m_gridMeters);
m_gridVertices.emplace_back(x0, y1, x0 / m_gridMeters, y1 / m_gridMeters);
m_gridVertices.emplace_back(x1, y0, x1 / m_gridMeters, y0 / m_gridMeters);
m_gridVertices.emplace_back(x0, y1, x0 / m_gridMeters, y1 / m_gridMeters);
m_gridVertices.emplace_back(x1, y1, x1 / m_gridMeters, y1 / m_gridMeters);
m_gridIndices.emplace_back(startIndex);
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 1));
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 2));
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 1));
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 2));
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 3));
m_gridIndices.emplace_back(aznumeric_cast<uint16_t>(startIndex + 2));
}
}
}
@@ -441,8 +461,6 @@ namespace Terrain
m_vertexBufferView = AZ::RHI::StreamBufferView(
*buffer, 0, static_cast<uint32_t>(elementSize), static_cast<uint32_t>(sizeof(Vertex)));
AZ::RHI::ValidateStreamBufferViews(m_pipelineStateDescriptor.m_inputStreamLayout, { { m_vertexBufferView } });
}
m_hostPool->UnmapBuffer(*buffer);
@@ -459,8 +477,9 @@ namespace Terrain
m_indexBufferView = {};
m_vertexBufferView = {};
m_pipelineStateDescriptor = AZ::RHI::PipelineStateDescriptorForDraw{};
m_pipelineState = nullptr;
for (ShaderState& shaderState : m_shaderStates)
{
shaderState.Reset();
}
}
}