Files
UnrealEngineUWP/Engine/Shaders/PostProcessVisualizeHDR.usf
Martin Mittring b3fe10ca26 added RGB to VisualizeHDR
[CL 2424000 by Martin Mittring in Main branch]
2015-01-29 12:35:56 -05:00

306 lines
9.7 KiB
Plaintext

// Copyright 1998-2015 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"
#include "MiniFontCommon.usf" // for PrintFloat()
// .xy:GatherExtent.xy, .zw:TexelPerThreadGroupXY
uint4 HistogramParams;
// 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;
}
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);
}
}
}
bool InUnitBox(float2 UV)
{
return UV.x >= 0 && UV.y >= 0 && UV.y < 1 && UV.y < 1;
}
// @param x 0=cold..1=hot
float3 Colorize(float x)
{
x = saturate(x);
float3 Heat = float3(1.0f, 0.0f, 0.0f);
float3 Middle = float3(0.0f, 1.0f, 0.0f);
float3 Cold = float3(0.0f, 0.0f, 1.0f);
float3 ColdHeat = lerp(Cold, Heat, x);
return lerp(Middle, ColdHeat, abs(0.5f - x) * 2);
}
//
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);
float2 ViewLocalUV = float2(UVAndScreenPos.z * 0.5f + 0.5f, 0.5f - 0.5f * UVAndScreenPos.w);
int2 ViewportCenter = (int2)(ViewportRect.xy + ViewportSize.xy / 2);
// background is the scene color
float4 SceneColor = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, UV);
// OutColor = SceneColor;
float LuminanceVal = max(SceneColor.r, max(SceneColor.g, SceneColor.b));
OutColor = float4(Colorize(ComputeHistogramPositionFromLuminance(LuminanceVal)), 1.0f);
// crosshair
{
float CrossHairMask = PixelPos.x == ViewportCenter.x || PixelPos.y == ViewportCenter.y;
float2 DistAbs = abs(PixelPos - ViewportCenter);
float Dist = max(DistAbs.x, DistAbs.y);
float DistMask = Dist >= 2 && Dist < 7;
OutColor.xyz = lerp(OutColor.xyz, float3(1, 1, 1), CrossHairMask * DistMask);
}
// value under crosshair
{
float3 CenterViewportHDRColor = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, ViewportCenter * PostprocessInput0Size.zw).rgb;
float Value = Luminance(CenterViewportHDRColor);
int2 Cursor;
Cursor = ViewportCenter + int2(-35, 7 + 0 * 8);
PrintCharacter(PixelPos, OutColor.xyz, float3(1, 1, 1), Cursor, 10 + 11); // 'L'
PrintFloat(PixelPos, OutColor.xyz, float3(1, 1, 1), Cursor, Value);
Cursor = ViewportCenter + int2(-35, 7 + 1 * 8);
PrintCharacter(PixelPos, OutColor.xyz, float3(1, 0, 0), Cursor, 10 + 17); // 'R'
PrintFloat(PixelPos, OutColor.xyz, float3(1, 1, 1), Cursor, CenterViewportHDRColor.r);
Cursor = ViewportCenter + int2(-35, 7 + 2 * 8);
PrintCharacter(PixelPos, OutColor.xyz, float3(0, 1, 0), Cursor, 10 + 6); // 'G'
PrintFloat(PixelPos, OutColor.xyz, float3(1, 1, 1), Cursor, CenterViewportHDRColor.g);
Cursor = ViewportCenter + int2(-35, 7 + 3 * 8);
PrintCharacter(PixelPos, OutColor.xyz, float3(0, 0, 1), Cursor, 10 + 1); // 'B'
PrintFloat(PixelPos, OutColor.xyz, float3(1, 1, 1), Cursor, CenterViewportHDRColor.b);
}
float2 IDAreaLocalUV = ViewLocalUV * 2 + float2(-1, 0);
// disabled for now, useful for debgugging the histogram content
if(0)
BRANCH if(InUnitBox(IDAreaLocalUV))
{
float2 HistogramUV = frac(IDAreaLocalUV * HistogramParams.xy / HistogramParams.zw);
int2 HistogramXY = int2(IDAreaLocalUV * HistogramParams.xy / HistogramParams.zw);
bool bChecker = dot(HistogramXY, 1) % 2;
int HistogramId = HistogramXY.x + HistogramXY.y * ceil(HistogramParams.x / (float)HistogramParams.z);
float2 UnrolledHistogramUV = float2(HistogramUV.x, (HistogramId + 0.5f) * PostprocessInput3Size.w);
float4 HistogramRGBA = Texture2DSampleLevel(PostprocessInput3, PostprocessInput3Sampler, UnrolledHistogramUV, 0);
// 16 buckets are good enough for the small space there
float Value = max(max(HistogramRGBA.r,HistogramRGBA.g), max(HistogramRGBA.b,HistogramRGBA.a));
// we want the histogram to be 0 at the bottom
float HistogramY = 1 - HistogramUV.y;
// /2 as we rea covering a quarter of the screen
float HistogramPixelHeight = ViewportSize.y / 2 / HistogramXY.y;
// checked overlay
OutColor = bChecker ? float4(0.2f,0,0,0) : float4(0,0.15f,0,0);
// SceneColor overlay
OutColor += 0.1f * Texture2DSampleLevel(PostprocessInput0, PostprocessInput0Sampler, IDAreaLocalUV * View.ViewSizeAndSceneTexelSize.xy * View.ViewSizeAndSceneTexelSize.zw, 0);
// could be the same as (HistogramY < Value) but a bit more antialiased
// float HistogramMask = HistogramY < Value;
float HistogramMask = saturate((Value - HistogramY) * HistogramPixelHeight);
OutColor = lerp(OutColor, float4(Colorize(HistogramUV.x), 1.0f), HistogramMask);
}
// 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;
// (0, 0) .. (1, 1)
float2 InsetPx = PixelPos - HistogramLeftTop;
float2 InsetUV = InsetPx / HistogramSize;
// const float3 BorderColor = float3(0.5f, 0.5f, 0.5f);
const float3 BorderColor = Colorize(InsetUV.x);
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
uint Bucket = (uint)(InsetUV.x * HISTOGRAM_SIZE);
// Texture2D HistogramTexture = InputNew1;
#define HistogramTexture PostprocessInput1 // WAR (workaround) 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, Colorize(InsetUV.x), 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);
}
}
// eye adaptation
{
float EyeApatationValue = ComputeHistogramPositionFromLuminance(1.0f / HistogramTexture.Load(int3(0, 1, 0)).x);
float ValuePx = EyeApatationValue * HistogramSize.x;
PrintFloat(PixelPos, OutColor.xyz, float3(1, 1, 1), HistogramLeftTop + int2(ValuePx + - 3 * 8 - 3, 1), EyeApatationValue);
if(abs(InsetPx.x - ValuePx) < 2 && PixelPos.y > HistogramLeftTop.y + 9)
{
// white line to show the smoothed exposure
OutColor = lerp(OutColor, float4(1, 1, 1, 0), 1.0f);
}
}
}