ShadowedSponzaExampleComponent.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  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 <ShadowedSponzaExampleComponent.h>
  9. #include <SampleComponentConfig.h>
  10. #include <Automation/ScriptableImGui.h>
  11. #include <Automation/ScriptRunnerBus.h>
  12. #include <Atom/Component/DebugCamera/CameraControllerBus.h>
  13. #include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
  14. #include <imgui/imgui.h>
  15. #include <Atom/RPI.Public/RPISystemInterface.h>
  16. #include <Atom/RPI.Public/Scene.h>
  17. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  18. #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
  19. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  20. #include <AzCore/Component/TransformBus.h>
  21. #include <AzCore/Math/Transform.h>
  22. #include <AzFramework/Components/CameraBus.h>
  23. namespace AtomSampleViewer
  24. {
  25. const AZ::Color ShadowedSponzaExampleComponent::DirectionalLightColor = AZ::Color::CreateOne();
  26. const AZ::Render::ShadowmapSize ShadowedSponzaExampleComponent::s_shadowmapSizes[] =
  27. {
  28. AZ::Render::ShadowmapSize::Size256,
  29. AZ::Render::ShadowmapSize::Size512,
  30. AZ::Render::ShadowmapSize::Size1024,
  31. AZ::Render::ShadowmapSize::Size2048
  32. };
  33. const char* ShadowedSponzaExampleComponent::s_directionalLightShadowmapSizeLabels[] =
  34. {
  35. "256",
  36. "512",
  37. "1024",
  38. "2048"
  39. };
  40. const AZ::Render::ShadowFilterMethod ShadowedSponzaExampleComponent::s_shadowFilterMethods[] =
  41. {
  42. AZ::Render::ShadowFilterMethod::None,
  43. AZ::Render::ShadowFilterMethod::Pcf,
  44. AZ::Render::ShadowFilterMethod::Esm,
  45. AZ::Render::ShadowFilterMethod::EsmPcf
  46. };
  47. const char* ShadowedSponzaExampleComponent::s_shadowFilterMethodLabels[] =
  48. {
  49. "None",
  50. "PCF",
  51. "ESM",
  52. "ESM+PCF"
  53. };
  54. void ShadowedSponzaExampleComponent::Reflect(AZ::ReflectContext* context)
  55. {
  56. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  57. {
  58. serializeContext->Class<ShadowedSponzaExampleComponent, AZ::Component>()
  59. ->Version(0)
  60. ;
  61. }
  62. }
  63. void ShadowedSponzaExampleComponent::Activate()
  64. {
  65. using namespace AZ;
  66. m_directionalLightShadowmapSizeIndex = s_shadowmapSizeIndexDefault;
  67. m_diskLightShadowmapSize = Render::ShadowmapSize::None; // random
  68. m_cascadeCount = s_cascadesCountDefault;
  69. m_ratioLogarithmUniform = s_ratioLogarithmUniformDefault;
  70. m_diskLightCount = 0;
  71. // heuristic disk light default position configuration
  72. m_diskLightsBasePosition[0] = 0.04f;
  73. m_diskLightsBasePosition[1] = 0.04f;;
  74. m_diskLightsBasePosition[2] = -0.03f;
  75. m_diskLightsPositionScatteringRatio = 0.27f;
  76. m_directionalLightFeatureProcessor = m_scene->GetFeatureProcessor<Render::DirectionalLightFeatureProcessorInterface>();
  77. m_diskLightFeatureProcessor = m_scene->GetFeatureProcessor<Render::DiskLightFeatureProcessorInterface>();
  78. SetupScene();
  79. // enable camera control
  80. Debug::CameraControllerRequestBus::Event(
  81. GetCameraEntityId(),
  82. &Debug::CameraControllerRequestBus::Events::Enable,
  83. azrtti_typeid<Debug::NoClipControllerComponent>());
  84. SaveCameraConfiguration();
  85. Camera::CameraRequestBus::Event(
  86. GetCameraEntityId(),
  87. &Camera::CameraRequestBus::Events::SetFarClipDistance,
  88. 75.f);
  89. m_cameraTransformInitialized = false;
  90. m_imguiSidebar.Activate();
  91. TickBus::Handler::BusConnect();
  92. // Don't continue the script until after the models have loaded.
  93. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScriptWithTimeout, 120.0f);
  94. }
  95. void ShadowedSponzaExampleComponent::Deactivate()
  96. {
  97. using namespace AZ;
  98. TickBus::Handler::BusDisconnect();
  99. m_imguiSidebar.Deactivate();
  100. // disable camera control
  101. RestoreCameraConfiguration();
  102. Debug::CameraControllerRequestBus::Event(
  103. GetCameraEntityId(),
  104. &Debug::CameraControllerRequestBus::Events::Disable);
  105. GetMeshFeatureProcessor()->ReleaseMesh(m_meshHandle);
  106. m_directionalLightFeatureProcessor->ReleaseLight(m_directionalLightHandle);
  107. UpdateDiskLightCount(0);
  108. }
  109. void ShadowedSponzaExampleComponent::OnTick(float deltaTime, AZ::ScriptTimePoint timePoint)
  110. {
  111. AZ_UNUSED(deltaTime);
  112. AZ_UNUSED(timePoint);
  113. using namespace AZ;
  114. SetInitialCameraTransform();
  115. const auto lightTrans = Transform::CreateRotationZ(m_directionalLightYaw) * Transform::CreateRotationX(m_directionalLightPitch);
  116. m_directionalLightFeatureProcessor->SetDirection(
  117. m_directionalLightHandle,
  118. lightTrans.GetBasis(1));
  119. DrawSidebar();
  120. // Camera Configuration
  121. {
  122. Camera::Configuration config;
  123. Camera::CameraRequestBus::EventResult(
  124. config,
  125. GetCameraEntityId(),
  126. &Camera::CameraRequestBus::Events::GetCameraConfiguration);
  127. m_directionalLightFeatureProcessor->SetCameraConfiguration(
  128. m_directionalLightHandle,
  129. config);
  130. }
  131. // Camera Transform
  132. {
  133. Transform transform = Transform::CreateIdentity();
  134. TransformBus::EventResult(
  135. transform,
  136. GetCameraEntityId(),
  137. &TransformBus::Events::GetWorldTM);
  138. m_directionalLightFeatureProcessor->SetCameraTransform(
  139. m_directionalLightHandle, transform);
  140. }
  141. }
  142. void ShadowedSponzaExampleComponent::OnModelReady(AZ::Data::Instance<AZ::RPI::Model> model)
  143. {
  144. m_sponzaExteriorAssetLoaded = true;
  145. m_worldAabb = model->GetModelAsset()->GetAabb();
  146. UpdateDiskLightCount(DiskLightCountDefault);
  147. // Now that the models are initialized, we can allow the script to continue.
  148. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript);
  149. }
  150. void ShadowedSponzaExampleComponent::SaveCameraConfiguration()
  151. {
  152. Camera::CameraRequestBus::EventResult(
  153. m_originalFarClipDistance,
  154. GetCameraEntityId(),
  155. &Camera::CameraRequestBus::Events::GetFarClipDistance);
  156. }
  157. void ShadowedSponzaExampleComponent::RestoreCameraConfiguration()
  158. {
  159. Camera::CameraRequestBus::Event(
  160. GetCameraEntityId(),
  161. &Camera::CameraRequestBus::Events::SetFarClipDistance,
  162. m_originalFarClipDistance);
  163. }
  164. void ShadowedSponzaExampleComponent::SetInitialCameraTransform()
  165. {
  166. using namespace AZ;
  167. if (!m_cameraTransformInitialized)
  168. {
  169. Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &Debug::NoClipControllerRequestBus::Events::SetPosition, Vector3(5.0f, 1.0f, 5.0f));
  170. Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &Debug::NoClipControllerRequestBus::Events::SetHeading, DegToRad(90.0f));
  171. Debug::NoClipControllerRequestBus::Event(GetCameraEntityId(), &Debug::NoClipControllerRequestBus::Events::SetPitch, DegToRad(-20.0f));
  172. m_cameraTransformInitialized = true;
  173. }
  174. }
  175. void ShadowedSponzaExampleComponent::SetupScene()
  176. {
  177. using namespace AZ;
  178. const char* modelPath = "objects/sponza.azmodel";
  179. Data::Asset<RPI::ModelAsset> modelAsset =
  180. RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(modelPath, RPI::AssetUtils::TraceLevel::Assert);
  181. Data::Asset<RPI::MaterialAsset> materialAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::MaterialAsset>(DefaultPbrMaterialPath, RPI::AssetUtils::TraceLevel::Assert);
  182. m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(Render::MeshHandleDescriptor{ modelAsset }, RPI::Material::FindOrCreate(materialAsset));
  183. // rotate the entity 180 degrees about Z (the vertical axis)
  184. // This makes it consistent with how it was positioned in the world when the world was Y-up.
  185. GetMeshFeatureProcessor()->SetTransform(m_meshHandle, Transform::CreateRotationZ(AZ::Constants::Pi));
  186. Data::Instance<RPI::Model> model = GetMeshFeatureProcessor()->GetModel(m_meshHandle);
  187. if (model)
  188. {
  189. OnModelReady(model);
  190. }
  191. else
  192. {
  193. GetMeshFeatureProcessor()->ConnectModelChangeEventHandler(m_meshHandle, m_meshChangedHandler);
  194. }
  195. // directional light
  196. {
  197. Render::DirectionalLightFeatureProcessorInterface* featureProcessor = m_directionalLightFeatureProcessor;
  198. const DirectionalLightHandle handle = featureProcessor->AcquireLight();
  199. const auto lightTransform = Transform::CreateLookAt(
  200. Vector3(100, 100, 100),
  201. Vector3::CreateZero());
  202. featureProcessor->SetDirection(
  203. handle,
  204. lightTransform.GetBasis(1));
  205. featureProcessor->SetRgbIntensity(handle, AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux>(DirectionalLightColor * m_directionalLightIntensity));
  206. featureProcessor->SetCascadeCount(handle, s_cascadesCountDefault);
  207. featureProcessor->SetShadowmapSize(handle, s_shadowmapSizes[s_shadowmapSizeIndexDefault]);
  208. featureProcessor->SetViewFrustumCorrectionEnabled(handle, m_isCascadeCorrectionEnabled);
  209. featureProcessor->SetShadowFilterMethod(handle, s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]);
  210. featureProcessor->SetFilteringSampleCount(handle, static_cast<uint16_t>(m_filteringSampleCountDirectional));
  211. featureProcessor->SetGroundHeight(handle, 0.f);
  212. m_directionalLightHandle = handle;
  213. SetupDebugFlags();
  214. }
  215. // disk lights are initialized after loading models.
  216. BuildDiskLightParameters();
  217. }
  218. void ShadowedSponzaExampleComponent::BuildDiskLightParameters()
  219. {
  220. m_random.SetSeed(0);
  221. m_diskLights.clear();
  222. m_diskLights.reserve(DiskLightCountMax);
  223. for (int index = 0; index < DiskLightCountMax; ++index)
  224. {
  225. m_diskLights.emplace_back(
  226. GetRandomColor(),
  227. GetRandomPosition(),
  228. GetRandomShadowmapSize());
  229. }
  230. }
  231. void ShadowedSponzaExampleComponent::UpdateDiskLightCount(uint16_t count)
  232. {
  233. // We suppose m_diskLights has been initialized except m_entity.
  234. using namespace AZ;
  235. // Don't assert here if count == 0, since the count is set to 0 during Deactivate
  236. if ((!m_worldAabb.IsValid() || !m_worldAabb.IsFinite()) && count != 0)
  237. {
  238. AZ_Assert(false, "World AABB is not initialized correctly.");
  239. return;
  240. }
  241. for (int index = count; index < m_diskLightCount; ++index)
  242. {
  243. DiskLightHandle& handle = m_diskLights[index].m_handle;
  244. m_diskLightFeatureProcessor->ReleaseLight(handle);
  245. }
  246. const int previousDiskLightCount = m_diskLightCount;
  247. for (int index = previousDiskLightCount; index < count; ++index)
  248. {
  249. Render::DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor;
  250. const DiskLightHandle handle = featureProcessor->AcquireLight();
  251. AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> lightColor(m_diskLights[index].m_color * m_diskLightIntensity);
  252. featureProcessor->SetRgbIntensity(handle, lightColor);
  253. featureProcessor->SetAttenuationRadius(
  254. handle,
  255. sqrtf(m_diskLightIntensity / CutoffIntensity));
  256. featureProcessor->SetConeAngles(handle, DegToRad(22.5f), DegToRad(27.5));
  257. featureProcessor->SetShadowsEnabled(handle, m_diskLightShadowEnabled);
  258. if (m_diskLightShadowEnabled)
  259. {
  260. featureProcessor->SetShadowmapMaxResolution(
  261. handle,
  262. m_diskLightShadowEnabled ?
  263. m_diskLights[index].m_shadowmapSize :
  264. Render::ShadowmapSize::None);
  265. featureProcessor->SetShadowFilterMethod(handle, aznumeric_cast<Render::ShadowFilterMethod>(m_shadowFilterMethodIndexDisk));
  266. featureProcessor->SetFilteringSampleCount(handle, aznumeric_cast<uint16_t>(m_filteringSampleCountDisk));
  267. }
  268. m_diskLights[index].m_handle = handle;
  269. UpdateDiskLightPosition(index);
  270. }
  271. m_diskLightCount = count;
  272. }
  273. const AZ::Color& ShadowedSponzaExampleComponent::GetRandomColor()
  274. {
  275. static const AZStd::vector<AZ::Color> colors =
  276. {
  277. AZ::Colors::Red,
  278. AZ::Colors::Green,
  279. AZ::Colors::Blue,
  280. AZ::Colors::Cyan,
  281. AZ::Colors::Fuchsia,
  282. AZ::Colors::Yellow,
  283. AZ::Colors::SpringGreen
  284. };
  285. return colors[m_random.GetRandom() % colors.size()];
  286. }
  287. AZ::Vector3 ShadowedSponzaExampleComponent::GetRandomPosition()
  288. {
  289. // returns a position in the range [-0.5, +0.5]^3.
  290. return AZ::Vector3(
  291. m_random.GetRandomFloat() - 0.5f,
  292. m_random.GetRandomFloat() - 0.5f,
  293. m_random.GetRandomFloat() - 0.5f);
  294. }
  295. AZ::Render::ShadowmapSize ShadowedSponzaExampleComponent::GetRandomShadowmapSize()
  296. {
  297. static const AZStd::vector<AZ::Render::ShadowmapSize> sizes =
  298. {
  299. AZ::Render::ShadowmapSize::Size256,
  300. AZ::Render::ShadowmapSize::Size512,
  301. AZ::Render::ShadowmapSize::Size1024,
  302. AZ::Render::ShadowmapSize::Size2048
  303. };
  304. return sizes[m_random.GetRandom() % sizes.size()];
  305. }
  306. void ShadowedSponzaExampleComponent::DrawSidebar()
  307. {
  308. using namespace AZ;
  309. using namespace AZ::Render;
  310. if (!m_imguiSidebar.Begin())
  311. {
  312. return;
  313. }
  314. if (!m_sponzaExteriorAssetLoaded)
  315. {
  316. const ImGuiWindowFlags windowFlags =
  317. ImGuiWindowFlags_NoCollapse |
  318. ImGuiWindowFlags_NoResize |
  319. ImGuiWindowFlags_NoMove;
  320. if (ImGui::Begin("Asset", nullptr, windowFlags))
  321. {
  322. ImGui::Text("Sponza Model: %s", m_sponzaExteriorAssetLoaded ? "Loaded" : "Loading...");
  323. ImGui::End();
  324. }
  325. m_imguiSidebar.End();
  326. return;
  327. }
  328. ImGui::Spacing();
  329. ImGui::Text("Directional Light");
  330. ImGui::Indent();
  331. {
  332. ScriptableImGui::SliderAngle("Pitch", &m_directionalLightPitch, -90.0f, 0.f);
  333. ScriptableImGui::SliderAngle("Yaw", &m_directionalLightYaw, 0.f, 360.f);
  334. if (ScriptableImGui::SliderFloat("Intensity##directional", &m_directionalLightIntensity, 0.f, 20.f, "%.1f", ImGuiSliderFlags_Logarithmic))
  335. {
  336. AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux> lightColor(DirectionalLightColor * m_directionalLightIntensity);
  337. m_directionalLightFeatureProcessor->SetRgbIntensity(m_directionalLightHandle, lightColor);
  338. }
  339. ImGui::Separator();
  340. ImGui::Text("Shadowmap Size");
  341. if (ScriptableImGui::Combo(
  342. "Size",
  343. &m_directionalLightShadowmapSizeIndex,
  344. s_directionalLightShadowmapSizeLabels,
  345. aznumeric_cast<int>(AZStd::size(s_directionalLightShadowmapSizeLabels))))
  346. {
  347. m_directionalLightFeatureProcessor->SetShadowmapSize(
  348. m_directionalLightHandle,
  349. s_shadowmapSizes[m_directionalLightShadowmapSizeIndex]);
  350. }
  351. ImGui::Text("Number of cascades");
  352. bool cascadesChanged = false;
  353. cascadesChanged = cascadesChanged ||
  354. ScriptableImGui::RadioButton("1", &m_cascadeCount, 1);
  355. ImGui::SameLine();
  356. cascadesChanged = cascadesChanged ||
  357. ScriptableImGui::RadioButton("2", &m_cascadeCount, 2);
  358. ImGui::SameLine();
  359. cascadesChanged = cascadesChanged ||
  360. ScriptableImGui::RadioButton("3", &m_cascadeCount, 3);
  361. ImGui::SameLine();
  362. cascadesChanged = cascadesChanged ||
  363. ScriptableImGui::RadioButton("4", &m_cascadeCount, 4);
  364. if (cascadesChanged)
  365. {
  366. m_directionalLightFeatureProcessor->SetCascadeCount(
  367. m_directionalLightHandle,
  368. static_cast<uint16_t>(m_cascadeCount));
  369. }
  370. ImGui::Spacing();
  371. ImGui::Text("Cascade partition scheme");
  372. ImGui::Text(" (uniform <--> logarithm)");
  373. if (ScriptableImGui::SliderFloat("Ratio", &m_ratioLogarithmUniform, 0.f, 1.f, "%0.3f"))
  374. {
  375. m_directionalLightFeatureProcessor->SetShadowmapFrustumSplitSchemeRatio(
  376. m_directionalLightHandle,
  377. m_ratioLogarithmUniform);
  378. }
  379. ImGui::Spacing();
  380. if (ScriptableImGui::Checkbox("Cascade Position Correction", &m_isCascadeCorrectionEnabled))
  381. {
  382. m_directionalLightFeatureProcessor->SetViewFrustumCorrectionEnabled(
  383. m_directionalLightHandle,
  384. m_isCascadeCorrectionEnabled);
  385. }
  386. bool DebugFlagsChanged = false;
  387. DebugFlagsChanged = DebugFlagsChanged || ScriptableImGui::Checkbox("Debug Coloring", &m_isDebugColoringEnabled);
  388. DebugFlagsChanged = DebugFlagsChanged || ScriptableImGui::Checkbox("Debug Bounding Box", &m_isDebugBoundingBoxEnabled);
  389. if (DebugFlagsChanged)
  390. {
  391. SetupDebugFlags();
  392. }
  393. ImGui::Spacing();
  394. ImGui::Text("Filtering");
  395. if (ScriptableImGui::Combo(
  396. "Filter Method##Directional",
  397. &m_shadowFilterMethodIndexDirectional,
  398. s_shadowFilterMethodLabels,
  399. aznumeric_cast<int>(AZStd::size(s_shadowFilterMethodLabels))))
  400. {
  401. m_directionalLightFeatureProcessor->SetShadowFilterMethod(
  402. m_directionalLightHandle,
  403. s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]);
  404. }
  405. if (m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
  406. m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
  407. {
  408. ImGui::Spacing();
  409. ImGui::Text("Filtering (PCF specific)");
  410. if (ScriptableImGui::SliderInt("Filtering # ##Directional", &m_filteringSampleCountDirectional, 4, 64))
  411. {
  412. m_directionalLightFeatureProcessor->SetFilteringSampleCount(
  413. m_directionalLightHandle,
  414. static_cast<uint16_t>(m_filteringSampleCountDirectional));
  415. }
  416. }
  417. }
  418. ImGui::Unindent();
  419. ImGui::Separator();
  420. ImGui::Text("Spot Lights");
  421. ImGui::Indent();
  422. {
  423. int diskLightCount = m_diskLightCount;
  424. if (ScriptableImGui::SliderInt("Number", &diskLightCount, 0, DiskLightCountMax))
  425. {
  426. UpdateDiskLightCount(static_cast<uint16_t>(diskLightCount));
  427. }
  428. if (ScriptableImGui::SliderFloat("Intensity##spot", &m_diskLightIntensity, 0.f, 100000.f, "%.1f", ImGuiSliderFlags_Logarithmic))
  429. {
  430. for (const DiskLight& light : m_diskLights)
  431. {
  432. if (light.m_handle.IsValid())
  433. {
  434. AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> lightColor(light.m_color * m_diskLightIntensity);
  435. m_diskLightFeatureProcessor->SetRgbIntensity(light.m_handle, lightColor);
  436. m_diskLightFeatureProcessor->SetAttenuationRadius(
  437. light.m_handle,
  438. sqrtf(m_diskLightIntensity / CutoffIntensity));
  439. }
  440. }
  441. }
  442. // avoiding SliderFloat3 since its sliders are too narrow.
  443. if (ScriptableImGui::SliderFloat("Center X", &m_diskLightsBasePosition[0], -0.5f, 0.5f) ||
  444. ScriptableImGui::SliderFloat("Center Y", &m_diskLightsBasePosition[1], -0.5f, 0.5f) ||
  445. ScriptableImGui::SliderFloat("Center Z", &m_diskLightsBasePosition[2], -0.5f, 0.5f) ||
  446. ScriptableImGui::SliderFloat("Pos. Scatt. Ratio", &m_diskLightsPositionScatteringRatio, 0.f, 1.f))
  447. {
  448. UpdateDiskLightPositions();
  449. }
  450. bool diskLightShadowmapChanged = ScriptableImGui::Checkbox("Enable Shadow", &m_diskLightShadowEnabled);
  451. ImGui::Text("Shadowmap Size");
  452. int newSize = static_cast<int>(m_diskLightShadowmapSize);
  453. // To avoid GPU memory consumption, we avoid bigger shadowmap sizes here.
  454. diskLightShadowmapChanged = diskLightShadowmapChanged ||
  455. ScriptableImGui::RadioButton("256", &newSize, static_cast<int>(Render::ShadowmapSize::Size256)) ||
  456. ScriptableImGui::RadioButton("512", &newSize, static_cast<int>(Render::ShadowmapSize::Size512)) ||
  457. ScriptableImGui::RadioButton("1024", &newSize, static_cast<int>(Render::ShadowmapSize::Size1024)) ||
  458. ScriptableImGui::RadioButton("Random", &newSize, static_cast<int>(Render::ShadowmapSize::None));
  459. if (diskLightShadowmapChanged)
  460. {
  461. m_diskLightShadowmapSize = static_cast<Render::ShadowmapSize>(newSize);
  462. UpdateDiskLightShadowmapSize();
  463. }
  464. ImGui::Spacing();
  465. ImGui::Text("Filtering");
  466. if (ScriptableImGui::Combo(
  467. "Filter Method##Spot",
  468. &m_shadowFilterMethodIndexDisk,
  469. s_shadowFilterMethodLabels,
  470. aznumeric_cast<int>(AZStd::size(s_shadowFilterMethodLabels))))
  471. {
  472. for (int index = 0; index < m_diskLightCount; ++index)
  473. {
  474. m_diskLightFeatureProcessor->SetShadowFilterMethod(m_diskLights[index].m_handle, aznumeric_cast<ShadowFilterMethod>(m_shadowFilterMethodIndexDisk));
  475. }
  476. }
  477. if (m_shadowFilterMethodIndexDisk == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
  478. m_shadowFilterMethodIndexDisk == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
  479. {
  480. ImGui::Spacing();
  481. ImGui::Text("Filtering (PCF specific)");
  482. if (ScriptableImGui::SliderInt("Filtering # ##Spot", &m_filteringSampleCountDisk, 4, 64))
  483. {
  484. for (int index = 0; index < m_diskLightCount; ++index)
  485. {
  486. m_diskLightFeatureProcessor->SetFilteringSampleCount(m_diskLights[index].m_handle, static_cast<uint16_t>(m_filteringSampleCountDisk));
  487. }
  488. }
  489. }
  490. }
  491. ImGui::Unindent();
  492. m_imguiSidebar.End();
  493. }
  494. void ShadowedSponzaExampleComponent::UpdateDiskLightShadowmapSize()
  495. {
  496. using namespace AZ::Render;
  497. DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor;
  498. for (const DiskLight& light : m_diskLights)
  499. {
  500. if (!light.m_handle.IsValid())
  501. {
  502. continue;
  503. }
  504. featureProcessor->SetShadowsEnabled(light.m_handle, m_diskLightShadowEnabled);
  505. if (m_diskLightShadowEnabled)
  506. {
  507. if (m_diskLightShadowmapSize != ShadowmapSize::None)
  508. {
  509. // Uniform size
  510. featureProcessor->SetShadowmapMaxResolution(
  511. light.m_handle,
  512. m_diskLightShadowmapSize);
  513. }
  514. else
  515. {
  516. // Random sizes
  517. featureProcessor->SetShadowmapMaxResolution(
  518. light.m_handle,
  519. light.m_shadowmapSize);
  520. }
  521. }
  522. }
  523. }
  524. void ShadowedSponzaExampleComponent::UpdateDiskLightPositions()
  525. {
  526. for (int index = 0; index < m_diskLightCount; ++index)
  527. {
  528. UpdateDiskLightPosition(index);
  529. }
  530. }
  531. void ShadowedSponzaExampleComponent::UpdateDiskLightPosition(int index)
  532. {
  533. using namespace AZ;
  534. Render::DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor;
  535. if (!m_worldAabb.IsValid() || !m_worldAabb.IsFinite())
  536. {
  537. AZ_Assert(false, "World AABB is not initialized correctly.");
  538. return;
  539. }
  540. const Vector3 basePosition(
  541. m_diskLightsBasePosition[0],
  542. m_diskLightsBasePosition[1],
  543. m_diskLightsBasePosition[2]);
  544. const Vector3 relativePosition = basePosition + m_diskLights[index].m_relativePosition * m_diskLightsPositionScatteringRatio;
  545. const Vector3 position = m_worldAabb.GetCenter() +
  546. m_worldAabb.GetExtents() * relativePosition;
  547. const auto transform =
  548. Transform::CreateTranslation(position) * Transform::CreateRotationX(-Constants::HalfPi);
  549. featureProcessor->SetPosition(
  550. m_diskLights[index].m_handle,
  551. position);
  552. featureProcessor->SetDirection(
  553. m_diskLights[index].m_handle,
  554. transform.GetBasis(1));
  555. }
  556. void ShadowedSponzaExampleComponent::SetupDebugFlags()
  557. {
  558. int flags = AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawNone;
  559. if (m_isDebugColoringEnabled)
  560. {
  561. flags |= AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawColoring;
  562. }
  563. if (m_isDebugBoundingBoxEnabled)
  564. {
  565. flags |= AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawBoundingBoxes;
  566. }
  567. m_directionalLightFeatureProcessor->SetDebugFlags(m_directionalLightHandle,
  568. static_cast<AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags>(flags));
  569. }
  570. } // namespace AtomSampleViewer