mirror of
https://github.com/italicsjenga/slang-shaders.git
synced 2024-10-18 07:41:31 +11:00
330 lines
13 KiB
Plaintext
330 lines
13 KiB
Plaintext
#version 450
|
|
|
|
/*
|
|
A shader that tries to emulate a sony PVM type aperture grille screen but with full brightness.
|
|
|
|
The novel thing about this shader is that it relies on the HDR shaders to brighten up the image so that when
|
|
we apply this shader which emulates the apperture grille the resulting screen isn't left too dark.
|
|
|
|
I think you'd need a HDR 1000 monitor to get close to CRT levels of brightness but my HDR 700 does an alright job of it.
|
|
|
|
Please Enable HDR in RetroArch NOTE: when the hdr10 and inverse_tonemap shaders are envoked the Peak Luminance and Paper White Luminance in the menu do nothing instead set those values through the shader parameters instead
|
|
|
|
Set Peak Luminance to the peak luminance of your monitor and set Paper White Luminance to roughly half dependent on the game and the monitor.
|
|
|
|
Also try to use a integer scaling - its just better - overscaling is fine.
|
|
|
|
This shader doesn't do any geometry warping or bouncing of light around inside the screen etc - I think these effects just add unwanted noise, I know people disagree. Please feel free to make you own and add them
|
|
|
|
Dont use this shader directly - use the crt\crt-sony-pvm-4k-hdr.slangp to have the proper chain of effects.
|
|
*/
|
|
|
|
#pragma format A2B10G10R10_UNORM_PACK32
|
|
|
|
#include "inverse_tonemap.h"
|
|
#include "hdr10.h"
|
|
|
|
layout(push_constant) uniform Push
|
|
{
|
|
vec4 SourceSize;
|
|
vec4 OriginalSize;
|
|
vec4 OutputSize;
|
|
uint FrameCount;
|
|
float PaperWhiteNits;
|
|
float MaxNits;
|
|
float ExpandGamut;
|
|
float CRTGamma;
|
|
float ScanlineMin;
|
|
float ScanlineMax;
|
|
float ResolutionPattern;
|
|
float HorizontalSharpness;
|
|
float HorizontalAttack;
|
|
float VerticalAttack;
|
|
float RedConvergence;
|
|
float GreenConvergence;
|
|
float BlueConvergence;
|
|
} params;
|
|
|
|
#pragma parameter PaperWhiteNits "Paper White Luminance" 650.0 0.0 10000.0 10.0
|
|
#pragma parameter MaxNits "Peak Luminance" 700.0 0.0 10000.0 10.0
|
|
#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
|
|
{
|
|
mat4 MVP;
|
|
} global;
|
|
|
|
#pragma stage vertex
|
|
layout(location = 0) in vec4 Position;
|
|
layout(location = 1) in vec2 TexCoord;
|
|
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()
|
|
{
|
|
gl_Position = global.MVP * Position;
|
|
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
|
|
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(set = 0, binding = 2) uniform sampler2D Source;
|
|
|
|
float ModInteger(float a, float b)
|
|
{
|
|
float m = a - floor((a + 0.5) / b) * b;
|
|
return floor(m + 0.5);
|
|
}
|
|
|
|
#define kPi 3.1415926536f
|
|
#define kEuler 2.718281828459f
|
|
#define kMax 1.0f
|
|
|
|
#define kLumaRatio 0.5f
|
|
|
|
#define kRed vec3(1.0, 0.0, 0.0)
|
|
#define kGreen vec3(0.0, 1.0, 0.0)
|
|
#define kBlue vec3(0.0, 0.0, 1.0)
|
|
#define kMagenta vec3(1.0, 0.0, 1.0)
|
|
#define kYellow vec3(1.0, 1.0, 0.0)
|
|
#define kCyan vec3(0.0, 1.0, 1.0)
|
|
#define kBlack vec3(0.0, 0.0, 0.0)
|
|
#define kWhite vec3(1.0, 1.0, 1.0)
|
|
|
|
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 falloff ? kFallOffControlPoints + vec4(0.0f, 0.0f, params.HorizontalAttack, 0.0f) : kAttackControlPoints - vec4(0.0f, params.HorizontalAttack, 0.0f, 0.0f);
|
|
}
|
|
|
|
float Bezier(const float t0, const vec4 control_points)
|
|
{
|
|
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()
|
|
{
|
|
uint resolution_pattern = uint(params.ResolutionPattern);
|
|
|
|
const vec2 current_position = vTexCoord * params.OutputSize.xy;
|
|
const float current_center = floor(current_position.y) + 0.5f;
|
|
|
|
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;
|
|
|
|
const float source_tex_coord_x = (current_source_center_x /* * PatternSize[resolution_pattern]*/) / params.SourceSize.x;
|
|
|
|
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
|
|
the CRT TV will likely be 4:3 and so higher TVL values will be required on your 16:9 screen
|
|
to get an equivalent TVL seen on a 4:3 CRT TV.
|
|
Pattern 1's 960TVL at 16:9 is about right for a 800TVL at 4:3.
|
|
*/
|
|
|
|
switch(resolution_pattern)
|
|
{
|
|
case 0: /* 2 pattern - 1440TVL (16:9) horiz resolution (too high?) on a 4K screen */
|
|
{
|
|
const vec3 mask[2] = vec3[]( kYellow, kCyan );
|
|
|
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[0]));
|
|
scanline_colour *= mask[pattern_x];
|
|
|
|
break;
|
|
}
|
|
case 1: /* 3 pattern - 960TVL (16:9) horiz resolution on a 4K screen */
|
|
{
|
|
const vec3 mask[3] = vec3[]( kRed, kGreen, kBlue );
|
|
|
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[1]));
|
|
scanline_colour *= mask[pattern_x];
|
|
|
|
break;
|
|
}
|
|
case 2: /* 4 pattern - 720TVL (16:9) horiz resolution on a 4K screen */
|
|
{
|
|
const vec3 mask[4] = vec3[]( kRed, kYellow, kCyan, kBlue );
|
|
|
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[2]));
|
|
scanline_colour *= mask[pattern_x];
|
|
|
|
break;
|
|
}
|
|
case 3: /* 4 pattern - 720TVL (16:9) horiz resolution on a 4K screen */
|
|
{
|
|
const vec3 mask[4] = vec3[]( kRed, kGreen, kBlue, kBlack );
|
|
|
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[3]));
|
|
scanline_colour *= mask[pattern_x];
|
|
|
|
break;
|
|
}
|
|
case 4: /* 5 BRG pattern - 576TVL (16:9) horiz resolution on a 4K screen */
|
|
{
|
|
const vec3 mask[5] = vec3[]( kRed, kMagenta, kBlue, kGreen, kGreen );
|
|
|
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[4]));
|
|
scanline_colour *= mask[pattern_x];
|
|
|
|
break;
|
|
}
|
|
case 5: /* 5 pattern - 576TVL (16:9) horiz resolution on a 4K screen */
|
|
{
|
|
const vec3 mask[5] = vec3[]( kRed, kYellow, kGreen, kCyan, kBlue );
|
|
|
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[5]));
|
|
scanline_colour *= mask[pattern_x];
|
|
|
|
break;
|
|
}
|
|
case 6: /* 6 pattern - 480TVL (16:9) horiz resolution on a 4K screen */
|
|
{
|
|
const vec3 mask[6] = vec3[]( kRed, kRed, kGreen, kGreen, kBlue, kBlue );
|
|
|
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[6]));
|
|
scanline_colour *= mask[pattern_x];
|
|
|
|
break;
|
|
}
|
|
case 7: /* 7 pattern - 410TVL (16:9) horiz resolution on a 4K screen */
|
|
{
|
|
const vec3 mask[7] = vec3[]( kRed, kRed, kYellow, kGreen, kCyan, kBlue, kBlue );
|
|
|
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[7]));
|
|
scanline_colour *= mask[pattern_x];
|
|
|
|
break;
|
|
}
|
|
case 8: /* 9 pattern - 640TVL (16:9) horiz resolution on a *8K* screen */
|
|
{
|
|
const vec3 mask[9] = vec3[]( kBlack, kRed, kRed, kBlack, kGreen, kGreen, kBlack, kBlue, kBlue );
|
|
|
|
uint pattern_x = uint(ModInteger(floor(current_position.x), PatternSize[8]));
|
|
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;
|
|
}
|
|
default:
|
|
{
|
|
scanline_colour = vec3(0.0);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
const vec3 hdr10 = Hdr10(scanline_colour, params.PaperWhiteNits, params.ExpandGamut);
|
|
|
|
//FragColor = vec4(scanline_colour, 1.0);
|
|
FragColor = vec4(hdr10, 1.0);
|
|
}
|