diff --git a/dithering/ps1-dedither-boxblur.slangp b/dithering/ps1-dedither-boxblur.slangp new file mode 100644 index 00000000..b773f1de --- /dev/null +++ b/dithering/ps1-dedither-boxblur.slangp @@ -0,0 +1,6 @@ +shaders = 1 + +shader0 = shaders/ps1dither/PS1-Undither-BoxBlur.slang +filter_linear0 = true +scale_type0 = source +scale0 = 1.0 \ No newline at end of file diff --git a/dithering/ps1-dedither-comparison.slangp b/dithering/ps1-dedither-comparison.slangp new file mode 100644 index 00000000..712b25a0 --- /dev/null +++ b/dithering/ps1-dedither-comparison.slangp @@ -0,0 +1,11 @@ +shaders = 2 + +shader0 = shaders/ps1dither/PS1-Undither-AntiBayer.slang +filter_linear0 = true +scale_type0 = source +scale0 = 1.0 + +shader1 = shaders/ps1dither/PS1-Undither-Comparison-MkII.slang +filter_linear1 = true +scale_type1 = source +scale1 = 1.0 \ No newline at end of file diff --git a/dithering/ps1-dither.slangp b/dithering/ps1-dither.slangp new file mode 100644 index 00000000..78054fa2 --- /dev/null +++ b/dithering/ps1-dither.slangp @@ -0,0 +1,6 @@ +shaders = 1 + +shader0 = shaders/ps1dither/PS1-Undither-AntiBayer.slang +filter_linear0 = true +scale_type0 = source +scale0 = 1.0 \ No newline at end of file diff --git a/dithering/shaders/ps1dither/PS1-Undither-AntiBayer.slang b/dithering/shaders/ps1dither/PS1-Undither-AntiBayer.slang new file mode 100644 index 00000000..672f0f5c --- /dev/null +++ b/dithering/shaders/ps1dither/PS1-Undither-AntiBayer.slang @@ -0,0 +1,62 @@ +#version 450 + +// PS1 Undither AntiBayer +// by torridgristle + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; +} params; + +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; + +void main() +{ + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; +} + +// PlayStation dithering pattern. The offset is selected based on the +// pixel position in VRAM, by blocks of 4x4 pixels. The value is added +// to the 8bit color components before they're truncated to 5 bits. +//"const int dither_pattern[16] =" +//" int[16](-4, 0, -3, 1," +//" 2, -2, 3, -1," +//" -3, 1, -4, 0," +//" 3, -1, 2, -2);" "\n" + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +void main() +{ + vec3 OriginPass = texture(Source, vTexCoord).xyz; + + float PatternLUT[16] = float[](-4.0, 0.0, -3.0, 1.0, + 2.0, -2.0, 3.0, -1.0, + -3.0, 1.0, -4.0, 0.0, + 3.0, -1.0, 2.0, -2.0); + + float PatternX = ceil(((mod(vTexCoord.x * params.SourceSize.x,4.00001)-0.5)/3.0)*3.0); + float PatternY = ceil(((mod(vTexCoord.y * params.SourceSize.y,4.00001)-0.5)/3.0)*3.0); + float Pattern = PatternLUT[int(PatternX+(PatternY*4))]; + + vec3 Result = OriginPass; + //Result = vec3(Pattern)/8.0 + 0.5; + Result = (Result * 255.0 - Pattern - round(Pattern*0.25)*2.0) / 255.0; + + FragColor = vec4(Result,1.0); +} \ No newline at end of file diff --git a/dithering/shaders/ps1dither/PS1-Undither-BoxBlur.slang b/dithering/shaders/ps1dither/PS1-Undither-BoxBlur.slang new file mode 100644 index 00000000..8788f7f9 --- /dev/null +++ b/dithering/shaders/ps1dither/PS1-Undither-BoxBlur.slang @@ -0,0 +1,66 @@ +#version 450 + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; +} params; + +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; + +void main() +{ + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +void main() +{ + vec3 OriginPass = texture(Source, vTexCoord).xyz;//No blending + + //Blend 4 pixels together by sampling between them with linear interpolation + vec2 SquareBlend = (vTexCoord * params.SourceSize.xy - 0.5) * params.SourceSize.zw; + //Blend 2 horizontal pixels together the same way as before + vec2 HorizoBlend = (vTexCoord * params.SourceSize.xy - vec2(0.5,0.0)) * params.SourceSize.zw; + vec3 SquarePass = texture(Source, SquareBlend).xyz; + vec3 HorizoPass = texture(Source, HorizoBlend).xyz; + + //Edge Detection for SquareBlend + vec3 SquareEdge = texture(Source, (SquareBlend * params.SourceSize.xy + vec2( 0.0, 1.0)) * params.SourceSize.zw).xyz + + texture(Source, (SquareBlend * params.SourceSize.xy + vec2( 1.0, 0.0)) * params.SourceSize.zw).xyz + + texture(Source, (SquareBlend * params.SourceSize.xy + vec2( 1.0, 1.0)) * params.SourceSize.zw).xyz; + SquareEdge = abs((SquareEdge / 3.0) - SquarePass); + //Try to adjust white / black range so that edges are black and non-edges are white + float SquareEdgeMask = 1.0-pow(1.0-pow(1.0-max(SquareEdge.x,max(SquareEdge.y,SquareEdge.z)),30.0),2.0); + + //Edge Detection for HorizoBlend + vec3 HorizoEdge = texture(Source, (HorizoBlend * params.SourceSize.xy + vec2( 0.0, 1.0)) * params.SourceSize.zw).xyz + + texture(Source, (HorizoBlend * params.SourceSize.xy + vec2( 1.0, 0.0)) * params.SourceSize.zw).xyz + + texture(Source, (HorizoBlend * params.SourceSize.xy + vec2( 1.0, 1.0)) * params.SourceSize.zw).xyz; + HorizoEdge = abs((HorizoEdge / 3.0) - HorizoPass); + //Try to adjust white / black range so that edges are black and non-edges are white + float HorizoEdgeMask = 1.0-pow(1.0-pow(1.0-max(HorizoEdge.x,max(HorizoEdge.y,HorizoEdge.z)),10.0),2.0); + + //If SquarePass has a detected edge, use HorizoPass + vec3 Result = mix(HorizoPass,SquarePass,SquareEdgeMask); + //If HorizoPass has a detected edge, use OriginPass + Result = mix(OriginPass,Result,HorizoEdgeMask); + + //It's complete + FragColor = vec4(Result,1.0); +} \ No newline at end of file diff --git a/dithering/shaders/ps1dither/PS1-Undither-Comparison-MkII.slang b/dithering/shaders/ps1dither/PS1-Undither-Comparison-MkII.slang new file mode 100644 index 00000000..92570587 --- /dev/null +++ b/dithering/shaders/ps1dither/PS1-Undither-Comparison-MkII.slang @@ -0,0 +1,61 @@ +#version 450 + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; +} params; + +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; + +void main() +{ + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +void main() +{ + vec3 Sample1 = texture(Source, (vTexCoord * params.SourceSize.xy + vec2( 0.0, 0.0)) * params.SourceSize.zw).xyz; + vec3 Sample2 = texture(Source, (vTexCoord * params.SourceSize.xy + vec2( 1.0, 1.0)) * params.SourceSize.zw).xyz; + vec3 Sample3 = texture(Source, (vTexCoord * params.SourceSize.xy + vec2( 1.0, 0.0)) * params.SourceSize.zw).xyz; + vec3 Sample4 = texture(Source, (vTexCoord * params.SourceSize.xy + vec2( 0.0, 1.0)) * params.SourceSize.zw).xyz; + + vec3 Average = (Sample1+Sample2+Sample3+Sample4)*0.25; + float Brightness = 1.0-pow(1.0-max(max(Average.x,Average.y),Average.z),2.0); + + vec3 Sample2Diff = abs(Sample2-Sample1); + float Sample2DiffMax = max(max(Sample2Diff.x,Sample2Diff.y),Sample2Diff.z); + vec3 Sample3Diff = abs(Sample3-Sample1); + float Sample3DiffMax = max(max(Sample3Diff.x,Sample3Diff.y),Sample3Diff.z); + vec3 Sample4Diff = abs(Sample4-Sample1); + float Sample4DiffMax = max(max(Sample4Diff.x,Sample4Diff.y),Sample4Diff.z); + + float DiffLimit = (Brightness * 4.0 + 2.0) / 32.0; + + Sample2 = mix(Sample1,Sample2,vec3(Sample2DiffMax < DiffLimit)); + Sample3 = mix(Sample1,Sample3,vec3(Sample3DiffMax < DiffLimit)); + Sample4 = mix(Sample1,Sample4,vec3(Sample4DiffMax < DiffLimit)); + + vec3 Mixing = Sample1 + Sample2 + Sample3 + Sample4; + Mixing *= 0.25; + + vec3 Result = vec3(Mixing); + + FragColor = vec4(Result,1.0); +} \ No newline at end of file diff --git a/dithering/shaders/ps1dither/PS1-Undither-Comparison.slang b/dithering/shaders/ps1dither/PS1-Undither-Comparison.slang new file mode 100644 index 00000000..6d3ae2b9 --- /dev/null +++ b/dithering/shaders/ps1dither/PS1-Undither-Comparison.slang @@ -0,0 +1,77 @@ +#version 450 + +layout(push_constant) uniform Push +{ + vec4 SourceSize; + vec4 OriginalSize; + vec4 OutputSize; + uint FrameCount; +} params; + +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; + +void main() +{ + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +void main() +{ + vec3 OriginPass = texture(Source, vTexCoord).xyz; + + //Get blended textures by offsetting between 4 pixels. + vec3 SquarePass1 = texture(Source, (vTexCoord * params.SourceSize.xy + vec2( 0.5, 0.5)) * params.SourceSize.zw).xyz; + vec3 SquarePass2 = texture(Source, (vTexCoord * params.SourceSize.xy + vec2(-0.5,-0.5)) * params.SourceSize.zw).xyz; + vec3 SquarePass3 = texture(Source, (vTexCoord * params.SourceSize.xy + vec2(-0.5, 0.5)) * params.SourceSize.zw).xyz; + vec3 SquarePass4 = texture(Source, (vTexCoord * params.SourceSize.xy + vec2( 0.5,-0.5)) * params.SourceSize.zw).xyz; + + //Which SquarePass is closest to the OriginPass? + vec3 SquarePass1Diff = abs(SquarePass1 - OriginPass); + vec3 SquarePass2Diff = abs(SquarePass2 - OriginPass); + vec3 SquarePass3Diff = abs(SquarePass3 - OriginPass); + vec3 SquarePass4Diff = abs(SquarePass4 - OriginPass); + float SquarePass1DiffAvg = (SquarePass1Diff.x + SquarePass1Diff.y + SquarePass1Diff.z) / 3.0; + float SquarePass2DiffAvg = (SquarePass2Diff.x + SquarePass2Diff.y + SquarePass2Diff.z) / 3.0; + float SquarePass3DiffAvg = (SquarePass3Diff.x + SquarePass3Diff.y + SquarePass3Diff.z) / 3.0; + float SquarePass4DiffAvg = (SquarePass4Diff.x + SquarePass4Diff.y + SquarePass4Diff.z) / 3.0; + float SquarePassDiffAvgMin = min(SquarePass1DiffAvg,min(SquarePass2DiffAvg,min(SquarePass3DiffAvg,SquarePass4DiffAvg))); + + vec3 SquarePass = + float(SquarePassDiffAvgMin == SquarePass1DiffAvg) * float(SquarePass1DiffAvg != SquarePass2DiffAvg) * float(SquarePass1DiffAvg != SquarePass3DiffAvg) * float(SquarePass1DiffAvg != SquarePass4DiffAvg) * SquarePass1 + + float(SquarePassDiffAvgMin == SquarePass2DiffAvg) * float(SquarePass2DiffAvg != SquarePass3DiffAvg) * float(SquarePass2DiffAvg != SquarePass4DiffAvg) * SquarePass2 + + float(SquarePassDiffAvgMin == SquarePass3DiffAvg) * float(SquarePass3DiffAvg != SquarePass4DiffAvg) * SquarePass3 + + float(SquarePassDiffAvgMin == SquarePass4DiffAvg) * SquarePass4; + + + //How different is the blended texture from the original texture? + //The difference between the least different and most different SquarePasses returns edges. + vec3 SquareCompareMin = min(SquarePass1Diff,min(SquarePass2Diff,min(SquarePass3Diff,SquarePass4Diff))); + vec3 SquareCompareMax = max(SquarePass1Diff,max(SquarePass2Diff,max(SquarePass3Diff,SquarePass4Diff))); + vec3 SquareCompareBoth = abs(SquareCompareMin - SquareCompareMax); + + float SquareCompare = max(SquareCompareBoth.x,max(SquareCompareBoth.y,SquareCompareBoth.z)); + + //Turn a range of the comparison result into a mask. 2.5 just happens to work well, it's not set in stone or anything. + float SquareMask = 1.0-pow(1.0-min(max(SquareCompare*2.5,0.0),1.0),2.0); + + //Blend it all together + vec3 Result = mix(SquarePass,OriginPass,SquareMask); + + //Result = vec3(SquareMask); + + FragColor = vec4(Result,1.0); +} \ No newline at end of file