mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-08-10 15:17:45 +02:00
122 lines
3.4 KiB
GLSL
122 lines
3.4 KiB
GLSL
const int FILTER_UNSHARP_MASKING = 1;
|
|
const int FILTER_CAS = 2;
|
|
|
|
precision highp float;
|
|
uniform sampler2D data;
|
|
uniform vec2 iResolution;
|
|
|
|
uniform int filterId;
|
|
uniform float sharpenFactor;
|
|
uniform float brightness;
|
|
uniform float contrast;
|
|
uniform float saturation;
|
|
|
|
vec3 textureAt(sampler2D tex, vec2 coord) {
|
|
return texture2D(tex, coord / iResolution.xy).rgb;
|
|
}
|
|
|
|
vec3 clarityBoost(sampler2D tex, vec2 coord)
|
|
{
|
|
// Load a collection of samples in a 3x3 neighorhood, where e is the current pixel.
|
|
// a b c
|
|
// d e f
|
|
// g h i
|
|
vec3 a = textureAt(tex, coord + vec2(-1, 1));
|
|
vec3 b = textureAt(tex, coord + vec2(0, 1));
|
|
vec3 c = textureAt(tex, coord + vec2(1, 1));
|
|
|
|
vec3 d = textureAt(tex, coord + vec2(-1, 0));
|
|
vec3 e = textureAt(tex, coord);
|
|
vec3 f = textureAt(tex, coord + vec2(1, 0));
|
|
|
|
vec3 g = textureAt(tex, coord + vec2(-1, -1));
|
|
vec3 h = textureAt(tex, coord + vec2(0, -1));
|
|
vec3 i = textureAt(tex, coord + vec2(1, -1));
|
|
|
|
if (filterId == FILTER_CAS) {
|
|
// Soft min and max.
|
|
// a b c b
|
|
// d e f * 0.5 + d e f * 0.5
|
|
// g h i h
|
|
// These are 2.0x bigger (factored out the extra multiply).
|
|
vec3 minRgb = min(min(min(d, e), min(f, b)), h);
|
|
vec3 minRgb2 = min(min(a, c), min(g, i));
|
|
minRgb += min(minRgb, minRgb2);
|
|
|
|
vec3 maxRgb = max(max(max(d, e), max(f, b)), h);
|
|
vec3 maxRgb2 = max(max(a, c), max(g, i));
|
|
maxRgb += max(maxRgb, maxRgb2);
|
|
|
|
// Smooth minimum distance to signal limit divided by smooth max.
|
|
vec3 reciprocalMaxRgb = 1.0 / maxRgb;
|
|
vec3 amplifyRgb = clamp(min(minRgb, 2.0 - maxRgb) * reciprocalMaxRgb, 0.0, 1.0);
|
|
|
|
// Shaping amount of sharpening.
|
|
amplifyRgb = inversesqrt(amplifyRgb);
|
|
|
|
float contrast = 0.8;
|
|
float peak = -3.0 * contrast + 8.0;
|
|
vec3 weightRgb = -(1.0 / (amplifyRgb * peak));
|
|
|
|
vec3 reciprocalWeightRgb = 1.0 / (4.0 * weightRgb + 1.0);
|
|
|
|
// 0 w 0
|
|
// Filter shape: w 1 w
|
|
// 0 w 0
|
|
vec3 window = (b + d) + (f + h);
|
|
vec3 outColor = clamp((window * weightRgb + e) * reciprocalWeightRgb, 0.0, 1.0);
|
|
|
|
outColor = mix(e, outColor, sharpenFactor / 2.0);
|
|
|
|
return outColor;
|
|
} else if (filterId == FILTER_UNSHARP_MASKING) {
|
|
vec3 gaussianBlur = (a * 1.0 + b * 2.0 + c * 1.0 +
|
|
d * 2.0 + e * 4.0 + f * 2.0 +
|
|
g * 1.0 + h * 2.0 + i * 1.0) / 16.0;
|
|
|
|
// Return edge detection
|
|
return e + (e - gaussianBlur) * sharpenFactor / 3.0;
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
vec3 adjustBrightness(vec3 color) {
|
|
return (1.0 + brightness) * color;
|
|
}
|
|
|
|
vec3 adjustContrast(vec3 color) {
|
|
return 0.5 + (1.0 + contrast) * (color - 0.5);
|
|
}
|
|
|
|
vec3 adjustSaturation(vec3 color) {
|
|
const vec3 luminosityFactor = vec3(0.2126, 0.7152, 0.0722);
|
|
vec3 grayscale = vec3(dot(color, luminosityFactor));
|
|
|
|
return mix(grayscale, color, 1.0 + saturation);
|
|
}
|
|
|
|
void main() {
|
|
vec3 color;
|
|
|
|
if (sharpenFactor > 0.0) {
|
|
color = clarityBoost(data, gl_FragCoord.xy);
|
|
} else {
|
|
color = textureAt(data, gl_FragCoord.xy);
|
|
}
|
|
|
|
if (saturation != 0.0) {
|
|
color = adjustSaturation(color);
|
|
}
|
|
|
|
if (contrast != 0.0) {
|
|
color = adjustContrast(color);
|
|
}
|
|
|
|
if (brightness != 0.0) {
|
|
color = adjustBrightness(color);
|
|
}
|
|
|
|
gl_FragColor = vec4(color, 1.0);
|
|
}
|