-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsprite.frag
More file actions
187 lines (147 loc) · 5.62 KB
/
sprite.frag
File metadata and controls
187 lines (147 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Ported from https://github.com/scratchfoundation/scratch-render/blob/4090e62e8abf427e55c83448da9b0df26120d2fb/src/shaders/sprite.frag
#undef lowp
#undef mediump
#undef highp
precision mediump float;
#ifdef ENABLE_color
uniform float u_color;
#endif // ENABLE_color
#ifdef ENABLE_brightness
uniform float u_brightness;
#endif // ENABLE_brightness
#ifdef ENABLE_ghost
uniform float u_ghost;
#endif // ENABLE_ghost
#ifdef ENABLE_fisheye
uniform float u_fisheye;
#endif // ENABLE_fisheye
#ifdef ENABLE_whirl
uniform float u_whirl;
#endif // ENABLE_whirl
#ifdef ENABLE_pixelate
uniform float u_pixelate;
uniform vec2 u_skinSize;
#endif // ENABLE_pixelate
#ifdef ENABLE_mosaic
uniform float u_mosaic;
#endif // ENABLE_mosaic
in vec2 v_texCoord;
out vec4 fragColor;
uniform sampler2D u_skin;
// Add this to divisors to prevent division by 0, which results in NaNs propagating through calculations.
// Smaller values can cause problems on some mobile devices.
const float epsilon = 1e-3;
#if defined(ENABLE_color)
// Branchless color conversions based on code from:
// http://www.chilliant.com/rgb2hsv.html by Ian Taylor
// Based in part on work by Sam Hocevar and Emil Persson
// See also: https://en.wikipedia.org/wiki/HSL_and_HSV#Formal_derivation
// Convert an RGB color to Hue, Saturation, and Value.
// All components of input and output are expected to be in the [0,1] range.
vec3 convertRGB2HSV(vec3 rgb)
{
// Hue calculation has 3 cases, depending on which RGB component is largest, and one of those cases involves a "mod"
// 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
// will be calculated in the negative and fed through abs() in the hue calculation at the end.
// See also: https://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma
const vec4 hueOffsets = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
// temp1.xy = sort B & G (largest first)
// temp1.z = the hue offset we'll use if it turns out that R is the largest component (M==R)
// temp1.w = the hue offset we'll use if it turns out that R is not the largest component (M==G or M==B)
vec4 temp1 = rgb.b > rgb.g ? vec4(rgb.bg, hueOffsets.wz) : vec4(rgb.gb, hueOffsets.xy);
// temp2.x = the largest component of RGB ("M" / "Max")
// temp2.yw = the smaller components of RGB, ordered for the hue calculation (not necessarily sorted by magnitude!)
// temp2.z = the hue offset we'll use in the hue calculation
vec4 temp2 = rgb.r > temp1.x ? vec4(rgb.r, temp1.yzx) : vec4(temp1.xyw, rgb.r);
// m = the smallest component of RGB ("min")
float m = min(temp2.y, temp2.w);
// Chroma = M - m
float C = temp2.x - m;
// Value = M
float V = temp2.x;
return vec3(
abs(temp2.z + (temp2.w - temp2.y) / (6.0 * C + epsilon)), // Hue
C / (temp2.x + epsilon), // Saturation
V); // Value
}
vec3 convertHue2RGB(float hue)
{
float r = abs(hue * 6.0 - 3.0) - 1.0;
float g = 2.0 - abs(hue * 6.0 - 2.0);
float b = 2.0 - abs(hue * 6.0 - 4.0);
return clamp(vec3(r, g, b), 0.0, 1.0);
}
vec3 convertHSV2RGB(vec3 hsv)
{
vec3 rgb = convertHue2RGB(hsv.x);
float c = hsv.z * hsv.y;
return rgb * c + hsv.z - c;
}
#endif // ENABLE_color
const vec2 kCenter = vec2(0.5, 0.5);
void main()
{
vec2 texcoord0 = v_texCoord;
#ifdef ENABLE_mosaic
texcoord0 = fract(u_mosaic * texcoord0);
#endif // ENABLE_mosaic
#ifdef ENABLE_pixelate
{
// TODO: clean up "pixel" edges
vec2 pixelTexelSize = u_skinSize / u_pixelate;
texcoord0 = (floor(texcoord0 * pixelTexelSize) + kCenter) / pixelTexelSize;
}
#endif // ENABLE_pixelate
#ifdef ENABLE_whirl
{
const float kRadius = 0.5;
vec2 offset = texcoord0 - kCenter;
float offsetMagnitude = length(offset);
float whirlFactor = max(1.0 - (offsetMagnitude / kRadius), 0.0);
float whirlActual = u_whirl * whirlFactor * whirlFactor;
float sinWhirl = sin(whirlActual);
float cosWhirl = cos(whirlActual);
mat2 rotationMatrix = mat2(
cosWhirl, -sinWhirl,
sinWhirl, cosWhirl
);
texcoord0 = rotationMatrix * offset + kCenter;
}
#endif // ENABLE_whirl
#ifdef ENABLE_fisheye
{
vec2 vec = (texcoord0 - kCenter) / kCenter;
float vecLength = length(vec);
float r = pow(min(vecLength, 1.0), u_fisheye) * max(1.0, vecLength);
vec2 unit = vec / vecLength;
texcoord0 = kCenter + r * unit * kCenter;
}
#endif // ENABLE_fisheye
fragColor = texture(u_skin, texcoord0);
#if defined(ENABLE_color) || defined(ENABLE_brightness)
// Divide premultiplied alpha values for proper color processing
// Add epsilon to avoid dividing by 0 for fully transparent pixels
fragColor.rgb = clamp(fragColor.rgb / (fragColor.a + epsilon), 0.0, 1.0);
#ifdef ENABLE_color
{
vec3 hsv = convertRGB2HSV(fragColor.rgb);
// Force grayscale values to be slightly saturated
const float minLightness = 0.11 / 2.0;
const float minSaturation = 0.09;
if (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness);
else if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z);
hsv.x = mod(hsv.x + u_color, 1.0);
if (hsv.x < 0.0) hsv.x += 1.0;
fragColor.rgb = convertHSV2RGB(hsv);
}
#endif // ENABLE_color
#ifdef ENABLE_brightness
fragColor.rgb = clamp(fragColor.rgb + vec3(u_brightness), vec3(0), vec3(1));
#endif // ENABLE_brightness
// Re-multiply color values
fragColor.rgb *= fragColor.a + epsilon;
#endif // defined(ENABLE_color) || defined(ENABLE_brightness)
#ifdef ENABLE_ghost
fragColor *= u_ghost;
#endif // ENABLE_ghost
}