-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathsmoke-and-ghost.glsl
193 lines (153 loc) · 7.29 KB
/
smoke-and-ghost.glsl
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
188
189
190
191
192
193
// Settings for detection
#define TARGET_COLOR vec3(0.0, 0.0, 0.0) // RGB target pixels to transform
#define REPLACE_COLOR vec3(0.0, 0.0, 0.0) // Color to replace target pixels
#define COLOR_TOLERANCE 0.001 // Color matching tolerance
// Smoke effect settings
#define SMOKE_COLOR vec3(1., 1., 1.0) // Base color of smoke
#define SMOKE_RADIUS 0.011 // How far the smoke spreads
#define SMOKE_SPEED 0.5 // Speed of smoke movement
#define SMOKE_SCALE 25.0 // Scale of smoke detail
#define SMOKE_INTENSITY 0.2 // Intensity of the smoke effect
#define SMOKE_RISE_HEIGHT 0.14 // How high the smoke rises
#define ALPHA_MAX 0.5 // Maximum opacity for smoke
#define VERTICAL_BIAS 1.0
// Ghost face settings
#define FACE_COUNT 1 // Number of ghost faces
#define FACE_SCALE vec2(0.03, 0.05) // Size of faces, can be wider/elongated
#define FACE_DURATION 1.2 // How long faces last, can be wider/elongated
#define FACE_TRANSITION 1.5 // Face fade in/out duration
#define FACE_COLOR vec3(0.0, 0.0, 0.0)
#define GHOST_BG_COLOR vec3(1.0, 1.0, 1.0)
#define GHOST_BG_SCALE vec2(0.03, 0.06)
float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}
float random1(float n) {
return fract(sin(n) * 43758.5453123);
}
vec2 random2(float n) {
return vec2(
random1(n),
random1(n + 1234.5678)
);
}
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) + (c - a)* u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
// Modified elongated ellipse for more cartoon-like shapes
float cartoonEllipse(vec2 uv, vec2 center, vec2 scale) {
vec2 d = (uv - center) / scale;
float len = length(d);
// Add cartoon-like falloff
return smoothstep(1.0, 0.8, len);
}
// Function to create ghost background shape
float ghostBackground(vec2 uv, vec2 center) {
vec2 d = (uv - center) / GHOST_BG_SCALE;
float baseShape = length(d * vec2(1.0, 0.8)); // Slightly oval
// Add wavy bottom
float wave = sin(d.x * 6.28 + iTime) * 0.2;
float bottomWave = smoothstep(0.0, -0.5, d.y + wave);
return smoothstep(1.0, 0.8, baseShape) + bottomWave;
}
float ghostFace(vec2 uv, vec2 center, float time, float seed) {
vec2 faceUV = (uv - center) / FACE_SCALE;
float eyeSize = 0.25 + random1(seed) * 0.05;
float eyeSpacing = 0.35;
vec2 leftEyePos = vec2(-eyeSpacing, 0.2);
vec2 rightEyePos = vec2(eyeSpacing, 0.2);
float leftEye = cartoonEllipse(faceUV, leftEyePos, vec2(eyeSize));
float rightEye = cartoonEllipse(faceUV, rightEyePos, vec2(eyeSize));
// Add simple eye highlights
float leftHighlight = cartoonEllipse(faceUV, leftEyePos + vec2(0.1, 0.1), vec2(eyeSize * 0.3));
float rightHighlight = cartoonEllipse(faceUV, rightEyePos + vec2(0.1, 0.1), vec2(eyeSize * 0.3));
vec2 mouthUV = faceUV - vec2(0.0, -0.9);
float mouthWidth = 0.5 + random1(seed + 3.0) * 0.1;
float mouthHeight = 0.8 + random1(seed + 7.0) * 0.1;
float mouth = cartoonEllipse(mouthUV, vec2(0.0), vec2(mouthWidth, mouthHeight));
// Combine features
float face = max(max(leftEye, rightEye), mouth);
face = max(face, max(leftHighlight, rightHighlight));
// Add border falloff
face *= smoothstep(1.2, 0.8, length(faceUV));
return face;
}
float calculateSmoke(vec2 uv, vec2 sourcePos) {
float verticalDisp = (uv.y - sourcePos.y) * VERTICAL_BIAS;
vec2 smokeUV = uv * SMOKE_SCALE;
smokeUV.y -= iTime * SMOKE_SPEED * (1.0 + verticalDisp);
smokeUV.x += sin(iTime * 0.5 + uv.y * 4.0) * 0.1;
float n = noise(smokeUV) * 0.5 + 0.5;
n += noise(smokeUV * 2.0 + iTime * 0.1) * 0.25;
float verticalFalloff = 1.0 - smoothstep(0.0, SMOKE_RISE_HEIGHT, verticalDisp);
return n * verticalFalloff;
}
float isTargetPixel(vec2 uv) {
vec4 color = texture(iChannel0, uv);
return float(all(lessThan(abs(color.rgb - TARGET_COLOR), vec3(COLOR_TOLERANCE))));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord/iResolution.xy;
vec4 originalColor = texture(iChannel0, uv);
// Calculate smoke effect
float smokeAccum = 0.0;
float targetInfluence = 0.0;
float stepSize = SMOKE_RADIUS / 4.0;
for (float x = -SMOKE_RADIUS; x <= SMOKE_RADIUS; x += stepSize) {
for (float y = -SMOKE_RADIUS; y <= 0.0; y += stepSize) {
vec2 offset = vec2(x, y);
vec2 sampleUV = uv + offset;
if (sampleUV.x >= 0.0 && sampleUV.x <= 1.0 &&
sampleUV.y >= 0.0 && sampleUV.y <= 1.0) {
float isTarget = isTargetPixel(sampleUV);
if (isTarget > 0.0) {
float dist = length(offset);
float falloff = 1.0 - smoothstep(0.0, SMOKE_RADIUS, dist);
float smoke = calculateSmoke(uv, sampleUV);
smokeAccum += smoke * falloff;
targetInfluence += falloff;
}
}
}
}
smokeAccum /= max(targetInfluence, 1.0);
targetInfluence = smoothstep(0.0, 1.0, targetInfluence);
float smokePresence = smokeAccum * targetInfluence;
// Calculate ghost faces with backgrounds
float faceAccum = 0.0;
float bgAccum = 0.0;
float timeBlock = floor(iTime / FACE_DURATION);
if (smokePresence > 0.2) {
for (int i = 0; i < FACE_COUNT; i++) {
vec2 facePos = random2(timeBlock + float(i) * 1234.5);
facePos = facePos * 0.8 + 0.1;
float faceTime = mod(iTime, FACE_DURATION);
float fadeFactor = smoothstep(0.0, FACE_TRANSITION, faceTime) *
(1.0 - smoothstep(FACE_DURATION - FACE_TRANSITION, FACE_DURATION, faceTime));
// Add ghost background
float ghostBg = ghostBackground(uv, facePos) * fadeFactor;
bgAccum = max(bgAccum, ghostBg);
// Add face features
float face = ghostFace(uv, facePos, iTime, timeBlock + float(i) * 100.0) * fadeFactor;
faceAccum = max(faceAccum, face);
}
bgAccum *= smoothstep(0.2, 0.4, smokePresence);
faceAccum *= smoothstep(0.2, 0.4, smokePresence);
}
// Combine all elements
bool isTarget = all(lessThan(abs(originalColor.rgb - TARGET_COLOR), vec3(COLOR_TOLERANCE)));
vec3 baseColor = isTarget ? REPLACE_COLOR : originalColor.rgb;
// Layer the effects: base -> smoke -> ghost background -> face features
vec3 smokeEffect = mix(baseColor, SMOKE_COLOR, smokeAccum * SMOKE_INTENSITY * targetInfluence * (1.0 - faceAccum));
vec3 withBackground = mix(smokeEffect, GHOST_BG_COLOR, bgAccum * 0.7);
vec3 finalColor = mix(withBackground, FACE_COLOR, faceAccum);
float alpha = mix(originalColor.a, ALPHA_MAX, max(smokePresence, max(bgAccum, faceAccum) * smokePresence));
fragColor = vec4(finalColor, alpha);
}