Files
UnrealEngineUWP/Engine/Shaders/PostProcessTonemap.usf
Jack Porter 2082f7ec9b Copying //UE4/Dev-Mobile to //UE4/Dev-Main (Source: //UE4/Dev-Mobile @ 3056055)
#lockdown Nick.Penwarden
#rb None

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3011102 on 2016/06/13 by Steve.Cano

	After taking a screenshot using glReadPixels, transfer the data to the target buffer from bottom row up to fix the "upside-down" render that OpenGL does. Confirmed with QA (owen.stupka_volt) that this does not appear to be happening on iOS (non-metal devices, inclusion of iOS in write-up was a mistake), verified on an ipod touch 5. Also confirmed that this does not happen on html5, and that Mobile HDR flag does not make a difference in function.

	#jira UE-26421
	#ue4
	#android

Change 3015801 on 2016/06/16 by Dmitriy.Dyomin

	Probbably fix for UE-30878, was not able to repro an actual crash(FFoliageInstanceBaseCache::AddInstanceBaseId). Added even more logging in case fix does not work.
	#jira  UE-30878

Change 3015903 on 2016/06/16 by Dmitriy.Dyomin

	Fixed: Levels window has Refresh/UI issues when World Composition is active
	#jira UE-26160

Change 3018352 on 2016/06/17 by Chris.Babcock

	Handle Android media prepare failure (URL without internet for example)
	#jira UE-32029
	#ue4
	#android

Change 3026387 on 2016/06/24 by Jack.Porter

	Remove FFuncTestManager warning about PIE when running on a standalone game binary

Change 3026398 on 2016/06/24 by Jack.Porter

	Prevent FSocketBSD::Recv returning false on SE_EWOULDBLOCK

Change 3027553 on 2016/06/25 by Niklas.Smedberg

	OpenGL: Made some block size calculation work for arbitrary block sizes (e.g. not pow-of-two).

Change 3027554 on 2016/06/25 by Niklas.Smedberg

	Metal: copyFromTexture now gets block-aligned size parameter (e.g. used for texture streaming)

Change 3028061 on 2016/06/26 by Jack.Porter

	Fixed a problem where newly discovered instances were not added to an existing session in the Session Browser.
	Fixed a problem where selecting an instance in a session with multiple instances didn't deselect the previously selected instance correctly.

Change 3029220 on 2016/06/27 by Steve.Cano

	Change Android Tilt values to use GetRotationMatrix/GetOrientation logic, same as java-side android would use, and adjust slightly to match as closely as possible to iOS values for tilt. There is drift and some differences in the "Y" value but the same sort of inconsistencies are also seen on iOS.

	#jira UE-6135
	#ue4
	#android

Change 3030420 on 2016/06/28 by Jack.Porter

	Fix crash with RenderOutputValidation when running with cooked content

Change 3030426 on 2016/06/28 by Jack.Porter

	Fix to CL 3026398 - make FSocketBSD(IPv6)::Recv(From) return false when recv returns 0.
	A return value of 0 indicates the connection was shutdown in an orderly manner.

Change 3030973 on 2016/06/28 by Steve.Cano

	Added a landscape downloader background along with the options to change it from within Android settings

	#ue4
	#android
	#jira UE-32318

Change 3031757 on 2016/06/28 by Chris.Babcock

	Remove unused methods from AndroidJNI header
	#ue4
	#android

Change 3032387 on 2016/06/29 by Allan.Bentham

	Rename android es31+aep -> glesdeferred.

Change 3032711 on 2016/06/29 by Allan.Bentham

	Rename GLSL_310_ES_EXT shader define:
	ES31_AEP_PROFILE -> ESDEFERRED_PROFILE
	bumped UE_SHADER_GLSL_310_ES_EXT_VER version number.

Change 3033698 on 2016/06/29 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3034210 on 2016/06/30 by Steve.Cano

	Added a new AndroidRuntimeSettings variable that allows creation of installers for both Windows and Mac/Linux if set to true.

	#jira UE-32302

	#ue4
	#android

Change 3034530 on 2016/06/30 by Chris.Babcock

	Rename FManifestReader to FAndroidFileManifestReader in AndroidFile
	#jira UE-32679
	#ue4
	#android

Change 3034612 on 2016/06/30 by Steve.Cano

	Change Alpha from being set to a range of 0-255 to being in a range of 0-1 (which is the correct range of values)

	#jira UE-25325
	#ue4
	#android

Change 3034679 on 2016/06/30 by Chris.Babcock

	Fix tooltip (.command for mac, not .sh)
	#jira UE-32302
	#ue4
	#android

Change 3038881 on 2016/07/05 by Jack.Porter

	Package and launch on multiple Android devices simultaneously using the -Device=xxxxxxx+yyyyyyyy+zzzzzzzz format generated by a Project Launcher profile when you select multiple devices

	#jira UEMOB-115

Change 3039240 on 2016/07/06 by Jack.Porter

	TcpMessageTransport - connection-based message bus transport.

	#jira UEMOB-112
	#jira UEMOB-113

Change 3039252 on 2016/07/06 by Jack.Porter

	Enable messaging and session services and functional testing on Android when launched with -messaging
	Android device detection module support for adding port forwarding and connection announcement for TcpMessageTransport

	#jira UEMOB-112
	#jira UEMOB-113

Change 3039264 on 2016/07/06 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3040041 on 2016/07/06 by Chris.Babcock

	Pass proper value to script generator functions
	#jira UE-32861
	#ue4
	#android

Change 3040890 on 2016/07/07 by Allan.Bentham

	Fix shadow crash
	#jira UE-32884

Change 3041458 on 2016/07/07 by Peter.Sauerbrei

	fix for IOS launch on failures

Change 3041542 on 2016/07/07 by Peter.Sauerbrei

	better fix for the multi-device deployment issue

Change 3041774 on 2016/07/07 by Steve.Cano

	Fixing crash that occurs when a games app id for Google Play is set before configuring the apk packaging. Also validating the value that is inserted and using it to override any values that have been hand-inserted into the GooglePlayAppID.xml

	#jira UE-16992
	#android
	#ue4

