123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <SkyBox/SkyBoxFeatureProcessor.h>
- #include <AzFramework/Asset/AssetSystemBus.h>
- #include <Atom/RHI/Factory.h>
- #include <Atom/RHI/RHISystemInterface.h>
- #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
- #include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
- #include <Atom/RPI.Public/ColorManagement/TransformColor.h>
- #include <Atom/RPI.Public/RenderPipeline.h>
- #include <Atom/RPI.Public/Image/StreamingImage.h>
- #include <Atom/RPI.Public/RPIUtils.h>
- #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
- #include <Atom/RPI.Public/Scene.h>
- #include <Atom/RPI.Public/View.h>
- #include <SkyBox/SkyBoxLUT.h>
- namespace AZ
- {
- namespace Render
- {
- const double* physicalSkyLUTRGB[] =
- {
- PhysicalSkyLUT::RGB1,
- PhysicalSkyLUT::RGB2,
- PhysicalSkyLUT::RGB3
- };
- const double* physicalSkyLUTRGBRad[] =
- {
- PhysicalSkyLUT::RGBRad1,
- PhysicalSkyLUT::RGBRad2,
- PhysicalSkyLUT::RGBRad3
- };
- void SkyBoxFeatureProcessor::Reflect(ReflectContext* context)
- {
- if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
- {
- serializeContext
- ->Class<SkyBoxFeatureProcessor, FeatureProcessor>()
- ->Version(1);
- }
- }
- SkyBoxFeatureProcessor::SkyBoxFeatureProcessor()
- :SkyBoxFeatureProcessorInterface()
- {
- m_skyIntensity = PhotometricValue(PhysicalSkyDefaultIntensity, AZ::Color::CreateOne(), PhotometricUnit::Ev100Luminance);
- m_sunIntensity = PhotometricValue(PhysicalSunDefaultIntensity, AZ::Color::CreateOne(), PhotometricUnit::Ev100Luminance);
- }
- void SkyBoxFeatureProcessor::Activate()
- {
- InitBuffer();
- // Load default cubemap
- // This is assigned when the skybox is disabled or removed from the scene to prevent a Vulkan TDR.
- // [GFX-TODO][ATOM-4181] This can be removed after Vulkan is changed to automatically handle this issue.
- LoadDefaultCubeMap();
- m_cubemapTexture = m_defaultCubemapTexture;
- // Find the relevant indices in the scene srg
- m_sceneSrg = GetParentScene()->GetShaderResourceGroup();
- m_skyboxEnableIndex.Reset();
- m_physicalSkyBufferIndex.Reset();
- m_physicalSkyIndex.Reset();
- m_cubemapIndex.Reset();
- m_cubemapRotationMatrixIndex.Reset();
- m_cubemapExposureIndex.Reset();
- m_fogEnableIndex.Reset();
- m_fogColorIndex.Reset();
- m_fogTopHeightIndex.Reset();
- m_fogBottomHeightIndex.Reset();
- if (m_buffer)
- {
- m_sceneSrg->SetBufferView(
- m_physicalSkyBufferIndex, m_buffer->GetBufferView());
- }
- }
- void SkyBoxFeatureProcessor::Deactivate()
- {
- m_buffer = nullptr;
- m_cubemapTexture = m_defaultCubemapTexture;
- m_sceneSrg = nullptr;
- }
- void SkyBoxFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet)
- {
- AZ_PROFILE_SCOPE(RPI, "SkyBoxFeatureProcessor: Simulate");
- AZ_UNUSED(packet);
- m_sceneSrg->SetConstant(m_skyboxEnableIndex, m_enable);
- if (m_enable)
- {
- switch (m_skyboxMode)
- {
- case SkyBoxMode::PhysicalSky:
- {
- if (m_skyNeedUpdate)
- {
- HosekSky skyParameters = ComputeHosekSky();
- skyParameters.a.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterA.data());
- skyParameters.b.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterB.data());
- skyParameters.c.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterC.data());
- skyParameters.d.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterD.data());
- skyParameters.e.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterE.data());
- skyParameters.f.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterF.data());
- skyParameters.g.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterG.data());
- skyParameters.h.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterH.data());
- skyParameters.i.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterI.data());
- skyParameters.z.StoreToFloat4(m_physicalSkyData.m_physicalSkyParameterZ.data());
- }
- if (m_sunNeedUpdate)
- {
- AZ::Vector4 sunRGB = ComputeSunRGB();
- sunRGB.StoreToFloat4(m_physicalSkyData.m_physicalSkySunRGB.data());
- }
- if (m_skyNeedUpdate || m_sunNeedUpdate || m_mapBuffer)
- {
- m_sunDirection.StoreToFloat4(m_physicalSkyData.m_physicalSkySunDirection.data());
- m_physicalSkyData.m_physicalSkySunParameters[0] = m_sunParameters.m_radius;
- m_physicalSkyData.m_physicalSkySunParameters[1] = m_sunParameters.m_distance;
- m_physicalSkyData.m_physicalSkySunParameters[2] = m_sunParameters.m_cosAngularDiameter;
- m_physicalSkyData.m_physicalSkySunParameters[3] = 0.0f;
- m_skyIntensity.ConvertToPhotometricUnit(PhotometricUnit::Nit);
- m_sunIntensity.ConvertToPhotometricUnit(PhotometricUnit::Nit);
- AZ::Vector4 artistParams = AZ::Vector4(m_skyIntensity.GetIntensity(), m_sunIntensity.GetIntensity(), 0.0f, 0.0f);
- artistParams.StoreToFloat4(m_physicalSkyData.m_physicalSkyAndSunIntensity.data());
- if (m_buffer)
- {
- m_buffer->UpdateData(&m_physicalSkyData, sizeof(PhysicalSkyData));
- }
- m_skyNeedUpdate = false;
- m_sunNeedUpdate = false;
- m_mapBuffer = false;
- }
- m_sceneSrg->SetConstant(m_fogEnableIndex, m_fogSettings.m_enable);
- if (m_fogSettings.m_enable)
- {
- m_sceneSrg->SetConstant(m_fogTopHeightIndex, m_fogSettings.m_topHeight);
- m_sceneSrg->SetConstant(m_fogBottomHeightIndex, m_fogSettings.m_bottomHeight);
- m_sceneSrg->SetConstant(m_fogColorIndex, m_fogSettings.m_color);
- }
- m_sceneSrg->SetConstant(m_physicalSkyIndex, true);
- break;
- }
- case SkyBoxMode::Cubemap:
- {
- // set texture
- m_sceneSrg->SetImage(m_cubemapIndex, m_cubemapTexture);
- // set cubemap rotation matrix
- m_sceneSrg->SetConstant(m_cubemapRotationMatrixIndex, m_cubemapRotationMatrix);
- // set exposure
- m_sceneSrg->SetConstant(m_cubemapExposureIndex, m_cubemapExposure);
- m_sceneSrg->SetConstant(m_physicalSkyIndex, false);
- break;
- }
- default:
- break;
- }
- }
- }
- void SkyBoxFeatureProcessor::Render(const FeatureProcessor::RenderPacket& packet)
- {
- AZ_PROFILE_FUNCTION(AzRender);
- AZ_UNUSED(packet);
- }
- void SkyBoxFeatureProcessor::InitBuffer()
- {
- const uint32_t byteCount = sizeof(PhysicalSkyData);
- RPI::CommonBufferDescriptor desc;
- desc.m_poolType = RPI::CommonBufferPoolType::Constant;
- desc.m_bufferName = "SkyboxBuffer";
- desc.m_byteCount = byteCount;
- desc.m_elementSize = byteCount;
- desc.m_bufferData = &m_physicalSkyData;
- m_buffer = RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc);
- }
- void SkyBoxFeatureProcessor::LoadDefaultCubeMap()
- {
- const constexpr char* DefaultCubeMapPath = "textures/default/default_skyboxcm.dds.streamingimage";
- m_defaultCubemapTexture = RPI::LoadStreamingTexture(DefaultCubeMapPath);
- AZ_Assert(m_defaultCubemapTexture, "Failed to load default cubemap");
- }
- void SkyBoxFeatureProcessor::Enable(bool enable)
- {
- m_enable = enable;
- }
- bool SkyBoxFeatureProcessor::IsEnabled()
- {
- return m_enable;
- }
- void SkyBoxFeatureProcessor::SetCubemapRotationMatrix(AZ::Matrix4x4 matrix)
- {
- m_cubemapRotationMatrix = matrix;
- }
- void SkyBoxFeatureProcessor::SetCubemap(Data::Instance<RPI::Image> cubemap)
- {
- m_cubemapTexture = cubemap.get() ? cubemap : m_defaultCubemapTexture;
- }
- void SkyBoxFeatureProcessor::SetCubemapExposure(float exposure)
- {
- m_cubemapExposure = exposure;
- }
- void SkyBoxFeatureProcessor::SetSkyboxMode(SkyBoxMode mode)
- {
- m_skyboxMode = mode;
- }
- void SkyBoxFeatureProcessor::SetFogSettings(const SkyBoxFogSettings& fogSettings)
- {
- m_fogSettings = fogSettings;
- }
- void SkyBoxFeatureProcessor::SetFogEnabled(bool enable)
- {
- m_fogSettings.m_enable = enable;
- }
- bool SkyBoxFeatureProcessor::IsFogEnabled()
- {
- return m_fogSettings.m_enable;
- }
- void SkyBoxFeatureProcessor::SetFogColor(const AZ::Color& color)
- {
- m_fogSettings.m_color = color;
- }
- void SkyBoxFeatureProcessor::SetFogTopHeight(float topHeight)
- {
- m_fogSettings.m_topHeight = topHeight;
- }
- void SkyBoxFeatureProcessor::SetFogBottomHeight(float bottomHeight)
- {
- m_fogSettings.m_bottomHeight = bottomHeight;
- }
- void SkyBoxFeatureProcessor::SetSunPosition(SunPosition sunPosition)
- {
- m_skyNeedUpdate = true;
- m_sunNeedUpdate = true;
- m_sunPosition = sunPosition;
- }
- void SkyBoxFeatureProcessor::SetSunPosition(float azimuth, float altitude)
- {
- m_skyNeedUpdate = true;
- m_sunNeedUpdate = true;
- m_sunPosition.m_azimuth = azimuth;
- m_sunPosition.m_altitude = altitude;
- }
- void SkyBoxFeatureProcessor::SetTurbidity(int turbidity)
- {
- m_skyNeedUpdate = true;
- m_sunNeedUpdate = true;
- m_turbidity = turbidity;
- }
-
- void SkyBoxFeatureProcessor::SetSkyIntensity(float intensity, PhotometricUnit unit)
- {
- m_mapBuffer = true;
- m_skyIntensity.ConvertToPhotometricUnit(unit);
- m_skyIntensity.SetIntensity(intensity);
- }
- void SkyBoxFeatureProcessor::SetSunIntensity(float intensity, PhotometricUnit unit)
- {
- m_mapBuffer = true;
- m_sunIntensity.ConvertToPhotometricUnit(unit);
- m_sunIntensity.SetIntensity(intensity);
- }
- void SkyBoxFeatureProcessor::SetSunRadiusFactor(float factor)
- {
- m_sunNeedUpdate = true;
- m_sunParameters.m_radius = PhysicalSunRadius * factor;
- m_sunParameters.m_cosAngularDiameter = cosf(atanf((m_sunParameters.m_radius * 2.0f) / (m_sunParameters.m_distance * 2.0f)) * 2.0f);
- }
- AZ::Vector3 SkyBoxFeatureProcessor::ComputeSpherical(float altitude, float azimuth) const
- {
- return AZ::Vector3(cos(altitude) * cos(azimuth), sin(altitude), sin(azimuth) * cos(altitude));
- }
- AZ::Vector3 SkyBoxFeatureProcessor::Vector3Pow(AZ::Vector3 a, AZ::Vector3 b)
- {
- AZ::Vector3 result;
- result.SetX(pow(a.GetX(), b.GetX()));
- result.SetY(pow(a.GetY(), b.GetY()));
- result.SetZ(pow(a.GetZ(), b.GetZ()));
- return result;
- }
- AZ::Vector3 SkyBoxFeatureProcessor::Vector3Exp(AZ::Vector3 a)
- {
- AZ::Vector3 result;
- result.SetX(exp(a.GetX()));
- result.SetY(exp(a.GetY()));
- result.SetZ(exp(a.GetZ()));
- return result;
- }
- AZ::Vector3 SkyBoxFeatureProcessor::EvaluateHosek(float cosTheta, float gamma, float cosGamma, const HosekSky& hosek)
- {
- AZ::Vector3 chi = AZ::Vector3(1 + cosGamma * cosGamma) /
- Vector3Pow(AZ::Vector3(1.0f) + hosek.h * hosek.h - 2.0f * cosGamma * hosek.h, AZ::Vector3(1.5f));
- 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) +
- hosek.g * chi + hosek.i * sqrt(AZ::GetMax(0.0f, cosTheta)));
- }
- double SkyBoxFeatureProcessor::EvaluateSpline(const double* spline, size_t stride, double value)
- {
- return
- 1 * pow(1 - value, 5) * spline[0 * stride] +
- 5 * pow(1 - value, 4) * pow(value, 1) * spline[1 * stride] +
- 10 * pow(1 - value, 3) * pow(value, 2) * spline[2 * stride] +
- 10 * pow(1 - value, 2) * pow(value, 3) * spline[3 * stride] +
- 5 * pow(1 - value, 1) * pow(value, 4) * spline[4 * stride] +
- 1 * pow(value, 5) * spline[5 * stride];
- }
- double SkyBoxFeatureProcessor::SampleLUT(const double* dataset, size_t stride, int turbidity, float albedo, float inverseAltitude)
- {
- // Splines are functions of elevation ^ 1/3
- double elevationK = pow(AZ::GetMax(0.0, 1.0 - inverseAltitude / AZ::Constants::HalfPi), 1.0 / 3.0);
- // table has values for turbidity 1..10
- int turbidity0 = AZ::GetMax(1, AZ::GetMin(turbidity, 10));
- int turbidity1 = AZ::GetMin(turbidity0 + 1, 10);
- double turbidityK = AZ::GetMax(0.0f, AZ::GetMin(static_cast<float>(turbidity - turbidity0), 1.0f));
- const double* datasetA0 = dataset;
- const double* datasetA1 = dataset + stride * 6 * 10;
- double a0t0 = EvaluateSpline(datasetA0 + stride * 6 * (turbidity0 - 1), stride, elevationK);
- double a1t0 = EvaluateSpline(datasetA1 + stride * 6 * (turbidity0 - 1), stride, elevationK);
- double a0t1 = EvaluateSpline(datasetA0 + stride * 6 * (turbidity1 - 1), stride, elevationK);
- double a1t1 = EvaluateSpline(datasetA1 + stride * 6 * (turbidity1 - 1), stride, elevationK);
- return
- a0t0 * (1 - albedo) * (1 - turbidityK) +
- a1t0 * albedo * (1 - turbidityK) +
- a0t1 * (1 - albedo) * turbidityK +
- a1t1 * albedo * turbidityK;
- }
- HosekSky SkyBoxFeatureProcessor::ComputeHosekSky()
- {
- // Valid turbidity values are in the range 1..10
- m_turbidity = AZ::GetMax(AZ::GetMin(m_turbidity, 10), 1); // to avoid silent crash
- AZ::Vector3 a, b, c, d, e, f, g, h, i, z;
- HosekSky result;
- float inverseAltitude = AZ::Constants::HalfPi - m_sunPosition.m_altitude;
- // Currently, we don't have an easy way to get this ground albedo value, so it's hard coded at zero
- float albedo = 0.0f;
- // Fill each 3 component vector of the parameters with data from the dataset
- for (int it = 0; it < 3; ++it)
- {
- a.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 0, 9, m_turbidity, albedo, inverseAltitude)));
- b.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 1, 9, m_turbidity, albedo, inverseAltitude)));
- c.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 2, 9, m_turbidity, albedo, inverseAltitude)));
- d.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 3, 9, m_turbidity, albedo, inverseAltitude)));
- e.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 4, 9, m_turbidity, albedo, inverseAltitude)));
- f.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 5, 9, m_turbidity, albedo, inverseAltitude)));
- g.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 6, 9, m_turbidity, albedo, inverseAltitude)));
- // H and I are swapped in dataset
- h.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 8, 9, m_turbidity, albedo, inverseAltitude)));
- i.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGB[it] + 7, 9, m_turbidity, albedo, inverseAltitude)));
- z.SetElement(it, static_cast<float>(SampleLUT(physicalSkyLUTRGBRad[it], 1, m_turbidity, albedo, inverseAltitude)));
- }
- m_sunDirection = ComputeSpherical(m_sunPosition.m_altitude, m_sunPosition.m_azimuth);
- // In the following block of code we get a "normalized" value representing sun altitude angle
- float sunAmount = fmodf((m_sunDirection.GetY() / AZ::Constants::HalfPi), 4.0f);
- if (sunAmount > 2.0)
- {
- sunAmount = 0.0;
- }
- else if (sunAmount > 1.0)
- {
- sunAmount = 2.0f - sunAmount;
- }
- else if (sunAmount < -1.0)
- {
- sunAmount = -2.0f - sunAmount;
- }
- float normalizedSunY = 0.6f + 0.45f * sunAmount;
- result = { a, b, c, d, e, f, g, h, i, z };
- AZ::Vector3 S = EvaluateHosek(static_cast<float>(cos(inverseAltitude)), 0.0f, 1.0f, result) * z;
- // dividing z by the luminance of S
- z /= S.Dot(AZ::Vector3(0.2126, 0.7152, 0.0722));
- z *= normalizedSunY;
- result.z = z;
- return result;
- }
- AZ::Vector4 SkyBoxFeatureProcessor::ComputeSunRGB()
- {
- AZ::Vector4 result = AZ::Vector4(0.0f);
- // Relative air mass, in this case, means that zenith = 1
- const float inverseAlt = AZ::Constants::HalfPi - m_sunPosition.m_altitude;
- const float relativeAirMass = 1.0f / (static_cast<float>(cos(inverseAlt)) + 0.15f / static_cast<float>(pow(93.885f - AZ::RadToDeg(inverseAlt), 1.253f)));
- // ratio of small to large particle sizes (0:4, usually 1.3)
- const float alpha = 1.3f;
- // amount of aerosols present
- const float beta = 0.04608f * static_cast<float>(m_turbidity) - 0.04586f;
- // amount of ozone in cm(NTP)
- const float ozoneL = 0.35f; // centimeters
- // precipitable water vapor in centimeters
- const float w = 2.0f; // centimeters
- const float solidAngle = AZ::Constants::TwoPi * (1.0f - m_sunParameters.m_cosAngularDiameter);
- AZ::Vector3 cieXYZ = AZ::Vector3(0.0f);
- const int wavelengthStep = 10;
- const int lambdaMin = 380;
- const int lambdaMax = 750;
- for (int lambda = lambdaMin; lambda <= lambdaMax; lambda += wavelengthStep)
- {
- int idx = (lambda - lambdaMin) / wavelengthStep;
- AZ::Vector4 data = PhysicalSkyLUT::Spectral[idx];
- // space radiance figures are in cm^-2, we need cm^-1
- const float spaceRadiance = data.GetX() * 10.0f;
- const float koLambda = data.GetY();
- const float kwLambda = data.GetZ();
- const float kgLambda = data.GetW();
- const float rayleighScattering = static_cast<float>(exp(-relativeAirMass * 0.008735f * static_cast<float>(pow(static_cast<float>(lambda) / 1000.0f, -4.08f))));
- const float aerosolScattering = static_cast<float>(exp(-relativeAirMass * beta * static_cast<float>(pow(static_cast<float>(lambda) / 1000.0f, -alpha))));
- const float ozoneAbsorption = static_cast<float>(exp(-relativeAirMass * koLambda * ozoneL));
- const float mixedGasAbsorption = static_cast<float>(exp(-1.41f * kgLambda * relativeAirMass / static_cast<float>(pow(1.0f + 118.93f * kgLambda * relativeAirMass, 0.45f))));
- 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))));
- // Multiply all the scattering coefficients to attain spectral radiance
- float spectralRadiance = spaceRadiance *
- rayleighScattering *
- aerosolScattering *
- ozoneAbsorption *
- mixedGasAbsorption *
- waterAbsorption;
- float spectralIrradiance = spectralRadiance * solidAngle;
- // Now we can go from irradiance => CIE XYZ
- // First get the matching function for our wavelength (lambda)
- AZ::Vector3 matchingFunction = EvaluateCIEXYZ(lambda);
- // Integrate over wavelengths to collect colour information
- cieXYZ += AZ::Vector3(spectralIrradiance) * matchingFunction;
- }
- cieXYZ /= (lambdaMax - lambdaMin) / wavelengthStep;
- // Go from cieXYZ to sRGB
- result.SetX(cieXYZ.GetX() * 3.2404542f + cieXYZ.GetY() * -1.15371385f + cieXYZ.GetZ() * -0.4985314f);
- result.SetY(cieXYZ.GetX() * -0.9692660f + cieXYZ.GetY() * 1.8760108f + cieXYZ.GetZ() * 0.0415560f);
- result.SetZ(cieXYZ.GetX() * 0.0556434f + cieXYZ.GetY() * -0.2040259f + cieXYZ.GetZ() * 1.0572252f);
- result.Normalize();
- return AZ::RPI::TransformColor(Color::CreateFromVector3(result.GetAsVector3()), AZ::RPI::ColorSpaceId::LinearSRGB, AZ::RPI::ColorSpaceId::ACEScg).GetAsVector4();
- }
- AZ::Vector3 SkyBoxFeatureProcessor::EvaluateCIEXYZ(int lambda)
- {
- // Opting for the easy analytical single - lobe fit
- // Fitting function computed from 1964 CIE-standard xyz functions
- // which are fitted for a 10-degree field of view.
- // While using the 1931 standard is more common, it only uses a 2-degree
- // field of view for its tests, making it less optimal for graphics,
- // where a monitor takes up much more than 2 degrees in typical viewing situations
- AZ::Vector3 result;
- // Values taken from Wyman/Sloan/Shirley paper titled "Simple Analytic Approximations to the CIE XYZ Color Matching Functions"
- // x, y, and z all have absolute errors below 3% and root mean square errors around 0.016
- // If more precision is needed, consider a different function
- float valX[4] = { 0.4f, 1014.0f, -0.02f, -570.f };
- float smallLobe = valX[0] * expf(-1250.0f * powf(logf((aznumeric_cast<float>(lambda) - valX[3]) / valX[1]), 2));
- float val2X[4] = { 1.13f, 234.f, -0.001345f, -1.799f };
- float bigLobe = val2X[0] * expf(-val2X[1] * powf(logf((1338.0f - aznumeric_cast<float>(lambda)) / 743.5f), 2));
- result.SetX(smallLobe + bigLobe);
- float valY[4] = { 1.011f, 556.1f, 46.14f, 0.0f };
- result.SetY(valY[0] * expf(-0.5f* powf((aznumeric_cast<float>(lambda) - valY[1]) / valY[2], 2)) + valY[3]);
- float valZ[4] = { 2.06f, 180.4f, 0.125f, 266.0f };
- result.SetZ(valZ[0] * expf(-32.0f* powf(logf((aznumeric_cast<float>(lambda) - valZ[3]) / valZ[1]), 2)));
- return result;
- }
- } // namespace Render
- } // namespace AZ
|