mirror of
https://github.com/italicsjenga/slang-shaders.git
synced 2024-11-22 15:51:30 +11:00
Major improvement to the Sony PVM 4K HDR shader Refactored it into a single pass for simplicity and speed Rewrote a lot of the curves Defaulted to a new mask RGBX Added ability for scanlines to overlap Broke out HDR and inverse tonemapper into headers
This commit is contained in:
parent
0d881b43bb
commit
0af8be9016
|
@ -1,6 +1,12 @@
|
||||||
#reference "crt-sony-pvm-4k-hdr.slangp"
|
#reference "crt-sony-pvm-4k-hdr.slangp"
|
||||||
Contrast = "3.7500000"
|
PaperWhiteNits = "650.000000"
|
||||||
PaperWhiteNits = "450.000000"
|
CRTGamma = "1.8"
|
||||||
ScanlineWidth = "0.950000"
|
ScanlineMin = "0.55000"
|
||||||
ResolutionPattern = "2.000000"
|
ScanlineMax = "0.90000"
|
||||||
Sharpness = "2.400000"
|
ResolutionPattern = "3.000000"
|
||||||
|
HorizontalSharpness = "1.500000"
|
||||||
|
HorizontalAttack = "0.600000"
|
||||||
|
VerticalAttack = "0.650000"
|
||||||
|
RedConvergence = "-1.800000"
|
||||||
|
GreenConvergence = "-0.600000"
|
||||||
|
BlueConvergence = "0.000000"
|
|
@ -31,49 +31,13 @@ We need 8K to really start to get round the right ballpark. We need 9600 resolut
|
||||||
TODO: Make the horizontal scanlines more blurry in the vertical direction - we're working in HDR space at this point so its trickier than normal.
|
TODO: Make the horizontal scanlines more blurry in the vertical direction - we're working in HDR space at this point so its trickier than normal.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
shaders = "4"
|
shaders = "1"
|
||||||
feedback_pass = "0"
|
feedback_pass = "0"
|
||||||
|
|
||||||
shader0 = "../stock.slang"
|
shader0 = "shaders/crt-sony-pvm-4k-hdr.slang"
|
||||||
filter_linear0 = false
|
filter_linear0 = "false"
|
||||||
wrap_mode0 = "clamp_to_border"
|
wrap_mode0 = "clamp_to_border"
|
||||||
mipmap_input0 = "false"
|
mipmap_input0 = "false"
|
||||||
alias0 = "StockPass"
|
alias0 = ""
|
||||||
float_framebuffer0 = "false"
|
float_framebuffer0 = "false"
|
||||||
srgb_framebuffer0 = "false"
|
srgb_framebuffer0 = "false"
|
||||||
scale_type_x0 = "source"
|
|
||||||
scale_x0 = "1.000000"
|
|
||||||
scale_type_y0 = "source"
|
|
||||||
scale_y0 = "1.000000"
|
|
||||||
|
|
||||||
shader1 = "shaders/inverse_tonemap.slang"
|
|
||||||
filter_linear1 = "false"
|
|
||||||
wrap_mode1 = "clamp_to_border"
|
|
||||||
mipmap_input1 = "false"
|
|
||||||
alias1 = ""
|
|
||||||
float_framebuffer1 = "false"
|
|
||||||
srgb_framebuffer1 = "false"
|
|
||||||
scale_type_x1 = "source"
|
|
||||||
scale_x1 = "1.000000"
|
|
||||||
scale_type_y1 = "source"
|
|
||||||
scale_y1 = "1.000000"
|
|
||||||
|
|
||||||
shader2 = "shaders/hdr10.slang"
|
|
||||||
filter_linear2 = "false"
|
|
||||||
wrap_mode2 = "clamp_to_border"
|
|
||||||
mipmap_input2 = "false"
|
|
||||||
alias2 = ""
|
|
||||||
float_framebuffer2 = "false"
|
|
||||||
srgb_framebuffer2 = "false"
|
|
||||||
scale_type_x2 = "source"
|
|
||||||
scale_x2 = "1.000000"
|
|
||||||
scale_type_y2 = "source"
|
|
||||||
scale_y2 = "1.000000"
|
|
||||||
|
|
||||||
shader3 = "shaders/crt-sony-pvm-4k-hdr.slang"
|
|
||||||
filter_linear3 = "false"
|
|
||||||
wrap_mode3 = "clamp_to_border"
|
|
||||||
mipmap_input3 = "false"
|
|
||||||
alias3 = ""
|
|
||||||
float_framebuffer3 = "false"
|
|
||||||
srgb_framebuffer3 = "false"
|
|
||||||
|
|
|
@ -21,20 +21,43 @@ Dont use this shader directly - use the crt\crt-sony-pvm-4k-hdr.slangp to have t
|
||||||
|
|
||||||
#pragma format A2B10G10R10_UNORM_PACK32
|
#pragma format A2B10G10R10_UNORM_PACK32
|
||||||
|
|
||||||
|
#include "inverse_tonemap.h"
|
||||||
|
#include "hdr10.h"
|
||||||
|
|
||||||
layout(push_constant) uniform Push
|
layout(push_constant) uniform Push
|
||||||
{
|
{
|
||||||
vec4 SourceSize;
|
vec4 SourceSize;
|
||||||
vec4 OriginalSize;
|
vec4 OriginalSize;
|
||||||
vec4 OutputSize;
|
vec4 OutputSize;
|
||||||
uint FrameCount;
|
uint FrameCount;
|
||||||
float ScanlineWidth;
|
float PaperWhiteNits;
|
||||||
|
float MaxNits;
|
||||||
|
float ExpandGamut;
|
||||||
|
float CRTGamma;
|
||||||
|
float ScanlineMin;
|
||||||
|
float ScanlineMax;
|
||||||
float ResolutionPattern;
|
float ResolutionPattern;
|
||||||
float Sharpness;
|
float HorizontalSharpness;
|
||||||
|
float HorizontalAttack;
|
||||||
|
float VerticalAttack;
|
||||||
|
float RedConvergence;
|
||||||
|
float GreenConvergence;
|
||||||
|
float BlueConvergence;
|
||||||
} params;
|
} params;
|
||||||
|
|
||||||
#pragma parameter ScanlineWidth "Scanline Width" 0.95 0.0 1.0 0.01
|
#pragma parameter PaperWhiteNits "Paper White Luminance" 650.0 0.0 10000.0 10.0
|
||||||
#pragma parameter ResolutionPattern "Resolution Pattern" 2.0 0.0 8.0 1.0
|
#pragma parameter MaxNits "Peak Luminance" 700.0 0.0 10000.0 10.0
|
||||||
#pragma parameter Sharpness "Sharpness" 2.4 0.0 5.0 0.1
|
#pragma parameter ExpandGamut "ExpandGamut" 1.0 0.0 1.0 1.0
|
||||||
|
#pragma parameter CRTGamma "CRTGamma" 1.8 0.0 5.0 0.05
|
||||||
|
#pragma parameter ScanlineMin "Scanline Min" 0.55 0.0 2.0 0.01
|
||||||
|
#pragma parameter ScanlineMax "Scanline Max" 0.90 0.0 2.0 0.01
|
||||||
|
#pragma parameter ResolutionPattern "Resolution Pattern" 3.0 0.0 8.0 1.0
|
||||||
|
#pragma parameter HorizontalSharpness "Horizontal Sharpness" 1.5 0.0 5.0 0.1
|
||||||
|
#pragma parameter HorizontalAttack "Horizontal Attack" 0.60 0.0 1.0 0.05
|
||||||
|
#pragma parameter VerticalAttack "Vertical Attack" 0.65 0.0 1.0 0.05
|
||||||
|
#pragma parameter RedConvergence "Red Convergence" 0.0 -10.0 10.0 0.05
|
||||||
|
#pragma parameter GreenConvergence "Green Convergence" 0.0 -10.0 10.0 0.05
|
||||||
|
#pragma parameter BlueConvergence "Blue Convergence" 0.0 -10.0 10.0 0.05
|
||||||
|
|
||||||
layout(std140, set = 0, binding = 0) uniform UBO
|
layout(std140, set = 0, binding = 0) uniform UBO
|
||||||
{
|
{
|
||||||
|
@ -45,18 +68,28 @@ layout(std140, set = 0, binding = 0) uniform UBO
|
||||||
layout(location = 0) in vec4 Position;
|
layout(location = 0) in vec4 Position;
|
||||||
layout(location = 1) in vec2 TexCoord;
|
layout(location = 1) in vec2 TexCoord;
|
||||||
layout(location = 0) out vec2 vTexCoord;
|
layout(location = 0) out vec2 vTexCoord;
|
||||||
|
layout(location = 1) out float ScanlineSize;
|
||||||
|
layout(location = 2) out float InverseScanlineSize;
|
||||||
|
layout(location = 3) out vec3 Convergence;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
gl_Position = global.MVP * Position;
|
gl_Position = global.MVP * Position;
|
||||||
vTexCoord = TexCoord * vec2(1.00001); // To resolve rounding issues when sampling
|
vTexCoord = TexCoord * vec2(1.00001); // To resolve rounding issues when sampling
|
||||||
|
|
||||||
|
ScanlineSize = params.OutputSize.y / params.SourceSize.y;
|
||||||
|
InverseScanlineSize = 1.0f / ScanlineSize;
|
||||||
|
|
||||||
|
Convergence = vec3(params.RedConvergence, params.GreenConvergence, params.BlueConvergence);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma stage fragment
|
#pragma stage fragment
|
||||||
layout(location = 0) in vec2 vTexCoord;
|
layout(location = 0) in vec2 vTexCoord;
|
||||||
|
layout(location = 1) in float ScanlineSize;
|
||||||
|
layout(location = 2) in float InverseScanlineSize;
|
||||||
|
layout(location = 3) in vec3 Convergence;
|
||||||
layout(location = 0) out vec4 FragColor;
|
layout(location = 0) out vec4 FragColor;
|
||||||
layout(set = 0, binding = 2) uniform sampler2D Source;
|
layout(set = 0, binding = 2) uniform sampler2D Source;
|
||||||
layout(set = 0, binding = 3) uniform sampler2D StockPass;
|
|
||||||
|
|
||||||
float ModInteger(float a, float b)
|
float ModInteger(float a, float b)
|
||||||
{
|
{
|
||||||
|
@ -64,12 +97,11 @@ float ModInteger(float a, float b)
|
||||||
return floor(m + 0.5);
|
return floor(m + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define kPi 3.1415926536
|
#define kPi 3.1415926536f
|
||||||
#define kEuler 2.718281828459
|
#define kEuler 2.718281828459f
|
||||||
#define kMax 1.0
|
#define kMax 1.0f
|
||||||
|
|
||||||
#define kGuassianMin vec3(1.1)
|
#define kLumaRatio 0.5f
|
||||||
#define kGuassianMax vec3(3.0)
|
|
||||||
|
|
||||||
#define kRed vec3(1.0, 0.0, 0.0)
|
#define kRed vec3(1.0, 0.0, 0.0)
|
||||||
#define kGreen vec3(0.0, 1.0, 0.0)
|
#define kGreen vec3(0.0, 1.0, 0.0)
|
||||||
|
@ -80,50 +112,109 @@ float ModInteger(float a, float b)
|
||||||
#define kBlack vec3(0.0, 0.0, 0.0)
|
#define kBlack vec3(0.0, 0.0, 0.0)
|
||||||
#define kWhite vec3(1.0, 1.0, 1.0)
|
#define kWhite vec3(1.0, 1.0, 1.0)
|
||||||
|
|
||||||
float Ramp(const float gaussian, float colour)
|
const float PatternSize[10] = {2.0f, 3.0f, 4.0f, 4.0f, 5.0f, 5.0f, 6.0f, 7.0f, 9.0f, 12.0f};
|
||||||
|
|
||||||
|
const mat4 kCubicBezier = mat4( 1.0f, 0.0f, 0.0f, 0.0f,
|
||||||
|
-3.0f, 3.0f, 0.0f, 0.0f,
|
||||||
|
3.0f, -6.0f, 3.0f, 0.0f,
|
||||||
|
-1.0f, 3.0f, -3.0f, 1.0f );
|
||||||
|
|
||||||
|
const vec4 kFallOffControlPoints = vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
const vec4 kAttackControlPoints = vec4(0.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
//const vec4 kScanlineControlPoints = vec4(1.0f, 1.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
vec4 HorizControlPoints(const bool falloff)
|
||||||
{
|
{
|
||||||
return clamp(gaussian * colour, 0.0, 1.0);
|
return falloff ? kFallOffControlPoints + vec4(0.0f, 0.0f, params.HorizontalAttack, 0.0f) : kAttackControlPoints - vec4(0.0f, params.HorizontalAttack, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 Ramp3(const vec3 gaussian, vec3 colour)
|
float Bezier(const float t0, const vec4 control_points)
|
||||||
{
|
{
|
||||||
return clamp(gaussian * colour, vec3(0.0), vec3(1.0));
|
vec4 t = vec4(1.0, t0, t0*t0, t0*t0*t0);
|
||||||
|
return dot(t, control_points * kCubicBezier);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ToLinear1(float channel)
|
||||||
|
{
|
||||||
|
return (channel > 0.04045f) ? pow(abs(channel) * (1.0f / 1.055f) + (0.055f / 1.055f), params.CRTGamma) : channel * (1.0f / 12.92f);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ToLinear(vec3 colour)
|
||||||
|
{
|
||||||
|
return vec3(ToLinear1(colour.r), ToLinear1(colour.g), ToLinear1(colour.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 Ramp(const vec3 luminance, const vec3 colour)
|
||||||
|
{
|
||||||
|
return clamp(luminance * colour, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 ScanlineColour(const float current_position, const float current_center, const float source_tex_coord_x, const float narrowed_source_pixel_offset, inout float next_prev )
|
||||||
|
{
|
||||||
|
const float current_source_position_y = (vTexCoord.y * params.SourceSize.y) - next_prev;
|
||||||
|
const float current_source_center_y = floor(current_source_position_y) + 0.5f;
|
||||||
|
|
||||||
|
const float source_tex_coord_y = current_source_center_y / params.SourceSize.y;
|
||||||
|
|
||||||
|
const vec2 source_tex_coord_0 = vec2(source_tex_coord_x, source_tex_coord_y);
|
||||||
|
const vec2 source_tex_coord_1 = vec2(source_tex_coord_x + (1.0f / params.SourceSize.x), source_tex_coord_y);
|
||||||
|
|
||||||
|
const float scanline_position = current_source_center_y * ScanlineSize;
|
||||||
|
const vec3 scanline_delta = vec3(scanline_position) - (vec3(current_center) - Convergence);
|
||||||
|
const vec3 scanline_distance = abs(scanline_delta * InverseScanlineSize * 2.0f);
|
||||||
|
|
||||||
|
next_prev = scanline_delta.x > 0.0f ? 1.0f : -1.0f;
|
||||||
|
|
||||||
|
const vec3 sdr_colour_0 = ToLinear(texture(Source, source_tex_coord_0).xyz);
|
||||||
|
const vec3 sdr_colour_1 = ToLinear(texture(Source, source_tex_coord_1).xyz);
|
||||||
|
|
||||||
|
const vec3 hdr_colour_0 = InverseTonemap(sdr_colour_0, params.MaxNits, params.PaperWhiteNits, kLumaRatio);
|
||||||
|
const vec3 hdr_colour_1 = InverseTonemap(sdr_colour_1, params.MaxNits, params.PaperWhiteNits, kLumaRatio);
|
||||||
|
|
||||||
|
/* Horizontal interpolation between pixels */
|
||||||
|
|
||||||
|
const vec3 horiz_interp = vec3(Bezier(narrowed_source_pixel_offset, HorizControlPoints(sdr_colour_0.x > sdr_colour_1.x)),
|
||||||
|
Bezier(narrowed_source_pixel_offset, HorizControlPoints(sdr_colour_0.y > sdr_colour_1.y)),
|
||||||
|
Bezier(narrowed_source_pixel_offset, HorizControlPoints(sdr_colour_0.z > sdr_colour_1.z)));
|
||||||
|
|
||||||
|
const vec3 hdr_colour = mix(hdr_colour_0, hdr_colour_1, horiz_interp);
|
||||||
|
const vec3 sdr_colour = mix(sdr_colour_0, sdr_colour_1, horiz_interp);
|
||||||
|
|
||||||
|
//const vec3 scanline_width = mix(vec3(0.2f), vec3(0.4f), sdr_colour) * params.ScanlineWidth;
|
||||||
|
//const vec3 luminance = pow(vec3(kEuler), vec3(-0.5f) * pow(scanline_distance / scanline_width, vec3(2.0f))); /* Gaussian distribution */
|
||||||
|
|
||||||
|
const vec3 narrowed_scanline_distance = clamp(scanline_distance / (sdr_colour * vec3(params.ScanlineMax - params.ScanlineMin) + vec3(params.ScanlineMin)), vec3(0.0f), vec3(1.0f));
|
||||||
|
|
||||||
|
const vec4 scanline_control_points = vec4(1.0f, 1.0f, params.VerticalAttack, 0.0f);
|
||||||
|
|
||||||
|
const vec3 luminance = vec3(Bezier(narrowed_scanline_distance.x, scanline_control_points),
|
||||||
|
Bezier(narrowed_scanline_distance.y, scanline_control_points),
|
||||||
|
Bezier(narrowed_scanline_distance.z, scanline_control_points));
|
||||||
|
|
||||||
|
return luminance * hdr_colour;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
float ScanlineSize = params.OutputSize.y / params.SourceSize.y;
|
uint resolution_pattern = uint(params.ResolutionPattern);
|
||||||
|
|
||||||
const vec2 InPixels = (vTexCoord * params.OutputSize.xy);
|
const vec2 current_position = vTexCoord * params.OutputSize.xy;
|
||||||
|
const float current_center = floor(current_position.y) + 0.5f;
|
||||||
|
|
||||||
const float ScanlinePosition = (floor(vTexCoord.y * params.SourceSize.y) * ScanlineSize) + (ScanlineSize * 0.5);
|
const float current_source_position_x = (vTexCoord.x * params.SourceSize.x) /* / PatternSize[resolution_pattern] */;
|
||||||
|
const float current_source_center_x = floor(current_source_position_x) + 0.5f;
|
||||||
float ScanlineDistance = ScanlinePosition - (floor(InPixels.y) + 0.5);
|
|
||||||
|
|
||||||
ScanlineDistance /= ScanlineSize * params.ScanlineWidth;
|
|
||||||
ScanlineDistance = clamp(abs(ScanlineDistance * 2.0), 0.0, 1.0);
|
|
||||||
|
|
||||||
const float Gaussian = pow(kEuler, -0.5 * pow(ScanlineDistance/0.3, 2.0)); /* Gaussian distribution */
|
|
||||||
|
|
||||||
float HorizInterp = (vTexCoord.x * params.SourceSize.x) - (floor(vTexCoord.x * params.SourceSize.x));
|
|
||||||
HorizInterp = clamp(((HorizInterp - 0.5) * params.Sharpness) + 0.5, 0.0f, 1.0);
|
|
||||||
|
|
||||||
const vec2 SourceTexCoord0 = vec2(vTexCoord.x, ScanlinePosition / params.OutputSize.y);
|
|
||||||
|
|
||||||
vec3 HDRColour0 = texture(Source, SourceTexCoord0).xyz;
|
|
||||||
vec3 SDRColour0 = texture(StockPass, SourceTexCoord0).xyz;
|
|
||||||
|
|
||||||
const vec2 SourceTexCoord1 = vec2(vTexCoord.x + (1.0 / params.SourceSize.x), ScanlinePosition / params.OutputSize.y);
|
|
||||||
|
|
||||||
const vec3 HDRColour1 = texture(Source, SourceTexCoord1).xyz;
|
|
||||||
const vec3 SDRColour1 = texture(StockPass, SourceTexCoord1).xyz;
|
|
||||||
|
|
||||||
const vec3 HDRColour = mix(HDRColour0, HDRColour1, vec3(HorizInterp));
|
const float source_tex_coord_x = (current_source_center_x /* * PatternSize[resolution_pattern]*/) / params.SourceSize.x;
|
||||||
const vec3 SDRColour = mix(SDRColour0, SDRColour1, vec3(HorizInterp));
|
|
||||||
|
|
||||||
vec3 Luminance = Ramp3(vec3(Gaussian), (SDRColour * kGuassianMax) + kGuassianMin);
|
|
||||||
|
|
||||||
vec3 OutputColour;
|
const float source_pixel_offset = current_source_position_x - floor(current_source_position_x);
|
||||||
|
const float narrowed_source_pixel_offset = clamp(((source_pixel_offset - 0.5f) * params.HorizontalSharpness) + 0.5f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
float next_prev = 0.0f;
|
||||||
|
|
||||||
|
const vec3 scanline_colour0 = ScanlineColour(current_position.y, current_center, source_tex_coord_x, narrowed_source_pixel_offset, next_prev);
|
||||||
|
const vec3 scanline_colour1 = ScanlineColour(current_position.y, current_center, source_tex_coord_x, narrowed_source_pixel_offset, next_prev);
|
||||||
|
|
||||||
|
vec3 scanline_colour = scanline_colour0 + scanline_colour1;
|
||||||
|
|
||||||
/* Various resolution patterns - remember your LCD 4K screen will likely be 16:9 whereas
|
/* Various resolution patterns - remember your LCD 4K screen will likely be 16:9 whereas
|
||||||
the CRT TV will likely be 4:3 and so higher TVL values will be required on your 16:9 screen
|
the CRT TV will likely be 4:3 and so higher TVL values will be required on your 16:9 screen
|
||||||
|
@ -131,107 +222,108 @@ void main()
|
||||||
Pattern 1's 960TVL at 16:9 is about right for a 800TVL at 4:3.
|
Pattern 1's 960TVL at 16:9 is about right for a 800TVL at 4:3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
uint ResolutionPattern = uint(params.ResolutionPattern);
|
switch(resolution_pattern)
|
||||||
|
|
||||||
switch(ResolutionPattern)
|
|
||||||
{
|
{
|
||||||
case 0: /* 2 pattern - 1440TVL (16:9) horiz resolution (too high?) on a 4K screen */
|
case 0: /* 2 pattern - 1440TVL (16:9) horiz resolution (too high?) on a 4K screen */
|
||||||
{
|
{
|
||||||
const uint PatternSize = 2;
|
const vec3 mask[2] = vec3[]( kYellow, kCyan );
|
||||||
const vec3 Mask[2] = vec3[]( kYellow, kCyan );
|
|
||||||
|
|
||||||
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[0]));
|
||||||
OutputColour = Luminance * HDRColour * Mask[PatternX];
|
scanline_colour *= mask[pattern_x];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1: /* 3 pattern - 960TVL (16:9) horiz resolution on a 4K screen */
|
case 1: /* 3 pattern - 960TVL (16:9) horiz resolution on a 4K screen */
|
||||||
{
|
{
|
||||||
const uint PatternSize = 3;
|
const vec3 mask[3] = vec3[]( kRed, kGreen, kBlue );
|
||||||
const vec3 Mask[3] = vec3[]( kRed, kGreen, kBlue );
|
|
||||||
|
|
||||||
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[1]));
|
||||||
OutputColour = Luminance * HDRColour * Mask[PatternX];
|
scanline_colour *= mask[pattern_x];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: /* 4 pattern - 720TVL (16:9) horiz resolution on a 4K screen */
|
case 2: /* 4 pattern - 720TVL (16:9) horiz resolution on a 4K screen */
|
||||||
{
|
{
|
||||||
const uint PatternSize = 4;
|
const vec3 mask[4] = vec3[]( kRed, kYellow, kCyan, kBlue );
|
||||||
const vec3 Mask[4] = vec3[]( kRed, kYellow, kCyan, kBlue );
|
|
||||||
|
|
||||||
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[2]));
|
||||||
OutputColour = Luminance * HDRColour * Mask[PatternX];
|
scanline_colour *= mask[pattern_x];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 3: /* 5 BRG pattern - 576TVL (16:9) horiz resolution on a 4K screen */
|
case 3: /* 4 pattern - 720TVL (16:9) horiz resolution on a 4K screen */
|
||||||
{
|
{
|
||||||
const uint PatternSize = 5;
|
const vec3 mask[4] = vec3[]( kRed, kGreen, kBlue, kBlack );
|
||||||
const vec3 Mask[5] = vec3[]( kRed, kMagenta, kBlue, kGreen, kGreen );
|
|
||||||
|
|
||||||
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[3]));
|
||||||
OutputColour = Luminance * HDRColour * Mask[PatternX];
|
scanline_colour *= mask[pattern_x];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 4: /* 5 pattern - 576TVL (16:9) horiz resolution on a 4K screen */
|
case 4: /* 5 BRG pattern - 576TVL (16:9) horiz resolution on a 4K screen */
|
||||||
{
|
{
|
||||||
const uint PatternSize = 5;
|
const vec3 mask[5] = vec3[]( kRed, kMagenta, kBlue, kGreen, kGreen );
|
||||||
const vec3 Mask[5] = vec3[]( kRed, kYellow, kGreen, kCyan, kBlue );
|
|
||||||
|
|
||||||
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[4]));
|
||||||
OutputColour = Luminance * HDRColour * Mask[PatternX];
|
scanline_colour *= mask[pattern_x];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 5: /* 6 pattern - 480TVL (16:9) horiz resolution on a 4K screen */
|
case 5: /* 5 pattern - 576TVL (16:9) horiz resolution on a 4K screen */
|
||||||
{
|
{
|
||||||
const uint PatternSize = 6;
|
const vec3 mask[5] = vec3[]( kRed, kYellow, kGreen, kCyan, kBlue );
|
||||||
const vec3 Mask[6] = vec3[]( kRed, kRed, kGreen, kGreen, kBlue, kBlue );
|
|
||||||
|
|
||||||
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[5]));
|
||||||
OutputColour = Luminance * HDRColour * Mask[PatternX];
|
scanline_colour *= mask[pattern_x];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 6: /* 7 pattern - 410TVL (16:9) horiz resolution on a 4K screen */
|
case 6: /* 6 pattern - 480TVL (16:9) horiz resolution on a 4K screen */
|
||||||
{
|
{
|
||||||
const uint PatternSize = 7;
|
const vec3 mask[6] = vec3[]( kRed, kRed, kGreen, kGreen, kBlue, kBlue );
|
||||||
const vec3 Mask[7] = vec3[]( kRed, kRed, kYellow, kGreen, kCyan, kBlue, kBlue );
|
|
||||||
|
|
||||||
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[6]));
|
||||||
OutputColour = Luminance * HDRColour * Mask[PatternX];
|
scanline_colour *= mask[pattern_x];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 7: /* 9 pattern - 640TVL (16:9) horiz resolution on a *8K* screen */
|
case 7: /* 7 pattern - 410TVL (16:9) horiz resolution on a 4K screen */
|
||||||
{
|
{
|
||||||
const uint PatternSize = 9;
|
const vec3 mask[7] = vec3[]( kRed, kRed, kYellow, kGreen, kCyan, kBlue, kBlue );
|
||||||
const vec3 Mask[9] = vec3[]( kBlack, kRed, kRed, kBlack, kGreen, kGreen, kBlack, kBlue, kBlue );
|
|
||||||
|
|
||||||
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[7]));
|
||||||
OutputColour = Luminance * HDRColour * Mask[PatternX];
|
scanline_colour *= mask[pattern_x];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 8: /* 12 pattern - 480TVL (16:9) horiz resolution on a *8K* screen */
|
case 8: /* 9 pattern - 640TVL (16:9) horiz resolution on a *8K* screen */
|
||||||
{
|
{
|
||||||
const uint PatternSize = 12;
|
const vec3 mask[9] = vec3[]( kBlack, kRed, kRed, kBlack, kGreen, kGreen, kBlack, kBlue, kBlue );
|
||||||
const vec3 Mask[12] = vec3[]( kBlack, kRed, kRed, kRed, kBlack, kGreen, kGreen, kGreen, kBlack, kBlue, kBlue, kBlue );
|
|
||||||
|
|
||||||
uint PatternX = uint(ModInteger(floor(InPixels.x), PatternSize));
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[8]));
|
||||||
OutputColour = Luminance * HDRColour * Mask[PatternX];
|
scanline_colour *= mask[pattern_x];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 9: /* 12 pattern - 480TVL (16:9) horiz resolution on a *8K* screen */
|
||||||
|
{
|
||||||
|
const vec3 mask[12] = vec3[]( kBlack, kRed, kRed, kRed, kBlack, kGreen, kGreen, kGreen, kBlack, kBlue, kBlue, kBlue );
|
||||||
|
|
||||||
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[9]));
|
||||||
|
scanline_colour *= mask[pattern_x];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
OutputColour = vec3(0.0);
|
scanline_colour = vec3(0.0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FragColor = vec4(OutputColour, 1.0);
|
const vec3 hdr10 = Hdr10(scanline_colour, params.PaperWhiteNits, params.ExpandGamut);
|
||||||
|
|
||||||
|
//FragColor = vec4(scanline_colour, 1.0);
|
||||||
|
FragColor = vec4(hdr10, 1.0);
|
||||||
}
|
}
|
||||||
|
|
37
hdr/shaders/hdr10.h
Normal file
37
hdr/shaders/hdr10.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
#define kMaxNitsFor2084 10000.0f
|
||||||
|
|
||||||
|
const mat3 k709to2020 = mat3 (
|
||||||
|
0.6274040f, 0.3292820f, 0.0433136f,
|
||||||
|
0.0690970f, 0.9195400f, 0.0113612f,
|
||||||
|
0.0163916f, 0.0880132f, 0.8955950f);
|
||||||
|
|
||||||
|
/* START Converted from (Copyright (c) Microsoft Corporation - Licensed under the MIT License.) https://github.com/microsoft/Xbox-ATG-Samples/tree/master/Kits/ATGTK/HDR */
|
||||||
|
const mat3 kExpanded709to2020 = mat3 (
|
||||||
|
0.6274040f, 0.3292820f, 0.0433136f,
|
||||||
|
0.0457456, 0.941777, 0.0124772,
|
||||||
|
-0.00121055, 0.0176041, 0.983607);
|
||||||
|
|
||||||
|
vec3 LinearToST2084(vec3 normalizedLinearValue)
|
||||||
|
{
|
||||||
|
vec3 ST2084 = pow((0.8359375f + 18.8515625f * pow(abs(normalizedLinearValue), vec3(0.1593017578f))) / (1.0f + 18.6875f * pow(abs(normalizedLinearValue), vec3(0.1593017578f))), vec3(78.84375f));
|
||||||
|
return ST2084; /* Don't clamp between [0..1], so we can still perform operations on scene values higher than 10,000 nits */
|
||||||
|
}
|
||||||
|
/* END Converted from (Copyright (c) Microsoft Corporation - Licensed under the MIT License.) https://github.com/microsoft/Xbox-ATG-Samples/tree/master/Kits/ATGTK/HDR */
|
||||||
|
|
||||||
|
/* Convert into HDR10 */
|
||||||
|
vec3 Hdr10(vec3 hdr_linear, float paper_white_nits, float expand_gamut)
|
||||||
|
{
|
||||||
|
vec3 rec2020 = hdr_linear * k709to2020;
|
||||||
|
|
||||||
|
if(expand_gamut > 0.0f)
|
||||||
|
{
|
||||||
|
rec2020 = hdr_linear * kExpanded709to2020;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 linearColour = rec2020 * (paper_white_nits / kMaxNitsFor2084);
|
||||||
|
vec3 hdr10 = LinearToST2084(linearColour);
|
||||||
|
|
||||||
|
return hdr10;
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ Originally part of the crt\crt-sony-pvm-4k-hdr.slangp but can be used for any sh
|
||||||
|
|
||||||
#pragma format A2B10G10R10_UNORM_PACK32
|
#pragma format A2B10G10R10_UNORM_PACK32
|
||||||
|
|
||||||
|
#include "hdr10.h"
|
||||||
|
|
||||||
layout(push_constant) uniform Push
|
layout(push_constant) uniform Push
|
||||||
{
|
{
|
||||||
vec4 SourceSize;
|
vec4 SourceSize;
|
||||||
|
@ -44,45 +46,9 @@ layout(location = 0) in vec2 vTexCoord;
|
||||||
layout(location = 0) out vec4 FragColor;
|
layout(location = 0) out vec4 FragColor;
|
||||||
layout(set = 0, binding = 2) uniform sampler2D Source;
|
layout(set = 0, binding = 2) uniform sampler2D Source;
|
||||||
|
|
||||||
#define kMaxNitsFor2084 10000.0f
|
|
||||||
|
|
||||||
const mat3 k709to2020 = mat3 (
|
|
||||||
0.6274040f, 0.3292820f, 0.0433136f,
|
|
||||||
0.0690970f, 0.9195400f, 0.0113612f,
|
|
||||||
0.0163916f, 0.0880132f, 0.8955950f);
|
|
||||||
|
|
||||||
/* START Converted from (Copyright (c) Microsoft Corporation - Licensed under the MIT License.) https://github.com/microsoft/Xbox-ATG-Samples/tree/master/Kits/ATGTK/HDR */
|
|
||||||
const mat3 kExpanded709to2020 = mat3 (
|
|
||||||
0.6274040f, 0.3292820f, 0.0433136f,
|
|
||||||
0.0457456, 0.941777, 0.0124772,
|
|
||||||
-0.00121055, 0.0176041, 0.983607);
|
|
||||||
|
|
||||||
vec3 LinearToST2084(vec3 normalizedLinearValue)
|
|
||||||
{
|
|
||||||
vec3 ST2084 = pow((0.8359375f + 18.8515625f * pow(abs(normalizedLinearValue), vec3(0.1593017578f))) / (1.0f + 18.6875f * pow(abs(normalizedLinearValue), vec3(0.1593017578f))), vec3(78.84375f));
|
|
||||||
return ST2084; /* Don't clamp between [0..1], so we can still perform operations on scene values higher than 10,000 nits */
|
|
||||||
}
|
|
||||||
/* END Converted from (Copyright (c) Microsoft Corporation - Licensed under the MIT License.) https://github.com/microsoft/Xbox-ATG-Samples/tree/master/Kits/ATGTK/HDR */
|
|
||||||
|
|
||||||
vec3 Hdr10(vec3 hdr)
|
|
||||||
{
|
|
||||||
/* Now convert into HDR10 */
|
|
||||||
vec3 rec2020 = hdr * k709to2020;
|
|
||||||
|
|
||||||
if(params.ExpandGamut > 0.0f)
|
|
||||||
{
|
|
||||||
rec2020 = hdr * kExpanded709to2020;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 linearColour = rec2020 * (params.PaperWhiteNits / kMaxNitsFor2084);
|
|
||||||
vec3 hdr10 = LinearToST2084(linearColour);
|
|
||||||
|
|
||||||
return hdr10;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 hdr = texture(Source, vTexCoord);
|
vec4 hdr_linear = texture(Source, vTexCoord);
|
||||||
FragColor = vec4(Hdr10(hdr.rgb), hdr.a);
|
FragColor = vec4(Hdr10(hdr_linear.rgb, params.PaperWhiteNits, params.ExpandGamut), hdr_linear.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
30
hdr/shaders/inverse_tonemap.h
Normal file
30
hdr/shaders/inverse_tonemap.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
#define kMaxNitsFor2084 10000.0f
|
||||||
|
#define kEpsilon 0.0001f
|
||||||
|
|
||||||
|
vec3 InverseTonemap(vec3 sdr_linear, float max_nits, float paper_white_nits, float luma_ratio)
|
||||||
|
{
|
||||||
|
float luma = dot(sdr_linear, vec3(0.2126, 0.7152, 0.0722)); /* Rec BT.709 luma coefficients - https://en.wikipedia.org/wiki/Luma_(video) */
|
||||||
|
|
||||||
|
/* Inverse reinhard tonemap */
|
||||||
|
float max_value = (max_nits / paper_white_nits) + kEpsilon;
|
||||||
|
float elbow = max_value / (max_value - 1.0f);
|
||||||
|
float offset = 1.0f - ((0.5f * elbow) / (elbow - 0.5f));
|
||||||
|
|
||||||
|
float hdr_luma_inv_tonemap = offset + ((luma * elbow) / (elbow - luma));
|
||||||
|
float sdr_luma_inv_tonemap = luma / ((1.0f + kEpsilon) - luma); /* Convert the srd < 0.5 to 0.0 -> 1.0 range */
|
||||||
|
|
||||||
|
float luma_inv_tonemap = (luma > 0.5f) ? hdr_luma_inv_tonemap : sdr_luma_inv_tonemap;
|
||||||
|
vec3 per_luma = sdr_linear / (luma + kEpsilon) * luma_inv_tonemap;
|
||||||
|
|
||||||
|
vec3 hdr_inv_tonemap = offset + ((sdr_linear * elbow) / (elbow - sdr_linear));
|
||||||
|
vec3 sdr_inv_tonemap = sdr_linear / ((1.0f + kEpsilon) - sdr_linear); /* Convert the srd < 0.5 to 0.0 -> 1.0 range */
|
||||||
|
|
||||||
|
vec3 per_channel = vec3(sdr_linear.x > 0.5f ? hdr_inv_tonemap.x : sdr_inv_tonemap.x,
|
||||||
|
sdr_linear.y > 0.5f ? hdr_inv_tonemap.y : sdr_inv_tonemap.y,
|
||||||
|
sdr_linear.z > 0.5f ? hdr_inv_tonemap.z : sdr_inv_tonemap.z);
|
||||||
|
|
||||||
|
vec3 hdr = mix(per_luma, per_channel, vec3(luma_ratio));
|
||||||
|
|
||||||
|
return hdr;
|
||||||
|
}
|
|
@ -10,6 +10,8 @@ Originally part of the crt\crt-sony-pvm-4k-hdr.slangp but can be used for any sh
|
||||||
|
|
||||||
#pragma format R16G16B16A16_SFLOAT
|
#pragma format R16G16B16A16_SFLOAT
|
||||||
|
|
||||||
|
#include "inverse_tonemap.h"
|
||||||
|
|
||||||
layout(push_constant) uniform Push
|
layout(push_constant) uniform Push
|
||||||
{
|
{
|
||||||
vec4 SourceSize;
|
vec4 SourceSize;
|
||||||
|
@ -19,11 +21,15 @@ layout(push_constant) uniform Push
|
||||||
float Contrast;
|
float Contrast;
|
||||||
float PaperWhiteNits;
|
float PaperWhiteNits;
|
||||||
float MaxNits;
|
float MaxNits;
|
||||||
|
float Saturation;
|
||||||
|
float DisplayGamma;
|
||||||
} params;
|
} params;
|
||||||
|
|
||||||
#pragma parameter Contrast "Contrast" 3.75 0.0 10.0 0.01
|
#pragma parameter Contrast "Contrast" 3.75 0.0 10.0 0.05
|
||||||
#pragma parameter PaperWhiteNits "Paper White Luminance" 450.0 0.0 10000.0 10.0
|
#pragma parameter PaperWhiteNits "Paper White Luminance" 450.0 0.0 10000.0 10.0
|
||||||
#pragma parameter MaxNits "Peak Luminance" 700.0 0.0 10000.0 10.0
|
#pragma parameter MaxNits "Peak Luminance" 700.0 0.0 10000.0 10.0
|
||||||
|
#pragma parameter Saturation "Saturation" 0.25 0.0 1.0 0.01
|
||||||
|
#pragma parameter DisplayGamma "Display Gamma" 2.2 0.0 5.0 0.1
|
||||||
|
|
||||||
layout(std140, set = 0, binding = 0) uniform UBO
|
layout(std140, set = 0, binding = 0) uniform UBO
|
||||||
{
|
{
|
||||||
|
@ -46,42 +52,12 @@ layout(location = 0) in vec2 vTexCoord;
|
||||||
layout(location = 0) out vec4 FragColor;
|
layout(location = 0) out vec4 FragColor;
|
||||||
layout(set = 0, binding = 2) uniform sampler2D Source;
|
layout(set = 0, binding = 2) uniform sampler2D Source;
|
||||||
|
|
||||||
#define kMaxNitsFor2084 10000.0f
|
|
||||||
#define kEpsilon 0.0001f
|
|
||||||
#define kLumaChannelRatio 0.25f
|
|
||||||
|
|
||||||
vec3 InverseTonemap(vec3 sdr)
|
|
||||||
{
|
|
||||||
sdr = pow(abs(sdr), vec3(params.Contrast / 2.2f)); /* Display Gamma - needs to be determined by calibration screen */
|
|
||||||
|
|
||||||
float luma = dot(sdr, vec3(0.2126, 0.7152, 0.0722)); /* Rec BT.709 luma coefficients - https://en.wikipedia.org/wiki/Luma_(video) */
|
|
||||||
|
|
||||||
/* Inverse reinhard tonemap */
|
|
||||||
float maxValue = (params.MaxNits / params.PaperWhiteNits) + kEpsilon;
|
|
||||||
float elbow = maxValue / (maxValue - 1.0f);
|
|
||||||
float offset = 1.0f - ((0.5f * elbow) / (elbow - 0.5f));
|
|
||||||
|
|
||||||
float hdrLumaInvTonemap = offset + ((luma * elbow) / (elbow - luma));
|
|
||||||
float sdrLumaInvTonemap = luma / ((1.0f + kEpsilon) - luma); /* Convert the srd < 0.5 to 0.0 -> 1.0 range */
|
|
||||||
|
|
||||||
float lumaInvTonemap = (luma > 0.5f) ? hdrLumaInvTonemap : sdrLumaInvTonemap;
|
|
||||||
vec3 perLuma = sdr / (luma + kEpsilon) * lumaInvTonemap;
|
|
||||||
|
|
||||||
vec3 hdrInvTonemap = offset + ((sdr * elbow) / (elbow - sdr));
|
|
||||||
vec3 sdrInvTonemap = sdr / ((1.0f + kEpsilon) - sdr); /* Convert the srd < 0.5 to 0.0 -> 1.0 range */
|
|
||||||
|
|
||||||
vec3 perChannel = vec3(sdr.x > 0.5f ? hdrInvTonemap.x : sdrInvTonemap.x,
|
|
||||||
sdr.y > 0.5f ? hdrInvTonemap.y : sdrInvTonemap.y,
|
|
||||||
sdr.z > 0.5f ? hdrInvTonemap.z : sdrInvTonemap.z);
|
|
||||||
|
|
||||||
vec3 hdr = mix(perLuma, perChannel, vec3(kLumaChannelRatio));
|
|
||||||
|
|
||||||
return hdr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 sdr = texture(Source, vTexCoord);
|
vec4 source = texture(Source, vTexCoord);
|
||||||
FragColor = vec4(InverseTonemap(sdr.rgb), sdr.a);
|
|
||||||
|
vec3 sdr = pow(abs(source.rgb), vec3(params.Contrast / params.DisplayGamma)); /* Display Gamma - needs to be determined by calibration screen */
|
||||||
|
|
||||||
|
FragColor = vec4(InverseTonemap(sdr, params.MaxNits, params.PaperWhiteNits, params.Saturation), source.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue