# Extended Levels (W\B point ) + Histogram port

4 years 9 months ago - 3 years 10 months ago #1 by v00d00m4n
Extended Levels (W\B point ) + Histogram port
Tired to wait for when Marty will release Lightroom shader, so i used old sweetFX levels and upgraded it and found old forgotten histogram shader and ported it to reshade 3 to achieve my goals and fully imitate Photoshop, Gimp and Paint.net kind of levels editing.

Now you have both input and output levels, gamma, everything separate for each color channel, plus some anti-clipping measure (but not so good, I forgot most of curve related math and hope someone could help me to s-curve into 0-255 range everything that gets beyond it and clipped, by using confirurable curve start start and end points.

So lets say I have 16-235 expanded to 0-255, since I its linear function, ill have values 0-16 appear expanded to -18-0 and values 235--255 expanded as 255-278, what I want, is set break point like 5 for black and 250 for white, and while everything in-between should be kep linear, since these points all values from 5 to -18 for black point should be smoothly curved into 5-0 range, and for white point values 250-278 should be smoothly curved to 250-255 range.
Since these points 5 and 250 should be edited in real time for each channel and end points 0 and 255 non constant and chould change with Ouput Level values, formula should be dynamic. I see it as 2nd step after linear function (to keep in corrections intact), with 2 if else functions where range 5-250 should be kept as is, and ranges -18-5 and 250-278 (again these are not constants, keep that in mind!) should be processed by two opposite curve formulas what will smoothly pack these ranges to output range.

My problem - these formulas and I don't remember much of hlsl syntax and don't know reshade syntax specifics to implement it properly.

Also, would be nice if someone could explain me how can I draw graph (y as output, x as input colors, on half transparent BG just like in histogram shader) on screen to visualize line and curve I'm actually getting for better debugging ?

As for histogram - it works like charm, should be loaded after levels to let you see color distribution. There is only one UI value, that for some reason gives me error during compilation, texture2d don't like int iResolution for some reason but #define works fine

Please keep comments, where I explain problem, In shader, i need to rise awareness of tonemap problem in modern games from lazy devs.this shader pack it just a temporary solution to improper white and black points and I hope that more people would notice problem and would force devs to fix it natively

save as: Levels.fx (you can safely replace old one with this updated version)
/**
* Levels version 1.6
* by Christian Cann Schuldt Jensen ~ CeeJay.dk
* updated to 1.3+ by Kirill Yarovoy ~ v00d00m4n
*
* Allows you to set a new black and a white level.
* This increases contrast, but clips any colors outside the new range to either black or white
* and so some details in the shadows or highlights can be lost.
*
* The shader is very useful for expanding the 16-235 TV range to 0-255 PC range.
* You might need it if you're playing a game meant to display on a TV with an emulator that does not do this.
* But it's also a quick and easy way to uniformly increase the contrast of an image.
*
* -- Version 1.0 --
* First release
* -- Version 1.1 --
* Optimized to only use 1 instruction (down from 2 - a 100% performance increase :) )
* -- Version 1.2 --
* Added the ability to highlight clipping regions of the image with #define HighlightClipping 1
*
* -- Version 1.3 --
* Added inddependant RGB channel levels that allow to fix impropely balanced console specific color space.
*
* Most of modern Xbox One \ PS4 ports has white point around 233 222 211 instead of TV 235 235 235
* which can be seen and aproximated by analyzing histograms of hundreds of hudless screenshots of modern games
* including big titles such as GTAV, Witcher 3, Watch_Dogs, most of UE4 based titles and so on.
*
* Most of these games lacking true balanced white and black colors and looks like if you play on very old and dusty display.
* This problem could be related to improper usage and settings of popular FILMIC shader, introduced in Uncharted 2.
*
* I used to prebake static luts to restore color balance, but doing so in image editors was slow, so once i discovered
* that Reshade 3 has RGB UI settings i decided that dynamic in-game correction would be more productive, so i updated this
* old shader to correct color mess in game. I can spot white oddities wiht my naked eyes, but i suggest to combine this shader
* with Ganossa Histogram shader, loaded after levels for this, but you need to update it for Rehade 3 and get it here:
* https://github.com/crosire/reshade-shaders/blob/382b28f33034809e52513332ca36398e72563e10/ReShade/Shaders/Ganossa/Histogram.fx
*
* -- Version 1.4 --
* Added ability to upshift color range before expanding it. Needed to fix stupid Ubisoft mistake in Watch Dogs 2 where they
* somehow downshifted color range.
*
* -- Version 1.5 --
* Changed formulas to allow gamma and output range controls.
*
* -- Version 1.6 --
* Added ACES curve, to avoid clipping.
*/

// Settings

uniform float3 InputBlackPoint <
ui_type = "color";
ui_tooltip = "The black point is the new black - literally. Everything darker than this will become completely black.";
> = float3(16/255.0f, 18/255.0f, 20/255.0f);

uniform float3 InputWhitePoint <
ui_type = "color";
ui_tooltip = "The new white point. Everything brighter than this becomes completely white";
> = float3(233/255.0f, 222/255.0f, 211/255.0f);

uniform float3 InputGamma <
ui_type = "drag";
ui_min = 0.33f; ui_max = 3.00f;
ui_label = "RGB Gamma";
ui_tooltip = "Adjust midtones for Red, Green and Blue.";
> = float3(1.00f,1.00f,1.00f);

uniform float3 OutputBlackPoint <
ui_type = "color";
ui_tooltip = "The black point is the new black - literally. Everything darker than this will become completely black.";
> = float3(0/255.0f, 0/255.0f, 0/255.0f);

uniform float3 OutputWhitePoint <
ui_type = "color";
ui_tooltip = "The new white point. Everything brighter than this becomes completely white";
> = float3(255/255.0f, 255/255.0f, 255/255.0f);

uniform float3 ColorRangeShift <
ui_type = "color";
ui_tooltip = "Some games like Watch Dogs 2 has color range 16-235 downshifted to 0-219, so this option was added to upshift color range before expanding it. RGB value entered here will be just added to default color value. Negative values impossible at the moment in game, but can be added, in shader if downshifting needed. 0 disables shifting.";
> = float3(0/255.0f, 0/255.0f, 0/255.0f);

uniform int ColorRangeShiftSwitch <
ui_type = "drag";
ui_min = -1; ui_max = 1;
ui_tooltip = "Workaround for lack of negative color values in Reshade UI: -1 to downshift, 1 to upshift, 0 to disable";
> = 0;

uniform bool AvoidClipping <
ui_tooltip = "Avoids the pixels that clip.";
> = false;

uniform bool HighlightClipping <
ui_tooltip = "Colors between the two points will be stretched, which increases contrast, but details above and below the points are lost (this is called clipping). Highlight the pixels that clip. Yellow = Some details lost in the highlights, Red = All details lost in the highlights, Cyan = Some details lost in the shadows, Blue = All details lost in the shadows.";
> = false;

// Helper functions

#include "ReShade.fxh"

float3 ACESFilmRec2020( float3 x )
{
float a = 15.8f;
float b = 2.12f;
float c = 1.2f;
float d = 5.92f;
float e = 1.9f;
x = x * 0.49f; // Restores luminance
return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e );
}

