You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Image exposure is locally adjusted by decomposing luminance of the frame into a base layer and a detail layer. - Base layer contrast can be reduced while preserving detail. - Support blending with gaussian blurred luminance as proposed in Advances in Real-Time Rendering in Games 2021. - Controls in PostProcessVolume. - r.LocalExposure cvar to control whether local exposure is supported. - Visualization mode in "Show->Visualize->Local Expsoure". Multiple modes controllable using r.LocalExposure.VisualizeDebugMode. - Bilateral grid generation not yet optimized. Optimization should include increasing depth of grid to match histogram size so that global histogram can be generated from grid. - Added option to use mirror address mode in PostProcessWeightedSampleSum, since black border adds noticeable vignetting to the blurred luminance. #preflight 612f65169db3090001ba3eec #rb Brian.Karis, Guillaume.Abadie, Krzysztof.Narkowicz #ROBOMERGE-SOURCE: CL 17385317 via CL 17387198 #ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v865-17346139) [CL 17387234 by tiago costa in ue5-release-engine-test branch]
169 lines
5.6 KiB
Plaintext
169 lines
5.6 KiB
Plaintext
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
PostProcessHistogram.usf: PostProcessing histogram
|
|
=============================================================================*/
|
|
|
|
#include "Common.ush"
|
|
#include "ScreenPass.ush"
|
|
#include "PostProcessHistogramCommon.ush"
|
|
|
|
SCREEN_PASS_TEXTURE_VIEWPORT(Input)
|
|
|
|
Texture2D InputTexture;
|
|
|
|
#if BILATERAL_GRID
|
|
// Output bilateral grid texture (UAV)
|
|
RWTexture3D<float2> BilateralGridRWTexture;
|
|
#else
|
|
// Output histogram texture (UAV)
|
|
RWTexture2D<float4> HistogramRWTexture;
|
|
#endif
|
|
|
|
// Number of thread groups in the dispatch
|
|
uint2 ThreadGroupCount;
|
|
|
|
// THREADGROUP_SIZEX*THREADGROUP_SIZEY histograms of the size HISTOGRAM_SIZE
|
|
groupshared float SharedHistogram[HISTOGRAM_SIZE][THREADGROUP_SIZEX][THREADGROUP_SIZEY];
|
|
|
|
#if BILATERAL_GRID
|
|
groupshared float SharedLuminanceSum[HISTOGRAM_SIZE][THREADGROUP_SIZEX][THREADGROUP_SIZEY];
|
|
#endif
|
|
|
|
[numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, 1)]
|
|
void MainCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID,
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint GroupIndex: SV_GroupIndex)
|
|
{
|
|
// todo: can be cleared more efficiently
|
|
// clear all THREADGROUP_SIZEX*THREADGROUP_SIZEY histograms
|
|
UNROLL for (uint i = 0; i < HISTOGRAM_SIZE; ++i)
|
|
{
|
|
SharedHistogram[i][GroupThreadId.x][GroupThreadId.y] = 0.0f;
|
|
#if BILATERAL_GRID
|
|
SharedLuminanceSum[i][GroupThreadId.x][GroupThreadId.y] = 0.0f;
|
|
#endif
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
// Each thread group processes LoopX * LoopY texels of the input.
|
|
uint2 TileSize = uint2(LOOP_SIZEX, LOOP_SIZEY);
|
|
|
|
// Top left input texel for this group.
|
|
uint2 LeftTop = Input_ViewportMin + DispatchThreadId.xy * TileSize;
|
|
|
|
float2 InvViewSize = Input_ViewportSizeInverse.xy;
|
|
|
|
// Accumulate all pixels into THREADGROUP_SIZEX*THREADGROUP_SIZEY histograms
|
|
LOOP for (uint y = 0; y < LOOP_SIZEY; ++y)
|
|
{
|
|
LOOP for (uint x = 0; x < LOOP_SIZEX; ++x)
|
|
{
|
|
uint2 Tile = uint2(x, y);
|
|
uint2 TexelPos = LeftTop + Tile;
|
|
|
|
if(TexelPos.x < Input_ViewportMax.x && TexelPos.y < Input_ViewportMax.y)
|
|
{
|
|
float4 SceneColor = InputTexture.Load(int3(TexelPos, 0));
|
|
|
|
SceneColor.xyz *= View.OneOverPreExposure;
|
|
|
|
float LuminanceVal = CalculateEyeAdaptationLuminance(SceneColor.xyz);
|
|
|
|
float LogLuminance = log2(LuminanceVal);
|
|
float LogLuminanceHist = ComputeHistogramPositionFromLogLuminance(LogLuminance);
|
|
|
|
float2 ScreenUV = (TexelPos.xy - Input_ViewportMin) * InvViewSize;
|
|
float ScreenWeight = AdaptationWeightTexture(ScreenUV);
|
|
|
|
// Map the normalized histogram position into texels.
|
|
float fBucket = saturate(LogLuminanceHist) * (HISTOGRAM_SIZE-1);// * 0.9999f;
|
|
|
|
// Find two discrete buckets that straddle the continuous histogram position.
|
|
uint Bucket0 = (uint)(fBucket);
|
|
uint Bucket1 = Bucket0 + 1;
|
|
|
|
Bucket0 = min(Bucket0, uint(HISTOGRAM_SIZE - 1));
|
|
Bucket1 = min(Bucket1, uint(HISTOGRAM_SIZE - 1));
|
|
|
|
// Weighted blend between the two buckets.
|
|
float Weight1 = frac(fBucket);
|
|
float Weight0 = 1.0f - Weight1;
|
|
|
|
#if !BILATERAL_GRID
|
|
// When EyeAdaptation_BlackHistogramBucketInfluence=.0, we will ignore the last bucket. The main use
|
|
// case is so the black background pixels in the editor have no effect. But if we have cases where
|
|
// pixel values can actually be black, we want to set EyeAdaptation_LastHistogramBucketInfluence=1.0.
|
|
// This value is controlled by the cvar "r.EyeAdaptation.BlackHistogramBucketInfluence"
|
|
if (Bucket0 == 0)
|
|
{
|
|
Weight0 *= EyeAdaptation_BlackHistogramBucketInfluence;
|
|
}
|
|
|
|
Weight0 *= ScreenWeight;
|
|
Weight1 *= ScreenWeight;
|
|
#endif
|
|
|
|
// Accumulate the weight to the nearby history buckets.
|
|
SharedHistogram[Bucket0][GroupThreadId.x][GroupThreadId.y] += Weight0;
|
|
SharedHistogram[Bucket1][GroupThreadId.x][GroupThreadId.y] += Weight1;
|
|
|
|
#if BILATERAL_GRID
|
|
SharedLuminanceSum[Bucket0][GroupThreadId.x][GroupThreadId.y] += LogLuminance * Weight0;
|
|
SharedLuminanceSum[Bucket1][GroupThreadId.x][GroupThreadId.y] += LogLuminance * Weight1;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
// Accumulate all histograms into one.
|
|
if (GroupIndex < HISTOGRAM_SIZE / 4)
|
|
{
|
|
float4 Sum = 0;
|
|
#if BILATERAL_GRID
|
|
float4 SumLuminance = 0;
|
|
#endif
|
|
|
|
LOOP for (uint y = 0; y < THREADGROUP_SIZEY; ++y)
|
|
{
|
|
LOOP for (uint x = 0; x < THREADGROUP_SIZEX; ++x)
|
|
{
|
|
Sum += float4(
|
|
SharedHistogram[GroupIndex * 4 + 0][x][y],
|
|
SharedHistogram[GroupIndex * 4 + 1][x][y],
|
|
SharedHistogram[GroupIndex * 4 + 2][x][y],
|
|
SharedHistogram[GroupIndex * 4 + 3][x][y]);
|
|
|
|
#if BILATERAL_GRID
|
|
SumLuminance += float4(
|
|
SharedLuminanceSum[GroupIndex * 4 + 0][x][y],
|
|
SharedLuminanceSum[GroupIndex * 4 + 1][x][y],
|
|
SharedLuminanceSum[GroupIndex * 4 + 2][x][y],
|
|
SharedLuminanceSum[GroupIndex * 4 + 3][x][y]);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if BILATERAL_GRID
|
|
BilateralGridRWTexture[uint3(GroupId.xy, GroupIndex * 4 + 0)] = float2(SumLuminance.x, Sum.x);
|
|
BilateralGridRWTexture[uint3(GroupId.xy, GroupIndex * 4 + 1)] = float2(SumLuminance.y, Sum.y);
|
|
BilateralGridRWTexture[uint3(GroupId.xy, GroupIndex * 4 + 2)] = float2(SumLuminance.z, Sum.z);
|
|
BilateralGridRWTexture[uint3(GroupId.xy, GroupIndex * 4 + 3)] = float2(SumLuminance.w, Sum.w);
|
|
#else
|
|
float2 MaxExtent = Input_ViewportSize;
|
|
float Area = MaxExtent.x * MaxExtent.y;
|
|
|
|
// Fixed to include borders.
|
|
float NormalizeFactor = 1.0f / Area;
|
|
|
|
// Output texture with one histogram per line, x and y unwrapped into all the lines
|
|
HistogramRWTexture[uint2(GroupIndex, GroupId.x + GroupId.y * ThreadGroupCount.x)] = Sum * NormalizeFactor;
|
|
#endif
|
|
}
|
|
}
|