Change 3042222 on 2016/07/08 by Dmitriy.Dyomin

	Mobile packaging scenarious
	Added a wizard for creating launcher profiles (Android & IOS) for scenario: Minimal App + Downloadable content
	Added Archive step to launcher profiles to be able to store build product into specified directory
	Changes to a cooker to be able to pack DLC based with a different flavor to a release App
	Changes to DLC packaging to be able to build streaming data without chunking pak files
	#jira UEMOB-119

Change 3042244 on 2016/07/08 by Dmitriy.Dyomin

	Fixed crash in FTcpMessageTransportConnection::Stop

Change 3042270 on 2016/07/08 by Dmitriy.Dyomin

	GitHub #2320 : [ULevelStreamingKismet] Load Level Instance, Enables UE4 Users to create multiple transformed instances of a .umap without having to include in persistent level's list ? Rama
	contributed by: EverNewJoy
	#jira UE-29867

Change 3042449 on 2016/07/08 by Dmitriy.Dyomin

	Fixing Mac Editor build erros from CL# 3042222

Change 3042480 on 2016/07/08 by Allan.Bentham

	Add ES3.1 profile & compiler_glsl_es3_1 to shaders.

Change 3042481 on 2016/07/08 by Allan.Bentham

	hlslcc - ES3.1 changes.
	set ES3.1 version number to 310
	Do not use ES2 keywords for ES3.1.
	Generate Layout Locations for ES3.1
	bump version.

Change 3042483 on 2016/07/08 by Allan.Bentham

	Add mobile ES3.1 support.
	Recreates EGL and ES3.1 context during PlatformInitOpenGL if ES3.1 is required.

Change 3042485 on 2016/07/08 by Allan.Bentham

	Undo android XGE change.

Change 3042506 on 2016/07/08 by Dmitriy.Dyomin

	One more compile fix from CL# 3042222

Change 3044173 on 2016/07/10 by Dmitriy.Dyomin

	UAT: Added support for building target platforms with multiple cook flavors
	ex: -targetplatform=Android -cookflavor=ETC1+ETC2

Change 3044213 on 2016/07/11 by Dmitriy.Dyomin

	Fixed: Can't stream in a level whose name is a substring of another streaming level
	#jira UE-32999

Change 3044221 on 2016/07/11 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3044815 on 2016/07/11 by Allan.Bentham

	Corrected NAME_GLSL_ES3_1_ANDROID format string.

Change 3046911 on 2016/07/12 by Chris.Babcock

	Add handling of OnTextChanged for virtual keyboard input on Android
	#jira UE-32348
	#ue4
	#android

Change 3046958 on 2016/07/12 by Chris.Babcock

	Rename some functions with Error in the name to prevent false coloring in the logs
	#jira UE-30541
	#ue4
	#android

Change 3047169 on 2016/07/12 by Chris.Babcock

	Return player ID and handle auth token for Google Play Games on Android (contributed by gameDNAstudio)
	#jira UE-30610
	#pr #2372
	#ue4
	#android

Change 3047406 on 2016/07/12 by Jack.Porter

	Add missing import to GameActivity.java

Change 3047442 on 2016/07/13 by Dmitriy.Dyomin

	Added: Mobile custom post-process
	Limitations: can fetch only from PostProcessInput0 (SceneColor) other scene textures are not supported. Does not support "Replacing the Tonemapper" blendable location.
	#jira UEMOB-147

Change 3047466 on 2016/07/13 by Dmitriy.Dyomin

	Disabled engine crash handler on Android, system crash handler works more reliably across different os versions/devices

Change 3047746 on 2016/07/13 by Jack.Porter

	Rename FBasePassFowardDynamicPointLightInfo

Change 3047778 on 2016/07/13 by Jack.Porter

	Missing file for rename FBasePassFowardDynamicPointLightInfo

Change 3047788 on 2016/07/13 by Allan.Bentham

	Fix incorrect TargetPlatformDescriptor string generation.

Change 3047790 on 2016/07/13 by Allan.Bentham

	Fixed half3x3 matrix use with ES3.1 glsl
	Fixed couple of interpolator precision mismatch.
	Fixed ES3.1 support detection issues

Change 3047816 on 2016/07/13 by Allan.Bentham

	Remove AndroidGL4 remnants.

Change 3048926 on 2016/07/13 by Chris.Babcock

	Added detection of Amazon Fire TV to disable requiring virtual joysticks
	#ue4
	#android

Change 3049335 on 2016/07/14 by Dmitriy.Dyomin

	Fixing UAT crash when packaging project for iOS

Change 3049390 on 2016/07/14 by Jack.Porter

	Disabled error for warning 4819 "The file contains a character that cannot be represented in the current code page (xxx). Save the file in Unicode format to prevent data loss"
	This is triggered by European characters and copyright symbols in source saved as latin-1 when compiling on non-US windows. Seen often in 3rd party headers, eg nvapi.

	#code_review: Ben.Marsh

Change 3049391 on 2016/07/14 by Jack.Porter

	Fixed incorrect comment order in CL 3049390

Change 3049545 on 2016/07/14 by Dmitriy.Dyomin

	Reworking some code from CL#3047442 to make static analizer happy

Change 3049626 on 2016/07/14 by Allan.Bentham

	Automatic CSM shader toggling
	#jira UE-27429

Change 3051574 on 2016/07/15 by Jack.Porter

	Support for lighting channels on Mobile
	- Multiple directional lights are supported in different channels but primitives are only affected by the directional light in the first channel they have set
	- CSM shadows from stationary or movable directional lights correctly follow their lighting channels
	- No channel limitations for dynamic point lights

	Notes:
	Removed mobile-specific directional light shadowing fields from View uniform buffer and mobile no longers uses SimpleDirectionalLight.
	Separate uniform buffers for mobile directional light are generated for each lighting channel.
	CSM culling information is now stored in FViewInfo and not per FVisibleLightViewInfo as the visibility bits are per view.

	#code_review Daniel.Wright
	#jira UEMOB-110

