/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace AtomSampleViewer { const AZ::Color ShadowExampleComponent::DirectionalLightColor = AZ::Color::CreateOne(); AZ::Color ShadowExampleComponent::s_positionalLightColors[] = { // they will be initialized in the constructor. AZ::Color::CreateZero(), AZ::Color::CreateZero(), AZ::Color::CreateZero() }; const AZ::Render::ShadowmapSize ShadowExampleComponent::s_shadowmapImageSizes[] = { AZ::Render::ShadowmapSize::Size256, AZ::Render::ShadowmapSize::Size512, AZ::Render::ShadowmapSize::Size1024, AZ::Render::ShadowmapSize::Size2048 }; const char* ShadowExampleComponent::s_shadowmapImageSizeLabels[] = { "256", "512", "1024", "2048" }; const AZ::Render::ShadowFilterMethod ShadowExampleComponent::s_shadowFilterMethods[] = { AZ::Render::ShadowFilterMethod::None, AZ::Render::ShadowFilterMethod::Pcf, AZ::Render::ShadowFilterMethod::Esm, AZ::Render::ShadowFilterMethod::EsmPcf }; const char* ShadowExampleComponent::s_shadowFilterMethodLabels[] = { "None", "PCF", "ESM", "ESM+PCF" }; ShadowExampleComponent::ShadowExampleComponent() : m_imguiSidebar("@user@/ShadowExampleComponent/sidebar.xml") { s_positionalLightColors[0] = AZ::Colors::Red; s_positionalLightColors[1] = AZ::Colors::Green; s_positionalLightColors[2] = AZ::Colors::Blue; } void ShadowExampleComponent::Reflect(AZ::ReflectContext* context) { if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { serializeContext->Class() ->Version(0) ; } } void ShadowExampleComponent::Activate() { using namespace AZ; m_sampleName = "ShadowExampleComponent"; // Don't continue the script until assets are ready and scene is setup ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScriptWithTimeout, 120.0f); // preload assets AZStd::vector assetList = { {DefaultPbrMaterialPath, azrtti_typeid()}, {BunnyModelFilePath, azrtti_typeid()}, {CubeModelFilePath, azrtti_typeid()} }; PreloadAssets(assetList); // initialize and then reset IBL, which will disable it by binding the default cubemaps m_defaultIbl.Init(m_scene); m_defaultIbl.Reset(); } void ShadowExampleComponent::OnAllAssetsReadyActivate() { using namespace AZ; // Load the assets m_bunnyModelAsset = RPI::AssetUtils::GetAssetByProductPath(BunnyModelFilePath, RPI::AssetUtils::TraceLevel::Assert); m_floorModelAsset = RPI::AssetUtils::GetAssetByProductPath(CubeModelFilePath, RPI::AssetUtils::TraceLevel::Assert); m_materialAsset = RPI::AssetUtils::GetAssetByProductPath(DefaultPbrMaterialPath, RPI::AssetUtils::TraceLevel::Assert); m_bunnyMeshIsReady = false; m_floorMeshIsReady = false; m_directionalLightImageSizeIndex = 2; // image size is 1024. for (uint32_t index = 0; index < PositionalLightCount; ++index) { m_positionalLightImageSizeIndices[index] = 2; // image size is 1024. } m_cascadeCount = 2; m_ratioLogarithmUniform = 0.5f; UseArcBallCameraController(); m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor(); m_diskLightFeatureProcessor = m_scene->GetFeatureProcessor(); m_pointLightFeatureProcessor = m_scene->GetFeatureProcessor(); CreateMeshes(); CreateDirectionalLight(); CreateDiskLights(); m_elapsedTime = 0.f; m_imguiSidebar.Activate(); AZ::TickBus::Handler::BusConnect(); } void ShadowExampleComponent::Deactivate() { AZ::TickBus::Handler::BusDisconnect(); RestoreCameraConfiguration(); RemoveController(); GetMeshFeatureProcessor()->ReleaseMesh(m_floorMeshHandle); GetMeshFeatureProcessor()->ReleaseMesh(m_bunnyMeshHandle); DestroyDiskLights(); DestroyPointLights(); m_directionalLightFeatureProcessor->ReleaseLight(m_directionalLightHandle); m_imguiSidebar.Deactivate(); } void ShadowExampleComponent::OnTick(float deltaTime, AZ::ScriptTimePoint) { using namespace AZ; constexpr float directionalLightPeriodTime = 5.f; // 5 seconds for a rotation of directional light position. constexpr float lightPeriodTime = 7.f; // 7 seconds for a rotation of light positions. if (m_elapsedTime == 0.f) { SaveCameraConfiguration(); SetInitialArcBallControllerParams(); SetInitialShadowParams(); } m_elapsedTime += deltaTime; if (m_isDirectionalLightAutoRotate) { m_directionalLightRotationAngle = fmodf(m_directionalLightRotationAngle + deltaTime * Constants::TwoPi / directionalLightPeriodTime, Constants::TwoPi); } if (m_isPositionalLightAutoRotate) { m_positionalLightRotationAngle = fmodf(m_positionalLightRotationAngle - deltaTime * Constants::TwoPi / lightPeriodTime + Constants::TwoPi, Constants::TwoPi); } UpdateDirectionalLight(); for (uint32_t index = 0; index < PositionalLightCount; ++index) { const auto transform = GetTransformForLight(index); if (m_diskLightHandles[index].IsValid()) { m_diskLightFeatureProcessor->SetPosition(m_diskLightHandles[index], transform.GetTranslation()); m_diskLightFeatureProcessor->SetDirection(m_diskLightHandles[index], transform.GetBasis(1)); } if (m_pointLightHandles[index].IsValid()) { m_pointLightFeatureProcessor->SetPosition(m_pointLightHandles[index], transform.GetTranslation()); } } Camera::CameraRequestBus::Event( GetCameraEntityId(), &Camera::CameraRequestBus::Events::SetFovRadians, m_cameraFovY); DrawSidebar(); } AZ::Transform ShadowExampleComponent::GetTransformForLight(const uint32_t index) const { constexpr float diskLightDist = 5.f; using namespace AZ; const float angle = m_positionalLightRotationAngle + index * Constants::TwoPi / 3; const auto location = Vector3(diskLightDist * sinf(angle), diskLightDist * cosf(angle), m_positionalLightHeights[index]); const auto transform = Transform::CreateLookAt(location, Vector3::CreateZero()); return transform; } float ShadowExampleComponent::GetAttenuationForLight(const uint32_t index) const { return sqrtf(m_lightIntensities[index] / CutoffIntensity); } AZ::Render::PhotometricColor ShadowExampleComponent::GetRgbIntensityForLight( const uint32_t index) const { AZ::Render::PhotometricColor lightRgbIntensity( s_positionalLightColors[index] * m_lightIntensities[index]); return lightRgbIntensity; } AZStd::pair ShadowExampleComponent::GetConeAnglesForLight(const uint32_t index) const { const float innerConeAngle = AZ::DegToRad(m_outerConeAngles[index]) * ConeAngleInnerRatio; const float outerConeAngle = AZ::DegToRad(m_outerConeAngles[index]); return AZStd::make_pair(innerConeAngle, outerConeAngle); } void ShadowExampleComponent::UpdateDirectionalLight() { using namespace AZ; constexpr float directionalLightDist = 10.f; // Directional Light Transform { const auto lightLocation = Vector3( directionalLightDist * sinf(m_directionalLightRotationAngle), directionalLightDist * cosf(m_directionalLightRotationAngle), m_directionalLightHeight); const auto lightTransform = Transform::CreateLookAt(lightLocation, Vector3::CreateZero()); m_directionalLightFeatureProcessor->SetDirection(m_directionalLightHandle, lightTransform.GetBasis(1)); } // Camera Configuration { Camera::Configuration config; Camera::CameraRequestBus::EventResult(config, GetCameraEntityId(), &Camera::CameraRequestBus::Events::GetCameraConfiguration); m_directionalLightFeatureProcessor->SetCameraConfiguration(m_directionalLightHandle, config); } // Camera Transform { Transform transform = Transform::CreateIdentity(); TransformBus::EventResult(transform, GetCameraEntityId(), &TransformBus::Events::GetWorldTM); m_directionalLightFeatureProcessor->SetCameraTransform(m_directionalLightHandle, transform); } } void ShadowExampleComponent::SaveCameraConfiguration() { Camera::CameraRequestBus::EventResult( m_originalFarClipDistance, GetCameraEntityId(), &Camera::CameraRequestBus::Events::GetFarClipDistance); Camera::CameraRequestBus::EventResult( m_originalCameraFovRadians, GetCameraEntityId(), &Camera::CameraRequestBus::Events::GetFovRadians); } void ShadowExampleComponent::RestoreCameraConfiguration() { Camera::CameraRequestBus::Event( GetCameraEntityId(), &Camera::CameraRequestBus::Events::SetFarClipDistance, m_originalFarClipDistance); Camera::CameraRequestBus::Event( GetCameraEntityId(), &Camera::CameraRequestBus::Events::SetFovRadians, m_originalCameraFovRadians); } void ShadowExampleComponent::UseArcBallCameraController() { using namespace AZ; Debug::CameraControllerRequestBus::Event( GetCameraEntityId(), &Debug::CameraControllerRequestBus::Events::Enable, azrtti_typeid()); } void ShadowExampleComponent::SetInitialArcBallControllerParams() { using namespace AZ; const auto cameraLocation = Vector3(0.f, -2.f, 2.f); Debug::ArcBallControllerRequestBus::Event( GetCameraEntityId(), &Debug::ArcBallControllerRequestBus::Events::SetCenter, cameraLocation); Debug::ArcBallControllerRequestBus::Event( GetCameraEntityId(), &Debug::ArcBallControllerRequestBus::Events::SetPitch, -Constants::QuarterPi); Debug::ArcBallControllerRequestBus::Event( GetCameraEntityId(), &Debug::ArcBallControllerRequestBus::Events::SetDistance, 50.f); } void ShadowExampleComponent::SetInitialShadowParams() { Camera::CameraRequestBus::Event( GetCameraEntityId(), &Camera::CameraRequestBus::Events::SetFovRadians, AZ::Constants::QuarterPi); } void ShadowExampleComponent::RemoveController() { AZ::Debug::CameraControllerRequestBus::Event( GetCameraEntityId(), &AZ::Debug::CameraControllerRequestBus::Events::Disable); } void ShadowExampleComponent::CreateMeshes() { using namespace AZ; auto updateFloorTransform = [&](const Data::Instance& model) { const AZ::Aabb& aabb = model->GetModelAsset()->GetAabb(); const float maxZ = aabb.GetMax().GetZ(); const AZ::Vector3 nonUniformScale{ 12.f, 12.f, 0.1f }; const AZ::Vector3 translation{ 0.f, 0.f, -maxZ * nonUniformScale.GetZ() }; const auto transform = AZ::Transform::CreateTranslation(translation); GetMeshFeatureProcessor()->SetTransform(m_floorMeshHandle, transform, nonUniformScale); m_floorMeshIsReady = true; if (m_bunnyMeshIsReady || m_floorMeshIsReady) { // Now that the models are initialized, we can allow the script to continue. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript); } }; auto updateBunnyTransform = [&](const Data::Instance& model) { const AZ::Aabb& aabb = model->GetModelAsset()->GetAabb(); const float minZ = aabb.GetMin().GetZ(); const AZ::Vector3 translation{ 0.f, 0.f, -minZ }; auto transform = AZ::Transform::CreateTranslation(translation); transform.SetUniformScale(1.5f); GetMeshFeatureProcessor()->SetTransform(m_bunnyMeshHandle, transform); m_bunnyMeshIsReady = true; if (m_bunnyMeshIsReady || m_floorMeshIsReady) { // Now that the models are initialized, we can allow the script to continue. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript); } }; m_materialInstance = RPI::Material::FindOrCreate(m_materialAsset); { Render::MeshHandleDescriptor descriptor(m_floorModelAsset, m_materialInstance); descriptor.m_modelChangedEventHandler = AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler(updateFloorTransform); m_floorMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(descriptor); } { Render::MeshHandleDescriptor descriptor(m_bunnyModelAsset, m_materialInstance); descriptor.m_modelChangedEventHandler = AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler(updateBunnyTransform); m_bunnyMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(descriptor); } } void ShadowExampleComponent::CreateDirectionalLight() { using namespace AZ; Render::DirectionalLightFeatureProcessorInterface* const featureProcessor = m_directionalLightFeatureProcessor; const DirectionalLightHandle handle = featureProcessor->AcquireLight(); AZ::Render::PhotometricColor lightColor(DirectionalLightColor * m_directionalLightIntensity); featureProcessor->SetRgbIntensity(handle, lightColor); featureProcessor->SetShadowEnabled(handle, m_shadowEnabled); featureProcessor->SetShadowmapSize(handle, s_shadowmapImageSizes[m_directionalLightImageSizeIndex]); featureProcessor->SetShadowFarClipDistance(handle, FarClipDistance); featureProcessor->SetCascadeCount(handle, static_cast(m_cascadeCount)); featureProcessor->SetShadowmapFrustumSplitSchemeRatio(handle, m_ratioLogarithmUniform); featureProcessor->SetViewFrustumCorrectionEnabled(handle, m_isCascadeCorrectionEnabled); featureProcessor->SetShadowFilterMethod(handle, s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]); featureProcessor->SetFilteringSampleCount(handle, static_cast(m_filteringSampleCountDirectional)); featureProcessor->SetGroundHeight(handle, 0.f); featureProcessor->SetFullscreenBlurEnabled(handle, m_useFullscreenBlur); m_directionalLightHandle = handle; SetupDebugFlags(); } void ShadowExampleComponent::CreateDiskLights() { DestroyDiskLights(); using namespace AZ; Render::DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor; for (uint32_t index = 0; index < PositionalLightCount; ++index) { const DiskLightHandle handle = featureProcessor->AcquireLight(); AZ_Assert(m_diskLightHandles[index].IsNull(), "Unreleased light"); m_diskLightHandles[index] = handle; } ApplyDiskLightSettings(); } void ShadowExampleComponent::CreatePointLights() { DestroyPointLights(); using namespace AZ; Render::PointLightFeatureProcessorInterface* const featureProcessor = m_pointLightFeatureProcessor; for (uint32_t index = 0; index < PositionalLightCount; ++index) { const PointLightHandle handle = featureProcessor->AcquireLight(); AZ_Assert(m_pointLightHandles[index].IsNull(), "Unreleased light"); m_pointLightHandles[index] = handle; } ApplyPointLightSettings(); } void ShadowExampleComponent::DrawSidebar() { using namespace AZ::Render; if (m_imguiSidebar.Begin()) { ImGui::Spacing(); DrawSidebarDirectionalLight(); ImGui::Separator(); if (DrawSidebarPositionalLights()) { ApplyDiskLightSettings(); ApplyPointLightSettings(); } ImGui::Separator(); DrawSidebarCamera(); ImGui::Separator(); if (ScriptableImGui::Button("Material Details...")) { m_imguiMaterialDetails.OpenDialog(); } m_imguiSidebar.End(); } m_imguiMaterialDetails.Tick(&GetMeshFeatureProcessor()->GetDrawPackets(m_bunnyMeshHandle), "Bunny Mesh"); } void ShadowExampleComponent::DrawSidebarDirectionalLight() { ImGui::Indent(); if (ImGui::CollapsingHeader("Directional Light", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed)) { ScriptableImGui::SliderFloat("Height##Directional", &m_directionalLightHeight, 1.f, 30.f, "%.1f", ImGuiSliderFlags_Logarithmic); ScriptableImGui::Checkbox("Auto Rotation##Directional", &m_isDirectionalLightAutoRotate); ScriptableImGui::SliderAngle("Direction##Directional", &m_directionalLightRotationAngle, 0, 360); if (ScriptableImGui::SliderFloat( "Intensity##Directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", ImGuiSliderFlags_Logarithmic)) { AZ::Render::PhotometricColor lightColor( DirectionalLightColor * m_directionalLightIntensity); m_directionalLightFeatureProcessor->SetRgbIntensity(m_directionalLightHandle, lightColor); } ImGui::Text("Shadowmap Size"); if (ScriptableImGui::Combo( "Size##Directional", &m_directionalLightImageSizeIndex, s_shadowmapImageSizeLabels, aznumeric_cast(AZStd::size(s_shadowmapImageSizeLabels)))) { m_directionalLightFeatureProcessor->SetShadowmapSize( m_directionalLightHandle, s_shadowmapImageSizes[m_directionalLightImageSizeIndex]); } ImGui::Text("Number of cascades"); bool cascadesChanged = false; cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("1", &m_cascadeCount, 1); ImGui::SameLine(); cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("2", &m_cascadeCount, 2); ImGui::SameLine(); cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("3", &m_cascadeCount, 3); ImGui::SameLine(); cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("4", &m_cascadeCount, 4); if (cascadesChanged) { m_directionalLightFeatureProcessor->SetCascadeCount(m_directionalLightHandle, static_cast(m_cascadeCount)); } ImGui::Spacing(); bool cascadeDepthIsChanged = ScriptableImGui::Checkbox("Automatic Cascade Split", &m_shadowmapFrustumSplitIsAutomatic); if (m_shadowmapFrustumSplitIsAutomatic) { ImGui::Text("Cascade partition scheme"); ImGui::Text(" (uniform <--> logarithm)"); cascadeDepthIsChanged = cascadeDepthIsChanged || ScriptableImGui::SliderFloat("Ratio", &m_ratioLogarithmUniform, 0.f, 1.f, "%0.3f"); if (cascadeDepthIsChanged) { m_directionalLightFeatureProcessor->SetShadowmapFrustumSplitSchemeRatio( m_directionalLightHandle, m_ratioLogarithmUniform); } } else { for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex) { const AZStd::string label = AZStd::string::format("FarDepth %d", cascadeIndex); cascadeDepthIsChanged = cascadeDepthIsChanged || ScriptableImGui::SliderFloat(label.c_str(), &m_cascadeFarDepth[cascadeIndex], 0.01f, 20.f); } if (cascadeDepthIsChanged) { for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex) { m_directionalLightFeatureProcessor->SetCascadeFarDepth( m_directionalLightHandle, aznumeric_cast(cascadeIndex), m_cascadeFarDepth[cascadeIndex]); } } } ImGui::Spacing(); ImGui::Text("Filtering"); if (ScriptableImGui::Combo( "Filter Method##Directional", &m_shadowFilterMethodIndexDirectional, s_shadowFilterMethodLabels, aznumeric_cast(AZStd::size(s_shadowFilterMethodLabels)))) { m_directionalLightFeatureProcessor->SetShadowFilterMethod( m_directionalLightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]); } if (m_shadowFilterMethodIndexDirectional == aznumeric_cast(AZ::Render::ShadowFilterMethod::Pcf) || m_shadowFilterMethodIndexDirectional == aznumeric_cast(AZ::Render::ShadowFilterMethod::EsmPcf)) { ImGui::Spacing(); ImGui::Text("Filtering (PCF specific)"); if (ScriptableImGui::SliderInt("Filtering # ##Directional", &m_filteringSampleCountDirectional, 4, 64)) { m_directionalLightFeatureProcessor->SetFilteringSampleCount( m_directionalLightHandle, static_cast(m_filteringSampleCountDirectional)); } } ImGui::Spacing(); bool debugFlagsChanged = false; debugFlagsChanged = ScriptableImGui::Checkbox("Debug Coloring", &m_isDebugColoringEnabled) || debugFlagsChanged; debugFlagsChanged = ScriptableImGui::Checkbox("Debug Bounding Box", &m_isDebugBoundingBoxEnabled) || debugFlagsChanged; if (debugFlagsChanged) { SetupDebugFlags(); } if (ScriptableImGui::Checkbox("Cascade Position Correction", &m_isCascadeCorrectionEnabled)) { m_directionalLightFeatureProcessor->SetViewFrustumCorrectionEnabled(m_directionalLightHandle, m_isCascadeCorrectionEnabled); } if (ScriptableImGui::Checkbox("Use Fullscreen Blur", &m_useFullscreenBlur)) { m_directionalLightFeatureProcessor->SetFullscreenBlurEnabled(m_directionalLightHandle, m_useFullscreenBlur); } } ImGui::Unindent(); } bool ShadowExampleComponent::DrawSidebarPositionalLights() { using namespace AZ::Render; bool settingsChanged = false; ImGui::Indent(); if (ImGui::CollapsingHeader("Positional Lights", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed)) { ImGui::Text("Light Type"); if (ScriptableImGui::RadioButton("Disk", &m_positionalLightTypeActive, 0)) { DestroyPointLights(); CreateDiskLights(); settingsChanged = true; } if (ScriptableImGui::RadioButton("Point", &m_positionalLightTypeActive, 1)) { DestroyDiskLights(); CreatePointLights(); settingsChanged = true; } ScriptableImGui::Checkbox("Auto Rotation##Positional", &m_isPositionalLightAutoRotate); ScriptableImGui::SliderAngle("Base Direction##Positional", &m_positionalLightRotationAngle, 0, 360); ImGui::Spacing(); ImGui::Text("Control Target"); ScriptableImGui::RadioButton("Red", &m_controlTargetPositionalLightIndex, 0); ImGui::SameLine(); ScriptableImGui::RadioButton("Green", &m_controlTargetPositionalLightIndex, 1); ImGui::SameLine(); ScriptableImGui::RadioButton("Blue", &m_controlTargetPositionalLightIndex, 2); const int index = m_controlTargetPositionalLightIndex; ScriptableImGui::SliderFloat( "Height##Positional", &m_positionalLightHeights[index], 1.f, 30.f, "%.1f", ImGuiSliderFlags_Logarithmic); if (ScriptableImGui::SliderFloat("Cone Angle", &m_outerConeAngles[index], 0.f, 120.f)) { settingsChanged = true; } if (ScriptableImGui::SliderFloat( "Intensity##Positional", &m_lightIntensities[index], 0.f, 20000.f, "%.1f", ImGuiSliderFlags_Logarithmic)) { settingsChanged = true; } bool shadowmapSizeChanged = ScriptableImGui::Checkbox("Enable Shadow", &m_positionalLightShadowEnabled[index]); ImGui::Text("Shadowmap Size"); shadowmapSizeChanged = shadowmapSizeChanged || ScriptableImGui::Combo("Size##Positional", &m_positionalLightImageSizeIndices[index], s_shadowmapImageSizeLabels, aznumeric_cast(AZStd::size(s_shadowmapImageSizeLabels))); if (shadowmapSizeChanged) { // Reset shadow parameters when shadow is disabled. if (!m_positionalLightShadowEnabled[index]) { m_shadowFilterMethodIndicesPositional[index] = 0; m_filteringSampleCountsPositional[index] = 0; } settingsChanged = true; } ImGui::Spacing(); ImGui::Text("Filtering"); if (ScriptableImGui::Combo( "Filter Method##Positional", &m_shadowFilterMethodIndicesPositional[index], s_shadowFilterMethodLabels, aznumeric_cast(AZStd::size(s_shadowFilterMethodLabels)))) { settingsChanged = true; } if (m_shadowFilterMethodIndicesPositional[index] == aznumeric_cast(ShadowFilterMethod::Pcf) || m_shadowFilterMethodIndicesPositional[index] == aznumeric_cast(ShadowFilterMethod::EsmPcf)) { ImGui::Spacing(); ImGui::Text("Filtering (PCF specific)"); if (ScriptableImGui::SliderInt("Filtering # ##Positional", &m_filteringSampleCountsPositional[index], 4, 64)) { settingsChanged = true; } } } ImGui::Unindent(); return settingsChanged; } void ShadowExampleComponent::DrawSidebarCamera() { ImGui::Indent(); if (ImGui::CollapsingHeader("Camera", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed)) { using namespace AZ; ScriptableImGui::SliderAngle("FoVY", &m_cameraFovY, 1.f, 120.f); ImGui::Spacing(); Transform cameraTransform; TransformBus::EventResult(cameraTransform, GetCameraEntityId(), &TransformBus::Events::GetWorldTM); const Vector3 eularDegrees = cameraTransform.GetEulerDegrees(); float cameraPitch = eularDegrees.GetElement(0); const float cameraYaw = eularDegrees.GetElement(1); if (cameraPitch > 180.f) { cameraPitch -= 360.f; } ImGui::Text("Pitch: %f", cameraPitch); ImGui::Text("Yaw: %f", cameraYaw); } ImGui::Unindent(); } void ShadowExampleComponent::SetupDebugFlags() { int flags = AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawNone; if (m_isDebugColoringEnabled) { flags |= AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawColoring; } if (m_isDebugBoundingBoxEnabled) { flags |= AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawBoundingBoxes; } m_directionalLightFeatureProcessor->SetDebugFlags(m_directionalLightHandle, static_cast(flags)); } void ShadowExampleComponent::DestroyDiskLights() { for (DiskLightHandle& handle : m_diskLightHandles) { m_diskLightFeatureProcessor->ReleaseLight(handle); } } void ShadowExampleComponent::DestroyPointLights() { for (PointLightHandle& handle : m_pointLightHandles) { m_pointLightFeatureProcessor->ReleaseLight(handle); } } void ShadowExampleComponent::ApplyDiskLightSettings() { for (uint32_t index = 0; index < PositionalLightCount; ++index) { auto lightHandle = m_diskLightHandles[index]; if (lightHandle.IsValid()) { auto [innerConeAngle, outerConeAngle] = GetConeAnglesForLight(index); m_diskLightFeatureProcessor->SetConeAngles(lightHandle, innerConeAngle, outerConeAngle); m_diskLightFeatureProcessor->SetPosition(lightHandle, GetTransformForLight(index).GetTranslation()); m_diskLightFeatureProcessor->SetDirection(lightHandle, GetTransformForLight(index).GetBasis(1)); m_diskLightFeatureProcessor->SetAttenuationRadius(lightHandle, GetAttenuationForLight(index)); m_diskLightFeatureProcessor->SetRgbIntensity(lightHandle, GetRgbIntensityForLight(index)); const bool shadowEnabled = m_positionalLightShadowEnabled[index]; m_diskLightFeatureProcessor->SetShadowsEnabled(lightHandle, shadowEnabled); if (shadowEnabled) { m_diskLightFeatureProcessor->SetShadowmapMaxResolution( lightHandle, s_shadowmapImageSizes[m_positionalLightImageSizeIndices[index]]); m_diskLightFeatureProcessor->SetShadowFilterMethod( lightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndicesPositional[index]]); m_diskLightFeatureProcessor->SetFilteringSampleCount(lightHandle, static_cast(m_filteringSampleCountsPositional[index])); } } } } void ShadowExampleComponent::ApplyPointLightSettings() { for (uint32_t index = 0; index < PositionalLightCount; ++index) { auto lightHandle = m_pointLightHandles[index]; if (lightHandle.IsValid()) { m_pointLightFeatureProcessor->SetPosition(lightHandle, GetTransformForLight(index).GetTranslation()); m_pointLightFeatureProcessor->SetBulbRadius(lightHandle, 0.0f); m_pointLightFeatureProcessor->SetAttenuationRadius(lightHandle, GetAttenuationForLight(index)); m_pointLightFeatureProcessor->SetRgbIntensity(lightHandle, GetRgbIntensityForLight(index)); const bool shadowEnabled = m_positionalLightShadowEnabled[index]; m_pointLightFeatureProcessor->SetShadowsEnabled(lightHandle, shadowEnabled); if (shadowEnabled) { m_pointLightFeatureProcessor->SetShadowmapMaxResolution( lightHandle, s_shadowmapImageSizes[m_positionalLightImageSizeIndices[index]]); m_pointLightFeatureProcessor->SetShadowFilterMethod( lightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndicesPositional[index]]); m_pointLightFeatureProcessor->SetFilteringSampleCount(lightHandle, static_cast(m_filteringSampleCountsPositional[index])); } } } } } // namespace AtomSampleViewer