// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. /*============================================================================= PostProcessVisualizeHDR.usf: PostProcessing shader to visualize HDR histogram =============================================================================*/ #include "Common.usf" #include "PostProcessCommon.usf" #include "PostProcessHistogramCommon.usf" #include "DeferredShadingCommon.usf" #include "TonemapCommon.usf" Texture2D MiniFontTexture; // only needed for nice visualization float ComputeHistogramMax(Texture2D HistogramTexture) { float Max = 0; for(uint i = 0; i < HISTOGRAM_SIZE; ++i) { Max = max(Max, GetHistogramBucket(HistogramTexture, i)); } return Max; } // for printf debugging in the shader // @param LeftTop - is advanced, in pixels void PrintCharacter(int2 PixelPos, inout float3 OutColor, float3 FontColor, inout int2 LeftTop, int CharacterID) { uint2 Rel = (uint2)(PixelPos - LeftTop); FLATTEN if(Rel.x < 8 && Rel.y < 8) { OutColor = lerp(OutColor, FontColor, MiniFontTexture.Load(int3(CharacterID * 8 + Rel.x, Rel.y, 0)).r); } LeftTop.x += 8; } // only for positive numbers // @param DigitValue - e.g. 1 for frist digit before period, 10 for second, 0.1 for first digit behind period uint ExtractDigitFromFloat(float Number, float DigitValue) { uint Temp = (uint)(Number / DigitValue); return Temp % 10; } // for printf debugging in the shader, has to be positive // outputs a float number in the form: xxx.yyy // @param LeftTop - in pixels void PrintFloat(int2 PixelPos, inout float3 OutColor, float3 FontColor, int2 LeftTop, float Number) { int2 Cursor = LeftTop; float BorderDistance = ComputeDistanceToRect(PixelPos, LeftTop, int2(7, 1) * 8 - 1); // black border around number // OutColor = lerp(0, OutColor, saturate(BorderDistance - 2)); // before period PrintCharacter(PixelPos, OutColor, FontColor, Cursor, ExtractDigitFromFloat(Number, 100)); PrintCharacter(PixelPos, OutColor, FontColor, Cursor, ExtractDigitFromFloat(Number, 10)); PrintCharacter(PixelPos, OutColor, FontColor, Cursor, ExtractDigitFromFloat(Number, 1)); // period PrintCharacter(PixelPos, OutColor, FontColor, Cursor, 512 / 8 - 3); // after period PrintCharacter(PixelPos, OutColor, FontColor, Cursor, ExtractDigitFromFloat(Number, 0.1)); PrintCharacter(PixelPos, OutColor, FontColor, Cursor, ExtractDigitFromFloat(Number, 0.01)); PrintCharacter(PixelPos, OutColor, FontColor, Cursor, ExtractDigitFromFloat(Number, 0.001)); } uint ComputeAdvice(float3 HDRColor) { float Lum = max(HDRColor.r, max(HDRColor.g, HDRColor.b)); if(Lum < EyeAdaptationParams[0].z) { return 1; } // for some reasons HLSL compiler seems to supress the return 1 when we comment in this code // if(Lum > EyeAdaptationParams[0].w) // { // return 2; // } return 0; } uint ComputeAdviceUV(float2 UV) { float3 HDRColor = Texture2DSample(PostprocessInput2, PostprocessInput2Sampler, UV).rgb; return ComputeAdvice(HDRColor); } // to highlight areas that have unrealistic materials void HighlightAdvice(inout float3 OutColor, float2 UV, int2 PixelPos) { uint AdviceInner = ComputeAdviceUV(UV); uint AdviceOuter = 0; bool SpecialDotInArea = ((PixelPos.x + PixelPos.y) % 6) == 0 && ((PixelPos.x - PixelPos.y) % 6) == 0; AdviceOuter = max(AdviceOuter, ComputeAdviceUV(UV + float2( 1, 0) * PostprocessInput0Size.zw)); AdviceOuter = max(AdviceOuter, ComputeAdviceUV(UV + float2( 0, 1) * PostprocessInput0Size.zw)); AdviceOuter = max(AdviceOuter, ComputeAdviceUV(UV + float2(-1, 0) * PostprocessInput0Size.zw)); AdviceOuter = max(AdviceOuter, ComputeAdviceUV(UV + float2( 0, -1) * PostprocessInput0Size.zw)); uint Advice = (AdviceInner == AdviceOuter && !SpecialDotInArea) ? 0 : AdviceOuter; FLATTEN if(Advice) { FLATTEN if(Advice == 1) { // heavy shading cost OutColor = float3(0, 0, 0.8f); } else FLATTEN if(Advice == 2) { // warning OutColor = float3(0.8f, 0.8f, 0); } else // if(Advice == 3) { // error OutColor = float3(1, 0, 0); } } } // void MainPS(float4 UVAndScreenPos : TEXCOORD0, out float4 OutColor : SV_Target0) { float2 UV = UVAndScreenPos.xy; int2 PixelPos = (int2)(UVAndScreenPos.zw * ScreenPosToPixel.xy + ScreenPosToPixel.zw + 0.5f); // background is the scene color OutColor = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV); // not fully functional yet // HighlightAdvice(OutColor.rgb, UV, PixelPos); // left top of the border const int2 HistogramLeftTop = int2(64, ViewportRect.w - 128 - 32); const int2 HistogramSize = int2(ViewportRect.z - ViewportRect.x - 64 * 2, 128); const int HistogramOuterBorder = 4; const float3 BorderColor = float3(0.5f, 0.5f, 0.5f); float BorderDistance = ComputeDistanceToRect(PixelPos, HistogramLeftTop, HistogramSize); // thin black border around the histogram OutColor.xyz = lerp(float3(0, 0, 0), OutColor.xyz, saturate(BorderDistance - (HistogramOuterBorder + 2))); // big solid border around the histogram OutColor.xyz = lerp(BorderColor, OutColor.xyz, saturate(BorderDistance - (HistogramOuterBorder + 1))); // thin black border around the histogram OutColor.xyz = lerp(float3(0, 0, 0), OutColor.xyz, saturate(BorderDistance - 1)); if(BorderDistance > 0) { // outside of the histogram return; } // inside the histogram // (0, 0) .. (1, 1) float2 InsetPx = PixelPos - HistogramLeftTop; float2 InsetUV = InsetPx / HistogramSize; uint Bucket = (uint)(InsetUV.x * HISTOGRAM_SIZE); // Texture2D HistogramTexture = InputNew1; #define HistogramTexture InputNew1 // WAR for HLSLCC not allowing assignment to samplers (yet) float HistogramSum = ComputeHistogramSum(HistogramTexture); if(InsetUV.x < ComputeHistogramPositionFromLuminance(EyeAdaptationParams[0].z)) { // < min: grey OutColor.xyz = lerp(OutColor.xyz, float3(0.5f, 0.5f, 0.5f), 0.5f); } else if(InsetUV.x < ComputeHistogramPositionFromLuminance(EyeAdaptationParams[0].w)) { // >= min && < max: green OutColor.xyz = lerp(OutColor.xyz, float3(0.5f, 0.8f, 0.5f), 0.5f); } else { // >= max: grey OutColor.xyz = lerp(OutColor.xyz, float3(0.5f, 0.5f, 0.5f), 0.5f); } float LocalHistogramValue = GetHistogramBucket(HistogramTexture, Bucket) / ComputeHistogramMax(HistogramTexture); if(LocalHistogramValue >= 1 - InsetUV.y) { // histogram bars OutColor.xyz = lerp(OutColor.xyz, float3(0.5f, 0.5f, 0.5f), 0.5f); } { // HDR luminance >0 float LuminanceVal = ComputeLuminanceFromHistogramPosition(InsetUV.x); // HDR > 0 float3 AdpatedLuminance = LuminanceVal * HistogramTexture.Load(int3(0, 1, 0)).xxx; // 0..1 float3 TonemappedLuminance = FilmPostProcess(AdpatedLuminance); float3 DistMask = saturate(1.0 - 100.0 * abs(TonemappedLuminance - (1.0 - InsetUV.y))); OutColor = lerp(OutColor, float4(1, 1, 1, 0), float4(DistMask, 0.0)); } { float ValuePx = ComputeHistogramPositionFromLuminance(ComputeEyeAdaptationExposure(HistogramTexture)) * HistogramSize.x; if(abs(InsetPx.x - ValuePx) < 3) { // blue line to show the clamped percentil OutColor = lerp(OutColor, float4(0, 0, 1, 0), 0.5f); } } { float Value = ComputeHistogramPositionFromLuminance(1.0f / HistogramTexture.Load(int3(0, 1, 0)).x); PrintFloat(PixelPos, OutColor.xyz, float3(1, 1, 1), HistogramLeftTop + int2(HistogramSize.x - 7 * 8 - 2, 3), Value); float ValuePx = Value * HistogramSize.x; if(abs(InsetPx.x - ValuePx) < 2) { // white line to show the smoothed exposure OutColor = lerp(OutColor, float4(1, 1, 1, 0), 1.0f); } } }