ShadowExampleComponent.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <ShadowExampleComponent.h>
  9. #include <Automation/ScriptableImGui.h>
  10. #include <Automation/ScriptRunnerBus.h>
  11. #include <Atom/Component/DebugCamera/ArcBallControllerComponent.h>
  12. #include <Atom/RPI.Public/RPISystemInterface.h>
  13. #include <Atom/RPI.Public/Scene.h>
  14. #include <Atom/RPI.Public/Model/Model.h>
  15. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  16. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  17. #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
  18. #include <AzCore/Component/Entity.h>
  19. #include <AzFramework/Components/CameraBus.h>
  20. #include <SampleComponentManager.h>
  21. #include <SampleComponentConfig.h>
  22. #include <RHI/BasicRHIComponent.h>
  23. #include <Atom/RPI.Public/ColorManagement/TransformColor.h>
  24. namespace AtomSampleViewer
  25. {
  26. const AZ::Color ShadowExampleComponent::DirectionalLightColor = AZ::Color::CreateOne();
  27. AZ::Color ShadowExampleComponent::s_positionalLightColors[] = {
  28. // they will be initialized in the constructor.
  29. AZ::Color::CreateZero(),
  30. AZ::Color::CreateZero(),
  31. AZ::Color::CreateZero()
  32. };
  33. const AZ::Render::ShadowmapSize ShadowExampleComponent::s_shadowmapImageSizes[] =
  34. {
  35. AZ::Render::ShadowmapSize::Size256,
  36. AZ::Render::ShadowmapSize::Size512,
  37. AZ::Render::ShadowmapSize::Size1024,
  38. AZ::Render::ShadowmapSize::Size2048
  39. };
  40. const char* ShadowExampleComponent::s_shadowmapImageSizeLabels[] =
  41. {
  42. "256",
  43. "512",
  44. "1024",
  45. "2048"
  46. };
  47. const AZ::Render::ShadowFilterMethod ShadowExampleComponent::s_shadowFilterMethods[] =
  48. {
  49. AZ::Render::ShadowFilterMethod::None,
  50. AZ::Render::ShadowFilterMethod::Pcf,
  51. AZ::Render::ShadowFilterMethod::Esm,
  52. AZ::Render::ShadowFilterMethod::EsmPcf
  53. };
  54. const char* ShadowExampleComponent::s_shadowFilterMethodLabels[] =
  55. {
  56. "None",
  57. "PCF",
  58. "ESM",
  59. "ESM+PCF"
  60. };
  61. ShadowExampleComponent::ShadowExampleComponent()
  62. : m_imguiSidebar("@user@/ShadowExampleComponent/sidebar.xml")
  63. {
  64. s_positionalLightColors[0] = AZ::Colors::Red;
  65. s_positionalLightColors[1] = AZ::Colors::Green;
  66. s_positionalLightColors[2] = AZ::Colors::Blue;
  67. }
  68. void ShadowExampleComponent::Reflect(AZ::ReflectContext* context)
  69. {
  70. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  71. {
  72. serializeContext->Class<ShadowExampleComponent, AZ::Component>()
  73. ->Version(0)
  74. ;
  75. }
  76. }
  77. void ShadowExampleComponent::Activate()
  78. {
  79. using namespace AZ;
  80. m_sampleName = "ShadowExampleComponent";
  81. // Don't continue the script until assets are ready and scene is setup
  82. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScriptWithTimeout, 120.0f);
  83. // preload assets
  84. AZStd::vector<AssetCollectionAsyncLoader::AssetToLoadInfo> assetList = {
  85. {DefaultPbrMaterialPath, azrtti_typeid<RPI::MaterialAsset>()},
  86. {BunnyModelFilePath, azrtti_typeid<RPI::ModelAsset>()},
  87. {CubeModelFilePath, azrtti_typeid<RPI::ModelAsset>()}
  88. };
  89. PreloadAssets(assetList);
  90. // initialize and then reset IBL, which will disable it by binding the default cubemaps
  91. m_defaultIbl.Init(m_scene);
  92. m_defaultIbl.Reset();
  93. }
  94. void ShadowExampleComponent::OnAllAssetsReadyActivate()
  95. {
  96. using namespace AZ;
  97. // Load the assets
  98. m_bunnyModelAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(BunnyModelFilePath, RPI::AssetUtils::TraceLevel::Assert);
  99. m_floorModelAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(CubeModelFilePath, RPI::AssetUtils::TraceLevel::Assert);
  100. m_materialAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::MaterialAsset>(DefaultPbrMaterialPath, RPI::AssetUtils::TraceLevel::Assert);
  101. m_bunnyMeshIsReady = false;
  102. m_floorMeshIsReady = false;
  103. m_directionalLightImageSizeIndex = 2; // image size is 1024.
  104. for (uint32_t index = 0; index < PositionalLightCount; ++index)
  105. {
  106. m_positionalLightImageSizeIndices[index] = 2; // image size is 1024.
  107. }
  108. m_cascadeCount = 2;
  109. m_ratioLogarithmUniform = 0.5f;
  110. UseArcBallCameraController();
  111. m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
  112. m_diskLightFeatureProcessor = m_scene->GetFeatureProcessor<Render::DiskLightFeatureProcessorInterface>();
  113. m_pointLightFeatureProcessor = m_scene->GetFeatureProcessor<Render::PointLightFeatureProcessorInterface>();
  114. CreateMeshes();
  115. CreateDirectionalLight();
  116. CreateDiskLights();
  117. m_elapsedTime = 0.f;
  118. m_imguiSidebar.Activate();
  119. AZ::TickBus::Handler::BusConnect();
  120. }
  121. void ShadowExampleComponent::Deactivate()
  122. {
  123. AZ::TickBus::Handler::BusDisconnect();
  124. RestoreCameraConfiguration();
  125. RemoveController();
  126. GetMeshFeatureProcessor()->ReleaseMesh(m_floorMeshHandle);
  127. GetMeshFeatureProcessor()->ReleaseMesh(m_bunnyMeshHandle);
  128. DestroyDiskLights();
  129. DestroyPointLights();
  130. m_directionalLightFeatureProcessor->ReleaseLight(m_directionalLightHandle);
  131. m_imguiSidebar.Deactivate();
  132. }
  133. void ShadowExampleComponent::OnTick(float deltaTime, AZ::ScriptTimePoint)
  134. {
  135. using namespace AZ;
  136. constexpr float directionalLightPeriodTime = 5.f; // 5 seconds for a rotation of directional light position.
  137. constexpr float lightPeriodTime = 7.f; // 7 seconds for a rotation of light positions.
  138. if (m_elapsedTime == 0.f)
  139. {
  140. SaveCameraConfiguration();
  141. SetInitialArcBallControllerParams();
  142. SetInitialShadowParams();
  143. }
  144. m_elapsedTime += deltaTime;
  145. if (m_isDirectionalLightAutoRotate)
  146. {
  147. m_directionalLightRotationAngle = fmodf(m_directionalLightRotationAngle + deltaTime * Constants::TwoPi / directionalLightPeriodTime, Constants::TwoPi);
  148. }
  149. if (m_isPositionalLightAutoRotate)
  150. {
  151. m_positionalLightRotationAngle = fmodf(m_positionalLightRotationAngle - deltaTime * Constants::TwoPi / lightPeriodTime + Constants::TwoPi, Constants::TwoPi);
  152. }
  153. UpdateDirectionalLight();
  154. for (uint32_t index = 0; index < PositionalLightCount; ++index)
  155. {
  156. const auto transform = GetTransformForLight(index);
  157. if (m_diskLightHandles[index].IsValid())
  158. {
  159. m_diskLightFeatureProcessor->SetPosition(m_diskLightHandles[index], transform.GetTranslation());
  160. m_diskLightFeatureProcessor->SetDirection(m_diskLightHandles[index], transform.GetBasis(1));
  161. }
  162. if (m_pointLightHandles[index].IsValid())
  163. {
  164. m_pointLightFeatureProcessor->SetPosition(m_pointLightHandles[index], transform.GetTranslation());
  165. }
  166. }
  167. Camera::CameraRequestBus::Event(
  168. GetCameraEntityId(),
  169. &Camera::CameraRequestBus::Events::SetFovRadians,
  170. m_cameraFovY);
  171. DrawSidebar();
  172. }
  173. AZ::Transform ShadowExampleComponent::GetTransformForLight(const uint32_t index) const
  174. {
  175. constexpr float diskLightDist = 5.f;
  176. using namespace AZ;
  177. const float angle = m_positionalLightRotationAngle + index * Constants::TwoPi / 3;
  178. const auto location = Vector3(diskLightDist * sinf(angle), diskLightDist * cosf(angle), m_positionalLightHeights[index]);
  179. const auto transform = Transform::CreateLookAt(location, Vector3::CreateZero());
  180. return transform;
  181. }
  182. float ShadowExampleComponent::GetAttenuationForLight(const uint32_t index) const
  183. {
  184. return sqrtf(m_lightIntensities[index] / CutoffIntensity);
  185. }
  186. AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> ShadowExampleComponent::GetRgbIntensityForLight(
  187. const uint32_t index) const
  188. {
  189. AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> lightRgbIntensity(
  190. s_positionalLightColors[index] * m_lightIntensities[index]);
  191. return lightRgbIntensity;
  192. }
  193. AZStd::pair<float, float> ShadowExampleComponent::GetConeAnglesForLight(const uint32_t index) const
  194. {
  195. const float innerConeAngle = AZ::DegToRad(m_outerConeAngles[index]) * ConeAngleInnerRatio;
  196. const float outerConeAngle = AZ::DegToRad(m_outerConeAngles[index]);
  197. return AZStd::make_pair(innerConeAngle, outerConeAngle);
  198. }
  199. void ShadowExampleComponent::UpdateDirectionalLight()
  200. {
  201. using namespace AZ;
  202. constexpr float directionalLightDist = 10.f;
  203. // Directional Light Transform
  204. {
  205. const auto lightLocation = Vector3(
  206. directionalLightDist * sinf(m_directionalLightRotationAngle), directionalLightDist * cosf(m_directionalLightRotationAngle),
  207. m_directionalLightHeight);
  208. const auto lightTransform = Transform::CreateLookAt(lightLocation, Vector3::CreateZero());
  209. m_directionalLightFeatureProcessor->SetDirection(m_directionalLightHandle, lightTransform.GetBasis(1));
  210. }
  211. // Camera Configuration
  212. {
  213. Camera::Configuration config;
  214. Camera::CameraRequestBus::EventResult(config, GetCameraEntityId(), &Camera::CameraRequestBus::Events::GetCameraConfiguration);
  215. m_directionalLightFeatureProcessor->SetCameraConfiguration(m_directionalLightHandle, config);
  216. }
  217. // Camera Transform
  218. {
  219. Transform transform = Transform::CreateIdentity();
  220. TransformBus::EventResult(transform, GetCameraEntityId(), &TransformBus::Events::GetWorldTM);
  221. m_directionalLightFeatureProcessor->SetCameraTransform(m_directionalLightHandle, transform);
  222. }
  223. }
  224. void ShadowExampleComponent::SaveCameraConfiguration()
  225. {
  226. Camera::CameraRequestBus::EventResult(
  227. m_originalFarClipDistance,
  228. GetCameraEntityId(),
  229. &Camera::CameraRequestBus::Events::GetFarClipDistance);
  230. Camera::CameraRequestBus::EventResult(
  231. m_originalCameraFovRadians,
  232. GetCameraEntityId(),
  233. &Camera::CameraRequestBus::Events::GetFovRadians);
  234. }
  235. void ShadowExampleComponent::RestoreCameraConfiguration()
  236. {
  237. Camera::CameraRequestBus::Event(
  238. GetCameraEntityId(),
  239. &Camera::CameraRequestBus::Events::SetFarClipDistance,
  240. m_originalFarClipDistance);
  241. Camera::CameraRequestBus::Event(
  242. GetCameraEntityId(),
  243. &Camera::CameraRequestBus::Events::SetFovRadians,
  244. m_originalCameraFovRadians);
  245. }
  246. void ShadowExampleComponent::UseArcBallCameraController()
  247. {
  248. using namespace AZ;
  249. Debug::CameraControllerRequestBus::Event(
  250. GetCameraEntityId(),
  251. &Debug::CameraControllerRequestBus::Events::Enable,
  252. azrtti_typeid<Debug::ArcBallControllerComponent>());
  253. }
  254. void ShadowExampleComponent::SetInitialArcBallControllerParams()
  255. {
  256. using namespace AZ;
  257. const auto cameraLocation = Vector3(0.f, -2.f, 2.f);
  258. Debug::ArcBallControllerRequestBus::Event(
  259. GetCameraEntityId(),
  260. &Debug::ArcBallControllerRequestBus::Events::SetCenter,
  261. cameraLocation);
  262. Debug::ArcBallControllerRequestBus::Event(
  263. GetCameraEntityId(),
  264. &Debug::ArcBallControllerRequestBus::Events::SetPitch,
  265. -Constants::QuarterPi);
  266. Debug::ArcBallControllerRequestBus::Event(
  267. GetCameraEntityId(),
  268. &Debug::ArcBallControllerRequestBus::Events::SetDistance,
  269. 50.f);
  270. }
  271. void ShadowExampleComponent::SetInitialShadowParams()
  272. {
  273. Camera::CameraRequestBus::Event(
  274. GetCameraEntityId(),
  275. &Camera::CameraRequestBus::Events::SetFovRadians,
  276. AZ::Constants::QuarterPi);
  277. }
  278. void ShadowExampleComponent::RemoveController()
  279. {
  280. AZ::Debug::CameraControllerRequestBus::Event(
  281. GetCameraEntityId(),
  282. &AZ::Debug::CameraControllerRequestBus::Events::Disable);
  283. }
  284. void ShadowExampleComponent::CreateMeshes()
  285. {
  286. using namespace AZ;
  287. auto updateFloorTransform = [&](const Data::Instance<RPI::Model>& model)
  288. {
  289. const AZ::Aabb& aabb = model->GetModelAsset()->GetAabb();
  290. const float maxZ = aabb.GetMax().GetZ();
  291. const AZ::Vector3 nonUniformScale{ 12.f, 12.f, 0.1f };
  292. const AZ::Vector3 translation{ 0.f, 0.f, -maxZ * nonUniformScale.GetZ() };
  293. const auto transform = AZ::Transform::CreateTranslation(translation);
  294. GetMeshFeatureProcessor()->SetTransform(m_floorMeshHandle, transform, nonUniformScale);
  295. m_floorMeshIsReady = true;
  296. if (m_bunnyMeshIsReady || m_floorMeshIsReady)
  297. {
  298. // Now that the models are initialized, we can allow the script to continue.
  299. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript);
  300. }
  301. };
  302. auto updateBunnyTransform = [&](const Data::Instance<RPI::Model>& model)
  303. {
  304. const AZ::Aabb& aabb = model->GetModelAsset()->GetAabb();
  305. const float minZ = aabb.GetMin().GetZ();
  306. const AZ::Vector3 translation{ 0.f, 0.f, -minZ };
  307. auto transform = AZ::Transform::CreateTranslation(translation);
  308. transform.SetUniformScale(1.5f);
  309. GetMeshFeatureProcessor()->SetTransform(m_bunnyMeshHandle, transform);
  310. m_bunnyMeshIsReady = true;
  311. if (m_bunnyMeshIsReady || m_floorMeshIsReady)
  312. {
  313. // Now that the models are initialized, we can allow the script to continue.
  314. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript);
  315. }
  316. };
  317. m_materialInstance = RPI::Material::FindOrCreate(m_materialAsset);
  318. {
  319. Render::MeshHandleDescriptor descriptor(m_floorModelAsset, m_materialInstance);
  320. descriptor.m_modelChangedEventHandler = AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler(updateFloorTransform);
  321. m_floorMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(descriptor);
  322. }
  323. {
  324. Render::MeshHandleDescriptor descriptor(m_bunnyModelAsset, m_materialInstance);
  325. descriptor.m_modelChangedEventHandler = AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler(updateBunnyTransform);
  326. m_bunnyMeshHandle = GetMeshFeatureProcessor()->AcquireMesh(descriptor);
  327. }
  328. }
  329. void ShadowExampleComponent::CreateDirectionalLight()
  330. {
  331. using namespace AZ;
  332. Render::DirectionalLightFeatureProcessorInterface* const featureProcessor = m_directionalLightFeatureProcessor;
  333. const DirectionalLightHandle handle = featureProcessor->AcquireLight();
  334. AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux> lightColor(DirectionalLightColor * m_directionalLightIntensity);
  335. featureProcessor->SetRgbIntensity(handle, lightColor);
  336. featureProcessor->SetShadowEnabled(handle, m_shadowEnabled);
  337. featureProcessor->SetShadowmapSize(handle, s_shadowmapImageSizes[m_directionalLightImageSizeIndex]);
  338. featureProcessor->SetShadowFarClipDistance(handle, FarClipDistance);
  339. featureProcessor->SetCascadeCount(handle, static_cast<uint16_t>(m_cascadeCount));
  340. featureProcessor->SetShadowmapFrustumSplitSchemeRatio(handle, m_ratioLogarithmUniform);
  341. featureProcessor->SetViewFrustumCorrectionEnabled(handle, m_isCascadeCorrectionEnabled);
  342. featureProcessor->SetShadowFilterMethod(handle, s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]);
  343. featureProcessor->SetFilteringSampleCount(handle, static_cast<uint16_t>(m_filteringSampleCountDirectional));
  344. featureProcessor->SetGroundHeight(handle, 0.f);
  345. featureProcessor->SetFullscreenBlurEnabled(handle, m_useFullscreenBlur);
  346. m_directionalLightHandle = handle;
  347. SetupDebugFlags();
  348. }
  349. void ShadowExampleComponent::CreateDiskLights()
  350. {
  351. DestroyDiskLights();
  352. using namespace AZ;
  353. Render::DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor;
  354. for (uint32_t index = 0; index < PositionalLightCount; ++index)
  355. {
  356. const DiskLightHandle handle = featureProcessor->AcquireLight();
  357. AZ_Assert(m_diskLightHandles[index].IsNull(), "Unreleased light");
  358. m_diskLightHandles[index] = handle;
  359. }
  360. ApplyDiskLightSettings();
  361. }
  362. void ShadowExampleComponent::CreatePointLights()
  363. {
  364. DestroyPointLights();
  365. using namespace AZ;
  366. Render::PointLightFeatureProcessorInterface* const featureProcessor = m_pointLightFeatureProcessor;
  367. for (uint32_t index = 0; index < PositionalLightCount; ++index)
  368. {
  369. const PointLightHandle handle = featureProcessor->AcquireLight();
  370. AZ_Assert(m_pointLightHandles[index].IsNull(), "Unreleased light");
  371. m_pointLightHandles[index] = handle;
  372. }
  373. ApplyPointLightSettings();
  374. }
  375. void ShadowExampleComponent::DrawSidebar()
  376. {
  377. using namespace AZ::Render;
  378. if (m_imguiSidebar.Begin())
  379. {
  380. ImGui::Spacing();
  381. DrawSidebarDirectionalLight();
  382. ImGui::Separator();
  383. if (DrawSidebarPositionalLights())
  384. {
  385. ApplyDiskLightSettings();
  386. ApplyPointLightSettings();
  387. }
  388. ImGui::Separator();
  389. DrawSidebarCamera();
  390. ImGui::Separator();
  391. if (ScriptableImGui::Button("Material Details..."))
  392. {
  393. m_imguiMaterialDetails.OpenDialog();
  394. }
  395. m_imguiSidebar.End();
  396. }
  397. m_imguiMaterialDetails.Tick(&GetMeshFeatureProcessor()->GetDrawPackets(m_bunnyMeshHandle), "Bunny Mesh");
  398. }
  399. void ShadowExampleComponent::DrawSidebarDirectionalLight()
  400. {
  401. ImGui::Indent();
  402. if (ImGui::CollapsingHeader("Directional Light", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
  403. {
  404. ScriptableImGui::SliderFloat("Height##Directional", &m_directionalLightHeight, 1.f, 30.f, "%.1f", ImGuiSliderFlags_Logarithmic);
  405. ScriptableImGui::Checkbox("Auto Rotation##Directional", &m_isDirectionalLightAutoRotate);
  406. ScriptableImGui::SliderAngle("Direction##Directional", &m_directionalLightRotationAngle, 0, 360);
  407. if (ScriptableImGui::SliderFloat(
  408. "Intensity##Directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", ImGuiSliderFlags_Logarithmic))
  409. {
  410. AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux> lightColor(
  411. DirectionalLightColor * m_directionalLightIntensity);
  412. m_directionalLightFeatureProcessor->SetRgbIntensity(m_directionalLightHandle, lightColor);
  413. }
  414. ImGui::Text("Shadowmap Size");
  415. if (ScriptableImGui::Combo(
  416. "Size##Directional", &m_directionalLightImageSizeIndex, s_shadowmapImageSizeLabels,
  417. aznumeric_cast<int>(AZStd::size(s_shadowmapImageSizeLabels))))
  418. {
  419. m_directionalLightFeatureProcessor->SetShadowmapSize(
  420. m_directionalLightHandle, s_shadowmapImageSizes[m_directionalLightImageSizeIndex]);
  421. }
  422. ImGui::Text("Number of cascades");
  423. bool cascadesChanged = false;
  424. cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("1", &m_cascadeCount, 1);
  425. ImGui::SameLine();
  426. cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("2", &m_cascadeCount, 2);
  427. ImGui::SameLine();
  428. cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("3", &m_cascadeCount, 3);
  429. ImGui::SameLine();
  430. cascadesChanged = cascadesChanged || ScriptableImGui::RadioButton("4", &m_cascadeCount, 4);
  431. if (cascadesChanged)
  432. {
  433. m_directionalLightFeatureProcessor->SetCascadeCount(m_directionalLightHandle, static_cast<uint16_t>(m_cascadeCount));
  434. }
  435. ImGui::Spacing();
  436. bool cascadeDepthIsChanged = ScriptableImGui::Checkbox("Automatic Cascade Split", &m_shadowmapFrustumSplitIsAutomatic);
  437. if (m_shadowmapFrustumSplitIsAutomatic)
  438. {
  439. ImGui::Text("Cascade partition scheme");
  440. ImGui::Text(" (uniform <--> logarithm)");
  441. cascadeDepthIsChanged =
  442. cascadeDepthIsChanged || ScriptableImGui::SliderFloat("Ratio", &m_ratioLogarithmUniform, 0.f, 1.f, "%0.3f");
  443. if (cascadeDepthIsChanged)
  444. {
  445. m_directionalLightFeatureProcessor->SetShadowmapFrustumSplitSchemeRatio(
  446. m_directionalLightHandle, m_ratioLogarithmUniform);
  447. }
  448. }
  449. else
  450. {
  451. for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
  452. {
  453. const AZStd::string label = AZStd::string::format("FarDepth %d", cascadeIndex);
  454. cascadeDepthIsChanged =
  455. cascadeDepthIsChanged || ScriptableImGui::SliderFloat(label.c_str(), &m_cascadeFarDepth[cascadeIndex], 0.01f, 20.f);
  456. }
  457. if (cascadeDepthIsChanged)
  458. {
  459. for (int cascadeIndex = 0; cascadeIndex < m_cascadeCount; ++cascadeIndex)
  460. {
  461. m_directionalLightFeatureProcessor->SetCascadeFarDepth(
  462. m_directionalLightHandle, aznumeric_cast<uint16_t>(cascadeIndex), m_cascadeFarDepth[cascadeIndex]);
  463. }
  464. }
  465. }
  466. ImGui::Spacing();
  467. ImGui::Text("Filtering");
  468. if (ScriptableImGui::Combo(
  469. "Filter Method##Directional", &m_shadowFilterMethodIndexDirectional, s_shadowFilterMethodLabels,
  470. aznumeric_cast<int>(AZStd::size(s_shadowFilterMethodLabels))))
  471. {
  472. m_directionalLightFeatureProcessor->SetShadowFilterMethod(
  473. m_directionalLightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]);
  474. }
  475. if (m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(AZ::Render::ShadowFilterMethod::Pcf) ||
  476. m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(AZ::Render::ShadowFilterMethod::EsmPcf))
  477. {
  478. ImGui::Spacing();
  479. ImGui::Text("Filtering (PCF specific)");
  480. if (ScriptableImGui::SliderInt("Filtering # ##Directional", &m_filteringSampleCountDirectional, 4, 64))
  481. {
  482. m_directionalLightFeatureProcessor->SetFilteringSampleCount(
  483. m_directionalLightHandle, static_cast<uint16_t>(m_filteringSampleCountDirectional));
  484. }
  485. }
  486. ImGui::Spacing();
  487. bool debugFlagsChanged = false;
  488. debugFlagsChanged = ScriptableImGui::Checkbox("Debug Coloring", &m_isDebugColoringEnabled) || debugFlagsChanged;
  489. debugFlagsChanged = ScriptableImGui::Checkbox("Debug Bounding Box", &m_isDebugBoundingBoxEnabled) || debugFlagsChanged;
  490. if (debugFlagsChanged)
  491. {
  492. SetupDebugFlags();
  493. }
  494. if (ScriptableImGui::Checkbox("Cascade Position Correction", &m_isCascadeCorrectionEnabled))
  495. {
  496. m_directionalLightFeatureProcessor->SetViewFrustumCorrectionEnabled(m_directionalLightHandle, m_isCascadeCorrectionEnabled);
  497. }
  498. if (ScriptableImGui::Checkbox("Use Fullscreen Blur", &m_useFullscreenBlur))
  499. {
  500. m_directionalLightFeatureProcessor->SetFullscreenBlurEnabled(m_directionalLightHandle, m_useFullscreenBlur);
  501. }
  502. }
  503. ImGui::Unindent();
  504. }
  505. bool ShadowExampleComponent::DrawSidebarPositionalLights()
  506. {
  507. using namespace AZ::Render;
  508. bool settingsChanged = false;
  509. ImGui::Indent();
  510. if (ImGui::CollapsingHeader("Positional Lights", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
  511. {
  512. ImGui::Text("Light Type");
  513. if (ScriptableImGui::RadioButton("Disk", &m_positionalLightTypeActive, 0))
  514. {
  515. DestroyPointLights();
  516. CreateDiskLights();
  517. settingsChanged = true;
  518. }
  519. if (ScriptableImGui::RadioButton("Point", &m_positionalLightTypeActive, 1))
  520. {
  521. DestroyDiskLights();
  522. CreatePointLights();
  523. settingsChanged = true;
  524. }
  525. ScriptableImGui::Checkbox("Auto Rotation##Positional", &m_isPositionalLightAutoRotate);
  526. ScriptableImGui::SliderAngle("Base Direction##Positional", &m_positionalLightRotationAngle, 0, 360);
  527. ImGui::Spacing();
  528. ImGui::Text("Control Target");
  529. ScriptableImGui::RadioButton("Red", &m_controlTargetPositionalLightIndex, 0);
  530. ImGui::SameLine();
  531. ScriptableImGui::RadioButton("Green", &m_controlTargetPositionalLightIndex, 1);
  532. ImGui::SameLine();
  533. ScriptableImGui::RadioButton("Blue", &m_controlTargetPositionalLightIndex, 2);
  534. const int index = m_controlTargetPositionalLightIndex;
  535. ScriptableImGui::SliderFloat(
  536. "Height##Positional", &m_positionalLightHeights[index], 1.f, 30.f, "%.1f", ImGuiSliderFlags_Logarithmic);
  537. if (ScriptableImGui::SliderFloat("Cone Angle", &m_outerConeAngles[index], 0.f, 120.f))
  538. {
  539. settingsChanged = true;
  540. }
  541. if (ScriptableImGui::SliderFloat(
  542. "Intensity##Positional", &m_lightIntensities[index], 0.f, 20000.f, "%.1f", ImGuiSliderFlags_Logarithmic))
  543. {
  544. settingsChanged = true;
  545. }
  546. bool shadowmapSizeChanged = ScriptableImGui::Checkbox("Enable Shadow", &m_positionalLightShadowEnabled[index]);
  547. ImGui::Text("Shadowmap Size");
  548. shadowmapSizeChanged = shadowmapSizeChanged ||
  549. ScriptableImGui::Combo("Size##Positional", &m_positionalLightImageSizeIndices[index], s_shadowmapImageSizeLabels,
  550. aznumeric_cast<int>(AZStd::size(s_shadowmapImageSizeLabels)));
  551. if (shadowmapSizeChanged)
  552. {
  553. // Reset shadow parameters when shadow is disabled.
  554. if (!m_positionalLightShadowEnabled[index])
  555. {
  556. m_shadowFilterMethodIndicesPositional[index] = 0;
  557. m_filteringSampleCountsPositional[index] = 0;
  558. }
  559. settingsChanged = true;
  560. }
  561. ImGui::Spacing();
  562. ImGui::Text("Filtering");
  563. if (ScriptableImGui::Combo(
  564. "Filter Method##Positional", &m_shadowFilterMethodIndicesPositional[index], s_shadowFilterMethodLabels,
  565. aznumeric_cast<int>(AZStd::size(s_shadowFilterMethodLabels))))
  566. {
  567. settingsChanged = true;
  568. }
  569. if (m_shadowFilterMethodIndicesPositional[index] == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
  570. m_shadowFilterMethodIndicesPositional[index] == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
  571. {
  572. ImGui::Spacing();
  573. ImGui::Text("Filtering (PCF specific)");
  574. if (ScriptableImGui::SliderInt("Filtering # ##Positional", &m_filteringSampleCountsPositional[index], 4, 64))
  575. {
  576. settingsChanged = true;
  577. }
  578. }
  579. }
  580. ImGui::Unindent();
  581. return settingsChanged;
  582. }
  583. void ShadowExampleComponent::DrawSidebarCamera()
  584. {
  585. ImGui::Indent();
  586. if (ImGui::CollapsingHeader("Camera", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
  587. {
  588. using namespace AZ;
  589. ScriptableImGui::SliderAngle("FoVY", &m_cameraFovY, 1.f, 120.f);
  590. ImGui::Spacing();
  591. Transform cameraTransform;
  592. TransformBus::EventResult(cameraTransform, GetCameraEntityId(), &TransformBus::Events::GetWorldTM);
  593. const Vector3 eularDegrees = cameraTransform.GetEulerDegrees();
  594. float cameraPitch = eularDegrees.GetElement(0);
  595. const float cameraYaw = eularDegrees.GetElement(1);
  596. if (cameraPitch > 180.f)
  597. {
  598. cameraPitch -= 360.f;
  599. }
  600. ImGui::Text("Pitch: %f", cameraPitch);
  601. ImGui::Text("Yaw: %f", cameraYaw);
  602. }
  603. ImGui::Unindent();
  604. }
  605. void ShadowExampleComponent::SetupDebugFlags()
  606. {
  607. int flags = AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawNone;
  608. if (m_isDebugColoringEnabled)
  609. {
  610. flags |= AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawColoring;
  611. }
  612. if (m_isDebugBoundingBoxEnabled)
  613. {
  614. flags |= AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawBoundingBoxes;
  615. }
  616. m_directionalLightFeatureProcessor->SetDebugFlags(m_directionalLightHandle,
  617. static_cast<AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags>(flags));
  618. }
  619. void ShadowExampleComponent::DestroyDiskLights()
  620. {
  621. for (DiskLightHandle& handle : m_diskLightHandles)
  622. {
  623. m_diskLightFeatureProcessor->ReleaseLight(handle);
  624. }
  625. }
  626. void ShadowExampleComponent::DestroyPointLights()
  627. {
  628. for (PointLightHandle& handle : m_pointLightHandles)
  629. {
  630. m_pointLightFeatureProcessor->ReleaseLight(handle);
  631. }
  632. }
  633. void ShadowExampleComponent::ApplyDiskLightSettings()
  634. {
  635. for (uint32_t index = 0; index < PositionalLightCount; ++index)
  636. {
  637. auto lightHandle = m_diskLightHandles[index];
  638. if (lightHandle.IsValid())
  639. {
  640. auto [innerConeAngle, outerConeAngle] = GetConeAnglesForLight(index);
  641. m_diskLightFeatureProcessor->SetConeAngles(lightHandle, innerConeAngle, outerConeAngle);
  642. m_diskLightFeatureProcessor->SetPosition(lightHandle, GetTransformForLight(index).GetTranslation());
  643. m_diskLightFeatureProcessor->SetDirection(lightHandle, GetTransformForLight(index).GetBasis(1));
  644. m_diskLightFeatureProcessor->SetAttenuationRadius(lightHandle, GetAttenuationForLight(index));
  645. m_diskLightFeatureProcessor->SetRgbIntensity(lightHandle, GetRgbIntensityForLight(index));
  646. const bool shadowEnabled = m_positionalLightShadowEnabled[index];
  647. m_diskLightFeatureProcessor->SetShadowsEnabled(lightHandle, shadowEnabled);
  648. if (shadowEnabled)
  649. {
  650. m_diskLightFeatureProcessor->SetShadowmapMaxResolution(
  651. lightHandle, s_shadowmapImageSizes[m_positionalLightImageSizeIndices[index]]);
  652. m_diskLightFeatureProcessor->SetShadowFilterMethod(
  653. lightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndicesPositional[index]]);
  654. m_diskLightFeatureProcessor->SetFilteringSampleCount(lightHandle, static_cast<uint16_t>(m_filteringSampleCountsPositional[index]));
  655. }
  656. }
  657. }
  658. }
  659. void ShadowExampleComponent::ApplyPointLightSettings()
  660. {
  661. for (uint32_t index = 0; index < PositionalLightCount; ++index)
  662. {
  663. auto lightHandle = m_pointLightHandles[index];
  664. if (lightHandle.IsValid())
  665. {
  666. m_pointLightFeatureProcessor->SetPosition(lightHandle, GetTransformForLight(index).GetTranslation());
  667. m_pointLightFeatureProcessor->SetBulbRadius(lightHandle, 0.0f);
  668. m_pointLightFeatureProcessor->SetAttenuationRadius(lightHandle, GetAttenuationForLight(index));
  669. m_pointLightFeatureProcessor->SetRgbIntensity(lightHandle, GetRgbIntensityForLight(index));
  670. const bool shadowEnabled = m_positionalLightShadowEnabled[index];
  671. m_pointLightFeatureProcessor->SetShadowsEnabled(lightHandle, shadowEnabled);
  672. if (shadowEnabled)
  673. {
  674. m_pointLightFeatureProcessor->SetShadowmapMaxResolution(
  675. lightHandle, s_shadowmapImageSizes[m_positionalLightImageSizeIndices[index]]);
  676. m_pointLightFeatureProcessor->SetShadowFilterMethod(
  677. lightHandle, s_shadowFilterMethods[m_shadowFilterMethodIndicesPositional[index]]);
  678. m_pointLightFeatureProcessor->SetFilteringSampleCount(lightHandle, static_cast<uint16_t>(m_filteringSampleCountsPositional[index]));
  679. }
  680. }
  681. }
  682. }
  683. } // namespace AtomSampleViewer