|
| 1 | +// Ported from https://github.com/scratchfoundation/scratch-render/blob/4090e62e8abf427e55c83448da9b0df26120d2fb/src/shaders/sprite.frag |
| 2 | + |
| 3 | +#ifdef ENABLE_color |
| 4 | +uniform float u_color; |
| 5 | +#endif // ENABLE_color |
| 6 | + |
| 7 | +#ifdef ENABLE_brightness |
| 8 | +uniform float u_brightness; |
| 9 | +#endif // ENABLE_brightness |
| 10 | + |
| 11 | +#ifdef ENABLE_ghost |
| 12 | +uniform float u_ghost; |
| 13 | +#endif // ENABLE_ghost |
| 14 | + |
| 15 | +in vec2 v_texCoord; |
| 16 | +//out vec4 FragColor; |
| 17 | +uniform sampler2D u_skin; |
| 18 | + |
| 19 | +// Add this to divisors to prevent division by 0, which results in NaNs propagating through calculations. |
| 20 | +// Smaller values can cause problems on some mobile devices. |
| 21 | +const float epsilon = 1e-3; |
| 22 | + |
| 23 | +#if defined(ENABLE_color) |
| 24 | +// Branchless color conversions based on code from: |
| 25 | +// http://www.chilliant.com/rgb2hsv.html by Ian Taylor |
| 26 | +// Based in part on work by Sam Hocevar and Emil Persson |
| 27 | +// See also: https://en.wikipedia.org/wiki/HSL_and_HSV#Formal_derivation |
| 28 | + |
| 29 | + |
| 30 | +// Convert an RGB color to Hue, Saturation, and Value. |
| 31 | +// All components of input and output are expected to be in the [0,1] range. |
| 32 | +vec3 convertRGB2HSV(vec3 rgb) |
| 33 | +{ |
| 34 | + // Hue calculation has 3 cases, depending on which RGB component is largest, and one of those cases involves a "mod" |
| 35 | + // operation. In order to avoid that "mod" we split the M==R case in two: one for G<B and one for B>G. The B>G case |
| 36 | + // will be calculated in the negative and fed through abs() in the hue calculation at the end. |
| 37 | + // See also: https://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma |
| 38 | + const vec4 hueOffsets = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); |
| 39 | + |
| 40 | + // temp1.xy = sort B & G (largest first) |
| 41 | + // temp1.z = the hue offset we'll use if it turns out that R is the largest component (M==R) |
| 42 | + // temp1.w = the hue offset we'll use if it turns out that R is not the largest component (M==G or M==B) |
| 43 | + vec4 temp1 = rgb.b > rgb.g ? vec4(rgb.bg, hueOffsets.wz) : vec4(rgb.gb, hueOffsets.xy); |
| 44 | + |
| 45 | + // temp2.x = the largest component of RGB ("M" / "Max") |
| 46 | + // temp2.yw = the smaller components of RGB, ordered for the hue calculation (not necessarily sorted by magnitude!) |
| 47 | + // temp2.z = the hue offset we'll use in the hue calculation |
| 48 | + vec4 temp2 = rgb.r > temp1.x ? vec4(rgb.r, temp1.yzx) : vec4(temp1.xyw, rgb.r); |
| 49 | + |
| 50 | + // m = the smallest component of RGB ("min") |
| 51 | + float m = min(temp2.y, temp2.w); |
| 52 | + |
| 53 | + // Chroma = M - m |
| 54 | + float C = temp2.x - m; |
| 55 | + |
| 56 | + // Value = M |
| 57 | + float V = temp2.x; |
| 58 | + |
| 59 | + return vec3( |
| 60 | + abs(temp2.z + (temp2.w - temp2.y) / (6.0 * C + epsilon)), // Hue |
| 61 | + C / (temp2.x + epsilon), // Saturation |
| 62 | + V); // Value |
| 63 | +} |
| 64 | + |
| 65 | +vec3 convertHue2RGB(float hue) |
| 66 | +{ |
| 67 | + float r = abs(hue * 6.0 - 3.0) - 1.0; |
| 68 | + float g = 2.0 - abs(hue * 6.0 - 2.0); |
| 69 | + float b = 2.0 - abs(hue * 6.0 - 4.0); |
| 70 | + return clamp(vec3(r, g, b), 0.0, 1.0); |
| 71 | +} |
| 72 | + |
| 73 | +vec3 convertHSV2RGB(vec3 hsv) |
| 74 | +{ |
| 75 | + vec3 rgb = convertHue2RGB(hsv.x); |
| 76 | + float c = hsv.z * hsv.y; |
| 77 | + return rgb * c + hsv.z - c; |
| 78 | +} |
| 79 | +#endif // ENABLE_color |
| 80 | + |
| 81 | +const vec2 kCenter = vec2(0.5, 0.5); |
| 82 | + |
| 83 | +void main() |
| 84 | +{ |
| 85 | + vec4 texColor = texture2D(u_skin, v_texCoord); |
| 86 | + |
| 87 | + #ifdef ENABLE_color |
| 88 | + { |
| 89 | + vec3 hsv = convertRGB2HSV(texColor.rgb); |
| 90 | + |
| 91 | + // Force grayscale values to be slightly saturated |
| 92 | + const float minLightness = 0.11 / 2.0; |
| 93 | + const float minSaturation = 0.09; |
| 94 | + if (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness); |
| 95 | + else if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z); |
| 96 | + |
| 97 | + hsv.x = mod(hsv.x + u_color, 1.0); |
| 98 | + if (hsv.x < 0.0) hsv.x += 1.0; |
| 99 | + |
| 100 | + texColor.rgb = convertHSV2RGB(hsv); |
| 101 | + } |
| 102 | + #endif // ENABLE_color |
| 103 | + |
| 104 | + #ifdef ENABLE_brightness |
| 105 | + texColor.rgb = clamp(texColor.rgb + vec3(u_brightness), vec3(0), vec3(1)); |
| 106 | + #endif // ENABLE_brightness |
| 107 | + |
| 108 | + #ifdef ENABLE_ghost |
| 109 | + texColor *= u_ghost; |
| 110 | + #endif // ENABLE_ghost |
| 111 | + |
| 112 | + // Set RGB components to zero if the color is fully transparent |
| 113 | + // This is a workaround for rendering issues when alpha is zero |
| 114 | + if(texColor.a == 0.0) |
| 115 | + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); |
| 116 | + else |
| 117 | + gl_FragColor = texColor; |
| 118 | +} |
0 commit comments