diff options
author | Patryk Obara <dreamer.tan@gmail.com> | 2020-03-20 23:25:29 +0300 |
---|---|---|
committer | Patryk Obara <dreamer.tan@gmail.com> | 2020-03-20 23:25:32 +0300 |
commit | 94ff5dea559e81a8f8b859b928341e3e28fcfb94 (patch) | |
tree | b34c43c468208aa45585faa4c2bece068e0b0925 | |
parent | 694e6f6cb11106b5b0bb3da11039613d9bf1882e (diff) |
WIP shaderspo/glshaders
Imported-from: https://www.vogons.org/viewtopic.php?p=835314#p835314
-rw-r--r-- | contrib/glshaders/crt-easy.glsl | 138 | ||||
-rw-r--r-- | contrib/glshaders/crt-lottes.glsl | 234 | ||||
-rw-r--r-- | contrib/glshaders/scanline.glsl | 48 |
3 files changed, 420 insertions, 0 deletions
diff --git a/contrib/glshaders/crt-easy.glsl b/contrib/glshaders/crt-easy.glsl new file mode 100644 index 000000000..0c9712c76 --- /dev/null +++ b/contrib/glshaders/crt-easy.glsl @@ -0,0 +1,138 @@ +#version 330 core + +varying vec2 v_texCoord; +uniform vec2 rubyTextureSize; +uniform vec2 rubyInputSize; +uniform vec2 rubyOutputSize; + +#if defined(VERTEX) + +attribute vec4 a_position; + +void main() + +{ + gl_Position = a_position; + v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize/rubyTextureSize; +} + +#elif defined(FRAGMENT) + +uniform sampler2D rubyTexture; + +#define BRIGHT_BOOST 1.25 +#define DILATION 1.0 +#define GAMMA_INPUT 2.0 +#define GAMMA_OUTPUT 1.8 +#define MASK_SIZE 1.0 +#define MASK_STAGGER 0.0 +#define MASK_STRENGTH 0.425 +#define MASK_DOT_HEIGHT 1.0 +#define MASK_DOT_WIDTH 1.0 +#define SCANLINE_BEAM_WIDTH_MAX 1.5 +#define SCANLINE_BEAM_WIDTH_MIN 1.5 +#define SCANLINE_BRIGHT_MAX 0.65 +#define SCANLINE_BRIGHT_MIN 0.35 +#define SCANLINE_CUTOFF 400.0 +#define SCANLINE_STRENGTH 1.0 +#define SHARPNESS_H 0.55 +#define SHARPNESS_V 0.55 +#define FIX(c) max(abs(c), 1e-5) +#define PI 3.141592653589 +#define TEX2D(c) dilate(texture(tex, c)) + +// Set to 0 to use linear filter and gain speed + +#define ENABLE_LANCZOS 1 + +vec4 dilate(vec4 col) +{ + vec4 x = mix(vec4(1.0), col, DILATION); + return col * x; +} + +float curve_distance(float x, float sharp) +{ +/* + apply half-circle s-curve to distance for sharper (more pixelated) interpolation + single line formula for Graph Toy: + 0.5 - sqrt(0.25 - (x - step(0.5, x)) * (x - step(0.5, x))) * sign(0.5 - x) +*/ + + float x_step = step(0.5, x); + float curve = 0.5 - sqrt(0.25 - (x - x_step) * (x - x_step)) * sign(0.5 - x); + return mix(x, curve, sharp); +} + + +mat4 get_color_matrix(sampler2D tex, vec2 co, vec2 dx) +{ + return mat4(TEX2D(co - dx), TEX2D(co), TEX2D(co + dx), TEX2D(co + 2.0 * dx)); +} + +vec3 filter_lanczos(vec4 coeffs, mat4 color_matrix) +{ + vec4 col = color_matrix * coeffs; + vec4 sample_min = min(color_matrix[1], color_matrix[2]); + vec4 sample_max = max(color_matrix[1], color_matrix[2]); + col = clamp(col, sample_min, sample_max); + return col.rgb; +} + +out vec4 color; + +void main() +{ + vec2 dx = vec2(1.0 / rubyTextureSize.x, 0.0); + vec2 dy = vec2(0.0, 1.0 / rubyTextureSize.y); + vec2 pix_co = v_texCoord * rubyTextureSize - vec2(0.5, 0.5); + vec2 tex_co = (floor(pix_co) + vec2(0.5, 0.5)) / rubyTextureSize; + vec2 dist = fract(pix_co); + + float curve_x; + vec3 col, col2; + +#if ENABLE_LANCZOS + + curve_x = curve_distance(dist.x, SHARPNESS_H * SHARPNESS_H); + vec4 coeffs = PI * vec4(1.0 + curve_x, curve_x, 1.0 - curve_x, 2.0 - curve_x); + coeffs = FIX(coeffs); + coeffs = 2.0 * sin(coeffs) * sin(coeffs / 2.0) / (coeffs * coeffs); + coeffs /= dot(coeffs, vec4(1.0)); + col = filter_lanczos(coeffs, get_color_matrix(rubyTexture, tex_co, dx)); + col2 = filter_lanczos(coeffs, get_color_matrix(rubyTexture, tex_co + dy, dx)); + +#else + + curve_x = curve_distance(dist.x, SHARPNESS_H); + col = mix(TEX2D(tex_co).rgb, TEX2D(tex_co + dx).rgb, curve_x); + col2 = mix(TEX2D(tex_co + dy).rgb, TEX2D(tex_co + dx + dy).rgb, curve_x); + +#endif + + col = mix(col, col2, curve_distance(dist.y, SHARPNESS_V)); + col = pow(col, vec3(GAMMA_INPUT / (DILATION + 1.0))); + float luma = dot(vec3(0.2126, 0.7152, 0.0722), col); + float bright = (max(col.r, max(col.g, col.b)) + luma) / 2.0; + float scan_bright = clamp(bright, SCANLINE_BRIGHT_MIN, SCANLINE_BRIGHT_MAX); + float scan_beam = clamp(bright * SCANLINE_BEAM_WIDTH_MAX, SCANLINE_BEAM_WIDTH_MIN, SCANLINE_BEAM_WIDTH_MAX); + float scan_weight = 1.0 - pow(cos(v_texCoord.y * 2.0 * PI * rubyTextureSize.y) * 0.5 + 0.5, scan_beam) * SCANLINE_STRENGTH; + float mask = 1.0 - MASK_STRENGTH; + vec2 mod_fac = floor(v_texCoord * rubyOutputSize * rubyTextureSize / (rubyInputSize * vec2(MASK_SIZE, MASK_DOT_HEIGHT * MASK_SIZE))); + int dot_no = int(mod((mod_fac.x + mod(mod_fac.y, 2.0) * MASK_STAGGER) / MASK_DOT_WIDTH, 3.0)); + vec3 mask_weight; + + if (dot_no == 0) mask_weight = vec3(1.0, mask, mask); + else if (dot_no == 1) mask_weight = vec3(mask, 1.0, mask); + else mask_weight = vec3(mask, mask, 1.0); + + if (rubyInputSize.y >= SCANLINE_CUTOFF) scan_weight = 1.0; + col2 = col.rgb; + col *= vec3(scan_weight); + col = mix(col, col2, scan_bright); + col *= mask_weight; + col = pow(col, vec3(1.0 / GAMMA_OUTPUT)); + color = vec4(col * BRIGHT_BOOST, 1.0); +} + +#endif diff --git a/contrib/glshaders/crt-lottes.glsl b/contrib/glshaders/crt-lottes.glsl new file mode 100644 index 000000000..00f74c51b --- /dev/null +++ b/contrib/glshaders/crt-lottes.glsl @@ -0,0 +1,234 @@ +#version 330 core + +varying vec2 v_texCoord; +uniform vec2 rubyTextureSize; +uniform vec2 rubyInputSize; +uniform vec2 rubyOutputSize; + +#if defined(VERTEX) + +attribute vec4 a_position; + +void main() +{ + gl_Position = a_position; + v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize/rubyTextureSize; +} + +#elif defined(FRAGMENT) + +uniform sampler2D rubyTexture; + +const float hardScan = -8.0; +const float hardPix = -3.0; +const float warpX = 0.0075; +const float warpY = 0.0075; +const float maskDark = 0.5; +const float maskLight = 1.5; +const float scaleInLinearGamma = 1; +const float shadowMask = 1; +const float brightBoost = 1.4; + +#define Blackmask 1 + +out vec4 color; + +//------------------------------------------------------------------------ + +// sRGB to Linear. +// Assuing using sRGB typed textures this should not be needed. +float ToLinear1(float c) +{ + if (scaleInLinearGamma == 0) + { + return c; + } + return (c <= 0.04045) ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4); +} + +vec3 ToLinear(vec3 c) +{ + if (scaleInLinearGamma == 0) + { + + return c; + } + return vec3(ToLinear1(c.r), ToLinear1(c.g), ToLinear1(c.b)); +} + +// Linear to sRGB. +// Assuing using sRGB typed textures this should not be needed. +float ToSrgb1(float c) +{ + if (scaleInLinearGamma == 0) + { + return c; + } + return(c < 0.0031308 ? c *12.92 : 1.055 * pow(c, 0.41666) - 0.055); +} + +vec3 ToSrgb(vec3 c) +{ + if (scaleInLinearGamma == 0) + { + return c; + } + return vec3(ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b)); +} + +// Nearest emulated sample given floating point position and texel offset. +// Also zero's off screen. +vec3 Fetch(vec2 pos,vec2 off) +{ + pos = (floor(pos * rubyTextureSize.xy + off) + vec2(0.5, 0.5)) / rubyTextureSize.xy; + return ToLinear(brightBoost * texture(rubyTexture, pos.xy).rgb); +} + +// Distance in emulated pixels to nearest texel. +vec2 Dist(vec2 pos) +{ + pos = pos * rubyTextureSize.xy; + return -((pos - floor(pos)) - vec2(0.5)); +} + +// 1D Gaussian. +float Gaus(float pos, float scale) +{ + return exp2(scale * pos * pos); +} + +// 3-tap Gaussian filter along horz line. +vec3 Horz3(vec2 pos, float off) +{ + vec3 b = Fetch(pos, vec2(-1.0, off)); + vec3 c = Fetch(pos, vec2(0.0, off)); + vec3 d = Fetch(pos, vec2(1.0, off)); + float dst = Dist(pos).x; + // Convert distance to weight. + float scale = hardPix; + float wb = Gaus(dst - 1.0, scale); + float wc = Gaus(dst + 0.0, scale); + float wd = Gaus(dst + 1.0, scale); + // Return filtered sample. + return (b * wb + c * wc + d * wd) / (wb + wc + wd); +} + +// 5-tap Gaussian filter along horz line. +vec3 Horz5(vec2 pos, float off) +{ + vec3 a = Fetch(pos, vec2(-2.0, off)); + vec3 b = Fetch(pos, vec2(-1.0, off)); + vec3 c = Fetch(pos, vec2(0.0, off)); + vec3 d = Fetch(pos, vec2(1.0, off)); + vec3 e = Fetch(pos, vec2(2.0, off)); + float dst = Dist(pos).x; + // Convert distance to weight. + float scale = hardPix; + float wa = Gaus(dst - 2.0, scale); + float wb = Gaus(dst - 1.0, scale); + float wc = Gaus(dst + 0.0, scale); + float wd = Gaus(dst + 1.0, scale); + float we = Gaus(dst + 2.0, scale); + // Return filtered sample. + return (a * wa + b * wb + c * wc + d * wd + e * we) / (wa + wb + wc + wd + we); +} + +// Return scanline weight. +float Scan(vec2 pos, float off) +{ + float dst = Dist(pos).y; + return Gaus(dst + off, hardScan); +} + +// Allow nearest three lines to effect pixel. +vec3 Tri(vec2 pos) +{ + vec3 a = Horz3(pos, -1.0); + vec3 b = Horz5(pos, 0.0); + vec3 c = Horz3(pos, 1.0); + float wa = Scan(pos, -1.0); + float wb = Scan(pos, 0.0); + float wc = Scan(pos, 1.0); + return a * wa + b * wb + c * wc; +} + +// Distortion of scanlines, and end of screen alpha. +vec2 Warp(vec2 pos) +{ + pos = pos * 2.0 -1.0; + pos *= vec2(1.0 + (pos.y * pos.y) * warpX, 1.0 + (pos.x * pos.x) * warpY); + return pos * 0.5 + 0.5; +} + +// Shadow mask. +vec3 Mask(vec2 pos) +{ + pos.x += pos.y * 3.0; + vec3 mask = vec3(maskDark, maskDark, maskDark); + pos.x = fract(pos.x / 6.0); + if (pos.x < 0.333) + { + mask.r = maskLight; + } + else if (pos.x < 0.666) + { + mask.g = maskLight; + } + else + { + mask.b = maskLight; + } + return mask; +} + +uniform vec2 resolution; +uniform vec2 mouse; +uniform float time; + +float box(vec2 _st, vec2 _size, float _smoothEdges) { + _size = vec2(.1) - _size*.2; + vec2 aa = vec2(_smoothEdges * 0.1); + vec2 uv = smoothstep(_size, _size+aa, _st); + uv *= smoothstep(_size, _size+aa, vec2(1.0)-_st); + return uv.x * uv.y; +} + +vec3 drawRectangle(in vec2 st) { + // Each result will return 1.0 (white) or 0.0 ( + vec3 color = vec3(0.0); + vec2 borders = step(vec2(0.0),st); + float pct = borders.x * borders.y; + + // top-right + vec2 tr = step(vec2(0.1),1.0-st); + pct *= tr.x * tr.y; + + // The multiplication of left*bottom will be similar to the logical AND. + color = vec3(pct); + return color; +} + +void main() +{ + vec2 pos = Warp(v_texCoord.xy * (rubyTextureSize.xy / rubyInputSize.xy)) * (rubyInputSize.xy / rubyTextureSize.xy); + + +#ifdef Blackmask + vec3 outColor = vec3(1.0); + outColor *= Tri(pos); + outColor *= drawRectangle(pos); + outColor *= vec3(box(pos, vec2(0.5), 0.01)); +#else + vec3 outColor = Tri(pos); +#endif + + + if (shadowMask != 0) + { + outColor.rgb *= Mask(floor(v_texCoord.xy * (rubyTextureSize.xy / rubyInputSize.xy) * rubyOutputSize.xy) + vec2(0.5, 0.5)); + } + + color = vec4(ToSrgb(outColor.rgb), 1.0); +} + +#endif diff --git a/contrib/glshaders/scanline.glsl b/contrib/glshaders/scanline.glsl new file mode 100644 index 000000000..b9f5bbc24 --- /dev/null +++ b/contrib/glshaders/scanline.glsl @@ -0,0 +1,48 @@ +#version 330 core + +varying vec2 v_texCoord; +uniform vec2 rubyTextureSize; +uniform vec2 rubyInputSize; +uniform vec2 rubyOutputSize; + +#if defined(VERTEX) + +attribute vec4 a_position; + +out sine_coord +{ + vec2 omega; +} coords; + + +void main() +{ + gl_Position = a_position; + v_texCoord = vec2(a_position.x+1.0,1.0-a_position.y)/2.0*rubyInputSize/rubyTextureSize; + coords.omega = vec2(3.1415 * rubyOutputSize.x * rubyTextureSize.x / rubyInputSize.x, 2.0 * 3.1415 * rubyTextureSize.y); +} + +#elif defined(FRAGMENT) + +const float SCANLINE_BASE_BRIGHTNESS = 0.95; +const float SCANLINE_SINE_COMP_A = 0.05; +const float SCANLINE_SINE_COMP_B = 0.15; + +uniform sampler2D rubyTexture; + +in sine_coord +{ + vec2 omega; +} coords; + +out vec4 color; + +void main() +{ + vec2 sine_comp = vec2(SCANLINE_SINE_COMP_A, SCANLINE_SINE_COMP_B); + vec3 res = texture(rubyTexture, v_texCoord).xyz; + vec3 scanline = res * (SCANLINE_BASE_BRIGHTNESS + dot(sine_comp * sin(v_texCoord * coords.omega), vec2(1.0, 1.0))); + color = vec4(scanline.x, scanline.y, scanline.z, 1.0); +} + +#endif |