// Main function

float3 LevelsPass(float4 vpos : SV_Position, float2 texcoord : TexCoord) : SV_Target
{

float3 InputColor = tex2D(ReShade::BackBuffer, texcoord).rgb;

//float3 black_point_float = InputBlackPoint; // / 255.0f;
//float3 white_point_float = InputWhitePoint == BlackPoint ? (1.0f / 0.000000001f) : (1.0f / (WhitePoint - BlackPoint)); // Avoid division by zero if the white and black point are the same

//color = (color + (ColorRangeShift * ColorRangeShiftSwitch)) * white_point_float - (black_point_float *  white_point_float); //old formula
//float3 OutputColor = (((InputColor + (ColorRangeShift * ColorRangeShiftSwitch)) - black_point_float) * white_point_float); // optimized

float3 OutputColor = pow(((InputColor + (ColorRangeShift * ColorRangeShiftSwitch)) - InputBlackPoint)/(InputWhitePoint - InputBlackPoint) , InputGamma) * (OutputWhitePoint - OutputBlackPoint) + OutputBlackPoint;

if (AvoidClipping == true)
{
OutputColor = ACESFilmRec2020(OutputColor);
}

if (HighlightClipping == true)
{
float3 ClippedColor;

ClippedColor = any(OutputColor > saturate(OutputColor)) // any colors whiter than white?
? float3(1.0, 1.0, 0.0)
: OutputColor;
ClippedColor = all(OutputColor > saturate(OutputColor)) // all colors whiter than white?
? float3(1.0, 0.0, 0.0)
: ClippedColor;
ClippedColor = any(OutputColor < saturate(OutputColor)) // any colors blacker than black?
? float3(0.0, 1.0, 1.0)
: ClippedColor;
ClippedColor = all(OutputColor < saturate(OutputColor)) // all colors blacker than black?
? float3(0.0, 0.0, 1.0)
: ClippedColor;

OutputColor = ClippedColor;
}

return OutputColor;
}

technique Levels
{
pass
{
VertexShader = PostProcessVS;
PixelShader = LevelsPass;
}
}

save as Histogram.fx
/**
* Copyright (C) 2015-2016 Ganossa (emailProtector.addCloakedMailto("ep_bb4d42bf",0);)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software with restriction, including without limitation the rights to
* use and/or sell copies of the Software, and to permit persons to whom the Software
* is furnished to do so, subject to the following conditions:
*
* The above copyright notice and the permission notices (this and below) shall
* be included in all copies or substantial portions of the Software.
*
* Permission needs to be specifically granted by the author of the software to any
* person obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including without
* limitation the rights to copy, modify, merge, publish, distribute, and/or
* sublicense the Software, and subject to the following conditions:
*
* The above copyright notice and the permission notices (this and above) shall
* be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

// Ported to Reshade 3 by v00d00m4n

////-------------//
///**HISTOGRAM**///
//-------------////
//#define USE_HISTOGRAM 1 //[Histogram] //-Histogram effect

//>Histogram Settings<\\
//#define bHistoMix 0 //[1:0] //-Mixes the colors in the histogram
uniform bool bHistoMix <
ui_tooltip = "Mixes the colors in the histogram.";
> = true;

#define iResolution 200 //[32:253] //-Adjust sample resolution (effects performance)
/* uniform int iResolution <
ui_type = "drag";
ui_min = 32; ui_max = 1024;
ui_tooltip = "Adjust sample resolution (effects performance)";
> = 200;
*/ // Why does not it work?

//#define iVerticalScale 100 //[1:100] //-CS scale
uniform int iVerticalScale <
ui_type = "drag";
ui_min = 0; ui_max = 100;
ui_tooltip = "CS scale";
> = 25;

//#define iHorizontalScale 4 //[1:4] //-CS scale
uniform int iHorizontalScale <
ui_type = "drag";
ui_min = 1; ui_max = 10;
ui_tooltip = "CS scale";
> = 4;

//#define Histogram_ToggleKey RESHADE_TOGGLE_KEY //[undef] //-Toggle key for Histogram

//#include EFFECT_CONFIG(Ganossa)

//#if USE_HISTOGRAM

//#pragma message "Histogram by Ganossa\n"

#include "ReShade.fxh"

//namespace Ganossa
//{

texture2D detectIntTex { Width = iResolution; Height = iResolution; Format = RGBA32F; };
sampler2D detectIntColor { Texture = detectIntTex; };

texture2D detectLowTex { Width = 256; Height = 1; Format = RGBA16F; };
sampler2D detectLowColor { Texture = detectLowTex; };

void PS_Histogram_DetectInt(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 detectInt : SV_Target)
{
detectInt = tex2D(ReShade::BackBuffer,texcoord);
}

