diff options
| author | Peter Fors <peter.fors@mindkiller.com> | 2025-03-29 20:11:56 +0100 |
|---|---|---|
| committer | Peter Fors <peter.fors@mindkiller.com> | 2025-03-29 20:11:56 +0100 |
| commit | d5486a5af100fb37fac08b60d862ac14943853ce (patch) | |
| tree | e2206f620745ba49c94f0f29d180587e03bc9fda /base/fragment_shader.glsl | |
| parent | ee4f15400998ca704c6ad8fc537f0d924930fabd (diff) | |
add base code for windowing and opengl crt-shader.
Diffstat (limited to 'base/fragment_shader.glsl')
| -rw-r--r-- | base/fragment_shader.glsl | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/base/fragment_shader.glsl b/base/fragment_shader.glsl new file mode 100644 index 0000000..c21b2de --- /dev/null +++ b/base/fragment_shader.glsl @@ -0,0 +1,149 @@ +// Specify default precision for fragment shaders + +out vec4 outcolor; +in vec2 frag_texture_coord; + +uniform vec2 resolution; +uniform vec2 src_image_size; +uniform float brightness; +uniform vec4 tone_data; +uniform bool crt_emulation; +uniform sampler2D iChannel0; + +vec3 CrtsFetch(vec2 uv) { + const float bias = 0.003333333; + return max(texture(iChannel0, uv, -16.0).rgb, vec3(bias)); +} + +#define CrtsRcpF1(x) (1.0 / (x)) +#define CrtsSatF1(x) clamp((x), 0.0, 1.0) +const float PI2 = 6.28318530717958; +const float HALF = 0.5; + +float CrtsMax3F1(float a, float b, float c) { + return max(a, max(b, c)); +} + +vec3 CrtsMask(vec2 pos, float dark) { + #ifdef CRTS_MASK_GRILLE + vec3 m = vec3(dark); + float x = fract(pos.x * (1.0 / 3.0)); + m.r = (x < (1.0 / 3.0)) ? 1.0 : dark; + m.g = (x >= (1.0 / 3.0) && x < (2.0 / 3.0)) ? 1.0 : dark; + m.b = (x >= (2.0 / 3.0)) ? 1.0 : dark; + return m; + #endif + + #ifdef CRTS_MASK_GRILLE_LITE + vec3 m = vec3(1.0); + float x = fract(pos.x * (1.0 / 3.0)); + m.r = (x < (1.0 / 3.0)) ? dark : 1.0; + m.g = (x >= (1.0 / 3.0) && x < (2.0 / 3.0)) ? dark : 1.0; + m.b = (x >= (2.0 / 3.0)) ? dark : 1.0; + return m; + #endif + + #ifdef CRTS_MASK_NONE + return vec3(1.0); + #endif + + #ifdef CRTS_MASK_SHADOW + pos.x += pos.y * 3.0; + vec3 m = vec3(dark); + float x = fract(pos.x * (1.0 / 6.0)); + m.r = (x < (1.0 / 3.0)) ? 1.0 : dark; + m.g = (x >= (1.0 / 3.0) && x < (2.0 / 3.0)) ? 1.0 : dark; + m.b = (x >= (2.0 / 3.0)) ? 1.0 : dark; + return m; + #endif +} + +vec3 CrtsFilter(vec2 ipos, vec2 inputSizeDivOutputSize, vec2 halfInputSize, vec2 rcpInputSize, vec2 rcpOutputSize, vec2 twoDivOutputSize, float inputHeight, vec2 warp, float thin, float blur, float mask, vec4 tone) { + vec2 pos = ipos * twoDivOutputSize - vec2(1.0); + pos *= vec2(1.0 + (pos.y * pos.y) * warp.x, 1.0 + (pos.x * pos.x) * warp.y); + float vin = 1.0 - ((1.0 - CrtsSatF1(pos.x * pos.x)) * (1.0 - CrtsSatF1(pos.y * pos.y))); + vin = CrtsSatF1((-vin) * inputHeight + inputHeight); + pos = pos * halfInputSize + halfInputSize; + + float y0 = floor(pos.y - 0.5) + 0.5; + float x0 = floor(pos.x - 1.5) + 0.5; + vec2 p = vec2(x0 * rcpInputSize.x, y0 * rcpInputSize.y); + + vec3 colA[4], colB[4]; + for (int i = 0; i < 4; i++) { + colA[i] = CrtsFetch(p); + p.x += rcpInputSize.x; + } + p.y += rcpInputSize.y; + for (int i = 3; i >= 0; i--) { + p.x -= rcpInputSize.x; + colB[i] = CrtsFetch(p); + } + + float off = pos.y - y0; + float scanA = cos(min(HALF, off * thin) * PI2) * HALF + HALF; + float scanB = cos(min(HALF, (-off) * thin + thin) * PI2) * HALF + HALF; + + float off0 = pos.x - x0; + float pix[4]; + for (int i = 0; i < 4; i++) { + float diff = off0 - float(i); + pix[i] = exp2(blur * diff * diff); + } + float pixT = CrtsRcpF1(pix[0] + pix[1] + pix[2] + pix[3]); + + #ifdef CRTS_WARP + pixT *= vin; + #endif + + scanA *= pixT; + scanB *= pixT; + + vec3 color = (colA[0] * pix[0] + colA[1] * pix[1] + colA[2] * pix[2] + colA[3] * pix[3]) * scanA + (colB[0] * pix[0] + colB[1] * pix[1] + colB[2] * pix[2] + colB[3] * pix[3]) * scanB; + color *= CrtsMask(ipos, mask); + + #ifdef CRTS_TONE + float peak = max(1.0 / (256.0 * 65536.0), CrtsMax3F1(color.r, color.g, color.b)); + vec3 ratio = color * CrtsRcpF1(peak); + #ifdef CRTS_CONTRAST + peak = pow(peak, tone.x); + #endif + peak = peak * CrtsRcpF1(peak * tone.y + tone.z); + #ifdef CRTS_SATURATION + ratio = pow(ratio, vec3(tone.w)); + #endif + return ratio * peak; + #else + return color; + #endif +} + +vec3 linearToSRGB(vec3 color) { + return pow(color, vec3(1.0 / 2.2)); +} + +void main() { + vec2 fragCoord = vec2(frag_texture_coord.x, 1.0 - frag_texture_coord.y); + if (crt_emulation) { + outcolor.rgb = CrtsFilter( + fragCoord.xy * resolution, + src_image_size / resolution, + src_image_size * vec2(0.5), + 1.0 / src_image_size, + 1.0 / resolution, + 2.0 / resolution, + src_image_size.y, + vec2(1.0 / 24.0, 1.0 / 16.0), // warp value + INPUT_THIN, + INPUT_BLUR, + INPUT_MASK, + tone_data + ); + + outcolor.rgb *= brightness; + outcolor = vec4(outcolor.rgb, 1.0); // Keep original color with alpha set to 1.0 + + } else { + outcolor = texture(iChannel0, fragCoord); + } +} |