Change 3051699 on 2016/07/15 by Steve.Cano

	Preserve the original, pre-transformed input vertices for Slate shaders, which is required to properly do anti-aliasing (the ViewProjection-transformed values were causing the lines to not be drawn).

	#jira UE-20320
	#ue4
	#android

Change 3051744 on 2016/07/15 by Chris.Babcock

	Fix Android Vulkan include path checks (contributed by kodomastro)
	#jira UE-33311
	#PR #2602
	#ue4
	#android

Change 3052023 on 2016/07/15 by Chris.Babcock

	Fix shadowed variables

Change 3052110 on 2016/07/15 by Chris.Babcock

	Compile fixes for light channel support on mobile
	- missing template
	- accessor function for MobileDirectionalLights from scene

Change 3052242 on 2016/07/15 by Chris.Babcock

	Compile fixes for light channel support on mobile
	- removed dependency on C++14 feature

Change 3052730 on 2016/07/16 by Dmitriy.Dyomin

	Win32 build fix

Change 3053041 on 2016/07/17 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3053054 on 2016/07/17 by Jack.Porter

	Changed use of old function ShouldUseDeferredRenderer() to new GetShadingPath()

Change 3053055 on 2016/07/17 by Jack.Porter

	Fixed local variable aliasing in unity build

Change 3053206 on 2016/07/18 by Jack.Porter

	Support ExecuteJavascript on iOS and Android
	Expose ExecuteJavascript to widget blueprint
	Fix ExecuteJavascript unicode string support on desktop platforms

	#jira UEMOB-152

Change 3053323 on 2016/07/18 by Dmitriy.Dyomin

	Added: Ability to set thread affinity for a device in Device Profiles (ex: +CVars=android.SetThreadAffinity=RT 0x02 GT 0x01)
	#jira UEMOB-107

Change 3053723 on 2016/07/18 by Jack.Porter

	Fix for UnrealTournamentProto.Automation.cs build errors

Change 3055090 on 2016/07/19 by Dmitriy.Dyomin

	Junk OnlineBlueprintSupport module binaries

[CL 3056789 by Jack Porter in Main branch]
2016-07-19 19:13:01 -04:00