void PS_Histogram_DetectLow(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 detectLow : SV_Target)
{
detectLow = float4(0,0,0,0);
float bucket = trunc(texcoord.x * 256f);
[fastopt][loop]
for (float i = 0.0; i <= 1; i+=1f/iResolution)
{	[fastopt][loop]
for ( float j = 0.0; j <= 1; j+=1f/iResolution )
{
float3 level = trunc(tex2D(detectIntColor,float2(j,i)).xyz*256f);
detectLow.xyz += (level == bucket);
}
}
detectLow.xyz /= float(iResolution*iResolution)/iVerticalScale;
}

float4 PS_Histogram_Display(float4 vpos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target0
{
float3 data = tex2D(detectLowColor,texcoord.x*iHorizontalScale).xyz;
float3 orig = tex2D(ReShade::BackBuffer,texcoord).xyz;
float4 hg = float4(0,0,0,1);
if(texcoord.x < (1./iHorizontalScale-BUFFER_RCP_WIDTH)) {
//#if bHistoMix
if (bHistoMix)
{
if(texcoord.y > 1-data.x) hg += float4(1,0,0,0);
if(texcoord.y > 1-data.y) hg += float4(0,1,0,0);
if(texcoord.y > 1-data.z) hg += float4(0,0,1,0);
if(max(hg.x,max(hg.y,hg.z)) == 0) hg = float4(orig,0)*0.5;
//#else
} else {
if(texcoord.y < 0.33) { if(texcoord.y+0.66 > 1-data.x ) hg += float4(1,0,0,0); else hg = float4(orig,0)*0.5; }
if(texcoord.y < 0.66 && texcoord.y > 0.33) { if(texcoord.y+0.33 > 1-data.y) hg += float4(0,1,0,0); else hg = float4(orig,0)*0.5; }
if(texcoord.y > 0.66) { if(texcoord.y > 1-data.z) hg += float4(0,0,1,0); else hg = float4(orig,0)*0.5; }
//#endif
}
} else hg = float4(orig,0);
return hg;
}

technique Histogram
{
pass Histogram_DetectInt
{
VertexShader = PostProcessVS;
PixelShader = PS_Histogram_DetectInt;
RenderTarget = detectIntTex;
}

pass Histogram_DetectLow
{
VertexShader = PostProcessVS;
PixelShader = PS_Histogram_DetectLow;
RenderTarget = detectLowTex;
}

pass Histogram_Display
{
VertexShader = PostProcessVS;
PixelShader = PS_Histogram_Display;
}
}

//}

//#endif

// #include EFFECT_CONFIG_UNDEF(Ganossa)

Watch how it helps to fix screwed colors of Watch Dogs 2:

.

Update:
Forgot to share port of more recent version for Media Player Classic, which would be helpful for fixing ugly color grading in movies.
/**
* Levels version 1.6.6 MPC Edition lite
* Original version by Christian Cann Schuldt Jensen ~ CeeJay.dk
* Updated to 1.3+ by Kirill Yarovoy ~ v00d00m4n
*
* Allows you to set a new black and a white level.
* This increases contrast, but clips any colors outside the new range to either black or white
* and so some details in the shadows or highlights can be lost.
*
* The shader is very useful for expanding the 16-235 TV range to 0-255 PC range.
* You might need it if you're playing a game meant to display on a TV with an emulator that does not do this.
* But it's also a quick and easy way to uniformly increase the contrast of an image.
*
* -- Version 1.0 --
* First release
* -- Version 1.1 --
* Optimized to only use 1 instruction (down from 2 - a 100% performance increase :) )
* -- Version 1.2 --
* Added the ability to highlight clipping regions of the image with #define HighlightClipping 1
*
* -- Version 1.3 --
* Added inddependant RGB channel levels that allow to fix impropely balanced console specific color space.
*
* Most of modern Xbox One \ PS4 ports has white point around 233 222 211 instead of TV 235 235 235
* which can be seen and aproximated by analyzing histograms of hundreds of hudless screenshots of modern games
* including big titles such as GTAV, Witcher 3, Watch_Dogs, most of UE4 based titles and so on.
*
* Most of these games lacking true balanced white and black colors and looks like if you play on very old and dusty display.
* This problem could be related to improper usage and settings of popular FILMIC shader, introduced in Uncharted 2.
*
* I used to prebake static luts to restore color balance, but doing so in image editors was slow, so once i discovered
* that Reshade 3 has RGB UI settings i decided that dynamic in-game correction would be more productive, so i updated this
* old shader to correct color mess in game. I can spot white oddities wiht my naked eyes, but i suggest to combine this shader
* with Ganossa Histogram shader, loaded after levels for this, but you need to update it for Rehade 3 and get it here:
* https://github.com/crosire/reshade-shaders/blob/382b28f33034809e52513332ca36398e72563e10/ReShade/Shaders/Ganossa/Histogram.fx
*
* -- Version 1.4 --
* Added ability to upshift color range before expanding it. Needed to fix stupid Ubisoft mistake in Watch Dogs 2 where they
* somehow downshifted color range.
*
* -- Version 1.5 --
* Changed formulas to allow gamma and output range controls.
*
* -- Version 1.6 --
* Added ACES curve, to avoid clipping.
*
* -- Version 1.6.5 --
* Ported for MPC, cleaned out broken and debug stuff.
*
* -- Version 1.6.6 --
* Made ACES useful and configurable.
*/

// Settings

// #define InputBlackPoint float3(16/255.0f, 18/255.0f, 20/255.0f) //the thing 2011

//#define InputWhitePoint float3(233/255.0f, 222/255.0f, 211/255.0f) // generic value for modern games like GTAV and Witcher 3
//#define InputWhitePoint float3(233/255.0f, 222/255.0f, 222/255.0f) // the thing 2011
//#define InputWhitePoint float3(184/255.0f, 182/255.0f, 164/255.0f) // blade runner 2049

#define InputBlackPoint float3(16/255.0f, 16/255.0f, 16/255.0f)

#define InputWhitePoint float3(235/255.0f, 235/255.0f, 235/255.0f)

#define InputGamma float3(1.0f,1.0f,1.0f)

#define OutputBlackPoint float3(0/255.0f, 0/255.0f, 0/255.0f)

#define OutputWhitePoint float3(255/255.0f, 255/255.0f, 255/255.0f)

#define ColorRangeShift float3(0/255.0f, 0/255.0f, 0/255.0f)

#define ColorRangeShiftSwitch 0

#define ACESLuminancePercentage 98

#define ACEScurve 1

#define LevelsEnabled 1

sampler BackBuffer : register(s0);

// Helper function

float3 ACESFilmRec2020( float3 x )
{
float a = 15.8f;
float b = 2.12f;
float c = 1.2f;
float d = 5.92f;
float e = 1.9f;
x = x * ACESLuminancePercentage * 0.005; // Restores luminance
return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e );
}

