/* * 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 #include #include #include "EyeAdaptationUtil.azsli" ShaderResourceGroup PassSrg : SRG_PerPass { // This is a mip chain containing the scene luminance info // x = min luminance // y = average luminance // z = max luminance Texture2D m_sceneLuminance; Sampler LinearSampler { MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = Clamp; AddressV = Clamp; AddressW = Clamp; }; // Contains the eye-adaptation feedback settings. This buffer will be updated in this pass. RWStructuredBuffer m_eyeAdaptationData; } [numthreads(1, 1, 1)] void MainCS(uint3 dispatch_id : SV_DispatchThreadID) { // Determine the goal exposure value in logarithmic(perceptual) space. float goalExposureLog2 = 0.0; // default goal is no adjustment if (ViewSrg::m_exposureControl.m_eyeAdaptationEnabled) { // Get the dimensions and mip levels from the luminance mip chain uint2 inputDimensions; uint numMipLevels = 1; PassSrg::m_sceneLuminance.GetDimensions(0, inputDimensions.x, inputDimensions.y, numMipLevels); // Use smallest 1x1 mip to get scene average luminance. float luminanceAvg = PassSrg::m_sceneLuminance.SampleLevel(PassSrg::LinearSampler, float2(0.5f, 0.5f), numMipLevels - 1).y; // Middle gray value is the half-tone between black and white in exposure control. // Defining Middle Grey value to 0.18f as the typical value used in photography. Cf. https://en.wikipedia.org/wiki/Middle_gray const float middleGray = 0.18f; // Protection from NaNs getting in the frame buffer leading to NaN luminance. if (isnan(luminanceAvg)) { luminanceAvg = middleGray; } // This finds the exposure at which the lumininaceAvg would be equal to middleGray, ie perfectly neutral. goalExposureLog2 = log2(luminanceAvg / middleGray); // Clamp goal exposure by min / max. float minExposure = ViewSrg::m_exposureControl.m_exposureMinLog2; float maxExposure = ViewSrg::m_exposureControl.m_exposureMaxLog2; goalExposureLog2 = clamp(goalExposureLog2, minExposure, maxExposure); } // Convert the previous frame linear exposure to a stop adjustment. float previousFrameExposureLog2 = -log2(max(0.00000001, PassSrg::m_eyeAdaptationData[0].m_exposureValue)); float exposureDifference = goalExposureLog2 - previousFrameExposureLog2; // Speed depends on if the exposure should be increased or decreased. const float speed = exposureDifference > 0.0 ? ViewSrg::m_exposureControl.m_speedUp : ViewSrg::m_exposureControl.m_speedDown; // Update the adjustment for this frame based on the frame deltaTime and speed float deltaTime = clamp(SceneSrg::m_time - PassSrg::m_eyeAdaptationData[0].m_setValueTime, 0.0f, 1.0f); float exposureAdjustment = exposureDifference * deltaTime * speed; float newExposureLog2 = previousFrameExposureLog2 + exposureAdjustment; // Store the linear exposure so it can be used by the look modification transform later. // newExposureLog2 is negated because m_exposureValue is used to correct for a given exposure. PassSrg::m_eyeAdaptationData[0].m_exposureValue = pow(2.0f, -newExposureLog2); PassSrg::m_eyeAdaptationData[0].m_setValueTime = SceneSrg::m_time; }