SkyBoxFeatureProcessor.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  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 <SkyBox/SkyBoxFeatureProcessor.h>
  9. #include <AzFramework/Asset/AssetSystemBus.h>
  10. #include <Atom/RHI/Factory.h>
  11. #include <Atom/RHI/RHISystemInterface.h>
  12. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  13. #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
  14. #include <Atom/RPI.Public/ColorManagement/TransformColor.h>
  15. #include <Atom/RPI.Public/RenderPipeline.h>
  16. #include <Atom/RPI.Public/Image/StreamingImage.h>
  17. #include <Atom/RPI.Public/RPIUtils.h>
  18. #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
  19. #include <Atom/RPI.Public/Scene.h>
  20. #include <Atom/RPI.Public/View.h>
  21. #include <SkyBox/SkyBoxLUT.h>
  22. namespace AZ
  23. {
  24. namespace Render
  25. {
  26. const double* physicalSkyLUTRGB[] =
  27. {
  28. PhysicalSkyLUT::RGB1,
  29. PhysicalSkyLUT::RGB2,
  30. PhysicalSkyLUT::RGB3
  31. };
  32. const double* physicalSkyLUTRGBRad[] =
  33. {
  34. PhysicalSkyLUT::RGBRad1,
  35. PhysicalSkyLUT::RGBRad2,
  36. PhysicalSkyLUT::RGBRad3
  37. };
  38. void SkyBoxFeatureProcessor::Reflect(ReflectContext* context)
  39. {
  40. if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
  41. {
  42. serializeContext
  43. ->Class<SkyBoxFeatureProcessor, FeatureProcessor>()
  44. ->Version(1);
  45. }
  46. }
  47. SkyBoxFeatureProcessor::SkyBoxFeatureProcessor()
  48. :SkyBoxFeatureProcessorInterface()
  49. {
  50. m_skyIntensity = PhotometricValue(PhysicalSkyDefaultIntensity, AZ::Color::CreateOne(), PhotometricUnit::Ev100Luminance);
  51. m_sunIntensity = PhotometricValue(PhysicalSunDefaultIntensity, AZ::Color::CreateOne(), PhotometricUnit::Ev100Luminance);
  52. }
  53. void SkyBoxFeatureProcessor::Activate()
  54. {
  55. InitBuffer();
  56. // Load default cubemap
  57. // This is assigned when the skybox is disabled or removed from the scene to prevent a Vulkan TDR.
  58. // [GFX-TODO][ATOM-4181] This can be removed after Vulkan is changed to automatically handle this issue.
  59. LoadDefaultCubeMap();
  60. m_cubemapTexture = m_defaultCubemapTexture;
  61. // Find the relevant indices in the scene srg
  62. m_sceneSrg = GetParentScene()->GetShaderResourceGroup();
  63. m_skyboxEnableIndex.Reset();
  64. m_physicalSkyBufferIndex.Reset();
  65. m_physicalSkyIndex.Reset();
  66. m_cubemapIndex.Reset();
  67. m_cubemapRotationMatrixIndex.Reset();
  68. m_cubemapExposureIndex.Reset();
  69. m_fogEnableIndex.Reset();
  70. m_fogColorIndex.Reset();
  71. m_fogTopHeightIndex.Reset();
  72. m_fogBottomHeightIndex.Reset();
  73. if (m_buffer)
  74. {
  75. m_sceneSrg->SetBufferView(
  76. m_physicalSkyBufferIndex, m_buffer->GetBufferView());
  77. }
  78. }
  79. void SkyBoxFeatureProcessor::Deactivate()
  80. {
  81. m_buffer = nullptr;
  82. m_cubemapTexture = m_defaultCubemapTexture;
  83. m_sceneSrg = nullptr;
  84. }
  85. void SkyBoxFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet)
  86. {
  87. AZ_PROFILE_SCOPE(RPI, "SkyBoxFeatureProcessor: Simulate");
  88. AZ_UNUSED(packet);
  89. m_sceneSrg->SetConstant(m_skyboxEnableIndex, m_enable);
  90. if (m_enable)
  91. {
  92. switch (m_skyboxMode)
  93. {
  94. case SkyBoxMode::PhysicalSky:
  95. {
  96. if (m_skyNeedUpdate)
  97. {
  98. HosekSky skyParameters = ComputeHosekSky();
  99. skyParameters.a.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterA.data());
  100. skyParameters.b.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterB.data());
  101. skyParameters.c.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterC.data());
  102. skyParameters.d.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterD.data());
  103. skyParameters.e.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterE.data());
  104. skyParameters.f.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterF.data());
  105. skyParameters.g.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterG.data());
  106. skyParameters.h.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterH.data());
  107. skyParameters.i.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterI.data());
  108. skyParameters.z.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterZ.data());
  109. }
  110. if (m_sunNeedUpdate)
  111. {
  112. AZ::Vector4 sunRGB = ComputeSunRGB();
  113. sunRGB.StoreToFloat4(m_physicalSkyData.m_physicalSkySunRGB.data());
  114. }
  115. if (m_skyNeedUpdate || m_sunNeedUpdate || m_mapBuffer)
  116. {
  117. m_sunDirection.StoreToFloat4(m_physicalSkyData.m_physicalSkySunDirection.data());
  118. m_physicalSkyData.m_physicalSkySunParameters[0] = m_sunParameters.m_radius;
  119. m_physicalSkyData.m_physicalSkySunParameters[1] = m_sunParameters.m_distance;
  120. m_physicalSkyData.m_physicalSkySunParameters[2] = m_sunParameters.m_cosAngularDiameter;
  121. m_physicalSkyData.m_physicalSkySunParameters[3] = 0.0f;
  122. m_skyIntensity.ConvertToPhotometricUnit(PhotometricUnit::Nit);
  123. m_sunIntensity.ConvertToPhotometricUnit(PhotometricUnit::Nit);
  124. AZ::Vector4 artistParams = AZ::Vector4(m_skyIntensity.GetIntensity(), m_sunIntensity.GetIntensity(), 0.0f, 0.0f);
  125. artistParams.StoreToFloat4(m_physicalSkyData.m_physicalSkyAndSunIntensity.data());
  126. if (m_buffer)
  127. {
  128. m_buffer->UpdateData(&m_physicalSkyData, sizeof(PhysicalSkyData));
  129. }
  130. m_skyNeedUpdate = false;
  131. m_sunNeedUpdate = false;
  132. m_mapBuffer = false;
  133. }
  134. m_sceneSrg->SetConstant(m_fogEnableIndex, m_fogSettings.m_enable);
  135. if (m_fogSettings.m_enable)
  136. {
  137. m_sceneSrg->SetConstant(m_fogTopHeightIndex, m_fogSettings.m_topHeight);
  138. m_sceneSrg->SetConstant(m_fogBottomHeightIndex, m_fogSettings.m_bottomHeight);
  139. m_sceneSrg->SetConstant(m_fogColorIndex, m_fogSettings.m_color);
  140. }
  141. m_sceneSrg->SetConstant(m_physicalSkyIndex, true);
  142. break;
  143. }
  144. case SkyBoxMode::Cubemap:
  145. {
  146. // set texture
  147. m_sceneSrg->SetImage(m_cubemapIndex, m_cubemapTexture);
  148. // set cubemap rotation matrix
  149. m_sceneSrg->SetConstant(m_cubemapRotationMatrixIndex, m_cubemapRotationMatrix);
  150. // set exposure
  151. m_sceneSrg->SetConstant(m_cubemapExposureIndex, m_cubemapExposure);
  152. m_sceneSrg->SetConstant(m_physicalSkyIndex, false);
  153. break;
  154. }
  155. default:
  156. break;
  157. }
  158. }
  159. }
  160. void SkyBoxFeatureProcessor::Render(const FeatureProcessor::RenderPacket& packet)
  161. {
  162. AZ_PROFILE_FUNCTION(AzRender);
  163. AZ_UNUSED(packet);
  164. }
  165. void SkyBoxFeatureProcessor::InitBuffer()
  166. {
  167. const uint32_t byteCount = sizeof(PhysicalSkyData);
  168. RPI::CommonBufferDescriptor desc;
  169. desc.m_poolType = RPI::CommonBufferPoolType::Constant;
  170. desc.m_bufferName = "SkyboxBuffer";
  171. desc.m_byteCount = byteCount;
  172. desc.m_elementSize = byteCount;
  173. desc.m_bufferData = &m_physicalSkyData;
  174. m_buffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc);
  175. }
  176. void SkyBoxFeatureProcessor::LoadDefaultCubeMap()
  177. {
  178. const constexpr char* DefaultCubeMapPath = "textures/default/default_skyboxcm.dds.streamingimage";
  179. m_defaultCubemapTexture = RPI::LoadStreamingTexture(DefaultCubeMapPath);
  180. AZ_Assert(m_defaultCubemapTexture, "Failed to load default cubemap");
  181. }
  182. void SkyBoxFeatureProcessor::Enable(bool enable)
  183. {
  184. m_enable = enable;
  185. }
  186. bool SkyBoxFeatureProcessor::IsEnabled()
  187. {
  188. return m_enable;
  189. }
  190. void SkyBoxFeatureProcessor::SetCubemapRotationMatrix(AZ::Matrix4x4 matrix)
  191. {
  192. m_cubemapRotationMatrix = matrix;
  193. }
  194. void SkyBoxFeatureProcessor::SetCubemap(Data::Instance<RPI::Image> cubemap)
  195. {
  196. m_cubemapTexture = cubemap.get() ? cubemap : m_defaultCubemapTexture;
  197. }
  198. void SkyBoxFeatureProcessor::SetCubemapExposure(float exposure)
  199. {
  200. m_cubemapExposure = exposure;
  201. }
  202. void SkyBoxFeatureProcessor::SetSkyboxMode(SkyBoxMode mode)
  203. {
  204. m_skyboxMode = mode;
  205. }
  206. void SkyBoxFeatureProcessor::SetFogSettings(const SkyBoxFogSettings& fogSettings)
  207. {
  208. m_fogSettings = fogSettings;
  209. }
  210. void SkyBoxFeatureProcessor::SetFogEnabled(bool enable)
  211. {
  212. m_fogSettings.m_enable = enable;
  213. }
  214. bool SkyBoxFeatureProcessor::IsFogEnabled()
  215. {
  216. return m_fogSettings.m_enable;
  217. }
  218. void SkyBoxFeatureProcessor::SetFogColor(const AZ::Color& color)
  219. {
  220. m_fogSettings.m_color = color;
  221. }
  222. void SkyBoxFeatureProcessor::SetFogTopHeight(float topHeight)
  223. {
  224. m_fogSettings.m_topHeight = topHeight;
  225. }
  226. void SkyBoxFeatureProcessor::SetFogBottomHeight(float bottomHeight)
  227. {
  228. m_fogSettings.m_bottomHeight = bottomHeight;
  229. }
  230. void SkyBoxFeatureProcessor::SetSunPosition(SunPosition sunPosition)
  231. {
  232. m_skyNeedUpdate = true;
  233. m_sunNeedUpdate = true;
  234. m_sunPosition = sunPosition;
  235. }
  236. void SkyBoxFeatureProcessor::SetSunPosition(float azimuth, float altitude)
  237. {
  238. m_skyNeedUpdate = true;
  239. m_sunNeedUpdate = true;
  240. m_sunPosition.m_azimuth = azimuth;
  241. m_sunPosition.m_altitude = altitude;
  242. }
  243. void SkyBoxFeatureProcessor::SetTurbidity(int turbidity)
  244. {
  245. m_skyNeedUpdate = true;
  246. m_sunNeedUpdate = true;
  247. m_turbidity = turbidity;
  248. }
  249. void SkyBoxFeatureProcessor::SetSkyIntensity(float intensity, PhotometricUnit unit)
  250. {
  251. m_mapBuffer = true;
  252. m_skyIntensity.ConvertToPhotometricUnit(unit);
  253. m_skyIntensity.SetIntensity(intensity);
  254. }
  255. void SkyBoxFeatureProcessor::SetSunIntensity(float intensity, PhotometricUnit unit)
  256. {
  257. m_mapBuffer = true;
  258. m_sunIntensity.ConvertToPhotometricUnit(unit);
  259. m_sunIntensity.SetIntensity(intensity);
  260. }
  261. void SkyBoxFeatureProcessor::SetSunRadiusFactor(float factor)
  262. {
  263. m_sunNeedUpdate = true;
  264. m_sunParameters.m_radius = PhysicalSunRadius * factor;
  265. m_sunParameters.m_cosAngularDiameter = cosf(atanf((m_sunParameters.m_radius * 2.0f) / (m_sunParameters.m_distance * 2.0f)) * 2.0f);
  266. }
  267. AZ::Vector3 SkyBoxFeatureProcessor::ComputeSpherical(float altitude, float azimuth) const
  268. {
  269. return AZ::Vector3(cos(altitude) * cos(azimuth), sin(altitude), sin(azimuth) * cos(altitude));
  270. }
  271. AZ::Vector3 SkyBoxFeatureProcessor::Vector3Pow(AZ::Vector3 a, AZ::Vector3 b)
  272. {
  273. AZ::Vector3 result;
  274. result.SetX(pow(a.GetX(), b.GetX()));
  275. result.SetY(pow(a.GetY(), b.GetY()));
  276. result.SetZ(pow(a.GetZ(), b.GetZ()));
  277. return result;
  278. }
  279. AZ::Vector3 SkyBoxFeatureProcessor::Vector3Exp(AZ::Vector3 a)
  280. {
  281. AZ::Vector3 result;
  282. result.SetX(exp(a.GetX()));
  283. result.SetY(exp(a.GetY()));
  284. result.SetZ(exp(a.GetZ()));
  285. return result;
  286. }
  287. AZ::Vector3 SkyBoxFeatureProcessor::EvaluateHosek(float cosTheta, float gamma, float cosGamma, const HosekSky& hosek)
  288. {
  289. AZ::Vector3 chi = AZ::Vector3(1 + cosGamma * cosGamma) /
  290. Vector3Pow(AZ::Vector3(1.0f) + hosek.h * hosek.h - 2.0f * cosGamma * hosek.h, AZ::Vector3(1.5f));
  291. return (AZ::Vector3(1.0f) + hosek.a * Vector3Exp(hosek.b / (cosTheta + 0.01f))) * (hosek.c + hosek.d * Vector3Exp(hosek.e * gamma) + hosek.f * (cosGamma * cosGamma) +
  292. hosek.g * chi + hosek.i * sqrt(AZ::GetMax(0.0f, cosTheta)));
  293. }
  294. double SkyBoxFeatureProcessor::EvaluateSpline(const double* spline, size_t stride, double value)
  295. {
  296. return
  297. 1 * pow(1 - value, 5) * spline[0 * stride] +
  298. 5 * pow(1 - value, 4) * pow(value, 1) * spline[1 * stride] +
  299. 10 * pow(1 - value, 3) * pow(value, 2) * spline[2 * stride] +
  300. 10 * pow(1 - value, 2) * pow(value, 3) * spline[3 * stride] +
  301. 5 * pow(1 - value, 1) * pow(value, 4) * spline[4 * stride] +
  302. 1 * pow(value, 5) * spline[5 * stride];
  303. }
  304. double SkyBoxFeatureProcessor::SampleLUT(const double* dataset, size_t stride, int turbidity, float albedo, float inverseAltitude)
  305. {
  306. // Splines are functions of elevation ^ 1/3
  307. double elevationK = pow(AZ::GetMax(0.0, 1.0 - inverseAltitude / AZ::Constants::HalfPi), 1.0 / 3.0);
  308. // table has values for turbidity 1..10
  309. int turbidity0 = AZ::GetMax(1, AZ::GetMin(turbidity, 10));
  310. int turbidity1 = AZ::GetMin(turbidity0 + 1, 10);
  311. double turbidityK = AZ::GetMax(0.0f, AZ::GetMin(static_cast<float>(turbidity - turbidity0), 1.0f));
  312. const double* datasetA0 = dataset;
  313. const double* datasetA1 = dataset + stride * 6 * 10;
  314. double a0t0 = EvaluateSpline(datasetA0 + stride * 6 * (turbidity0 - 1), stride, elevationK);
  315. double a1t0 = EvaluateSpline(datasetA1 + stride * 6 * (turbidity0 - 1), stride, elevationK);
  316. double a0t1 = EvaluateSpline(datasetA0 + stride * 6 * (turbidity1 - 1), stride, elevationK);
  317. double a1t1 = EvaluateSpline(datasetA1 + stride * 6 * (turbidity1 - 1), stride, elevationK);
  318. return
  319. a0t0 * (1 - albedo) * (1 - turbidityK) +
  320. a1t0 * albedo * (1 - turbidityK) +
  321. a0t1 * (1 - albedo) * turbidityK +
  322. a1t1 * albedo * turbidityK;
  323. }
  324. HosekSky SkyBoxFeatureProcessor::ComputeHosekSky()
  325. {
  326. // Valid turbidity values are in the range 1..10
  327. m_turbidity = AZ::GetMax(AZ::GetMin(m_turbidity, 10), 1); // to avoid silent crash
  328. AZ::Vector3 a, b, c, d, e, f, g, h, i, z;
  329. HosekSky result;
  330. float inverseAltitude = AZ::Constants::HalfPi - m_sunPosition.m_altitude;
  331. // Currently, we don't have an easy way to get this ground albedo value, so it's hard coded at zero
  332. float albedo = 0.0f;
  333. // Fill each 3 component vector of the parameters with data from the dataset
  334. for (int it = 0; it < 3; ++it)
  335. {
  336. a.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 0, 9, m_turbidity, albedo, inverseAltitude)));
  337. b.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 1, 9, m_turbidity, albedo, inverseAltitude)));
  338. c.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 2, 9, m_turbidity, albedo, inverseAltitude)));
  339. d.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 3, 9, m_turbidity, albedo, inverseAltitude)));
  340. e.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 4, 9, m_turbidity, albedo, inverseAltitude)));
  341. f.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 5, 9, m_turbidity, albedo, inverseAltitude)));
  342. g.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 6, 9, m_turbidity, albedo, inverseAltitude)));
  343. // H and I are swapped in dataset
  344. h.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 8, 9, m_turbidity, albedo, inverseAltitude)));
  345. i.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 7, 9, m_turbidity, albedo, inverseAltitude)));
  346. z.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGBRad[it], 1, m_turbidity, albedo, inverseAltitude)));
  347. }
  348. m_sunDirection = ComputeSpherical(m_sunPosition.m_altitude, m_sunPosition.m_azimuth);
  349. // In the following block of code we get a "normalized" value representing sun altitude angle
  350. float sunAmount = fmodf((m_sunDirection.GetY() / AZ::Constants::HalfPi), 4.0f);
  351. if (sunAmount > 2.0)
  352. {
  353. sunAmount = 0.0;
  354. }
  355. else if (sunAmount > 1.0)
  356. {
  357. sunAmount = 2.0f - sunAmount;
  358. }
  359. else if (sunAmount < -1.0)
  360. {
  361. sunAmount = -2.0f - sunAmount;
  362. }
  363. float normalizedSunY = 0.6f + 0.45f * sunAmount;
  364. result = { a, b, c, d, e, f, g, h, i, z };
  365. AZ::Vector3 S = EvaluateHosek(static_cast<float>(cos(inverseAltitude)), 0.0f, 1.0f, result) * z;
  366. // dividing z by the luminance of S
  367. z /= S.Dot(AZ::Vector3(0.2126, 0.7152, 0.0722));
  368. z *= normalizedSunY;
  369. result.z = z;
  370. return result;
  371. }
  372. AZ::Vector4 SkyBoxFeatureProcessor::ComputeSunRGB()
  373. {
  374. AZ::Vector4 result = AZ::Vector4(0.0f);
  375. // Relative air mass, in this case, means that zenith = 1
  376. const float inverseAlt = AZ::Constants::HalfPi - m_sunPosition.m_altitude;
  377. const float relativeAirMass = 1.0f / (static_cast<float>(cos(inverseAlt)) + 0.15f / static_cast<float>(pow(93.885f - AZ::RadToDeg(inverseAlt), 1.253f)));
  378. // ratio of small to large particle sizes (0:4, usually 1.3)
  379. const float alpha = 1.3f;
  380. // amount of aerosols present
  381. const float beta = 0.04608f * static_cast<float>(m_turbidity) - 0.04586f;
  382. // amount of ozone in cm(NTP)
  383. const float ozoneL = 0.35f; // centimeters
  384. // precipitable water vapor in centimeters
  385. const float w = 2.0f; // centimeters
  386. const float solidAngle = AZ::Constants::TwoPi * (1.0f - m_sunParameters.m_cosAngularDiameter);
  387. AZ::Vector3 cieXYZ = AZ::Vector3(0.0f);
  388. const int wavelengthStep = 10;
  389. const int lambdaMin = 380;
  390. const int lambdaMax = 750;
  391. for (int lambda = lambdaMin; lambda <= lambdaMax; lambda += wavelengthStep)
  392. {
  393. int idx = (lambda - lambdaMin) / wavelengthStep;
  394. AZ::Vector4 data = PhysicalSkyLUT::Spectral[idx];
  395. // space radiance figures are in cm^-2, we need cm^-1
  396. const float spaceRadiance = data.GetX() * 10.0f;
  397. const float koLambda = data.GetY();
  398. const float kwLambda = data.GetZ();
  399. const float kgLambda = data.GetW();
  400. const float rayleighScattering = static_cast<float>(exp(-relativeAirMass * 0.008735f * static_cast<float>(pow(static_cast<float>(lambda) / 1000.0f, -4.08f))));
  401. const float aerosolScattering = static_cast<float>(exp(-relativeAirMass * beta * static_cast<float>(pow(static_cast<float>(lambda) / 1000.0f, -alpha))));
  402. const float ozoneAbsorption = static_cast<float>(exp(-relativeAirMass * koLambda * ozoneL));
  403. const float mixedGasAbsorption = static_cast<float>(exp(-1.41f * kgLambda * relativeAirMass / static_cast<float>(pow(1.0f + 118.93f * kgLambda * relativeAirMass, 0.45f))));
  404. const float waterAbsorption = static_cast<float>(exp(-0.2385f * kwLambda * w * relativeAirMass / static_cast<float>(pow(1.0f + 20.07f * kwLambda * w * relativeAirMass, 0.45f))));
  405. // Multiply all the scattering coefficients to attain spectral radiance
  406. float spectralRadiance = spaceRadiance *
  407. rayleighScattering *
  408. aerosolScattering *
  409. ozoneAbsorption *
  410. mixedGasAbsorption *
  411. waterAbsorption;
  412. float spectralIrradiance = spectralRadiance * solidAngle;
  413. // Now we can go from irradiance => CIE XYZ
  414. // First get the matching function for our wavelength (lambda)
  415. AZ::Vector3 matchingFunction = EvaluateCIEXYZ(lambda);
  416. // Integrate over wavelengths to collect colour information
  417. cieXYZ += AZ::Vector3(spectralIrradiance) * matchingFunction;
  418. }
  419. cieXYZ /= (lambdaMax - lambdaMin) / wavelengthStep;
  420. // Go from cieXYZ to sRGB
  421. result.SetX(cieXYZ.GetX() * 3.2404542f + cieXYZ.GetY() * -1.15371385f + cieXYZ.GetZ() * -0.4985314f);
  422. result.SetY(cieXYZ.GetX() * -0.9692660f + cieXYZ.GetY() * 1.8760108f + cieXYZ.GetZ() * 0.0415560f);
  423. result.SetZ(cieXYZ.GetX() * 0.0556434f + cieXYZ.GetY() * -0.2040259f + cieXYZ.GetZ() * 1.0572252f);
  424. result.Normalize();
  425. return AZ::RPI::TransformColor(Color::CreateFromVector3(result.GetAsVector3()), AZ::RPI::ColorSpaceId::LinearSRGB, AZ::RPI::ColorSpaceId::ACEScg).GetAsVector4();
  426. }
  427. AZ::Vector3 SkyBoxFeatureProcessor::EvaluateCIEXYZ(int lambda)
  428. {
  429. // Opting for the easy analytical single - lobe fit
  430. // Fitting function computed from 1964 CIE-standard xyz functions
  431. // which are fitted for a 10-degree field of view.
  432. // While using the 1931 standard is more common, it only uses a 2-degree
  433. // field of view for its tests, making it less optimal for graphics,
  434. // where a monitor takes up much more than 2 degrees in typical viewing situations
  435. AZ::Vector3 result;
  436. // Values taken from Wyman/Sloan/Shirley paper titled "Simple Analytic Approximations to the CIE XYZ Color Matching Functions"
  437. // x, y, and z all have absolute errors below 3% and root mean square errors around 0.016
  438. // If more precision is needed, consider a different function
  439. float valX[4] = { 0.4f, 1014.0f, -0.02f, -570.f };
  440. float smallLobe = valX[0] * expf(-1250.0f * powf(logf((aznumeric_cast<float>(lambda) - valX[3]) / valX[1]), 2));
  441. float val2X[4] = { 1.13f, 234.f, -0.001345f, -1.799f };
  442. float bigLobe = val2X[0] * expf(-val2X[1] * powf(logf((1338.0f - aznumeric_cast<float>(lambda)) / 743.5f), 2));
  443. result.SetX(smallLobe + bigLobe);
  444. float valY[4] = { 1.011f, 556.1f, 46.14f, 0.0f };
  445. result.SetY(valY[0] * expf(-0.5f* powf((aznumeric_cast<float>(lambda) - valY[1]) / valY[2], 2)) + valY[3]);
  446. float valZ[4] = { 2.06f, 180.4f, 0.125f, 266.0f };
  447. result.SetZ(valZ[0] * expf(-32.0f* powf(logf((aznumeric_cast<float>(lambda) - valZ[3]) / valZ[1]), 2)));
  448. return result;
  449. }
  450. } // namespace Render
  451. } // namespace AZ