// Main function

float4 main(float2 texcoord : TEXCOORD0) : COLOR
{
float3 InputColor = tex2D(BackBuffer, texcoord).rgb;
float3 OutputColor;

if (LevelsEnabled == true)
{
OutputColor = pow(((InputColor + (ColorRangeShift * ColorRangeShiftSwitch)) - InputBlackPoint)/(InputWhitePoint - InputBlackPoint) , InputGamma) * (OutputWhitePoint - OutputBlackPoint) + OutputBlackPoint;
} else {
OutputColor = InputColor;
}

if (ACEScurve == true)
{
OutputColor = ACESFilmRec2020(OutputColor);
}

return float4(OutputColor, 1.0);
}

ACES here works like some sort of HDR color look simulator and sorta like TV > PC color range expander.

Here few shots of how that works in MPC in movies:

screenshotcomparison.com/comparison/130740/picture:0
screenshotcomparison.com/comparison/130740/picture:1

Original vs Shader:

Here is the little tutorial of how to find correct values:

1) Find source of light or well lit objects in game or movie that should have while color in theory (like clouds at mid day in games like GTA 5 or Witcher 3), make a hudless creenshot or remove (by cropping or stetching nearby areas) any hud in editor

2.A) in Use in game histogram and set black and white levels so that 1st side peaks could reach borders of historgram for each color

2. Use Paint.net or Gimp, open LEVELS tool, press one AUTO LEVELS, that manualy tweak black and white levels slider to a 1st peak points. Don't Set Slider to where color just starts, set it where 1st peak gets about half up. Copy 0-255 range values into reshade UI or add them to INI (keep in mind that reshade stores 0-1 range, so you have to do 1/255*your_value).

2.C) Manually pick the brightest point of screenshot that suppose to be white but is not, do same for darkest point (better do it at night or indoor screenshots), repeat rest of actions from 2.B
Last edit: 3 years 10 months ago by v00d00m4n.
The following user(s) said Thank You: jas01, Elimina, andrew, Genrix, Flamex, Echo, Sinclair, p6kocka, SandyCheeks

Please Log in or Create an account to join the conversation.

4 years 9 months ago #2 by XIIICaesar
Replied by XIIICaesar on topic Extended Levels (W\B point ) + Histogram port
You see the change mainly in the sky or at least I do. It pops more. Theres more depth and realism IMO.

Please Log in or Create an account to join the conversation.

4 years 9 months ago #3 by v00d00m4n
Replied by v00d00m4n on topic Extended Levels (W\B point ) + Histogram port
Yeah, extra depth and realism appear because contrast between highlights and shadows getting higher and more distinguishable, by default compressed color range looks flat because there is less difference between shadows and lights. Same goes at nights, nights just don't look dark enough and feels more like evening, with some desaturation.
Also while many people cant explain it, they can notice when highlights are not white, this is what people usually call dimmed or washed out colors. Because it feels unnatural it feels less realistic, this is why its important to keep correct RGB balance in white point, so that white is white an black is black. Many AAA devs don't understand that, especially those who does porting to PC and don't care about color range difference.

I still have no idea why balance usually 233 222 211 instead of standard 235 235 235, I cant find specs of Xbox One and PS4 to check if theory is right and that internal color range of some of these consoles is screwed and they reduce green and blue range, which looks fine on console, but looks too redish on PC.

Btw, I'm finishing testing and about to release version 1.7 with linear anti-clipping.
The following user(s) said Thank You: jas01, WalterDasTrevas, hunt1hunt

Please Log in or Create an account to join the conversation.

4 years 9 months ago - 4 years 9 months ago #4 by hunt1hunt
Replied by hunt1hunt on topic Extended Levels (W\B point ) + Histogram port
v00d00m4n hello:
can you updata adaptive-fog to reshade3.0?
reshade.me/forum/shader-presentation/198...-effect-adaptive-fog
very very good fog
Last edit: 4 years 9 months ago by Marty McFly.

Please Log in or Create an account to join the conversation.

4 years 9 months ago #5 by WalterDasTrevas
Replied by WalterDasTrevas on topic Extended Levels (W\B point ) + Histogram port
Thank you so much!!

Please Log in or Create an account to join the conversation.

4 years 9 months ago #6 by v00d00m4n
Replied by v00d00m4n on topic Extended Levels (W\B point ) + Histogram port
Done, I think
reshade.me/forum/shader-presentation/198...t-adaptive-fog#20766
had no time to test and implement UI but ill get back to that later.

Please Log in or Create an account to join the conversation.

4 years 9 months ago #7 by hunt1hunt
Replied by hunt1hunt on topic Extended Levels (W\B point ) + Histogram port
thank you very much.and wish you update adapitivefog to be use in 3.0.6

Please Log in or Create an account to join the conversation.

4 years 7 months ago - 4 years 7 months ago #8 by Genrix
Hello! I tried to use Histogram shader, but I dont see Histogram, for ex. in JC3 and Last Man Standing.
imgur.com/MxrzUi0
Loading after levels or before them - nothing changes.
I tried to use Reshade 3.0,4 and 3.0.7.
Whats happened?

ps. Спасибо за шейдер.
Last edit: 4 years 7 months ago by Genrix.

Please Log in or Create an account to join the conversation.

3 years 10 months ago #9 by v00d00m4n
Replied by v00d00m4n on topic Extended Levels (W\B point ) + Histogram port
I don't know, histogram shader just does not work in some games.

Please Log in or Create an account to join the conversation.

3 years 10 months ago #10 by v00d00m4n
Replied by v00d00m4n on topic Extended Levels (W\B point ) + Histogram port
Added mini guide how to find correct black and white point, and added Media Player Classic port of shader for real time color correction in movies.
The following user(s) said Thank You: andrew, SandyCheeks

Please Log in or Create an account to join the conversation.

3 years 5 months ago #11 by Estebanium
Replied by Estebanium on topic Extended Levels (W\B point ) + Histogram port
I don´t get it. Do I have to modify the input or the output black/white point, if I got the new values via Gimp?

Please Log in or Create an account to join the conversation.

3 years 5 months ago #12 by v00d00m4n
Replied by v00d00m4n on topic Extended Levels (W\B point ) + Histogram port
I did this video for developer of one Tomb Raider kind of game called Die Young to explain what's wrong with colors in game and how to fix it, but basically it shows how to calibrate correct colors in GIMP by my method I briefly described above, use it as visual GUIDE:

Please Log in or Create an account to join the conversation.

2 years 11 months ago - 2 years 11 months ago #13 by dawidezzo
Replied by dawidezzo on topic Extended Levels (W\B point ) + Histogram port
Hello Voodooman and big thanks for your work! This shader is AWESOME and finally my games look like should look like

"Btw, I'm finishing testing and about to release version 1.7 with linear anti-clipping."

Does this version exist? Download option for any new version would be appreciated

Cheers!

Edit1: Voodooman I have 3 important Q for You:

1.When I make screen which format is optimal: BMP or PNG, something else or it is not important?

2.Which graphic settings in game should I choose? I ask because I notice that, for example, bloom and other postprocess effects probably affect the result. I am not sure for 100%

3.On Nvidia CP Full RBG for hdmi/DP is correct as I understand, right?

Please answer and help me a little when you find moments
Last edit: 2 years 11 months ago by dawidezzo. Reason: supplement

Please Log in or Create an account to join the conversation.

2 years 11 months ago #14 by SavvyKraken
Replied by SavvyKraken on topic Extended Levels (W\B point ) + Histogram port
I've been testing your technique on a few games that had similar color issues, and I must say the results are a dramatic improvement!

Games like GTA5 and Farcry always seemed to look more vibrant compared to the likes of Fallout NV and Kotor2, and I could never put my finger on the reason why; never would I have guessed that the colors were just poorly calibrated..

Ironically, I could never get histogram to work so I had to work manually, but your tutorial was so well explained so it was all I needed.

Please Log in or Create an account to join the conversation.

2 years 10 months ago #15 by Crystrex
Replied by Crystrex on topic Extended Levels (W\B point ) + Histogram port
Any chance to port it to reshade 4?

Please Log in or Create an account to join the conversation.

2 years 9 months ago #16 by v00d00m4n
Replied by v00d00m4n on topic Extended Levels (W\B point ) + Histogram port
It aint works by default? I have not played much with Reshade 4, i trited 1st release, it gave me shit load of compilation errors in other shader, i rage quit and uninstalled it and returned to 3 and posted some angry rant here explaining Crossire why breaking compatibility AGAIN is no way to go.

Damn, i got to look if it works with 4 now, and i also got to remember which of my games had the latest version of it, i got to update what i posted here, its not the latest one.

Please Log in or Create an account to join the conversation.

2 years 9 months ago - 2 years 9 months ago #17 by v00d00m4n
Replied by v00d00m4n on topic Extended Levels (W\B point ) + Histogram port
Version 1.7 existed until i did something stupid when was sleepy and broke it, than had no time for month to fix it and forgot everything, then i just reverted to 1.6 and did some fixes for 1.8 and upper. Which im going to post right now, but im not sure is is last version i touched, i have bad habbit to edit shader per game folder and then i have all the different version in different game folders, and they also spread across 3 different hdds, so need to check every game i tweaked to see where the real latest version was left XD

1 - png
2 - whatever you like, but i prefer to turn off anything that does not look realistic, until turning it off breaks something or makes game look duller and flatter
3 - right, also in games where reshade is no option use this combo - FULL on video card and LIMITED on tv\display, this should make games look like console versions.

