ShadowedSponzaExampleComponent.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  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.fbx.azmodel";
  179. Data::Asset<RPI::ModelAsset> modelAsset =
  180. RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(modelPath, RPI::AssetUtils::TraceLevel::Assert);
  181. Data::Asset<RPI::MaterialAsset> materialAsset =
  182. RPI::AssetUtils::GetAssetByProductPath<RPI::MaterialAsset>(DefaultPbrMaterialPath, RPI::AssetUtils::TraceLevel::Assert);
  183. Render::MeshHandleDescriptor descriptor(modelAsset, RPI::Material::FindOrCreate(materialAsset));
  184. descriptor.m_modelChangedEventHandler =
  185. AZ::Render::MeshHandleDescriptor::ModelChangedEvent::Handler{ [this](const AZ::Data::Instance<AZ::RPI::Model>& model)
  186. {
  187. OnModelReady(model);
  188. } };
  189. m_meshHandle = GetMeshFeatureProcessor()->AcquireMesh(descriptor);
  190. // rotate the entity 180 degrees about Z (the vertical axis)
  191. // This makes it consistent with how it was positioned in the world when the world was Y-up.
  192. GetMeshFeatureProcessor()->SetTransform(m_meshHandle, Transform::CreateRotationZ(AZ::Constants::Pi));
  193. // directional light
  194. {
  195. Render::DirectionalLightFeatureProcessorInterface* featureProcessor = m_directionalLightFeatureProcessor;
  196. const DirectionalLightHandle handle = featureProcessor->AcquireLight();
  197. const auto lightTransform = Transform::CreateLookAt(
  198. Vector3(100, 100, 100),
  199. Vector3::CreateZero());
  200. featureProcessor->SetDirection(
  201. handle,
  202. lightTransform.GetBasis(1));
  203. featureProcessor->SetRgbIntensity(handle, AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Lux>(DirectionalLightColor * m_directionalLightIntensity));
  204. featureProcessor->SetShadowEnabled(handle, m_shadowEnabled);
  205. featureProcessor->SetCascadeCount(handle, s_cascadesCountDefault);
  206. featureProcessor->SetShadowmapSize(handle, s_shadowmapSizes[s_shadowmapSizeIndexDefault]);
  207. featureProcessor->SetViewFrustumCorrectionEnabled(handle, m_isCascadeCorrectionEnabled);
  208. featureProcessor->SetShadowFilterMethod(handle, s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]);
  209. featureProcessor->SetFilteringSampleCount(handle, static_cast<uint16_t>(m_filteringSampleCountDirectional));
  210. featureProcessor->SetGroundHeight(handle, 0.f);
  211. featureProcessor->SetFullscreenBlurEnabled(handle, m_useFullscreenBlur);
  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("Use Fullscreen Blur", &m_useFullscreenBlur))
  381. {
  382. m_directionalLightFeatureProcessor->SetFullscreenBlurEnabled(m_directionalLightHandle, m_useFullscreenBlur);
  383. }
  384. if (ScriptableImGui::Checkbox("Cascade Position Correction", &m_isCascadeCorrectionEnabled))
  385. {
  386. m_directionalLightFeatureProcessor->SetViewFrustumCorrectionEnabled(
  387. m_directionalLightHandle,
  388. m_isCascadeCorrectionEnabled);
  389. }
  390. bool DebugFlagsChanged = false;
  391. DebugFlagsChanged = DebugFlagsChanged || ScriptableImGui::Checkbox("Debug Coloring", &m_isDebugColoringEnabled);
  392. DebugFlagsChanged = DebugFlagsChanged || ScriptableImGui::Checkbox("Debug Bounding Box", &m_isDebugBoundingBoxEnabled);
  393. if (DebugFlagsChanged)
  394. {
  395. SetupDebugFlags();
  396. }
  397. ImGui::Spacing();
  398. ImGui::Text("Filtering");
  399. if (ScriptableImGui::Combo(
  400. "Filter Method##Directional",
  401. &m_shadowFilterMethodIndexDirectional,
  402. s_shadowFilterMethodLabels,
  403. aznumeric_cast<int>(AZStd::size(s_shadowFilterMethodLabels))))
  404. {
  405. m_directionalLightFeatureProcessor->SetShadowFilterMethod(
  406. m_directionalLightHandle,
  407. s_shadowFilterMethods[m_shadowFilterMethodIndexDirectional]);
  408. }
  409. if (m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
  410. m_shadowFilterMethodIndexDirectional == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
  411. {
  412. ImGui::Spacing();
  413. ImGui::Text("Filtering (PCF specific)");
  414. if (ScriptableImGui::SliderInt("Filtering # ##Directional", &m_filteringSampleCountDirectional, 4, 64))
  415. {
  416. m_directionalLightFeatureProcessor->SetFilteringSampleCount(
  417. m_directionalLightHandle,
  418. static_cast<uint16_t>(m_filteringSampleCountDirectional));
  419. }
  420. }
  421. }
  422. ImGui::Unindent();
  423. ImGui::Separator();
  424. ImGui::Text("Spot Lights");
  425. ImGui::Indent();
  426. {
  427. int diskLightCount = m_diskLightCount;
  428. if (ScriptableImGui::SliderInt("Number", &diskLightCount, 0, DiskLightCountMax))
  429. {
  430. UpdateDiskLightCount(static_cast<uint16_t>(diskLightCount));
  431. }
  432. if (ScriptableImGui::SliderFloat("Intensity##spot", &m_diskLightIntensity, 0.f, 100000.f, "%.1f", ImGuiSliderFlags_Logarithmic))
  433. {
  434. for (const DiskLight& light : m_diskLights)
  435. {
  436. if (light.m_handle.IsValid())
  437. {
  438. AZ::Render::PhotometricColor<AZ::Render::PhotometricUnit::Candela> lightColor(light.m_color * m_diskLightIntensity);
  439. m_diskLightFeatureProcessor->SetRgbIntensity(light.m_handle, lightColor);
  440. m_diskLightFeatureProcessor->SetAttenuationRadius(
  441. light.m_handle,
  442. sqrtf(m_diskLightIntensity / CutoffIntensity));
  443. }
  444. }
  445. }
  446. // avoiding SliderFloat3 since its sliders are too narrow.
  447. if (ScriptableImGui::SliderFloat("Center X", &m_diskLightsBasePosition[0], -0.5f, 0.5f) ||
  448. ScriptableImGui::SliderFloat("Center Y", &m_diskLightsBasePosition[1], -0.5f, 0.5f) ||
  449. ScriptableImGui::SliderFloat("Center Z", &m_diskLightsBasePosition[2], -0.5f, 0.5f) ||
  450. ScriptableImGui::SliderFloat("Pos. Scatt. Ratio", &m_diskLightsPositionScatteringRatio, 0.f, 1.f))
  451. {
  452. UpdateDiskLightPositions();
  453. }
  454. bool diskLightShadowmapChanged = ScriptableImGui::Checkbox("Enable Shadow", &m_diskLightShadowEnabled);
  455. ImGui::Text("Shadowmap Size");
  456. int newSize = static_cast<int>(m_diskLightShadowmapSize);
  457. // To avoid GPU memory consumption, we avoid bigger shadowmap sizes here.
  458. diskLightShadowmapChanged = diskLightShadowmapChanged ||
  459. ScriptableImGui::RadioButton("256", &newSize, static_cast<int>(Render::ShadowmapSize::Size256)) ||
  460. ScriptableImGui::RadioButton("512", &newSize, static_cast<int>(Render::ShadowmapSize::Size512)) ||
  461. ScriptableImGui::RadioButton("1024", &newSize, static_cast<int>(Render::ShadowmapSize::Size1024)) ||
  462. ScriptableImGui::RadioButton("Random", &newSize, static_cast<int>(Render::ShadowmapSize::None));
  463. if (diskLightShadowmapChanged)
  464. {
  465. m_diskLightShadowmapSize = static_cast<Render::ShadowmapSize>(newSize);
  466. UpdateDiskLightShadowmapSize();
  467. }
  468. ImGui::Spacing();
  469. ImGui::Text("Filtering");
  470. if (ScriptableImGui::Combo(
  471. "Filter Method##Spot",
  472. &m_shadowFilterMethodIndexDisk,
  473. s_shadowFilterMethodLabels,
  474. aznumeric_cast<int>(AZStd::size(s_shadowFilterMethodLabels))))
  475. {
  476. for (int index = 0; index < m_diskLightCount; ++index)
  477. {
  478. m_diskLightFeatureProcessor->SetShadowFilterMethod(m_diskLights[index].m_handle, aznumeric_cast<ShadowFilterMethod>(m_shadowFilterMethodIndexDisk));
  479. }
  480. }
  481. if (m_shadowFilterMethodIndexDisk == aznumeric_cast<int>(ShadowFilterMethod::Pcf) ||
  482. m_shadowFilterMethodIndexDisk == aznumeric_cast<int>(ShadowFilterMethod::EsmPcf))
  483. {
  484. ImGui::Spacing();
  485. ImGui::Text("Filtering (PCF specific)");
  486. if (ScriptableImGui::SliderInt("Filtering # ##Spot", &m_filteringSampleCountDisk, 4, 64))
  487. {
  488. for (int index = 0; index < m_diskLightCount; ++index)
  489. {
  490. m_diskLightFeatureProcessor->SetFilteringSampleCount(m_diskLights[index].m_handle, static_cast<uint16_t>(m_filteringSampleCountDisk));
  491. }
  492. }
  493. }
  494. }
  495. ImGui::Unindent();
  496. m_imguiSidebar.End();
  497. }
  498. void ShadowedSponzaExampleComponent::UpdateDiskLightShadowmapSize()
  499. {
  500. using namespace AZ::Render;
  501. DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor;
  502. for (const DiskLight& light : m_diskLights)
  503. {
  504. if (!light.m_handle.IsValid())
  505. {
  506. continue;
  507. }
  508. featureProcessor->SetShadowsEnabled(light.m_handle, m_diskLightShadowEnabled);
  509. if (m_diskLightShadowEnabled)
  510. {
  511. if (m_diskLightShadowmapSize != ShadowmapSize::None)
  512. {
  513. // Uniform size
  514. featureProcessor->SetShadowmapMaxResolution(
  515. light.m_handle,
  516. m_diskLightShadowmapSize);
  517. }
  518. else
  519. {
  520. // Random sizes
  521. featureProcessor->SetShadowmapMaxResolution(
  522. light.m_handle,
  523. light.m_shadowmapSize);
  524. }
  525. }
  526. }
  527. }
  528. void ShadowedSponzaExampleComponent::UpdateDiskLightPositions()
  529. {
  530. for (int index = 0; index < m_diskLightCount; ++index)
  531. {
  532. UpdateDiskLightPosition(index);
  533. }
  534. }
  535. void ShadowedSponzaExampleComponent::UpdateDiskLightPosition(int index)
  536. {
  537. using namespace AZ;
  538. Render::DiskLightFeatureProcessorInterface* const featureProcessor = m_diskLightFeatureProcessor;
  539. if (!m_worldAabb.IsValid() || !m_worldAabb.IsFinite())
  540. {
  541. AZ_Assert(false, "World AABB is not initialized correctly.");
  542. return;
  543. }
  544. const Vector3 basePosition(
  545. m_diskLightsBasePosition[0],
  546. m_diskLightsBasePosition[1],
  547. m_diskLightsBasePosition[2]);
  548. const Vector3 relativePosition = basePosition + m_diskLights[index].m_relativePosition * m_diskLightsPositionScatteringRatio;
  549. const Vector3 position = m_worldAabb.GetCenter() +
  550. m_worldAabb.GetExtents() * relativePosition;
  551. const auto transform =
  552. Transform::CreateTranslation(position) * Transform::CreateRotationX(-Constants::HalfPi);
  553. featureProcessor->SetPosition(
  554. m_diskLights[index].m_handle,
  555. position);
  556. featureProcessor->SetDirection(
  557. m_diskLights[index].m_handle,
  558. transform.GetBasis(1));
  559. }
  560. void ShadowedSponzaExampleComponent::SetupDebugFlags()
  561. {
  562. int flags = AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawNone;
  563. if (m_isDebugColoringEnabled)
  564. {
  565. flags |= AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawColoring;
  566. }
  567. if (m_isDebugBoundingBoxEnabled)
  568. {
  569. flags |= AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags::DebugDrawBoundingBoxes;
  570. }
  571. m_directionalLightFeatureProcessor->SetDebugFlags(m_directionalLightHandle,
  572. static_cast<AZ::Render::DirectionalLightFeatureProcessorInterface::DebugDrawFlags>(flags));
  573. }
  574. } // namespace AtomSampleViewer