SphericalHarmonicsExampleComponent.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  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 <RHI/SphericalHarmonicsExampleComponent.h>
  9. #include <Utils/Utils.h>
  10. #include <SampleComponentManager.h>
  11. #include <SampleComponentConfig.h>
  12. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  13. #include <Atom/RHI.Reflect/RenderAttachmentLayoutBuilder.h>
  14. #include <Atom/RPI.Public/Shader/Shader.h>
  15. #include <Atom/RPI.Public/View.h>
  16. #include <Atom/RPI.Public/ViewProviderBus.h>
  17. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  18. #include <AzCore/Serialization/SerializeContext.h>
  19. #include <Atom/Component/DebugCamera/ArcBallControllerBus.h>
  20. #include <Atom/Component/DebugCamera/ArcBallControllerComponent.h>
  21. #include <Atom/Component/DebugCamera/CameraControllerBus.h>
  22. #include <Atom/Feature/SphericalHarmonics/SphericalHarmonicsUtility.h>
  23. #include <random>
  24. #include <sstream>
  25. namespace AtomSampleViewer
  26. {
  27. namespace SHExampleComponent
  28. {
  29. const char* imGui_solverListItem[] = { "poly3", "naive16", "naiveFull"};
  30. const char* imGui_presetItem[] = { "Grace", "RNL", "StPeter", "fakeLight", "fakeLightOriginal" , "fakeLightRotation"};
  31. enum PresetItemEnum { Grace, RNL, StPeter, FakeLight, FakeLightOriginal, FakeLightRotation};
  32. const char* sampleName = "SphericalHarmonicsExampleComponent";
  33. // demo pipeline state
  34. const char* demoShaderFilePath = "Shaders/RHI/shdemo.azshader";
  35. const char* renderShaderFilePath = "Shaders/RHI/shrender.azshader";
  36. // uniform distribution generator & sampler
  37. std::default_random_engine generator;
  38. std::uniform_real_distribution<double> distribution(0.0, 1.0);
  39. }
  40. void SphericalHarmonicsExampleComponent::Reflect(AZ::ReflectContext* context)
  41. {
  42. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  43. {
  44. serializeContext->Class<SphericalHarmonicsExampleComponent, AZ::Component>()
  45. ->Version(0)
  46. ;
  47. }
  48. }
  49. SphericalHarmonicsExampleComponent::SphericalHarmonicsExampleComponent()
  50. {
  51. m_supportRHISamplePipeline = true;
  52. }
  53. void SphericalHarmonicsExampleComponent::Activate()
  54. {
  55. using namespace AZ;
  56. const RHI::Ptr<RHI::Device> device = Utils::GetRHIDevice();
  57. BufferData bufferData;
  58. const auto positionBufSize = static_cast<uint32_t>(bufferData.m_positions.size() * sizeof(VertexPosition));
  59. const auto indexBufSize = static_cast<uint32_t>(bufferData.m_indices.size() * sizeof(uint16_t));
  60. const auto uvBufSize = static_cast<uint32_t>(bufferData.m_uvs.size() * sizeof(VertexUV));
  61. AZ::RHI::RenderAttachmentLayoutBuilder attachmentsBuilder;
  62. AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  63. {
  64. AZ::Debug::CameraControllerRequestBus::Event(m_cameraEntityId,
  65. &AZ::Debug::CameraControllerRequestBus::Events::Enable,
  66. azrtti_typeid<AZ::Debug::ArcBallControllerComponent>());
  67. RPI::ViewPtr cameraView;
  68. // The RPI::View associated to this component can be obtained through the ViewProvider, by using Entity Id.
  69. RPI::ViewProviderBus::EventResult(cameraView, m_cameraEntityId, &RPI::ViewProvider::GetView);
  70. if (cameraView)
  71. {
  72. m_viewShaderResourceGroup = cameraView->GetShaderResourceGroup();
  73. }
  74. }
  75. {
  76. m_bufferPool = aznew RHI::BufferPool();
  77. RHI::BufferPoolDescriptor bufferPoolDesc;
  78. bufferPoolDesc.m_bindFlags = RHI::BufferBindFlags::InputAssembly;
  79. bufferPoolDesc.m_heapMemoryLevel = RHI::HeapMemoryLevel::Device;
  80. m_bufferPool->Init(bufferPoolDesc);
  81. SetFullScreenRect(bufferData.m_positions.data(), bufferData.m_uvs.data(), bufferData.m_indices.data());
  82. m_positionBuffer = aznew RHI::Buffer();
  83. m_indexBuffer = aznew RHI::Buffer();
  84. m_uvBuffer = aznew RHI::Buffer();
  85. RHI::ResultCode result = RHI::ResultCode::Success;
  86. RHI::BufferInitRequest request;
  87. request.m_buffer = m_positionBuffer.get();
  88. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, positionBufSize };
  89. request.m_initialData = bufferData.m_positions.data();
  90. result = m_bufferPool->InitBuffer(request);
  91. if (result != RHI::ResultCode::Success)
  92. {
  93. AZ_Error(SHExampleComponent::sampleName, false, "Failed to initialize position buffer with error code %d", result);
  94. return;
  95. }
  96. request.m_buffer = m_indexBuffer.get();
  97. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, indexBufSize };
  98. request.m_initialData = bufferData.m_indices.data();
  99. result = m_bufferPool->InitBuffer(request);
  100. if (result != RHI::ResultCode::Success)
  101. {
  102. AZ_Error(SHExampleComponent::sampleName, false, "Failed to initialize index buffer with error code %d", result);
  103. return;
  104. }
  105. request.m_buffer = m_uvBuffer.get();
  106. request.m_descriptor = RHI::BufferDescriptor{ RHI::BufferBindFlags::InputAssembly, uvBufSize };
  107. request.m_initialData = bufferData.m_uvs.data();
  108. result = m_bufferPool->InitBuffer(request);
  109. if (result != RHI::ResultCode::Success)
  110. {
  111. AZ_Error(SHExampleComponent::sampleName, false, "Failed to initialize uv buffer with error code %d", result);
  112. return;
  113. }
  114. m_geometryView.SetDrawArguments(RHI::DrawIndexed(0, 6, 0));
  115. m_geometryView.SetIndexBufferView({
  116. *m_indexBuffer,
  117. 0,
  118. indexBufSize,
  119. RHI::IndexFormat::Uint16
  120. });
  121. m_geometryView.AddStreamBufferView({
  122. *m_positionBuffer,
  123. 0,
  124. positionBufSize,
  125. sizeof(VertexPosition)
  126. });
  127. m_geometryView.AddStreamBufferView({
  128. *m_uvBuffer,
  129. 0,
  130. uvBufSize,
  131. sizeof(VertexUV)
  132. });
  133. RHI::InputStreamLayoutBuilder layoutBuilder;
  134. layoutBuilder.AddBuffer()->Channel("POSITION", RHI::Format::R32G32B32_FLOAT);
  135. layoutBuilder.AddBuffer()->Channel("UV", RHI::Format::R32G32_FLOAT);
  136. pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
  137. RHI::ValidateStreamBufferViews(pipelineStateDescriptor.m_inputStreamLayout, m_geometryView, m_geometryView.GetFullStreamBufferIndices());
  138. }
  139. constexpr float objectMatrixScale = 0.8f;
  140. {
  141. auto shader = LoadShader(SHExampleComponent::demoShaderFilePath, SHExampleComponent::sampleName);
  142. if (shader == nullptr)
  143. return;
  144. auto shaderVariant = shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
  145. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  146. attachmentsBuilder.AddSubpass()
  147. ->RenderTargetAttachment(m_outputFormat);
  148. [[maybe_unused]] AZ::RHI::ResultCode result = attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  149. AZ_Assert(result == AZ::RHI::ResultCode::Success, "Failed to create render attachment layout");
  150. m_demoPipelineState = shader->AcquirePipelineState(pipelineStateDescriptor);
  151. if (!m_demoPipelineState)
  152. {
  153. AZ_Error(SHExampleComponent::sampleName, false, "Failed to acquire default pipeline state for shader '%s'", SHExampleComponent::demoShaderFilePath);
  154. return;
  155. }
  156. const AZ::Name demoObjectMatrixShaderInput{ "m_objectMatrix" };
  157. const AZ::Name SHBandInput{ "m_shBand" };
  158. const AZ::Name SHOrderInput{ "m_shOrder" };
  159. const AZ::Name SHSolverInput{ "m_shSolver" };
  160. const AZ::Name enableDistortionInput{ "m_enableDistortion" };
  161. m_demoShaderResourceGroup = CreateShaderResourceGroup(shader, "SphericalHarmonicsInstanceSrg", SHExampleComponent::sampleName);
  162. FindShaderInputIndex(&m_demoObjectMatrixInputIndex, m_demoShaderResourceGroup, demoObjectMatrixShaderInput, SHExampleComponent::sampleName);
  163. FindShaderInputIndex(&m_SHBandInputIndex, m_demoShaderResourceGroup, SHBandInput, SHExampleComponent::sampleName);
  164. FindShaderInputIndex(&m_SHOrderInputIndex, m_demoShaderResourceGroup, SHOrderInput, SHExampleComponent::sampleName);
  165. FindShaderInputIndex(&m_SHSolverInputIndex, m_demoShaderResourceGroup, SHSolverInput, SHExampleComponent::sampleName);
  166. FindShaderInputIndex(&m_EnableDistortionInputIndex, m_demoShaderResourceGroup, enableDistortionInput, SHExampleComponent::sampleName);
  167. // Scale and translate the texture quad so we can fit the ImGuiSideBar with the samplers options.
  168. // x axis scale is multiplied by 9 / 16 (0.5625) to convert the output plane from rectangle to square to
  169. // maintain correct aspect ratio during screen space ray traicng
  170. AZ::Matrix4x4 matrix = AZ::Matrix4x4::CreateScale(Vector3(objectMatrixScale*0.5625f, objectMatrixScale, objectMatrixScale)) * AZ::Matrix4x4::CreateTranslation(Vector3(objectMatrixScale - 1.0f, 0, 0));
  171. m_demoShaderResourceGroup->SetConstant(m_demoObjectMatrixInputIndex, matrix);
  172. }
  173. {
  174. // render pipeline state
  175. auto shader = LoadShader(SHExampleComponent::renderShaderFilePath, SHExampleComponent::sampleName);
  176. if (shader == nullptr)
  177. return;
  178. auto shaderVariant = shader->GetVariant(RPI::ShaderAsset::RootShaderVariantStableId);
  179. shaderVariant.ConfigurePipelineState(pipelineStateDescriptor);
  180. attachmentsBuilder.Reset();
  181. attachmentsBuilder.AddSubpass()
  182. ->RenderTargetAttachment(m_outputFormat);
  183. [[maybe_unused]] AZ::RHI::ResultCode result = attachmentsBuilder.End(pipelineStateDescriptor.m_renderAttachmentConfiguration.m_renderAttachmentLayout);
  184. AZ_Assert(result == AZ::RHI::ResultCode::Success, "Failed to create render attachment layout");
  185. m_renderPipelineState = shader->AcquirePipelineState(pipelineStateDescriptor);
  186. if (!m_renderPipelineState)
  187. {
  188. AZ_Error(SHExampleComponent::sampleName, false, "Failed to acquire default pipeline state for shader '%s'", SHExampleComponent::demoShaderFilePath);
  189. return;
  190. }
  191. const AZ::Name renderObjectMatrixInput{ "m_objectMatrix" };
  192. const AZ::Name presetIndexInput{ "m_presetIndex" };
  193. const AZ::Name exposureInput{ "m_exposure" };
  194. const AZ::Name enableGammaCorrectionInput{ "m_enableGammaCorrection" };
  195. const AZ::Name shFakeLightCoefficientInput{ "m_fakeLightSH" };
  196. const AZ::Name rotationAngleInput{ "m_rotationAngle" };
  197. m_renderShaderResourceGroup = CreateShaderResourceGroup(shader, "SphericalHarmonicsInstanceSrg", SHExampleComponent::sampleName);
  198. FindShaderInputIndex(&m_renderObjectMatrixInputIndex, m_renderShaderResourceGroup, renderObjectMatrixInput, SHExampleComponent::sampleName);
  199. FindShaderInputIndex(&m_presetIndexInputIndex, m_renderShaderResourceGroup, presetIndexInput, SHExampleComponent::sampleName);
  200. FindShaderInputIndex(&m_exposureInputIndex, m_renderShaderResourceGroup, exposureInput, SHExampleComponent::sampleName);
  201. FindShaderInputIndex(&m_enableGammaCorrectionInputIndex, m_renderShaderResourceGroup, enableGammaCorrectionInput, SHExampleComponent::sampleName);
  202. FindShaderInputIndex(&m_SHFakeLightCoefficientsInputIndex, m_renderShaderResourceGroup, shFakeLightCoefficientInput, SHExampleComponent::sampleName);
  203. FindShaderInputIndex(&m_rotationAngleInputIndex, m_renderShaderResourceGroup, rotationAngleInput, SHExampleComponent::sampleName);
  204. // Scale and translate the texture quad so we can fit the ImGuiSideBar with the samplers options.
  205. AZ::Matrix4x4 matrix = AZ::Matrix4x4::CreateScale(Vector3(objectMatrixScale*0.5625f, objectMatrixScale, objectMatrixScale)) * AZ::Matrix4x4::CreateTranslation(Vector3(objectMatrixScale - 1.0f, 0, 0));
  206. m_renderShaderResourceGroup->SetConstant(m_renderObjectMatrixInputIndex, matrix);
  207. m_shaderInputSHFakeLightCoefficients = AZ::Matrix4x4::CreateZero();
  208. m_renderShaderResourceGroup->SetConstant(m_SHFakeLightCoefficientsInputIndex, m_shaderInputSHFakeLightCoefficients);
  209. m_shaderInputRotationAngle = AZ::Vector3(0.0, 0.0, 0.0);
  210. m_renderShaderResourceGroup->SetConstant(m_rotationAngleInputIndex, m_shaderInputRotationAngle);
  211. }
  212. {
  213. struct ScopeData
  214. {
  215. };
  216. const auto prepareFunction = [this](RHI::FrameGraphInterface frameGraph, [[maybe_unused]] ScopeData& scopeData)
  217. {
  218. {
  219. RHI::ImageScopeAttachmentDescriptor desc;
  220. desc.m_attachmentId = m_outputAttachmentId;
  221. frameGraph.UseColorAttachment(desc);
  222. }
  223. frameGraph.SetEstimatedItemCount(1);
  224. };
  225. const auto compileFunction = [this]([[maybe_unused]] const AZ::RHI::FrameGraphCompileContext& context, [[maybe_unused]] const ScopeData& scopeData)
  226. {
  227. if (m_updateDemoSRG)
  228. {
  229. m_demoShaderResourceGroup->SetConstant(m_SHBandInputIndex, m_shaderInputSHBand);
  230. m_demoShaderResourceGroup->SetConstant(m_SHOrderInputIndex, m_shaderInputSHOrder);
  231. m_demoShaderResourceGroup->SetConstant(m_SHSolverInputIndex, m_shaderInputSHSolver);
  232. m_demoShaderResourceGroup->SetConstant(m_EnableDistortionInputIndex, m_shaderInputEnableDistortion);
  233. m_demoShaderResourceGroup->Compile();
  234. m_updateDemoSRG = false;
  235. }
  236. if (m_updateRenderSRG)
  237. {
  238. m_renderShaderResourceGroup->SetConstant(m_presetIndexInputIndex, m_shaderInputPresetIndex);
  239. m_renderShaderResourceGroup->SetConstant(m_exposureInputIndex, m_shaderInputExposure);
  240. m_renderShaderResourceGroup->SetConstant(m_enableGammaCorrectionInputIndex, m_shaderInputEnableGammaCorrection);
  241. m_renderShaderResourceGroup->SetConstant(m_SHFakeLightCoefficientsInputIndex, m_shaderInputSHFakeLightCoefficients);
  242. m_renderShaderResourceGroup->SetConstant(m_rotationAngleInputIndex, m_shaderInputRotationAngle);
  243. m_renderShaderResourceGroup->Compile();
  244. m_updateRenderSRG = false;
  245. }
  246. };
  247. const auto executeFunction = [=]([[maybe_unused]] const RHI::FrameGraphExecuteContext& context, [[maybe_unused]] const ScopeData& scopeData)
  248. {
  249. RHI::CommandList* commandList = context.GetCommandList();
  250. commandList->SetViewports(&m_viewport, 1);
  251. commandList->SetScissors(&m_scissor, 1);
  252. // Bind ViewSrg
  253. commandList->SetShaderResourceGroupForDraw(*m_viewShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get());
  254. const RHI::DeviceShaderResourceGroup* shaderResourceGroups[] = {
  255. (m_mode ? m_demoShaderResourceGroup->GetRHIShaderResourceGroup()
  256. ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
  257. .get()
  258. : m_renderShaderResourceGroup->GetRHIShaderResourceGroup()
  259. ->GetDeviceShaderResourceGroup(context.GetDeviceIndex())
  260. .get()),
  261. m_viewShaderResourceGroup->GetRHIShaderResourceGroup()->GetDeviceShaderResourceGroup(context.GetDeviceIndex()).get()
  262. };
  263. RHI::DeviceDrawItem drawItem;
  264. drawItem.m_geometryView = m_geometryView.GetDeviceGeometryView(context.GetDeviceIndex());
  265. drawItem.m_streamIndices = m_geometryView.GetFullStreamBufferIndices();
  266. drawItem.m_pipelineState = m_mode ? m_demoPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get() : m_renderPipelineState->GetDevicePipelineState(context.GetDeviceIndex()).get();
  267. drawItem.m_shaderResourceGroupCount = static_cast<uint8_t>(RHI::ArraySize(shaderResourceGroups));
  268. drawItem.m_shaderResourceGroups = shaderResourceGroups;
  269. commandList->Submit(drawItem);
  270. };
  271. m_scopeProducers.emplace_back(aznew RHI::ScopeProducerFunction<
  272. ScopeData,
  273. decltype(prepareFunction),
  274. decltype(compileFunction),
  275. decltype(executeFunction)>(
  276. RHI::ScopeId{ "SHSample" },
  277. ScopeData{},
  278. prepareFunction,
  279. compileFunction,
  280. executeFunction));
  281. }
  282. AZ::TickBus::Handler::BusConnect();
  283. AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
  284. m_imguiSidebar.Activate();
  285. }
  286. void SphericalHarmonicsExampleComponent::Deactivate()
  287. {
  288. m_positionBuffer = nullptr;
  289. m_indexBuffer = nullptr;
  290. m_uvBuffer = nullptr;
  291. m_bufferPool = nullptr;
  292. m_demoPipelineState = nullptr;
  293. m_demoShaderResourceGroup = nullptr;
  294. m_viewShaderResourceGroup = nullptr;
  295. m_shaderInputSHFakeLightCoefficients = AZ::Matrix4x4::CreateZero();
  296. m_shaderInputRotationAngle = AZ::Vector3(0.0, 0.0, 0.0);
  297. AZ::RHI::RHISystemNotificationBus::Handler::BusDisconnect();
  298. AZ::TickBus::Handler::BusDisconnect();
  299. m_windowContext = nullptr;
  300. m_scopeProducers.clear();
  301. m_imguiSidebar.Deactivate();
  302. }
  303. void SphericalHarmonicsExampleComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  304. {
  305. if (m_imguiSidebar.Begin())
  306. {
  307. DrawIMGui();
  308. }
  309. }
  310. //import camera configuration
  311. bool SphericalHarmonicsExampleComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  312. {
  313. BasicRHIComponent::ReadInConfig(baseConfig);
  314. const auto* config = azrtti_cast<const SampleComponentConfig*>(baseConfig);
  315. if (config && config->IsValid())
  316. {
  317. m_cameraEntityId = config->m_cameraEntityId;
  318. return true;
  319. }
  320. else
  321. {
  322. AZ_Assert(false, "SampleComponentConfig required for sample component configuration.");
  323. return false;
  324. }
  325. }
  326. float SphericalHarmonicsExampleComponent::CalFakeLightSH(bool& flag)
  327. {
  328. static const uint32_t maxSample = 200000;
  329. // distribute calculation over ticks to avoid blocking rendering pipeline
  330. static const uint32_t samplePerTick = 1000;
  331. static uint32_t sampleCount = 0;
  332. // differential of solid angle, the surface area of unit sphere is (4 pi),
  333. // and (maxSample) number of samples are drawn uniformly, thus
  334. // for each sample its solid angle is (4pi / maxSample)
  335. static double dw = (4.0 * AZ::Constants::Pi) / maxSample;
  336. if (flag)
  337. {
  338. // reinitialize state
  339. sampleCount = 0;
  340. m_shaderInputSHFakeLightCoefficients = AZ::Matrix4x4::CreateZero();
  341. flag = false;
  342. }
  343. if (sampleCount >= maxSample)
  344. {
  345. return 0.0;
  346. }
  347. // the shape of this light is demonsatrated in preset "fakeLightOriginal" in render mode
  348. // this function is a copy of fake light function in:
  349. // http://silviojemma.com/public/papers/lighting/spherical-harmonic-lighting.pdf, page 15, figure 7
  350. auto FakeLight = [](double theta, double phi)->double
  351. {
  352. // increase energy level to form hdr
  353. return 5.0 * (fmax(0.0, 5.0 * cos(theta) - 4.0) +
  354. fmax(0.0, -4.0 * sin(theta - AZ::Constants::Pi) * cos(phi - 2.5) - 3.0));
  355. };
  356. // quasi Monte Carlo sampling, without importance
  357. for (uint32_t i = 0; i < samplePerTick; ++i)
  358. {
  359. // independent identically distributed(i.i.d) samples drawn from uniform distribution [0, 1]
  360. // because importance sampling is not used here so don't need to project to target distribution
  361. double u1 = SHExampleComponent::distribution(SHExampleComponent::generator);
  362. double u2 = SHExampleComponent::distribution(SHExampleComponent::generator);
  363. // spherical coordinates (assume unit sphere radius = 1)
  364. // zenith angle between up axis (y axis in this case) and sampled direction
  365. double theta = acos(1.0 - 2.0 * u1);
  366. // azimuth angle between +x axis and sampled direction's projection on parallel plane (x-z plane in this case)
  367. double phi = 2.0 * AZ::Constants::Pi * u2;
  368. // cartesian coordinates of sample on unit sphere
  369. // here Y-up -Z-forward axis system is used
  370. float dir[3];
  371. dir[0] = static_cast<float>(sin(theta) * cos(phi));
  372. dir[1] = static_cast<float>(cos(theta));
  373. dir[2] = static_cast<float>(sin(theta) * sin(phi));
  374. // fake radiance computed from analytical light source at given direction
  375. double L = FakeLight(theta, phi);
  376. // evaluate single iteration of integral for all basises from band 0, order 0 to band 3, order 3
  377. // this coefficient set will be shared by all three color channels, thus final reconstructed output will be greylevel color
  378. // band 0
  379. m_shaderInputSHFakeLightCoefficients.SetElement(0, 0, m_shaderInputSHFakeLightCoefficients.GetElement(0, 0) +
  380. static_cast<float>(L * AZ::Render::SHBasis::Naive16(0, 0, dir) * dw));
  381. // band 1
  382. m_shaderInputSHFakeLightCoefficients.SetElement(0, 1, m_shaderInputSHFakeLightCoefficients.GetElement(0, 1) +
  383. static_cast<float>(L * AZ::Render::SHBasis::Naive16(1, -1, dir) * dw));
  384. m_shaderInputSHFakeLightCoefficients.SetElement(0, 2, m_shaderInputSHFakeLightCoefficients.GetElement(0, 2) +
  385. static_cast<float>(L * AZ::Render::SHBasis::Naive16(1, 0, dir) * dw));
  386. m_shaderInputSHFakeLightCoefficients.SetElement(0, 3, m_shaderInputSHFakeLightCoefficients.GetElement(0, 3) +
  387. static_cast<float>(L * AZ::Render::SHBasis::Naive16(1, 1, dir) * dw));
  388. // band 2
  389. m_shaderInputSHFakeLightCoefficients.SetElement(1, 0, m_shaderInputSHFakeLightCoefficients.GetElement(1, 0) +
  390. static_cast<float>(L * AZ::Render::SHBasis::Naive16(2, -2, dir) * dw));
  391. m_shaderInputSHFakeLightCoefficients.SetElement(1, 1, m_shaderInputSHFakeLightCoefficients.GetElement(1, 1) +
  392. static_cast<float>(L * AZ::Render::SHBasis::Naive16(2, -1, dir) * dw));
  393. m_shaderInputSHFakeLightCoefficients.SetElement(1, 2, m_shaderInputSHFakeLightCoefficients.GetElement(1, 2) +
  394. static_cast<float>(L * AZ::Render::SHBasis::Naive16(2, 0, dir) * dw));
  395. m_shaderInputSHFakeLightCoefficients.SetElement(1, 3, m_shaderInputSHFakeLightCoefficients.GetElement(1, 3) +
  396. static_cast<float>(L * AZ::Render::SHBasis::Naive16(2, 1, dir) * dw));
  397. m_shaderInputSHFakeLightCoefficients.SetElement(2, 0, m_shaderInputSHFakeLightCoefficients.GetElement(2, 0) +
  398. static_cast<float>(L * AZ::Render::SHBasis::Naive16(2, 2, dir) * dw));
  399. // band 3
  400. m_shaderInputSHFakeLightCoefficients.SetElement(2, 1, m_shaderInputSHFakeLightCoefficients.GetElement(2, 1) +
  401. static_cast<float>(L * AZ::Render::SHBasis::Naive16(3, -3, dir) * dw));
  402. m_shaderInputSHFakeLightCoefficients.SetElement(2, 2, m_shaderInputSHFakeLightCoefficients.GetElement(2, 2) +
  403. static_cast<float>(L * AZ::Render::SHBasis::Naive16(3, -2, dir) * dw));
  404. m_shaderInputSHFakeLightCoefficients.SetElement(2, 3, m_shaderInputSHFakeLightCoefficients.GetElement(2, 3) +
  405. static_cast<float>(L * AZ::Render::SHBasis::Naive16(3, -1, dir) * dw));
  406. m_shaderInputSHFakeLightCoefficients.SetElement(3, 0, m_shaderInputSHFakeLightCoefficients.GetElement(3, 0) +
  407. static_cast<float>(L * AZ::Render::SHBasis::Naive16(3, 0, dir) * dw));
  408. m_shaderInputSHFakeLightCoefficients.SetElement(3, 1, m_shaderInputSHFakeLightCoefficients.GetElement(3, 1) +
  409. static_cast<float>(L * AZ::Render::SHBasis::Naive16(3, 1, dir) * dw));
  410. m_shaderInputSHFakeLightCoefficients.SetElement(3, 2, m_shaderInputSHFakeLightCoefficients.GetElement(3, 2) +
  411. static_cast<float>(L * AZ::Render::SHBasis::Naive16(3, 2, dir) * dw));
  412. m_shaderInputSHFakeLightCoefficients.SetElement(3, 3, m_shaderInputSHFakeLightCoefficients.GetElement(3, 3) +
  413. static_cast<float>(L * AZ::Render::SHBasis::Naive16(3, 3, dir) * dw));
  414. }
  415. sampleCount += samplePerTick;
  416. return sampleCount / float(maxSample);
  417. }
  418. void SphericalHarmonicsExampleComponent::DrawIMGui()
  419. {
  420. ImGui::Text("\n\n\n\n\n\n\n\n\n");
  421. std::stringstream ss;
  422. ss << "Switch to " << (m_mode ? "Render" : "Demo") << " mode";
  423. if (ScriptableImGui::Button(ss.str().c_str()))
  424. {
  425. m_mode = !m_mode;
  426. }
  427. ImGui::Text("\n\n");
  428. if (m_mode)
  429. {
  430. ImGui::Text("Band L: non negative integer");
  431. int l = m_shaderInputSHBand;
  432. if (ImGui::InputInt("Band", &m_shaderInputSHBand))
  433. {
  434. if (m_shaderInputSHBand >= 0 && m_shaderInputSHBand >= m_shaderInputSHOrder)
  435. {
  436. m_updateDemoSRG = true;
  437. }
  438. else
  439. {
  440. m_shaderInputSHBand = l;
  441. }
  442. }
  443. ImGui::Text("Order M: integer within range [-L, L]");
  444. int m = m_shaderInputSHOrder;
  445. if (ImGui::InputInt("Order", &m_shaderInputSHOrder))
  446. {
  447. if (m_shaderInputSHOrder >= -m_shaderInputSHBand && m_shaderInputSHOrder <= m_shaderInputSHBand)
  448. {
  449. m_updateDemoSRG = true;
  450. }
  451. else
  452. {
  453. m_shaderInputSHOrder = m;
  454. }
  455. }
  456. if (ImGui::ListBox("Solver", &m_shaderInputSHSolver, SHExampleComponent::imGui_solverListItem,
  457. IM_ARRAYSIZE(SHExampleComponent::imGui_solverListItem), 4))
  458. {
  459. m_updateDemoSRG = true;
  460. }
  461. if (ScriptableImGui::Checkbox("Distortion", &m_shaderInputEnableDistortion))
  462. {
  463. m_updateDemoSRG = true;
  464. }
  465. }
  466. else
  467. {
  468. if (ImGui::ListBox("Coefficient set", &m_shaderInputPresetIndex, SHExampleComponent::imGui_presetItem,
  469. IM_ARRAYSIZE(SHExampleComponent::imGui_presetItem), 4))
  470. {
  471. m_updateRenderSRG = true;
  472. }
  473. static int angle[3] = {0, 0, 0};
  474. static const float deg2rad = (AZ::Constants::Pi / 180.0);
  475. if (m_shaderInputPresetIndex == SHExampleComponent::FakeLightRotation)
  476. {
  477. if (ScriptableImGui::SliderInt("Set X rotation", &angle[0], 0, 360))
  478. {
  479. m_shaderInputRotationAngle.SetX(angle[0] * deg2rad);
  480. m_updateRenderSRG = true;
  481. }
  482. if (ScriptableImGui::SliderInt("Set Y rotation", &angle[1], 0, 360))
  483. {
  484. m_shaderInputRotationAngle.SetZ(angle[1] * deg2rad);
  485. m_updateRenderSRG = true;
  486. }
  487. if (ScriptableImGui::SliderInt("Set Z rotation", &angle[2], 0, 360))
  488. {
  489. // since SH computation assume z-up frame
  490. m_shaderInputRotationAngle.SetY(angle[2] * deg2rad);
  491. m_updateRenderSRG = true;
  492. }
  493. }
  494. ss = std::stringstream();
  495. ss << (m_shaderInputEnableGammaCorrection ? "Disable" : "Enable") << " Gamma Correction";
  496. if (ScriptableImGui::Button(ss.str().c_str()))
  497. {
  498. m_shaderInputEnableGammaCorrection = !m_shaderInputEnableGammaCorrection;
  499. m_updateRenderSRG = true;
  500. }
  501. static float sliderFloatValue = 1.0f;
  502. if (ScriptableImGui::SliderFloat("set exposure", &sliderFloatValue, 0.1f, 2.0f, "ratio = %.1f"))
  503. {
  504. m_shaderInputExposure = sliderFloatValue;
  505. m_updateRenderSRG = true;
  506. }
  507. static bool fakeLightDone = false;
  508. float progress = CalFakeLightSH(m_recomputeFakeLight);
  509. if (progress)
  510. {
  511. ImGui::Text("\n\nCalculaitng fake light SH: %.2f percent \n", progress * 100);
  512. if (m_shaderInputPresetIndex == SHExampleComponent::FakeLight)
  513. {
  514. m_updateRenderSRG = true;
  515. }
  516. }
  517. else
  518. {
  519. if (!fakeLightDone)
  520. {
  521. m_updateRenderSRG = true;
  522. fakeLightDone = true;
  523. }
  524. ImGui::Text("\n\nFake light SH ready to use, result: ");
  525. for (int32_t i = 0; i < 4; ++i)
  526. {
  527. for (int32_t j = -i; j <= i; ++j)
  528. {
  529. int32_t index = i * (i + 1) + j;
  530. const float temp = m_shaderInputSHFakeLightCoefficients.GetElement(index / 4, index % 4);
  531. ImGui::Text(" Band %d, Order %d: \n %f", i, j, temp);
  532. }
  533. }
  534. if (ScriptableImGui::Button("Recompute"))
  535. {
  536. fakeLightDone = false;
  537. m_recomputeFakeLight = true;
  538. }
  539. }
  540. }
  541. m_imguiSidebar.End();
  542. }
  543. }