This code has unfinished and broken remains of 1.7 commented, in case someone wants to correct and finish them, but it think its only part of code and here was a portion which was deleted by my head sleeping on keyboard XD
/**
* Levels version 1.8
* by Christian Cann Schuldt Jensen ~ CeeJay.dk
* updated to 1.3+ by Kirill Yarovoy ~ v00d00m4n
*
* Allows you to set a new black and a white level.
* This increases contrast, but clips any colors outside the new range to either black or white
* and so some details in the shadows or highlights can be lost.
*
* The shader is very useful for expanding the 16-235 TV range to 0-255 PC range.
* You might need it if you're playing a game meant to display on a TV with an emulator that does not do this.
* But it's also a quick and easy way to uniformly increase the contrast of an image.
*
* -- Version 1.0 --
* First release
* -- Version 1.1 --
* Optimized to only use 1 instruction (down from 2 - a 100% performance increase :) )
* -- Version 1.2 --
* Added the ability to highlight clipping regions of the image with #define HighlightClipping 1
*
* -- Version 1.3 --
* Added independant RGB channel levels that allow to fix impropely balanced console specific color space.
*
* Most of modern Xbox One \ PS4 ports has white point around 233 222 211 instead of TV 235 235 235
* which can be seen and aproximated by analyzing histograms of hundreds of hudless screenshots of modern games
* including big titles such as GTAV, Witcher 3, Watch_Dogs, most of UE4 based titles and so on.
*
* Most of these games lacking true balanced white and black colors and looks like if you play on very old and dusty display.
* This problem could be related to improper usage and settings of popular FILMIC shader, introduced in Uncharted 2.
*
* I used to prebake static luts to restore color balance, but doing so in image editors was slow, so once i discovered
* that Reshade 3 has RGB UI settings i decided that dynamic in-game correction would be more productive, so i updated this
* old shader to correct color mess in game. I can spot white oddities wiht my naked eyes, but i suggest to combine this shader
* with Ganossa Histogram shader, loaded after levels for this, but you need to update it for Rehade 3 and get it here:
* https://github.com/crosire/reshade-shaders/blob/382b28f33034809e52513332ca36398e72563e10/ReShade/Shaders/Ganossa/Histogram.fx
*
* -- Version 1.4 --
* Added ability to upshift color range before expanding it. Needed to fix stupid Ubisoft mistake in Watch Dogs 2 where they
* somehow downshifted color range.
*
* -- Version 1.5 --
* Changed formulas to allow gamma and output range controls.
*
* -- Version 1.6 --
* Added ACES curve, to avoid clipping.
*
* -- Version 1.7 --
* Removed ACES and added linear Z-curve to avoid clipping. Optional Alt calculation added.
*
* -- Version 1.8
* Previous version features was broken when i was sleepy, than i did not touch this shader for months and forgot what i did there.
* So, i commented messed up code in hope to fix it later, and reintroduced ACES in useful way.
*/

#include "ReShade.fxh"
static const float PI = 3.141592653589793238462643383279f;

// Settings

uniform bool EnableLevels <
ui_tooltip = "Enable or Disable Levels for TV <> PC or custome color range";
> = true;

uniform float3 InputBlackPoint <
ui_type = "color";
ui_tooltip = "The black point is the new black - literally. Everything darker than this will become completely black.";
> = float3(16/255.0f, 18/255.0f, 20/255.0f);

uniform float3 InputWhitePoint <
ui_type = "color";
ui_tooltip = "The new white point. Everything brighter than this becomes completely white";
> = float3(233/255.0f, 222/255.0f, 211/255.0f);

uniform float3 InputGamma <
ui_type = "drag";
ui_min = 0.001f; ui_max = 10.00f; step = 0.001f;
ui_label = "RGB Gamma";
ui_tooltip = "Adjust midtones for Red, Green and Blue.";
> = float3(1.00f,1.00f,1.00f);

uniform float3 OutputBlackPoint <
ui_type = "color";
ui_tooltip = "The black point is the new black - literally. Everything darker than this will become completely black.";
> = float3(0/255.0f, 0/255.0f, 0/255.0f);

uniform float3 OutputWhitePoint <
ui_type = "color";
ui_tooltip = "The new white point. Everything brighter than this becomes completely white";
> = float3(255/255.0f, 255/255.0f, 255/255.0f);

// Anti clipping measures

/*
uniform float3 MinBlackPoint <
ui_type = "color";
ui_min = 0.0f; ui_max = 0.5f;
ui_tooltip = "If avoid clipping enabled this is the percentage break point relative to Output black. Anything lower than this will be compressed to fit into output range.";
> = float3(16/255.0f, 18/255.0f, 20/255.0f);

uniform float3 MinWhitePoint <
ui_type = "color";
ui_min = 0.5f; ui_max = 1.0f;
ui_tooltip = "If avoid clipping enabled this is the percentage white point relative to Output white. Anything higher than this will be compressed to fit into output range.";
> = float3(233/255.0f/1.1f, 222/255.0f/1.1f, 211/255.0f/1.1f);
*/

uniform float3 ColorRangeShift <
ui_type = "color";
ui_tooltip = "Some games like Watch Dogs 2 has color range 16-235 downshifted to 0-219, so this option was added to upshift color range before expanding it. RGB value entered here will be just added to default color value. Negative values impossible at the moment in game, but can be added, in shader if downshifting needed. 0 disables shifting.";
> = float3(0/255.0f, 0/255.0f, 0/255.0f);

uniform int ColorRangeShiftSwitch <
ui_type = "drag";
ui_min = -1; ui_max = 1;
ui_tooltip = "Workaround for lack of negative color values in Reshade UI: -1 to downshift, 1 to upshift, 0 to disable";
> = 0;

/*
uniform bool AvoidClipping <
ui_tooltip = "Avoid pixels clip.";
> = false;

uniform bool AvoidClippingWhite <
ui_tooltip = "Avoid white pixels clip.";
> = false;

uniform bool AvoidClippingBlack <
ui_tooltip = "Avoid black pixels clip.";
> = false;

uniform bool SmoothCurve <
ui_tooltip = "Improves contrast";
> = true;
*/

uniform bool ACEScurve <
ui_tooltip = "Enable or Disable ACES for improved contrast and luminance";
> = false;

uniform int3 ACESLuminancePercentage <
ui_type = "drag";
ui_min = 75; ui_max = 175; step = 1;
ui_tooltip = "Percentage of ACES Luminance. Can be used to avoid some color clipping.";
> = int3(100,100,100);

uniform bool HighlightClipping <
ui_tooltip = "Colors between the two points will stretched, which increases contrast, but details above and below the points are lost (this is called clipping).\n0 Highlight the pixels that clip. Red = Some details are lost in the highlights, Yellow = All details are lost in the highlights, Blue = Some details are lost in the shadows, Cyan = All details are lost in the shadows.";
> = false;

// Helper functions

float3 ACESFilmRec2020( float3 x )
{
float a = 15.8f;
float b = 2.12f;
float c = 1.2f;
float d = 5.92f;
float e = 1.9f;
x = x * ACESLuminancePercentage * 0.005f; // Restores luminance
return ( x * ( a * x + b ) ) / ( x * ( c * x + d ) + e );
}