801 lines
26 KiB
Plaintext

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PostProcessTonemap.usf: PostProcessing tone mapping
=============================================================================*/
#include "Common.usf"
#include "PostProcessCommon.usf"
#include "TonemapCommon.usf"
#include "GammaCorrectionCommon.usf"
#include "EyeAdaptationCommon.usf"
// 0 / 1(slower, visualize multiple internals side by side for the same image portion, see DebugTile)
#define DEBUG_TONEMAPPER 0
// 64x64 tiling noise texture, optimized to have mostly high frequency content
Texture2D NoiseTexture;
SamplerState NoiseTextureSampler;
// xyz:SceneColorTint.rgb, w:unused
float4 ColorScale0;
// xyz:Bloom1Tint.rgb, w:unused
float4 ColorScale1;
// to scale UV (with border) to NormaizedUV (without border)
float2 TexScale;
// from the postprocess settings, x:VignetteIntensity, y:SharpenDiv6
float2 TonemapperParams;
// Fine film grain
float3 GrainRandomFull;
float3 GrainScaleBiasJitter;
#if EYEADAPTATION_EXPOSURE_FIX != 1
// Default value used when eye adaptation is disabled (e.g fixed exposure) or not available.
float DefaultEyeExposure;
#endif
half GrainFromUV(float2 GrainUV)
{
half Grain = frac(sin(GrainUV.x + GrainUV.y * 543.31) * 493013.0);
return Grain;
}
uint InvertTonemapping;
uint ACESInversion;
uint OutputDevice;
// LUT for color grading
#if USE_VOLUME_LUT == 1
Texture3D ColorGradingLUT;
SamplerState ColorGradingLUTSampler;
#endif
static const float LUTSize = 32;
half3 ColorLookupTable( half3 LinearColor )
{
float3 LUTEncodedColor;
// Encode as ST-2084 (Dolby PQ) values
if( OutputDevice >= 3 )
LUTEncodedColor = LinearToST2084( LinearColor );
// Encode as log values
else
LUTEncodedColor = LinToLog( LinearColor );
float3 UVW = LUTEncodedColor * ((LUTSize - 1) / LUTSize) + (0.5f / LUTSize);
#if USE_VOLUME_LUT == 1
half3 OutDeviceColor = ColorGradingLUT.Sample( ColorGradingLUTSampler, UVW ).rgb;
#else
half3 OutDeviceColor = UnwrappedTexture3DSample( PostprocessInput3, PostprocessInput3Sampler, UVW, LUTSize ).rgb;
#endif
return OutDeviceColor * 1.05;
}
// xy should be a integer position (e.g. pixel position on the screen)
// use the PseudoRandom() function if you have ALU performance left
// and this if you have TEX performance left.
float PseudoRandomTex(float2 xy)
{
return Texture2DSample(NoiseTexture, NoiseTextureSampler, xy / 64.0f).r;
}
float SwitchVerticalAxis;
#define NEEDTOSWITCHVERTICLEAXIS (ES2_PROFILE && COMPILER_GLSL_ES2) || (ES3_1_PROFILE && COMPILER_GLSL_ES3_1)
// can be optimized
float2 ScreenPosToUV(float2 ScreenPos)
{
float2 UV = (ScreenPos * ScreenPosToPixel.xy + ScreenPosToPixel.zw) * PostprocessInput0Size.zw;
#if NEEDTOSWITCHVERTICLEAXIS
UV.y = 1.0 - UV.y;
#endif
return UV;
}
float2 UVToScreenPos(float2 UV)
{
#if NEEDTOSWITCHVERTICLEAXIS
UV.y = 1.0 - UV.y;
#endif
return (UV * PostprocessInput0Size.xy - ScreenPosToPixel.zw) / ScreenPosToPixel.xy;
}
float4 FringeUVParams;
// vertex shader entry point
void MainVS(
in float4 InPosition : ATTRIBUTE0,
in float2 InTexCoord : ATTRIBUTE1,
out noperspective float2 OutTexCoord : TEXCOORD0,
out noperspective float3 OutExposureScaleVignette : TEXCOORD1,
out noperspective float4 OutGrainUV : TEXCOORD2,
out noperspective float4 OutFringe : TEXCOORD3,
out noperspective float4 OutFullViewUV : TEXCOORD4,
out float4 OutPosition : SV_POSITION
)
{
#if NEEDTOSWITCHVERTICLEAXIS
// This is currently the last pass, so flip the texture on V to match D3D
InTexCoord.y = 1.0 - InTexCoord.y;
#endif
DrawRectangle(InPosition, InTexCoord, OutPosition, OutTexCoord.xy);
// Forward renderer uses view size texture
// TODO: Looks to be Ronin specific.. check this:
OutFullViewUV.xy = OutPosition.xy * float2(0.5,-0.5) + 0.5;
#if NEEDTOSWITCHVERTICLEAXIS
OutFullViewUV.y = 1.0 - OutFullViewUV.y;
#endif
#if EYEADAPTATION_EXPOSURE_FIX == 1
// texture can be GWhiteTexture which is 1x1. It's important we don't read outside bounds.
OutExposureScaleVignette.x = EyeAdaptation.Load(int3(0, 0, 0)).r;
#else
// If eye adaptation is disabled (e.g. fixed exposure level ) or not available.
OutExposureScaleVignette.x = DefaultEyeExposure;
#endif
// Scale vignette to always be a circle with consistent corner intensity.
OutExposureScaleVignette.yz = VignetteSpace(OutPosition.xy);
// Grain
OutGrainUV.xy = OutTexCoord + PostprocessInput0Size.zw * float2(-0.5,0.5);
OutGrainUV.zw = OutTexCoord + GrainRandomFull.xy;
// Fringe
float2 ScreenPos = UVToScreenPos(OutTexCoord);
OutFringe.xy = ScreenPosToUV(ScreenPos * FringeUVParams.r);
OutFringe.zw = ScreenPosToUV(ScreenPos * FringeUVParams.g);
}
// Function graphing
float F0( float x )
{
return x*saturate( (x - 0.5)/2 );
}
float F1( float x )
{
return x;
}
float F2( float x )
{
return x;
}
float F3( float x )
{
return x;
}
float LineShade( float fx, float y, float dydx, float LineWidth )
{
return 1 - smoothstep( 0.5 * LineWidth, LineWidth, abs( fx - y ) / sqrt( 1 + Square( dydx ) ) );
}
float3 Graph( float2 ScreenSpacePos )
{
float2 WindowMin = float2( 0, 0 );
float2 WindowMax = float2( 1, 1 );
float2 p = ( (ScreenSpacePos + 1) * 0.5 - WindowMin ) * ( WindowMax - WindowMin );
float LineWidth = dot( WindowMax - WindowMin, 0.0005 );
float3 Color;
Color = float3( 1, 0, 0 ) * LineShade( F0(p.x), p.y, ( F0(p.x + LineWidth) - F0(p.x - LineWidth) ) / (2 * LineWidth), LineWidth );
Color += float3( 0, 1, 0 ) * LineShade( F1(p.x), p.y, ( F1(p.x + LineWidth) - F1(p.x - LineWidth) ) / (2 * LineWidth), LineWidth );
Color += float3( 0, 0, 1 ) * LineShade( F2(p.x), p.y, ( F2(p.x + LineWidth) - F2(p.x - LineWidth) ) / (2 * LineWidth), LineWidth );
Color += float3( 1, 1, 0 ) * LineShade( F3(p.x), p.y, ( F3(p.x + LineWidth) - F3(p.x - LineWidth) ) / (2 * LineWidth), LineWidth );
//Color += float3( 0, 1, 1 ) * LineShade( F4(p.x), p.y, ( F4(p.x + LineWidth) - F4(p.x - LineWidth) ) / (2 * LineWidth), LineWidth );
//Color += float3( 1, 1, 1 ) * LineShade( F5(p.x), p.y, ( F5(p.x + LineWidth) - F5(p.x - LineWidth) ) / (2 * LineWidth), LineWidth );
return Color;
}
// @return color in SRGB
float3 SimpleToneMap(float3 HDRColor)
{
// from http://filmicgames.com/archives/75
// float3 x = max(0, HDRColor - 0.004f); return (x * (6.2f * x + 0.5f)) / (x * (6.2f * x + 1.7f) + 0.06f);
// linear/no tonemapper
return HDRColor;
}
float max3(float3 In)
{
return max(In.x, max(In.y, In.z));
}
// @return 0 at low, 1 at high and linear interpolated inbetween
float RemapScalar(float Low, float High, float x)
{
return (x - Low) / (High - Low);
}
uint OutputGamut;
// Doesn't work with PQ output
float3 InvertACESTonemapping( float3 OutColor )
{
const float3x3 AP1_2_sRGB = mul( XYZ_2_sRGB_MAT, mul( D60_2_D65_CAT, AP1_2_XYZ_MAT ) );
const float3x3 sRGB_2_AP1 = mul( XYZ_2_AP1_MAT, mul( D65_2_D60_CAT, sRGB_2_XYZ_MAT ) );
const float3x3 AP1_2_Output = OuputGamutMappingMatrix( OutputGamut );
const float3x3 Output_2_AP1 = OuputInverseGamutMappingMatrix( OutputGamut );
const float3x3 sRGB_2_Output = mul( AP1_2_Output, sRGB_2_AP1 );
const float3x3 Output_2_sRGB = mul( AP1_2_Output, Output_2_AP1 );
half3 PseudoLinearColor, Approach0, Approach1;
PseudoLinearColor = saturate(sRGBToLinear( OutColor.rgb ));
// Approximate inversion
//Approach0 = mul( Output_2_AP1, PseudoLinearColor );
// FIXME: Workaround for UE-29935, pushing all colors with a 0 component to black output
// Workaround in PostProcessCombineLUTs.usf mean that the output color is always in the
// sRGB gamut. Once that is resolved, the matrix multiply above should be re-enabled.
Approach0 = PseudoLinearColor;
Approach0 = aces100nitFitInverse( Approach0 );
Approach0 = max(0, mul( AP1_2_Output, Approach0 ));
// Exact inversion
//Approach1 = mul(Output_2_sRGB, PseudoLinearColor);
// FIXME: Workaround for UE-29935, pushing all colors with a 0 component to black output
// Workaround in PostProcessCombineLUTs.usf mean that the output color is always in the
// sRGB gamut. Once that is resolved, the matrix multiply above should be re-enabled.
Approach1 = PseudoLinearColor;
Approach1 = InverseACESOutputTransformsRGBD65( Approach1 );
Approach1 = max(0, mul( sRGB_2_Output, Approach1 ));
return lerp(Approach0, Approach1, ACESInversion);
}
float max4(float4 x)
{
return max(max(x.r, x.g), max(x.b, x.a));
}
// useful debug helper (not optimized), could be moved into a more common place
// @param DebugTile 0,0 is center, -1,0 is left, 1,0 is right, ...
float2 ComputeCounterStepForTileDebugging(float4 SvPosition, int2 TileSize, out int2 DebugTile, out int2 LocalDebugTilePosition)
{
// ViewportRect is defined for postprocessing passes
float2 CenterPos = (ViewportRect.xy + ViewportRect.zw) / 2.0f;
float2 LeftTopPos = CenterPos - TileSize / 2;
float2 LocalPos = SvPosition.xy - LeftTopPos;
DebugTile = floor(LocalPos / TileSize);
LocalDebugTilePosition = LocalPos - DebugTile * TileSize;
float2 CounterStep = -DebugTile * TileSize;
return CounterStep;
}
// useful debug helper (works with ComputeCounterStepForTileDebugging()), could be moved into a more common place
#define OffsetInterpolator(CounterStep, InterpolatorName) \
InterpolatorName += ddx(InterpolatorName) * CounterStep.x + ddy(InterpolatorName) * CounterStep.y;
half3 LookupSceneColor(float2 SceneUV, int2 PixelOffset)
{
#if (ES2_PROFILE && COMPILER_GLSL_ES2) || (ES3_1_PROFILE && COMPILER_GLSL_ES3_1)
// slower but always works
// to prevent "error: Texture offset not supported on GLSL ES"
return Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, SceneUV + PixelOffset * PostprocessInput0Size.zw).rgb;
#else
// faster
return PostprocessInput0.SampleLevel(PostprocessInput0Sampler, SceneUV, 0, PixelOffset).rgb;
#endif
}
// pixel shader entry point
void MainPS(
in noperspective float2 UV : TEXCOORD0,
in noperspective float3 InExposureScaleVignette : TEXCOORD1,
in noperspective float4 GrainUV : TEXCOORD2,
in noperspective float4 FringeUV : TEXCOORD3,
in noperspective float4 FullViewUV : TEXCOORD4,
float4 SvPosition : SV_POSITION, // after all interpolators
out float4 OutColor : SV_Target0
)
{
OutColor = 0;
#if DEBUG_TONEMAPPER
// @param DebugTile 0,0 is center, -1,0 is left, 1,0 is right, ...
int2 DebugTile;
int2 LocalDebugTilePosition;
bool bOutputDebugTile = false;
{
// split the screen in a 3x3 layout and add some border (0.3)
int2 TileSize = floor(ViewportSize.xy / float2(3.3f, 3.3f));
bool bBorder;
float2 CounterStep = ComputeCounterStepForTileDebugging(SvPosition, TileSize, DebugTile, LocalDebugTilePosition);
OffsetInterpolator(CounterStep, UV);
OffsetInterpolator(CounterStep, GrainUV);
OffsetInterpolator(CounterStep, FringeUV);
OffsetInterpolator(CounterStep, FullViewUV);
}
if (all(DebugTile == int2(0, 0)))
{
// center is unaltered tonemapper output
bOutputDebugTile = true;
}
#endif
#if USE_GRAIN_JITTER || USE_GRAIN_INTENSITY || USE_GRAIN_QUANTIZATION
half Grain = GrainFromUV(GrainUV.zw);
#endif
float2 SceneUV = UV.xy;
#if USE_GRAIN_JITTER
SceneUV = lerp(UV.xy, GrainUV.xy, (1.0 - Grain*Grain) * GrainScaleBiasJitter.z);
#endif
#if USE_COLOR_FRINGE
float2 SceneUVJitter = float2(0.0, 0.0);
#if USE_GRAIN_JITTER
SceneUVJitter = SceneUV.xy - UV.xy;
#endif
half3 SceneColor = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, FringeUV.zw + SceneUVJitter.xy).rgb;
half SceneColorG = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, FringeUV.xy + SceneUVJitter.xy).g;
half SceneColorB = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, SceneUV).b;
SceneColor.g = SceneColorG;
SceneColor.b = SceneColorB;
#else
half3 SceneColor = Texture2DSample(PostprocessInput0, PostprocessInput0Sampler, SceneUV).rgb;
#endif
float ExposureScale = InExposureScaleVignette.x;
#if DEBUG_TONEMAPPER
if (all(DebugTile == int2(-1, 1)))
{
// left below to the center is: no ExposureScale
ExposureScale = 1.0f;
bOutputDebugTile = true;
}
#endif
// 0..x, 0:no effect .. 1:strong, from r.Tonemapper.Sharpen
// Div6 is to save one instruction
float SharpenMultiplierDiv6 = TonemapperParams.y;
#if DEBUG_TONEMAPPER
if (all(DebugTile == int2(0, -1)) || all(DebugTile == int2(-1, -1)))
{
// top row: no sharpen
SharpenMultiplierDiv6 = 0;
bOutputDebugTile = true;
}
#endif //DEBUG_TONEMAPPER
#if USE_SHARPEN
{
half3 C1 = LookupSceneColor(SceneUV, int2(-1, 0));
half3 C2 = LookupSceneColor(SceneUV, int2( 1, 0));
half3 C3 = LookupSceneColor(SceneUV, int2(-1, -1));
half3 C4 = LookupSceneColor(SceneUV, int2(-1, 1));
// reference solution
// half A0 = max3(SceneColor);
// half4 LuminanceNeightbors = half4(max3(C1), max3(C2), max3(C3), max3(C4));
// 5 instructiopns less than reference
// half A0 = Luminance(SceneColor);
// half4 LuminanceNeightbors = half4(Luminance(C1), Luminance(C2), Luminance(C3), Luminance(C4));
// 9 instructions less than reference (green is often a good approximation for the luminance)
half A0 = SceneColor.g;
half4 LuminanceNeightbors = half4(C1.g, C2.g, C3.g, C4.g);
// compute mask to not sharpen near very bright HDR content
// Note: using max instead of summming up saves 1 instruction
// Note: We could multiply this to tweak the edge weight but it might instroduce a cost
// half HDREdge = ExposureScale * max4(abs(A0 - LuminanceNeightbors));
// 3 instructions less but less accurate (faint black dots near bright edges)
half HDREdge = ExposureScale * max(abs(A0 * 2 - (LuminanceNeightbors.r + LuminanceNeightbors.g)), abs(A0 * 2 - (LuminanceNeightbors.b + LuminanceNeightbors.a)));
// 0..1
half EdgeMask = saturate(1.0f - HDREdge);
#if DEBUG_TONEMAPPER
if (all(DebugTile == int2(1, 0)))
{
// right to the center is: Sharpen EdgeMask
OutColor = EdgeMask; return;
}
#endif //DEBUG_TONEMAPPER
// -1:sharpen, 0:no effect, 1:blur
float LerpFactor = -EdgeMask * SharpenMultiplierDiv6;
// reference
// same instruction count as if the center weight is 1.0
// half3 BlurredSceneColor = ((C1 + C2 + C3 + C4) + SceneColor * 2) / 6;
// SceneColor = lerp(SceneColor, BlurredSceneColor, LerpFactor);
// optimized, Div6 went into the C++ code
half3 DeltaColor = (C1 + C2 + C3 + C4) - SceneColor * 4;
SceneColor += DeltaColor * LerpFactor;
}
#endif
#if USE_GAMMA_ONLY
OutColor.rgb = pow(SceneColor, InverseGamma.x);
#else
half3 LinearColor = SceneColor * ColorScale0.rgb;
#if USE_BLOOM
float2 BloomUV = UV;
#if ES2_PROFILE || ES3_1_PROFILE
BloomUV = FullViewUV.xy;
#endif
float4 CombinedBloom = Texture2DSample(PostprocessInput1, PostprocessInput1Sampler, BloomUV);
float3 BloomDirtMaskColor = Texture2DSample(BloomDirtMask.Mask, BloomDirtMask.MaskSampler, SvPositionToViewportUV(SvPosition)).rgb * BloomDirtMask.Tint.rgb;
#if DEBUG_TONEMAPPER
if (all(DebugTile == int2(-1, -1)))
{
// left to the center is: no bloom
CombinedBloom = 0;
bOutputDebugTile = true;
}
if (all(DebugTile == int2(1, -1)))
{
// right to the center is: bloom only
LinearColor = 0;
bOutputDebugTile = true;
}
#endif //DEBUG_TONEMAPPER
LinearColor += CombinedBloom.rgb * (ColorScale1.rgb + BloomDirtMaskColor);
#endif
#if NO_EYEADAPTATION_EXPOSURE_FIX == 1
ExposureScale = BloomDirtMask.Tint.w;
#endif
LinearColor *= ExposureScale;
#if USE_VIGNETTE
LinearColor.rgb *= ComputeVignetteMask( InExposureScaleVignette.yz, TonemapperParams.x );
#endif
#if USE_GRAIN_INTENSITY
// Needs to go before tonemapping.
half GrainMul = Grain * GrainScaleBiasJitter.x + GrainScaleBiasJitter.y;
LinearColor.rgb *= GrainMul;
#endif
half3 OutDeviceColor = ColorLookupTable( LinearColor );
//OutDeviceColor = Graph(SvPositionToScreenPosition(SvPosition).xy);
half LuminanceForPostProcessAA = dot(OutDeviceColor, half3(0.299f, 0.587f, 0.114f));
#if USE_GRAIN_QUANTIZATION
// Needs to go after tonemapping.
half GrainQuantization = 1.0/256.0;
half GrainAdd = (Grain * GrainQuantization) + (-0.5 * GrainQuantization);
OutDeviceColor.rgb += GrainAdd;
#endif
// RETURN_COLOR not needed unless writing to SceneColor
OutColor = float4(OutDeviceColor, saturate(LuminanceForPostProcessAA));
#if USE_INVERSE_TONEMAPPING
// Post-tonemap linearization. Used when writing out EXRs
OutColor.rgb = InvertACESTonemapping( OutColor.rgb );
#endif
#endif
#if DEBUG_TONEMAPPER
if (!bOutputDebugTile)
{
// the original tonemapped color is only in the center
// other tiles can output like this:
// if(all(DebugTile == int2(1, 0)))
// { OutColor = ..; return; }
// or
// if(all(DebugTile == int2(1, 0)))
// { ..; bOutputDebugTile = true; }
OutColor = 0;
}
if (any(LocalDebugTilePosition == int2(0, 0)))
{
// border grid
OutColor = 1;
}
#endif //DEBUG_TONEMAPPER
}
// ES2 version
// TODO: Convert float to half.
float FringeIntensity;
// vertex shader entry point
void MainVS_ES2(
in float4 InPosition : ATTRIBUTE0,
in float2 InTexCoord : ATTRIBUTE1,
out float4 OutTexCoord : TEXCOORD0,
out float4 OutFineDofGrain : TEXCOORD1,
out float4 OutFullViewUV : TEXCOORD2,
out float2 OutVignette : TEXCOORD3,
out float2 OutTexCoords[4] : TEXCOORD4,
out float4 OutFringeTexCoords : FRINGE_COORDS,
out float4 OutPosition : SV_POSITION
)
{
#if NEEDTOSWITCHVERTICLEAXIS
// This is currently the last pass, so flip the texture on V to match D3D
InTexCoord.y = 1.0-InTexCoord.y;
#endif
DrawRectangle(InPosition, InTexCoord, OutPosition, OutTexCoord.xy);
OutTexCoord = float4(OutTexCoord.xy, OutPosition.xy);
// Fine adjustment is inside the possible non-full viewport in the full resolution texture.
OutFineDofGrain.xy = OutTexCoord.xy + PostprocessInput0Size.zw * float2(-0.5,0.5);
// Want grain and a second UV based on the knowledge that the source texture has a full viewport.
OutFullViewUV.xy = OutPosition.xy * float2(0.5,-0.5) + 0.5;
#if NEEDTOSWITCHVERTICLEAXIS
// This is currently the last pass, so flip the texture on V to match D3D
OutFullViewUV.y = 1.0 - OutFullViewUV.y;
#endif
// For DOF attempt to undo sampling bias for the first transition region.
// This is better for the fine transition, breaks down for the larger bokeh.
// This is the best compromise for mobile using 4 bilinear taps only.
OutFullViewUV.zw = OutFullViewUV.xy + PostprocessInput2Size.zw * float2(0.25,-0.5);
OutFineDofGrain.zw = OutFullViewUV.xy + GrainRandomFull.xy;
// NEWS
OutTexCoords[0] = OutTexCoord.xy + PostprocessInput0Size.zw * float2( 0,-1);
OutTexCoords[1] = OutTexCoord.xy + PostprocessInput0Size.zw * float2( 1, 0);
OutTexCoords[2] = OutTexCoord.xy + PostprocessInput0Size.zw * float2(-1, 0);
OutTexCoords[3] = OutTexCoord.xy + PostprocessInput0Size.zw * float2( 0, 1);
// Scale vignette to always be a circle with consistent corner intensity.
OutVignette.xy = VignetteSpace(InPosition.xy);
// Compute fringe tex coord offsets
BRANCH
if (FringeIntensity > 0.0)
{
half2 FringeOffset = OutTexCoord.xy;
FringeOffset -= 0.5;
FringeOffset = abs(FringeOffset * 2.0);
FringeOffset *= FringeIntensity;
OutFringeTexCoords.xy = OutTexCoord.xy * half2(1.0 - FringeOffset.x * 0.5, 1.0 - FringeOffset.y * 0.5);
OutFringeTexCoords.zw = OutTexCoord.xy * half2(1.0 - FringeOffset.x, 1.0 - FringeOffset.y);
}
else
{
OutFringeTexCoords = 0.0;
}
}
// Constants for DOF blend in.
half CocMaxRadiusInPixelsRcp()
{
half2 MaxOffset = half2(-2.125,-0.50)*2.0;
return rcp(sqrt(dot(MaxOffset, MaxOffset)));
}
half2 CocBlendScaleBias()
{
half Start = 0.25 * CocMaxRadiusInPixelsRcp();
half End = 1.0 * CocMaxRadiusInPixelsRcp();
half2 ScaleBias;
ScaleBias.x = 1.0/(End-Start);
ScaleBias.y = (-Start)*ScaleBias.x;
return ScaleBias;
}
half2 CocBlendScaleBiasFine()
{
half Start = 0.0 * CocMaxRadiusInPixelsRcp();
half End = 0.5 * CocMaxRadiusInPixelsRcp();
half2 ScaleBias;
ScaleBias.x = 1.0/(End-Start);
ScaleBias.y = (-Start)*ScaleBias.x;
return ScaleBias;
}
float4 OverlayColor;
void MainPS_ES2(
in float4 UVAndScreenPos : TEXCOORD0,
in float4 FineDofGrain : TEXCOORD1,
in float4 FullViewUV : TEXCOORD2,
in float2 InVignette : TEXCOORD3,
in float2 InTexCoords[4] : TEXCOORD4,
in float4 InFringeTexCoords : FRINGE_COORDS,
#if USE_32BPP_HDR
in float4 SvPosition : SV_Position, // after all interpolators
#endif
out half4 OutColor : SV_Target0
)
{
float2 UV = UVAndScreenPos.xy;
float2 ScreenSpacePos = UVAndScreenPos.zw;
half4 SceneColor;
#if !USE_32BPP_HDR
BRANCH
if (FringeIntensity > 0.0)
{
SceneColor.r = PostprocessInput0.Sample(PostprocessInput0Sampler, UV).r;
SceneColor.g = PostprocessInput0.Sample(PostprocessInput0Sampler, InFringeTexCoords.xy).g;
SceneColor.b = PostprocessInput0.Sample(PostprocessInput0Sampler, InFringeTexCoords.zw).b;
SceneColor.a = 1.0;
}
else
#endif
{
SceneColor = PostprocessInput0.Sample(PostprocessInput0Sampler, UV);
}
#if USE_GAMMA_ONLY
#if USE_MSAA
SceneColor.rgb *= rcp(SceneColor.r*(-0.299) + SceneColor.g*(-0.587) + SceneColor.b*(-0.114) + 1.0);
#endif
OutColor.rgb = sqrt(SceneColor.rgb);
#else
#if USE_GRAIN_JITTER || USE_GRAIN_INTENSITY || USE_GRAIN_QUANTIZATION
half Grain = GrainFromUV(FineDofGrain.zw);
#endif
#if USE_DOF
half4 DofFine = PostprocessInput0.Sample(PostprocessInput0Sampler, FineDofGrain.xy);
half4 Dof = PostprocessInput2.Sample(PostprocessInput2Sampler, FullViewUV.zw);
// Convert alpha back into circle of confusion.
SceneColor.a = max(Dof.a, abs(SceneColor.a * 2.0 - 1.0));
// Convert circle of confusion into blend factors.
half2 ScaleBias = CocBlendScaleBias(); // Constant.
half DofAmount = saturate(SceneColor.a * ScaleBias.x + ScaleBias.y);
half2 ScaleBias2 = CocBlendScaleBiasFine(); // Constant.
half DofAmountFine = saturate(SceneColor.a * ScaleBias2.x + ScaleBias2.y);
#if USE_GRAIN_JITTER
// Grain can increase fine DOF.
DofAmountFine = max((1.0-Grain*Grain) * GrainScaleBiasJitter.z, DofAmountFine);
#endif
// Blend in fine DOF.
SceneColor.rgb = lerp(SceneColor.rgb, DofFine.rgb, DofAmountFine);
// Blend in coarse DOF.
SceneColor.rgb = lerp(SceneColor.rgb, Dof.rgb, DofAmount);
#else
#if USE_GRAIN_JITTER
#if USE_32BPP_HDR
// This has grain jitter off.
#else
// Do jitter for grain.
half4 DofFine = PostprocessInput0.Sample(PostprocessInput0Sampler, FineDofGrain.xy);
// Grain jitter.
SceneColor.rgb = lerp(SceneColor.rgb, DofFine.rgb, (1.0-Grain*Grain) * GrainScaleBiasJitter.z);
#endif
#endif
#endif
#if USE_MSAA
// Do after jitter for grain as an optimization.
SceneColor.rgb *= rcp(SceneColor.r*(-0.299) + SceneColor.g*(-0.587) + SceneColor.b*(-0.114) + 1.0);
// Try to kill negatives and NaNs here
SceneColor.rgb = max(SceneColor.rgb, 0);
#endif
#if USE_32BPP_HDR
{
// TODO: Support odd frame inversion of mosaic pattern?
#if 0
half3 SceneColorE = PostprocessInput0.Sample(PostprocessInput0Sampler, InTexCoords[1].xy).rgb;
SceneColor.rgb = HdrDemosaic(SceneColor.rgb, SceneColorE, SvPosition.xy);
#endif
#if 1
// Higher quality path.
half3 SceneColorN = PostprocessInput0.Sample(PostprocessInput0Sampler, InTexCoords[0].xy).rgb;
half3 SceneColorE = PostprocessInput0.Sample(PostprocessInput0Sampler, InTexCoords[1].xy).rgb;
half3 SceneColorW = PostprocessInput0.Sample(PostprocessInput0Sampler, InTexCoords[2].xy).rgb;
half3 SceneColorS = PostprocessInput0.Sample(PostprocessInput0Sampler, InTexCoords[3].xy).rgb;
half3 SceneColorV = SceneColorN * 0.5 + SceneColorS * 0.5;
half3 SceneColorH = SceneColorW * 0.5 + SceneColorE * 0.5;
if(abs(SceneColorN.g - SceneColorS.g) < abs(SceneColorW.g - SceneColorE.g))
{
SceneColorH = SceneColorV;
}
SceneColor.rgb = Decode32BPPHDR(SceneColor, SceneColorH, SvPosition.xy).rgb;
SceneColor.a = 0.0;
#endif
}
#endif
#if !USE_DOF
// Set so temporal AA shader knows everything is in focus.
SceneColor.a = 0.0;
#endif
// Match PC naming.
half3 LinearColor = SceneColor.rgb;
#if USE_32BPP_HDR && USE_VIGNETTE
LinearColor.rgb *= ComputeVignetteMask( InVignette, TonemapperParams.x );
LinearColor = max(LinearColor, float3(0, 0, 0));
#endif
// It is faster to do vignette as a texture lookup + mad because this is an ALU bound shader.
#if (USE_BLOOM || USE_LIGHT_SHAFTS || USE_VIGNETTE)
{
half4 CombinedBloomSunVignette = Texture2DSample(PostprocessInput1, PostprocessInput1Sampler, FullViewUV.xy);
if (GetHDR32bppEncodeMode() != HDR_ENCODE_MOSAIC)
{
#if USE_32BPP_HDR
CombinedBloomSunVignette.rgb = Decode32BPPHDR(CombinedBloomSunVignette).rgb;
LinearColor.rgb = LinearColor.rgb + CombinedBloomSunVignette.rgb;
#else
LinearColor.rgb = LinearColor.rgb * CombinedBloomSunVignette.a + CombinedBloomSunVignette.rgb;
#endif
}
}
#endif
#if USE_GRAIN_INTENSITY
// Needs to go before tonemapping.
half GrainMul = Grain * GrainScaleBiasJitter.x + GrainScaleBiasJitter.y;
LinearColor.rgb *= GrainMul;
#endif
half3 FilmColor = FilmPostProcess(LinearColor.rgb);
#if IOS
// Note, iOS native output is raw gamma 2.2 not sRGB!
half3 TonemappedColor = pow(FilmColor.rgb, 1.0/2.2);
#else
// Apply conversion to sRGB (this must be an exact sRGB conversion else darks are bad).
half3 TonemappedColor = LinearToSrgbBranchless(FilmColor);
#endif
// Blend with custom LDR color, used for Fade track in Matinee.
// This is the 101% wrong way to do this,
// - It adds an extra redundant lerp.
// - It is not going to work with the future-forward ES3 fast path of sRGB output.
// - And it does super ugly non-linear blending.
// The right way is to adjust exposure instead.
TonemappedColor = lerp(TonemappedColor.rgb, OverlayColor.rgb, OverlayColor.a);
#if USE_GRAIN_QUANTIZATION
// Needs to go after tonemapping.
half GrainQuantization = 1.0/256.0;
half GrainAdd = (Grain * GrainQuantization) + (-0.5 * GrainQuantization);
TonemappedColor.rgb += GrainAdd;
#endif
OutColor = half4(TonemappedColor, SceneColor.a);
#endif
}