Skip to content

Commit 9e01c65

Browse files
committed
Add shaders for color effects
1 parent 4a558a3 commit 9e01c65

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ qt_add_qml_module(scratchcpp-render
1212
internal/ValueMonitor.qml
1313
internal/MonitorSlider.qml
1414
internal/ListMonitor.qml
15+
shaders/sprite.vert
16+
shaders/sprite.frag
1517
SOURCES
1618
global.h
1719
projectloader.cpp

src/shaders/sprite.frag

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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+
}

src/shaders/sprite.vert

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
layout (location = 0) in vec2 a_position;
2+
layout (location = 1) in vec2 a_texCoord;
3+
4+
out vec2 v_texCoord;
5+
6+
void main() {
7+
gl_Position = vec4(a_position, 0.0, 1.0);
8+
v_texCoord = a_texCoord;
9+
}

0 commit comments

Comments
 (0)