/*
float3 Smooth(float3 color, float3 inputwhitepoint, float3 inputblackpoint)
{
//color =
return clamp((color - inputblackpoint)/(inputwhitepoint - inputblackpoint), 0.0, 1.0);
//return pow(sin(PI * 0.5 * color),2);
}
*/

/*
float Curve(float x, float centerX, float centerY)
{
if (centerX > 0  && centerX < 1 && centerY > 0  && centerY < 1)
{
if (x < 0.5)
{
return 0-pow(sin(PI * ((0-x)/4*(0-centerX))),2)*2*(0-centerY);
} else if (x > 0.5)
{
return 1-pow(sin(PI * ((1-x)/4*(1-centerX))),2)*2*(1-centerY);
} else
{
return x;
}
} else
{
return x;
}
}
*/

//RGB input levels
float3 InputLevels(float3 color, float3 inputwhitepoint, float3 inputblackpoint)
{
return color = (color - inputblackpoint)/(inputwhitepoint - inputblackpoint);
//return pow(sin(PI * 0.5 * color),2);
}

//RGB output levels
float3  Outputlevels(float3 color, float3 outputwhitepoint, float3 outputblackpoint)
{
return color * (outputwhitepoint - outputblackpoint) + outputblackpoint;
}

//1 channel input level
float  InputLevel(float color, float inputwhitepoint, float inputblackpoint)
{
return (color - inputblackpoint)/(inputwhitepoint - inputblackpoint);
}

//1 channel output level
float  Outputlevel(float color, float outputwhitepoint, float outputblackpoint)
{
return color * (outputwhitepoint - outputblackpoint) + outputblackpoint;
}

// Main function

float3 LevelsPass(float4 vpos : SV_Position, float2 texcoord : TexCoord) : SV_Target
{
float3 InputColor = tex2D(ReShade::BackBuffer, texcoord).rgb;
float3 OutputColor = InputColor;

// outPixel = (pow(((inPixel * 255.0) - inBlack) / (inWhite - inBlack), inGamma) * (outWhite - outBlack) + outBlack) / 255.0; // Nvidia reference formula

/*
if (EnableLevels == true)
{
OutputColor = Outputlevels(pow(InputLevels(OutputColor + (ColorRangeShift * ColorRangeShiftSwitch), InputWhitePoint, InputBlackPoint), InputGamma), OutputWhitePoint, OutputBlackPoint);

/*
if (AvoidClipping == true)
{

//float3 OutputMaxBlackPoint = pow(((0 + (ColorRangeShift * ColorRangeShiftSwitch)) - InputBlackPoint)/(InputWhitePoint - InputBlackPoint) , InputGamma) * (OutputWhitePoint - OutputBlackPoint) + OutputBlackPoint;
//float3 OutputMaxWhitePoint = pow(((1 + (ColorRangeShift * ColorRangeShiftSwitch)) - InputBlackPoint)/(InputWhitePoint - InputBlackPoint) , InputGamma) * (OutputWhitePoint - OutputBlackPoint) + OutputBlackPoint;

if (AvoidClippingWhite == true)
{
//White
float3 OutputMaxWhitePoint;
float3 OutputMinWhitePoint;

// doest not give smooth gradient :-(
OutputMaxWhitePoint = Outputlevels(pow(InputLevels(OutputWhitePoint + (ColorRangeShift * ColorRangeShiftSwitch), InputWhitePoint, InputBlackPoint), InputGamma), OutputWhitePoint, OutputBlackPoint);
OutputMinWhitePoint = Outputlevels(pow(InputLevels(InputWhitePoint + (ColorRangeShift * ColorRangeShiftSwitch), InputWhitePoint, InputBlackPoint), InputGamma), OutputWhitePoint, OutputBlackPoint);

OutputColor.r = (OutputColor.r >= OutputMinWhitePoint.r)
? Curve( InputColor.r, MinWhitePoint.r, OutputMinWhitePoint.r)
//? Outputlevel( InputLevel( OutputColor.r, OutputMaxWhitePoint.r, OutputMinWhitePoint.r ), OutputWhitePoint.r, OutputMinWhitePoint.r)
: OutputColor.r;

OutputColor.g = (OutputColor.g >= OutputMinWhitePoint.g)
? Curve( InputColor.g, MinWhitePoint.g, OutputMinWhitePoint.g)
//? Outputlevel( InputLevel( OutputColor.g, OutputMaxWhitePoint.g, OutputMinWhitePoint.g ), OutputWhitePoint.g, OutputMinWhitePoint.g)
: OutputColor.g;

OutputColor.b = (OutputColor.b >= OutputMinWhitePoint.b)
? Curve( InputColor.b, MinWhitePoint.b, OutputMinWhitePoint.b)
//? Outputlevel( InputLevel( OutputColor.b, OutputMaxWhitePoint.b, OutputMinWhitePoint.b ), OutputWhitePoint.b, OutputMinWhitePoint.b)
: OutputColor.b;
}

if (AvoidClippingBlack == true)
{
//Black

float3 OutputMaxBlackPoint;
float3 OutputMinBlackPoint;
float3 OutputMinBlackPointY;

OutputMaxBlackPoint = pow(((0 + (ColorRangeShift * ColorRangeShiftSwitch)) - InputBlackPoint)/(InputWhitePoint - InputBlackPoint) , InputGamma) * (OutputWhitePoint - OutputBlackPoint) + OutputBlackPoint;
OutputMinBlackPoint = MinBlackPoint;
OutputMinBlackPointY = pow(((OutputMinBlackPoint + (ColorRangeShift * ColorRangeShiftSwitch)) - InputBlackPoint)/(InputWhitePoint - InputBlackPoint) , InputGamma) * (OutputWhitePoint - OutputBlackPoint) + OutputBlackPoint;

OutputColor.r = (OutputColor.r <= OutputMinBlackPoint.r)
? Curve(OutputMinBlackPoint.r,OutputMinBlackPointY.r,((OutputColor.r - OutputMaxBlackPoint.r)/(OutputMinBlackPoint.r - OutputMaxBlackPoint.r)) * (OutputMinBlackPoint.r - OutputBlackPoint.r) + OutputBlackPoint.r)
: OutputColor.r;

OutputColor.g = (OutputColor.g <= OutputMinBlackPoint.g)
? Curve(OutputMinBlackPoint.g,OutputMinBlackPointY.g,((OutputColor.g - OutputMaxBlackPoint.g)/(OutputMinBlackPoint.g - OutputMaxBlackPoint.g)) * (OutputMinBlackPoint.g - OutputBlackPoint.g) + OutputBlackPoint.g)
: OutputColor.g;

OutputColor.b = (OutputColor.b <= OutputMinBlackPoint.b)
? Curve(OutputMinBlackPoint.b,OutputMinBlackPointY.b,((OutputColor.b - OutputMaxBlackPoint.b)/(OutputMinBlackPoint.b - OutputMaxBlackPoint.b)) * (OutputMinBlackPoint.b - OutputBlackPoint.b) + OutputBlackPoint.b)
: OutputColor.b;
}
}
//
}
*/

if (EnableLevels == true)
{
OutputColor = pow(((InputColor + (ColorRangeShift * ColorRangeShiftSwitch)) - InputBlackPoint)/(InputWhitePoint - InputBlackPoint) , InputGamma) * (OutputWhitePoint - OutputBlackPoint) + OutputBlackPoint;
} else {
OutputColor = InputColor;
}

if (ACEScurve == true)
{
OutputColor = ACESFilmRec2020(OutputColor);
}

if (HighlightClipping == true)
{
float3 ClippedColor;
ClippedColor = any(OutputColor > saturate(OutputColor)) // any colors whiter than white?
? float3(1.0, 1.0, 0.0)
: OutputColor;
ClippedColor = all(OutputColor > saturate(OutputColor)) // all colors whiter than white?
? float3(1.0, 0.0, 0.0)
: ClippedColor;
ClippedColor = any(OutputColor < saturate(OutputColor)) // any colors blacker than black?
? float3(0.0, 1.0, 1.0)
: ClippedColor;
ClippedColor = all(OutputColor < saturate(OutputColor)) // all colors blacker than black?
? float3(0.0, 0.0, 1.0)
: ClippedColor;
OutputColor = ClippedColor;
}

return OutputColor;
}

