@dopaminefx/effect-fail 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fail-shader.d.ts +19 -0
- package/dist/fail-shader.d.ts.map +1 -0
- package/dist/fail-shader.js +196 -0
- package/dist/fail-shader.js.map +1 -0
- package/dist/fail.dope.json +523 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
- package/src/fail-shader.ts +205 -0
- package/src/fail.dope.json +523 -0
- package/src/index.ts +56 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLSL ES 3.00 source for the FAIL / error effect — the emotional OPPOSITE of
|
|
3
|
+
* the three success effects.
|
|
4
|
+
*
|
|
5
|
+
* A red/amber ✗ cross is STAMPED in light over a tight, recoiling error flare;
|
|
6
|
+
* the whole frame desaturates and collapses fast. A new shader (the fail *feel*
|
|
7
|
+
* is distinct), but it borrows the shared look chunks (hash/dither/tonemap/
|
|
8
|
+
* palette/segment-SDF) and — crucially — its ✗ ICON is sampled from the baked
|
|
9
|
+
* SDF (the Phase-1 geometry seam, uSdfTex), driven by the .dope `svgPath`.
|
|
10
|
+
*
|
|
11
|
+
* Light pass (screen blend): the cross + a hot rim + a short angry flare summed
|
|
12
|
+
* as light. Shadow pass (multiply): the cross silhouette + flare mass, so the
|
|
13
|
+
* error casts real light AND shadow on the page beneath. whimsy is the
|
|
14
|
+
* photoreal↔glitch axis: 0 = soft photographic flare; 1 = a desaturated,
|
|
15
|
+
* RGB-split, scanline GLITCH collapse (cel/stylized error).
|
|
16
|
+
*/
|
|
17
|
+
export declare const FAIL_VERTEX_SRC = "#version 300 es\nvoid main() {\n vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));\n gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);\n}";
|
|
18
|
+
export declare const FAIL_FRAGMENT_SRC = "#version 300 es\nprecision highp float;\nout vec4 fragColor;\n\nuniform vec2 uResolution; // device pixels\nuniform vec2 uOrigin; // cross center, gl coords (y up)\nuniform float uAmp; // fail envelope amplitude 0..1\nuniform float uStamp; // \u2717 stamp/slash progress 0..1\nuniform float uLife; // total normalized progress 0..1\nuniform float uTimeS; // elapsed seconds\nuniform float uShake; // signed recoil shake (-1..1), pre-scaled by intensity\nuniform float uExposure;\nuniform float uSeverity; // 0..1 intensity (size/heat of the flare + rim)\nuniform float uStyle; // 0..1 photoreal -> glitch/desaturated collapse\nuniform float uShadow; // 0 = light pass (screen), 1 = shadow pass (multiply)\nuniform vec2 uShadowOffset;\nuniform float uShadowSoft;\nuniform float uShadowStrength;\nuniform vec3 uC0; // error palette (hot core)\nuniform vec3 uC1; // mid\nuniform vec3 uC2; // outer/accent\nuniform sampler2D uSdfTex; // baked \u2717 outline SDF (R = normalized distance)\nuniform float uSdfOn; // 1 = drive the cross from the baked SDF\nuniform float uSdfRangePx; // device px mapping to the SDF's 0..1 distance range\nuniform float uSdfStrokePx; // half stroke width (device px)\nuniform float uBoxPx; // half-size of the SDF box around uOrigin (device px)\n\n\n#define TAU 6.28318530718\n\n\nfloat hash11(float p){ p = fract(p * 0.1031); p *= p + 33.33; p *= p + p; return fract(p); }\nvec2 hash21(float p){\n vec3 p3 = fract(vec3(p) * vec3(0.1031, 0.1030, 0.0973));\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.xx + p3.yz) * p3.zy);\n}\n\n\nvec3 paletteMix(float t){\n t = clamp(t, 0.0, 1.0);\n return t < 0.5 ? mix(uC0, uC1, t * 2.0) : mix(uC1, uC2, (t - 0.5) * 2.0);\n}\n\n\nfloat sdSeg(vec2 p, vec2 a, vec2 b){\n vec2 pa = p - a, ba = b - a;\n float h = clamp(dot(pa, ba) / max(dot(ba, ba), 1e-3), 0.0, 1.0);\n return length(pa - ba * h);\n}\n\n\nvec3 tonemapACES(vec3 x){\n const float a = 2.51, b = 0.03, c = 2.43, d = 0.59, e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);\n}\n\n\nvec3 ditherAdd(vec3 col, vec2 frag, float t, float fade){\n float dz = hash11(dot(frag, vec2(12.989, 78.233)) + t) - 0.5;\n return col + (dz / 255.0) * fade;\n}\n\n\n// Map a device-pixel sample to the SDF box UV (origin bottom-left, y up).\nvec2 boxUV(vec2 frag){ return (frag - uOrigin) / (2.0 * uBoxPx) + 0.5; }\n\n// \u2717 stroke distance (device px) from the baked SDF, or an analytic two-bar\n// fallback when no SDF is bound. Both reveal as a fast diagonal \"slash\" stamp.\nfloat crossDist(vec2 frag){\n if (uSdfOn > 0.5) {\n vec2 uv = boxUV(frag);\n if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return 1e9;\n return texture(uSdfTex, uv).r * uSdfRangePx;\n }\n // Analytic \u2717: two diagonal bars (fallback if the SDF failed to bake/load).\n float r = uBoxPx * 0.62;\n vec2 a1 = uOrigin + vec2(-r, -r), b1 = uOrigin + vec2(r, r);\n vec2 a2 = uOrigin + vec2(-r, r), b2 = uOrigin + vec2(r, -r);\n return min(sdSeg(frag, a1, b1), sdSeg(frag, a2, b2));\n}\n\n// The \u2717 is stamped along a diagonal slash: the \\ bar reveals first, then the /.\n// Returns a 0..1 reveal gate at this point given the stamp progress.\nfloat stampGate(vec2 frag){\n vec2 uv = boxUV(frag) - 0.5; // -0.5..0.5\n // Slash axis: lower-left -> upper-right then the second bar. Use |.| so both\n // diagonals fill outward from the center as the stamp lands.\n float axis = clamp(0.5 + 0.5 * (abs(uv.x) + abs(uv.y)), 0.0, 1.0);\n float frontier = uStamp * 1.15;\n return smoothstep(frontier, frontier - 0.12, axis);\n}\n\n// Tight angry error flare around the cross \u2014 collapses with uAmp. Hotter +\n// larger with severity. Unlike the bloom, this stays compact and punchy.\nfloat flare(vec2 frag, float minDim){\n float d = length(frag - uOrigin);\n float r = minDim * mix(0.16, 0.30, uSeverity);\n float dn = d / r;\n return (exp(-dn * dn * 2.2) * 0.9 + exp(-dn * 1.6) * 0.25);\n}\n\nfloat occlusion(vec2 p, float minDim){\n float occ = flare(p, minDim) * 0.7;\n float dc = crossDist(p);\n occ += (1.0 - smoothstep(uSdfStrokePx * 0.6, uSdfStrokePx * 1.5, dc)) * stampGate(p) * 0.9;\n return clamp(occ * uAmp, 0.0, 1.0);\n}\n\nvec4 shadowColor(vec2 frag){\n float minDim = min(uResolution.x, uResolution.y);\n vec2 sp = frag - uShadowOffset;\n float occ = occlusion(sp, minDim);\n float s = uShadowSoft;\n occ += occlusion(sp + vec2(s,0.0), minDim);\n occ += occlusion(sp + vec2(-s,0.0), minDim);\n occ += occlusion(sp + vec2(0.0,s), minDim);\n occ += occlusion(sp + vec2(0.0,-s), minDim);\n occ /= 5.0;\n float dark = clamp(occ, 0.0, 1.0) * uShadowStrength;\n // A cold, slightly desaturated shadow tint (error grey, not coloured glow).\n vec3 tint = mix(vec3(1.0), vec3(0.72, 0.66, 0.66), 1.0);\n vec3 mul = mix(vec3(1.0), tint, dark);\n return vec4(mul, 1.0);\n}\n\nvoid main(){\n vec2 frag = gl_FragCoord.xy;\n float minDim = min(uResolution.x, uResolution.y);\n\n if (uShadow > 0.5) { fragColor = shadowColor(frag); return; }\n\n // Recoil SHAKE: jitter the whole sample horizontally (a \"no\" head-shake), plus\n // a per-frame glitch slice offset toward the stylized end.\n float shakePx = uShake * minDim * 0.012;\n float glitch = 0.0;\n if (uStyle > 0.001) {\n float band = floor(frag.y / max(2.0, minDim * 0.02));\n float g = hash11(band + floor(uTimeS * 30.0));\n glitch = (step(0.82, g) * (g - 0.82) / 0.18) * minDim * 0.05 * uStyle * uAmp;\n }\n vec2 sf = frag - vec2(shakePx + glitch, 0.0);\n\n vec3 col = vec3(0.0);\n\n // ---- Angry error flare (summed as light) --------------------------------\n // The error palette is generated OKLCH biased to reds/ambers; we keep the\n // flare IN-BAND by ramping the HOT core hue (uC0) from bright at the center to\n // a deeper ember toward the rim (instead of fanning to the golden-angle stops,\n // which would drift out of the error band). uC0 still varies per fire.\n float fl = flare(sf, minDim);\n float rn = clamp(length(sf - uOrigin) / (minDim * 0.3), 0.0, 1.0);\n vec3 ember = uC0 * mix(1.0, 0.45, rn); // bright core \u2192 deep ember rim\n col += ember * fl * uAmp * uExposure * mix(0.9, 1.25, uSeverity);\n\n // ---- The \u2717 cross, stamped in light --------------------------------------\n float dc = crossDist(sf);\n float gate = stampGate(sf);\n float sw = uSdfStrokePx;\n float soft = smoothstep(sw, sw * 0.3, dc);\n float hard = 1.0 - smoothstep(sw * 0.85, sw, dc);\n float core = mix(soft, hard, uStyle) * gate;\n float rim = exp(-dc / (sw * 2.2)) * 0.7 * gate;\n // The baked SDF saturates (distance clamps) beyond its encoded range, so the\n // soft rim's exp() never reaches zero inside the box \u2014 leaving a faint ghost\n // BOX fill over the whole SDF region. Fade the rim out at the range edge so it\n // stays a glow around the strokes, not a box. (No-op for the analytic fallback,\n // whose distance is unbounded.)\n rim *= 1.0 - smoothstep(uSdfRangePx * 0.55, uSdfRangePx * 0.9, dc);\n // The cross is the unambiguous \"no\" \u2014 it must out-shine the flare. Hot white\n // core biased toward the error hue; a sharp rim sells the stamp.\n vec3 crossTint = mix(vec3(1.0), uC0 + 0.35, 0.5);\n float collapse = 1.0 - smoothstep(0.6, 1.0, uLife);\n col += (vec3(1.0) * core * 1.7 + crossTint * rim) * collapse * uExposure;\n\n // A hot stamp FLASH at the instant of impact (first ~1/3 of the stamp).\n float flash = exp(-uStamp * 6.0) * (1.0 - uStamp);\n col += crossTint * flash * core * 1.2 * uExposure;\n\n // ---- Filmic tonemap -----------------------------------------------------\n col = tonemapACES(col * 0.7);\n\n // ---- Stylized GLITCH / DESATURATE collapse (whimsy) ----------------------\n if (uStyle > 0.001) {\n // RGB split along the shake axis.\n float sep = minDim * 0.004 * uStyle * uAmp;\n float dr = crossDist(sf - vec2(sep, 0.0));\n float db = crossDist(sf + vec2(sep, 0.0));\n float gr = (1.0 - smoothstep(sw*0.85, sw, dr)) * gate * collapse;\n float gb = (1.0 - smoothstep(sw*0.85, sw, db)) * gate * collapse;\n col.r = max(col.r, gr * 1.2 * uExposure);\n col.b = max(col.b, gb * 1.2 * uExposure);\n // Desaturate the whole frame toward a sick grey as it collapses.\n float l = dot(col, vec3(0.299, 0.587, 0.114));\n col = mix(col, vec3(l), uStyle * 0.5 * smoothstep(0.4, 1.0, uLife));\n // Scanlines.\n float scan = 0.92 + 0.08 * sin(frag.y * 3.14159);\n col *= mix(1.0, scan, uStyle * 0.6);\n }\n\n col = ditherAdd(col, frag, uTimeS, 1.0 - uStyle);\n fragColor = vec4(col, 1.0);\n}";
|
|
19
|
+
//# sourceMappingURL=fail-shader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fail-shader.d.ts","sourceRoot":"","sources":["../src/fail-shader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAWH,eAAO,MAAM,eAAe,mKAI1B,CAAC;AAEH,eAAO,MAAM,iBAAiB,+hRA4K5B,CAAC"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLSL ES 3.00 source for the FAIL / error effect — the emotional OPPOSITE of
|
|
3
|
+
* the three success effects.
|
|
4
|
+
*
|
|
5
|
+
* A red/amber ✗ cross is STAMPED in light over a tight, recoiling error flare;
|
|
6
|
+
* the whole frame desaturates and collapses fast. A new shader (the fail *feel*
|
|
7
|
+
* is distinct), but it borrows the shared look chunks (hash/dither/tonemap/
|
|
8
|
+
* palette/segment-SDF) and — crucially — its ✗ ICON is sampled from the baked
|
|
9
|
+
* SDF (the Phase-1 geometry seam, uSdfTex), driven by the .dope `svgPath`.
|
|
10
|
+
*
|
|
11
|
+
* Light pass (screen blend): the cross + a hot rim + a short angry flare summed
|
|
12
|
+
* as light. Shadow pass (multiply): the cross silhouette + flare mass, so the
|
|
13
|
+
* error casts real light AND shadow on the page beneath. whimsy is the
|
|
14
|
+
* photoreal↔glitch axis: 0 = soft photographic flare; 1 = a desaturated,
|
|
15
|
+
* RGB-split, scanline GLITCH collapse (cel/stylized error).
|
|
16
|
+
*/
|
|
17
|
+
import { GLSL_CONSTANTS, GLSL_DITHER, GLSL_HASH, GLSL_PALETTE_MIX, GLSL_SD_SEG, GLSL_TONEMAP_ACES, } from "@dopaminefx/core";
|
|
18
|
+
export const FAIL_VERTEX_SRC = /* glsl */ `#version 300 es
|
|
19
|
+
void main() {
|
|
20
|
+
vec2 pos = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
|
21
|
+
gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
|
|
22
|
+
}`;
|
|
23
|
+
export const FAIL_FRAGMENT_SRC = /* glsl */ `#version 300 es
|
|
24
|
+
precision highp float;
|
|
25
|
+
out vec4 fragColor;
|
|
26
|
+
|
|
27
|
+
uniform vec2 uResolution; // device pixels
|
|
28
|
+
uniform vec2 uOrigin; // cross center, gl coords (y up)
|
|
29
|
+
uniform float uAmp; // fail envelope amplitude 0..1
|
|
30
|
+
uniform float uStamp; // ✗ stamp/slash progress 0..1
|
|
31
|
+
uniform float uLife; // total normalized progress 0..1
|
|
32
|
+
uniform float uTimeS; // elapsed seconds
|
|
33
|
+
uniform float uShake; // signed recoil shake (-1..1), pre-scaled by intensity
|
|
34
|
+
uniform float uExposure;
|
|
35
|
+
uniform float uSeverity; // 0..1 intensity (size/heat of the flare + rim)
|
|
36
|
+
uniform float uStyle; // 0..1 photoreal -> glitch/desaturated collapse
|
|
37
|
+
uniform float uShadow; // 0 = light pass (screen), 1 = shadow pass (multiply)
|
|
38
|
+
uniform vec2 uShadowOffset;
|
|
39
|
+
uniform float uShadowSoft;
|
|
40
|
+
uniform float uShadowStrength;
|
|
41
|
+
uniform vec3 uC0; // error palette (hot core)
|
|
42
|
+
uniform vec3 uC1; // mid
|
|
43
|
+
uniform vec3 uC2; // outer/accent
|
|
44
|
+
uniform sampler2D uSdfTex; // baked ✗ outline SDF (R = normalized distance)
|
|
45
|
+
uniform float uSdfOn; // 1 = drive the cross from the baked SDF
|
|
46
|
+
uniform float uSdfRangePx; // device px mapping to the SDF's 0..1 distance range
|
|
47
|
+
uniform float uSdfStrokePx; // half stroke width (device px)
|
|
48
|
+
uniform float uBoxPx; // half-size of the SDF box around uOrigin (device px)
|
|
49
|
+
|
|
50
|
+
${GLSL_CONSTANTS}
|
|
51
|
+
${GLSL_HASH}
|
|
52
|
+
${GLSL_PALETTE_MIX}
|
|
53
|
+
${GLSL_SD_SEG}
|
|
54
|
+
${GLSL_TONEMAP_ACES}
|
|
55
|
+
${GLSL_DITHER}
|
|
56
|
+
|
|
57
|
+
// Map a device-pixel sample to the SDF box UV (origin bottom-left, y up).
|
|
58
|
+
vec2 boxUV(vec2 frag){ return (frag - uOrigin) / (2.0 * uBoxPx) + 0.5; }
|
|
59
|
+
|
|
60
|
+
// ✗ stroke distance (device px) from the baked SDF, or an analytic two-bar
|
|
61
|
+
// fallback when no SDF is bound. Both reveal as a fast diagonal "slash" stamp.
|
|
62
|
+
float crossDist(vec2 frag){
|
|
63
|
+
if (uSdfOn > 0.5) {
|
|
64
|
+
vec2 uv = boxUV(frag);
|
|
65
|
+
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return 1e9;
|
|
66
|
+
return texture(uSdfTex, uv).r * uSdfRangePx;
|
|
67
|
+
}
|
|
68
|
+
// Analytic ✗: two diagonal bars (fallback if the SDF failed to bake/load).
|
|
69
|
+
float r = uBoxPx * 0.62;
|
|
70
|
+
vec2 a1 = uOrigin + vec2(-r, -r), b1 = uOrigin + vec2(r, r);
|
|
71
|
+
vec2 a2 = uOrigin + vec2(-r, r), b2 = uOrigin + vec2(r, -r);
|
|
72
|
+
return min(sdSeg(frag, a1, b1), sdSeg(frag, a2, b2));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// The ✗ is stamped along a diagonal slash: the \\ bar reveals first, then the /.
|
|
76
|
+
// Returns a 0..1 reveal gate at this point given the stamp progress.
|
|
77
|
+
float stampGate(vec2 frag){
|
|
78
|
+
vec2 uv = boxUV(frag) - 0.5; // -0.5..0.5
|
|
79
|
+
// Slash axis: lower-left -> upper-right then the second bar. Use |.| so both
|
|
80
|
+
// diagonals fill outward from the center as the stamp lands.
|
|
81
|
+
float axis = clamp(0.5 + 0.5 * (abs(uv.x) + abs(uv.y)), 0.0, 1.0);
|
|
82
|
+
float frontier = uStamp * 1.15;
|
|
83
|
+
return smoothstep(frontier, frontier - 0.12, axis);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Tight angry error flare around the cross — collapses with uAmp. Hotter +
|
|
87
|
+
// larger with severity. Unlike the bloom, this stays compact and punchy.
|
|
88
|
+
float flare(vec2 frag, float minDim){
|
|
89
|
+
float d = length(frag - uOrigin);
|
|
90
|
+
float r = minDim * mix(0.16, 0.30, uSeverity);
|
|
91
|
+
float dn = d / r;
|
|
92
|
+
return (exp(-dn * dn * 2.2) * 0.9 + exp(-dn * 1.6) * 0.25);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
float occlusion(vec2 p, float minDim){
|
|
96
|
+
float occ = flare(p, minDim) * 0.7;
|
|
97
|
+
float dc = crossDist(p);
|
|
98
|
+
occ += (1.0 - smoothstep(uSdfStrokePx * 0.6, uSdfStrokePx * 1.5, dc)) * stampGate(p) * 0.9;
|
|
99
|
+
return clamp(occ * uAmp, 0.0, 1.0);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
vec4 shadowColor(vec2 frag){
|
|
103
|
+
float minDim = min(uResolution.x, uResolution.y);
|
|
104
|
+
vec2 sp = frag - uShadowOffset;
|
|
105
|
+
float occ = occlusion(sp, minDim);
|
|
106
|
+
float s = uShadowSoft;
|
|
107
|
+
occ += occlusion(sp + vec2(s,0.0), minDim);
|
|
108
|
+
occ += occlusion(sp + vec2(-s,0.0), minDim);
|
|
109
|
+
occ += occlusion(sp + vec2(0.0,s), minDim);
|
|
110
|
+
occ += occlusion(sp + vec2(0.0,-s), minDim);
|
|
111
|
+
occ /= 5.0;
|
|
112
|
+
float dark = clamp(occ, 0.0, 1.0) * uShadowStrength;
|
|
113
|
+
// A cold, slightly desaturated shadow tint (error grey, not coloured glow).
|
|
114
|
+
vec3 tint = mix(vec3(1.0), vec3(0.72, 0.66, 0.66), 1.0);
|
|
115
|
+
vec3 mul = mix(vec3(1.0), tint, dark);
|
|
116
|
+
return vec4(mul, 1.0);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
void main(){
|
|
120
|
+
vec2 frag = gl_FragCoord.xy;
|
|
121
|
+
float minDim = min(uResolution.x, uResolution.y);
|
|
122
|
+
|
|
123
|
+
if (uShadow > 0.5) { fragColor = shadowColor(frag); return; }
|
|
124
|
+
|
|
125
|
+
// Recoil SHAKE: jitter the whole sample horizontally (a "no" head-shake), plus
|
|
126
|
+
// a per-frame glitch slice offset toward the stylized end.
|
|
127
|
+
float shakePx = uShake * minDim * 0.012;
|
|
128
|
+
float glitch = 0.0;
|
|
129
|
+
if (uStyle > 0.001) {
|
|
130
|
+
float band = floor(frag.y / max(2.0, minDim * 0.02));
|
|
131
|
+
float g = hash11(band + floor(uTimeS * 30.0));
|
|
132
|
+
glitch = (step(0.82, g) * (g - 0.82) / 0.18) * minDim * 0.05 * uStyle * uAmp;
|
|
133
|
+
}
|
|
134
|
+
vec2 sf = frag - vec2(shakePx + glitch, 0.0);
|
|
135
|
+
|
|
136
|
+
vec3 col = vec3(0.0);
|
|
137
|
+
|
|
138
|
+
// ---- Angry error flare (summed as light) --------------------------------
|
|
139
|
+
// The error palette is generated OKLCH biased to reds/ambers; we keep the
|
|
140
|
+
// flare IN-BAND by ramping the HOT core hue (uC0) from bright at the center to
|
|
141
|
+
// a deeper ember toward the rim (instead of fanning to the golden-angle stops,
|
|
142
|
+
// which would drift out of the error band). uC0 still varies per fire.
|
|
143
|
+
float fl = flare(sf, minDim);
|
|
144
|
+
float rn = clamp(length(sf - uOrigin) / (minDim * 0.3), 0.0, 1.0);
|
|
145
|
+
vec3 ember = uC0 * mix(1.0, 0.45, rn); // bright core → deep ember rim
|
|
146
|
+
col += ember * fl * uAmp * uExposure * mix(0.9, 1.25, uSeverity);
|
|
147
|
+
|
|
148
|
+
// ---- The ✗ cross, stamped in light --------------------------------------
|
|
149
|
+
float dc = crossDist(sf);
|
|
150
|
+
float gate = stampGate(sf);
|
|
151
|
+
float sw = uSdfStrokePx;
|
|
152
|
+
float soft = smoothstep(sw, sw * 0.3, dc);
|
|
153
|
+
float hard = 1.0 - smoothstep(sw * 0.85, sw, dc);
|
|
154
|
+
float core = mix(soft, hard, uStyle) * gate;
|
|
155
|
+
float rim = exp(-dc / (sw * 2.2)) * 0.7 * gate;
|
|
156
|
+
// The baked SDF saturates (distance clamps) beyond its encoded range, so the
|
|
157
|
+
// soft rim's exp() never reaches zero inside the box — leaving a faint ghost
|
|
158
|
+
// BOX fill over the whole SDF region. Fade the rim out at the range edge so it
|
|
159
|
+
// stays a glow around the strokes, not a box. (No-op for the analytic fallback,
|
|
160
|
+
// whose distance is unbounded.)
|
|
161
|
+
rim *= 1.0 - smoothstep(uSdfRangePx * 0.55, uSdfRangePx * 0.9, dc);
|
|
162
|
+
// The cross is the unambiguous "no" — it must out-shine the flare. Hot white
|
|
163
|
+
// core biased toward the error hue; a sharp rim sells the stamp.
|
|
164
|
+
vec3 crossTint = mix(vec3(1.0), uC0 + 0.35, 0.5);
|
|
165
|
+
float collapse = 1.0 - smoothstep(0.6, 1.0, uLife);
|
|
166
|
+
col += (vec3(1.0) * core * 1.7 + crossTint * rim) * collapse * uExposure;
|
|
167
|
+
|
|
168
|
+
// A hot stamp FLASH at the instant of impact (first ~1/3 of the stamp).
|
|
169
|
+
float flash = exp(-uStamp * 6.0) * (1.0 - uStamp);
|
|
170
|
+
col += crossTint * flash * core * 1.2 * uExposure;
|
|
171
|
+
|
|
172
|
+
// ---- Filmic tonemap -----------------------------------------------------
|
|
173
|
+
col = tonemapACES(col * 0.7);
|
|
174
|
+
|
|
175
|
+
// ---- Stylized GLITCH / DESATURATE collapse (whimsy) ----------------------
|
|
176
|
+
if (uStyle > 0.001) {
|
|
177
|
+
// RGB split along the shake axis.
|
|
178
|
+
float sep = minDim * 0.004 * uStyle * uAmp;
|
|
179
|
+
float dr = crossDist(sf - vec2(sep, 0.0));
|
|
180
|
+
float db = crossDist(sf + vec2(sep, 0.0));
|
|
181
|
+
float gr = (1.0 - smoothstep(sw*0.85, sw, dr)) * gate * collapse;
|
|
182
|
+
float gb = (1.0 - smoothstep(sw*0.85, sw, db)) * gate * collapse;
|
|
183
|
+
col.r = max(col.r, gr * 1.2 * uExposure);
|
|
184
|
+
col.b = max(col.b, gb * 1.2 * uExposure);
|
|
185
|
+
// Desaturate the whole frame toward a sick grey as it collapses.
|
|
186
|
+
float l = dot(col, vec3(0.299, 0.587, 0.114));
|
|
187
|
+
col = mix(col, vec3(l), uStyle * 0.5 * smoothstep(0.4, 1.0, uLife));
|
|
188
|
+
// Scanlines.
|
|
189
|
+
float scan = 0.92 + 0.08 * sin(frag.y * 3.14159);
|
|
190
|
+
col *= mix(1.0, scan, uStyle * 0.6);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
col = ditherAdd(col, frag, uTimeS, 1.0 - uStyle);
|
|
194
|
+
fragColor = vec4(col, 1.0);
|
|
195
|
+
}`;
|
|
196
|
+
//# sourceMappingURL=fail-shader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fail-shader.js","sourceRoot":"","sources":["../src/fail-shader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,cAAc,EACd,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;;;;EAIxC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2B1C,cAAc;EACd,SAAS;EACT,gBAAgB;EAChB,WAAW;EACX,iBAAiB;EACjB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4IX,CAAC"}
|