technique Levels
{
pass
{
VertexShader = PostProcessVS;
PixelShader = LevelsPass;
}
}

/*
for visualisation
https://www.desmos.com/calculator
\frac{\left(x-\frac{16}{255}\right)}{\left(\frac{233}{255}-\frac{16}{255}\ \right)}\cdot \left(\frac{255}{255}-0\right)+0

\left(\frac{\left(\left(\frac{\left(x-\frac{16}{255}\right)}{\left(\frac{233}{255}-\frac{16}{255}\ \right)}\cdot \left(\frac{255}{255}-0\right)+0\right)-\frac{250}{255}\right)}{\left(\left(\frac{\left(1-\frac{16}{255}\right)}{\left(\frac{233}{255}-\frac{16}{255}\ \right)}\cdot \left(\frac{255}{255}-0\right)+0\right)-\frac{250}{255}\ \right)}\cdot \left(\frac{255}{255}-\frac{250}{255}\right)+\frac{250}{255}\right)

\left(\frac{\left(\left(\frac{\left(x-\frac{16}{255}\right)}{\left(\frac{233}{255}-\frac{16}{255}\ \right)}\cdot \left(\frac{255}{255}-0\right)+0\right)-\left(\frac{\left(0-\frac{16}{255}\right)}{\left(\frac{233}{255}-\frac{16}{255}\ \right)}\cdot \left(\frac{255}{255}-0\right)+0\right)\right)}{\left(\frac{5}{255}-\left(\frac{\left(0-\frac{16}{255}\right)}{\left(\frac{233}{255}-\frac{16}{255}\ \right)}\cdot \left(\frac{255}{255}-0\right)+0\right)\right)}\cdot \left(\frac{5}{255}-\frac{0}{255}\right)+0\right)

//
//this is for x,y<0.5
\left(\sin (\pi *\left(-\frac{x}{4\cdot 0.1352}\right))^2\right)\cdot 2\cdot 0.0782

\left(\sin (\pi *\left(-\frac{x}{4\cdot [black point curve break\center] x}\right))^2\right)\cdot 2\cdot [black point curve break\center] y

//this is for x,y>0.5

1-\left(\sin (\pi *\left(-\frac{1-x}{4\cdot \left(1-0.8528\right)}\right))^2\right)\cdot 2\cdot \left(1-0.9137\right)

1-\left(\sin (\pi *\left(-\frac{1-x}{4\cdot \left(1-[white point curve break\center] x\right)}\right))^2\right)\cdot 2\cdot \left(1-[white point curve break\center] y\right)

*/
Last edit: 2 years 9 months ago by v00d00m4n.
The following user(s) said Thank You: Marty

Please Log in or Create an account to join the conversation.

2 years 9 months ago #18 by aaronth07
Replied by aaronth07 on topic Extended Levels (W\B point ) + Histogram port
Seems pretty cool, can you select a white/black point in-game by clicking? Or is it just manual?

Please Log in or Create an account to join the conversation.

2 years 9 months ago - 2 years 9 months ago #19 by v00d00m4n
Replied by v00d00m4n on topic Extended Levels (W\B point ) + Histogram port
Manual. But, its actually good idea to request Crosire to add color picker for all the RGB UI elements.
Last edit: 2 years 9 months ago by v00d00m4n.

Please Log in or Create an account to join the conversation.

2 years 8 months ago #20 by xristosv
Replied by xristosv on topic Extended Levels (W\B point ) + Histogram port
Can anybody help me? How can i use this? Im new to reshade stuff

Please Log in or Create an account to join the conversation.