diff --git a/AGENTS.md b/AGENTS.md index 1bcdcb7..06484ea 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,13 +2,19 @@ ## Сборка и деплой -Деплой автоматический: пуш в git → автодеплой на хостинг. +Деплой автоматический, но **только по пушу в ветку `main`** → автодеплой на хостинг. + +Пуш в любую другую ветку (включая `dev`) деплоя **не вызывает** — `dev` это обычная рабочая ветка, в неё можно пушить свободно. + +Правило коммитов: **после каждой правки делай локальный git commit** (без пуша, если не просили). Пуш — только по явному запросу пользователя, и для деплоя он должен идти в `main`. По запросу пользователя «собрать», «билд»: 1. При правках `src/pages/**` или `src/partials/**` — `npm run build:pages`. 2. При смене шрифтов/Plyr или `package.json` — `npm run build` (включает `vendor:copy`). -3. Для деплоя — сделать git commit + push. +3. Для деплоя — пуш в `main` (предварительно сделав commit). + +> Если пользователь просит «влить в мастер» — см. раздел «Влить в master» ниже. Это не то же самое, что пуш в `dev`. ## Git diff --git a/public/assets/index/resume-photo.png b/public/assets/index/resume-photo.png new file mode 100644 index 0000000..7c3acfe Binary files /dev/null and b/public/assets/index/resume-photo.png differ diff --git a/public/assets/index/resume-photo.webp b/public/assets/index/resume-photo.webp new file mode 100644 index 0000000..8ca5150 Binary files /dev/null and b/public/assets/index/resume-photo.webp differ diff --git a/public/cases/ai-assistant-figma/index.html b/public/cases/ai-assistant-figma/index.html index 18cec64..967e1da 100644 --- a/public/cases/ai-assistant-figma/index.html +++ b/public/cases/ai-assistant-figma/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/ai-platform-primo-rpa-2/index.html b/public/cases/ai-platform-primo-rpa-2/index.html index fa4e59f..a8f782b 100644 --- a/public/cases/ai-platform-primo-rpa-2/index.html +++ b/public/cases/ai-platform-primo-rpa-2/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/ai-platform-primo-rpa-v2/index.html b/public/cases/ai-platform-primo-rpa-v2/index.html index fb44c46..d70ffa5 100644 --- a/public/cases/ai-platform-primo-rpa-v2/index.html +++ b/public/cases/ai-platform-primo-rpa-v2/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/ai-platform-primo-rpa/index.html b/public/cases/ai-platform-primo-rpa/index.html index 223a5b2..3d2f191 100644 --- a/public/cases/ai-platform-primo-rpa/index.html +++ b/public/cases/ai-platform-primo-rpa/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/card-columns-obsidian/index.html b/public/cases/card-columns-obsidian/index.html index 7326aad..324d8e7 100644 --- a/public/cases/card-columns-obsidian/index.html +++ b/public/cases/card-columns-obsidian/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/design-system/index.html b/public/cases/design-system/index.html index 56af9dc..8335e9c 100644 --- a/public/cases/design-system/index.html +++ b/public/cases/design-system/index.html @@ -97,6 +97,7 @@ + diff --git a/public/cases/lunacy/index.html b/public/cases/lunacy/index.html index 6b8c1a7..b87250b 100644 --- a/public/cases/lunacy/index.html +++ b/public/cases/lunacy/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/manu-game-maker/index.html b/public/cases/manu-game-maker/index.html index 97032e8..200d4fe 100644 --- a/public/cases/manu-game-maker/index.html +++ b/public/cases/manu-game-maker/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/mini-projects-showcase/index.html b/public/cases/mini-projects-showcase/index.html index 1dad478..b09a38e 100644 --- a/public/cases/mini-projects-showcase/index.html +++ b/public/cases/mini-projects-showcase/index.html @@ -97,6 +97,7 @@ + diff --git a/public/cases/mobile-clinic-app/index.html b/public/cases/mobile-clinic-app/index.html index d1bc29a..fd3cb65 100644 --- a/public/cases/mobile-clinic-app/index.html +++ b/public/cases/mobile-clinic-app/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/obsidian-plugin-columns-view/index.html b/public/cases/obsidian-plugin-columns-view/index.html index 7d353fd..aae203e 100644 --- a/public/cases/obsidian-plugin-columns-view/index.html +++ b/public/cases/obsidian-plugin-columns-view/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/orc-automation-panel/index.html b/public/cases/orc-automation-panel/index.html index 234ddd1..d0600fc 100644 --- a/public/cases/orc-automation-panel/index.html +++ b/public/cases/orc-automation-panel/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/site-with-ai-ide/index.html b/public/cases/site-with-ai-ide/index.html index 7412e5d..40c9b67 100644 --- a/public/cases/site-with-ai-ide/index.html +++ b/public/cases/site-with-ai-ide/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/task-distribution-concept/index.html b/public/cases/task-distribution-concept/index.html index 58d3483..0af60fe 100644 --- a/public/cases/task-distribution-concept/index.html +++ b/public/cases/task-distribution-concept/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/task-tracker/index.html b/public/cases/task-tracker/index.html index e033e95..d282046 100644 --- a/public/cases/task-tracker/index.html +++ b/public/cases/task-tracker/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI-дизайн и проектирование интерфейсов" }, { diff --git a/public/cases/template-elements-demo/index.html b/public/cases/template-elements-demo/index.html index be572e4..2301429 100644 --- a/public/cases/template-elements-demo/index.html +++ b/public/cases/template-elements-demo/index.html @@ -94,6 +94,7 @@ + diff --git a/public/cookie-policy/index.html b/public/cookie-policy/index.html index f01ffdd..9f08c58 100644 --- a/public/cookie-policy/index.html +++ b/public/cookie-policy/index.html @@ -97,6 +97,7 @@ + diff --git a/public/css/main.css b/public/css/main.css index 5d36f7a..3f9002a 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -289,7 +289,13 @@ body { width: 100%; height: 100%; display: block; - opacity: 0.4; /* приглушаем яркость живого шейдера */ + opacity: 0; /* старт для fade-in живого шэйдера */ + transition: opacity 0.9s ease; +} + +/* Шэйдер отрисовал первый кадр — плавно проявляем его поверх статичного фона */ +.page-mesh-bg.is-ready canvas { + opacity: 0.4; /* приглушаем яркость живого шэйдера */ } /* Вертикальный градиент-оверлэй: снизу фон закрыт цветом страницы (--bg, diff --git a/public/en/cases/ai-assistant-figma/index.html b/public/en/cases/ai-assistant-figma/index.html index 42b2507..d8b3a6c 100644 --- a/public/en/cases/ai-assistant-figma/index.html +++ b/public/en/cases/ai-assistant-figma/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/ai-platform-primo-rpa-2/index.html b/public/en/cases/ai-platform-primo-rpa-2/index.html index 8266d73..f8fee07 100644 --- a/public/en/cases/ai-platform-primo-rpa-2/index.html +++ b/public/en/cases/ai-platform-primo-rpa-2/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/ai-platform-primo-rpa-v2/index.html b/public/en/cases/ai-platform-primo-rpa-v2/index.html index fe840ef..d21fb6e 100644 --- a/public/en/cases/ai-platform-primo-rpa-v2/index.html +++ b/public/en/cases/ai-platform-primo-rpa-v2/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/ai-platform-primo-rpa/index.html b/public/en/cases/ai-platform-primo-rpa/index.html index 73f5a36..35bd76c 100644 --- a/public/en/cases/ai-platform-primo-rpa/index.html +++ b/public/en/cases/ai-platform-primo-rpa/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/card-columns-obsidian/index.html b/public/en/cases/card-columns-obsidian/index.html index 5307a54..a7d099f 100644 --- a/public/en/cases/card-columns-obsidian/index.html +++ b/public/en/cases/card-columns-obsidian/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/design-system/index.html b/public/en/cases/design-system/index.html index baab557..be63275 100644 --- a/public/en/cases/design-system/index.html +++ b/public/en/cases/design-system/index.html @@ -97,6 +97,7 @@ + diff --git a/public/en/cases/lunacy/index.html b/public/en/cases/lunacy/index.html index 1f06a28..8eeb6fd 100644 --- a/public/en/cases/lunacy/index.html +++ b/public/en/cases/lunacy/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/manu-game-maker/index.html b/public/en/cases/manu-game-maker/index.html index 777f2b3..1267200 100644 --- a/public/en/cases/manu-game-maker/index.html +++ b/public/en/cases/manu-game-maker/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/mini-projects-showcase/index.html b/public/en/cases/mini-projects-showcase/index.html index 795c3c1..fed12d2 100644 --- a/public/en/cases/mini-projects-showcase/index.html +++ b/public/en/cases/mini-projects-showcase/index.html @@ -97,6 +97,7 @@ + diff --git a/public/en/cases/mobile-clinic-app/index.html b/public/en/cases/mobile-clinic-app/index.html index eaeaf43..aa75394 100644 --- a/public/en/cases/mobile-clinic-app/index.html +++ b/public/en/cases/mobile-clinic-app/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/obsidian-plugin-columns-view/index.html b/public/en/cases/obsidian-plugin-columns-view/index.html index 44186df..88ac5a2 100644 --- a/public/en/cases/obsidian-plugin-columns-view/index.html +++ b/public/en/cases/obsidian-plugin-columns-view/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/orc-automation-panel/index.html b/public/en/cases/orc-automation-panel/index.html index e98667a..8ed2f4b 100644 --- a/public/en/cases/orc-automation-panel/index.html +++ b/public/en/cases/orc-automation-panel/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/site-with-ai-ide/index.html b/public/en/cases/site-with-ai-ide/index.html index 6b09227..5643c74 100644 --- a/public/en/cases/site-with-ai-ide/index.html +++ b/public/en/cases/site-with-ai-ide/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/task-distribution-concept/index.html b/public/en/cases/task-distribution-concept/index.html index adbe7c4..faf416e 100644 --- a/public/en/cases/task-distribution-concept/index.html +++ b/public/en/cases/task-distribution-concept/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/task-tracker/index.html b/public/en/cases/task-tracker/index.html index 49a72b4..144499f 100644 --- a/public/en/cases/task-tracker/index.html +++ b/public/en/cases/task-tracker/index.html @@ -97,6 +97,7 @@ + @@ -180,7 +181,7 @@ "creator": { "@id": "https://mamadaevv.site/#person" }, - "datePublished": "2026-09-15", + "datePublished": "2026-09-24", "about": "UX/UI design and interface design" }, { diff --git a/public/en/cases/template-elements-demo/index.html b/public/en/cases/template-elements-demo/index.html index 3af4e5e..f985693 100644 --- a/public/en/cases/template-elements-demo/index.html +++ b/public/en/cases/template-elements-demo/index.html @@ -94,6 +94,7 @@ + diff --git a/public/en/cookie-policy/index.html b/public/en/cookie-policy/index.html index 0048a35..c330d24 100644 --- a/public/en/cookie-policy/index.html +++ b/public/en/cookie-policy/index.html @@ -97,6 +97,7 @@ + diff --git a/public/en/index.html b/public/en/index.html index 581d4e2..3fad5cc 100644 --- a/public/en/index.html +++ b/public/en/index.html @@ -97,6 +97,7 @@ + diff --git a/public/en/resume/index.html b/public/en/resume/index.html index 978d004..bfbafbc 100644 --- a/public/en/resume/index.html +++ b/public/en/resume/index.html @@ -97,6 +97,7 @@ + diff --git a/public/index.html b/public/index.html index fc84cdf..e7c2cb0 100644 --- a/public/index.html +++ b/public/index.html @@ -97,6 +97,7 @@ + diff --git a/public/js/modules/ui/content-edit/content-edit-storage.js b/public/js/modules/ui/content-edit/content-edit-storage.js index e2a8db2..020b3ee 100644 --- a/public/js/modules/ui/content-edit/content-edit-storage.js +++ b/public/js/modules/ui/content-edit/content-edit-storage.js @@ -24,6 +24,6 @@ export async function saveEdits(payload) { export function isContentEditHost() { const host = window.location.hostname; - const ALLOWED_HOSTS = ["localhost", "127.0.0.1", "dev.mamadaevv.site", "f2ogw6fzi6trnp7cp43x8wsd.31.129.103.242.sslip.io"]; + const ALLOWED_HOSTS = ["localhost", "127.0.0.1", "10.200.201.3", "dev.mamadaevv.site", "f2ogw6fzi6trnp7cp43x8wsd.31.129.103.242.sslip.io"]; return ALLOWED_HOSTS.includes(host); } diff --git a/public/js/modules/ui/page-mesh-shader.js b/public/js/modules/ui/page-mesh-shader.js new file mode 100644 index 0000000..2d4f8ea --- /dev/null +++ b/public/js/modules/ui/page-mesh-shader.js @@ -0,0 +1,112 @@ +import { ShaderMount, meshGradientFragmentShader, getShaderColorFromString, defaultObjectSizing, ShaderFitOptions } from "/vendor/paper-shaders/index.js"; +import { prefersReducedMotion } from "../shared/media.js"; + +// Живой mesh-gradient фон главной страницы (Paper Shaders, zero-dep WebGL). +// Цвета берутся из акцентной темы сайта (--theme-h/s/l), чтобы фон всегда +// совпадал с выбранным пользователем акцентом. + +function hsl(h, s, l, a = 1) { + // getShaderColorFromString понимает "hsl(h, s%, l%)" — собираем ровно в этом виде. + return `hsl(${Math.round(h)}, ${Math.round(s)}%, ${Math.round(l)}%)`; +} + +function paletteFromTheme(root) { + const cs = getComputedStyle(root); + const h = parseFloat(cs.getPropertyValue("--theme-h")); + const s = parseFloat(cs.getPropertyValue("--theme-s")); + const l = parseFloat(cs.getPropertyValue("--theme-l")); + if (![h, s, l].every(Number.isFinite)) return null; + // Монохромная палитра: все цвета — вариации акцента (±18°/±36° hue, + // без крупных сдвигов), чтобы фон оставался однотонным, не разноцветным. + const a1 = (h + 36) % 360; + const a2 = (h + 18) % 360; + const a3 = (h - 18 + 360) % 360; + return [ + hsl(a1, s, Math.min(l + 2, 54)), + hsl(a2, s, Math.min(l, 50)), + hsl(a3, s, Math.min(l - 2, 46)), + hsl(h, s, Math.min(l + 6, 58)), + ]; +} + +function colorVec4Array(colors) { + // Шейдер ждёт u_colors как vec4[10] → массив из 10 под-массивов [r,g,b,a]. + // setUniformValues сам flatten'ит массив массивов в uniform4fv. + const out = []; + for (let i = 0; i < 10; i++) { + const c = colors[i % colors.length]; + out.push(getShaderColorFromString(c)); + } + return out; +} + +export function initPageMeshShader() { + const host = document.querySelector(".page-mesh-bg"); + if (!host) return; + if (prefersReducedMotion()) return; // статичный CSS-fallback остаётся виден + + const sizing = () => ({ + u_fit: ShaderFitOptions[defaultObjectSizing.fit], + u_scale: defaultObjectSizing.scale, + u_rotation: defaultObjectSizing.rotation, + u_offsetX: defaultObjectSizing.offsetX, + u_offsetY: defaultObjectSizing.offsetY, + u_originX: defaultObjectSizing.originX, + u_originY: defaultObjectSizing.originY, + u_worldWidth: defaultObjectSizing.worldWidth, + u_worldHeight: defaultObjectSizing.worldHeight, + }); + + const root = document.documentElement; + const base = paletteFromTheme(root) || ["#241d9a", "#f75092", "#9f50d3", "#0e6b8f"]; + + const uniforms = { + ...sizing(), + u_colors: colorVec4Array(base), + u_colorsCount: base.length, + u_distortion: 0.85, + u_swirl: 0.35, + u_grainMixer: 0.06, + u_grainOverlay: 0.04, + }; + + let mount; + try { + mount = new ShaderMount( + host, + meshGradientFragmentShader, + uniforms, + { antialias: true, alpha: true, preserveDrawingBuffer: true }, + 1.0, // speed + 0, // frame + 1, // minPixelRatio: ровно CSS-пиксели, без 2x апскейла (главный источник нагрузки) + 2_500_000 // maxPixelCount: потолок ~1600x1000, фон размытый — качество не страдает + ); + // не даём IntersectionObserver ставить паузу вне вьюпорта — фон на весь экран + try { mount.intersectionObserver?.disconnect(); } catch {} + + // Первый кадр уже отрисован в конструкторе — отмечаем готовность, + // чтобы CSS плавно проявил canvas (fade-in), без рывка. + host.classList.add("is-ready"); + } catch (e) { + console.warn("Mesh shader mount failed, fallback to static bg", e); + return; + } + + // Обновляем палитру при смене акцента/темы. + const onTheme = () => { + const next = paletteFromTheme(root); + if (!next) return; + mount.setUniforms({ + u_colors: colorVec4Array(next), + u_colorsCount: next.length, + }); + }; + window.addEventListener("site-theme-applied", onTheme, { passive: true }); + + // при уходе со страницы — чистим + window.addEventListener("pagehide", () => { + window.removeEventListener("site-theme-applied", onTheme); + try { mount.dispose(); } catch {} + }, { once: true }); +} diff --git a/public/resume/index.html b/public/resume/index.html index d6c967d..18e3c4d 100644 --- a/public/resume/index.html +++ b/public/resume/index.html @@ -97,6 +97,7 @@ + diff --git a/public/sitemap.xml b/public/sitemap.xml index 0f1c18b..de40751 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -3,112 +3,112 @@ xmlns:xhtml="http://www.w3.org/1999/xhtml"> https://mamadaevv.site/cases/ai-assistant-figma/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/ai-platform-primo-rpa-2/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/ai-platform-primo-rpa-v2/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/ai-platform-primo-rpa/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/card-columns-obsidian/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/lunacy/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/manu-game-maker/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/mobile-clinic-app/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/obsidian-plugin-columns-view/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/orc-automation-panel/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/site-with-ai-ide/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/task-distribution-concept/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cases/task-tracker/ - 2026-09-15 + 2026-09-24 https://mamadaevv.site/cookie-policy/ - 2026-09-16 + 2026-09-24 https://mamadaevv.site/ - 2026-09-19 + 2026-09-24 https://mamadaevv.site/resume/ - 2026-09-21 + 2026-09-24 diff --git a/public/vendor/paper-shaders/empty-pixel.d.ts b/public/vendor/paper-shaders/empty-pixel.d.ts new file mode 100644 index 0000000..eb24a1c --- /dev/null +++ b/public/vendor/paper-shaders/empty-pixel.d.ts @@ -0,0 +1 @@ +export declare const emptyPixel = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; diff --git a/public/vendor/paper-shaders/empty-pixel.js b/public/vendor/paper-shaders/empty-pixel.js new file mode 100644 index 0000000..3e1e5fa --- /dev/null +++ b/public/vendor/paper-shaders/empty-pixel.js @@ -0,0 +1,10 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +const emptyPixel = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; +export { + emptyPixel +}; +//# sourceMappingURL=empty-pixel.js.map diff --git a/public/vendor/paper-shaders/empty-pixel.js.map b/public/vendor/paper-shaders/empty-pixel.js.map new file mode 100644 index 0000000..5822ee8 --- /dev/null +++ b/public/vendor/paper-shaders/empty-pixel.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../src/empty-pixel.ts"], + "sourcesContent": ["export const emptyPixel = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';\n"], + "mappings": ";;;;;AAAO,MAAM,aAAa;", + "names": [] +} diff --git a/public/vendor/paper-shaders/get-shader-color-from-string.d.ts b/public/vendor/paper-shaders/get-shader-color-from-string.d.ts new file mode 100644 index 0000000..b2ca63b --- /dev/null +++ b/public/vendor/paper-shaders/get-shader-color-from-string.d.ts @@ -0,0 +1,3 @@ +/** Convert color string from HSL, RGB, or hex to 0-to-1-range-RGBA array */ +export declare function getShaderColorFromString(colorString: string | [number, number, number] | [number, number, number, number] | undefined): [number, number, number, number]; +export declare const clamp: (n: number, min: number, max: number) => number; diff --git a/public/vendor/paper-shaders/get-shader-color-from-string.js b/public/vendor/paper-shaders/get-shader-color-from-string.js new file mode 100644 index 0000000..f90cf28 --- /dev/null +++ b/public/vendor/paper-shaders/get-shader-color-from-string.js @@ -0,0 +1,101 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +function getShaderColorFromString(colorString) { + if (Array.isArray(colorString)) { + if (colorString.length === 4) return colorString; + if (colorString.length === 3) return [...colorString, 1]; + return fallbackColor; + } + if (typeof colorString !== "string") { + return fallbackColor; + } + let r, g, b, a = 1; + if (colorString.startsWith("#")) { + [r, g, b, a] = hexToRgba(colorString); + } else if (colorString.startsWith("rgb")) { + const rgba = parseRgba(colorString); + if (rgba === null) return fallbackColor; + [r, g, b, a] = rgba; + } else if (colorString.startsWith("hsl")) { + const hsla = parseHsla(colorString); + if (hsla === null) return fallbackColor; + [r, g, b, a] = hslaToRgba(hsla); + } else { + console.error("Unsupported color format", colorString); + return fallbackColor; + } + return [clamp(r, 0, 1), clamp(g, 0, 1), clamp(b, 0, 1), clamp(a, 0, 1)]; +} +function hexToRgba(hex) { + hex = hex.replace(/^#/, ""); + if (hex.length === 3 || hex.length === 4) { + hex = hex.split("").map((char) => char + char).join(""); + } + if (hex.length === 6) { + hex = hex + "ff"; + } + if (!/^[0-9a-f]{8}$/i.test(hex)) { + console.warn("Invalid hex color"); + return fallbackColor; + } + const r = parseInt(hex.slice(0, 2), 16) / 255; + const g = parseInt(hex.slice(2, 4), 16) / 255; + const b = parseInt(hex.slice(4, 6), 16) / 255; + const a = parseInt(hex.slice(6, 8), 16) / 255; + return [r, g, b, a]; +} +function parseRgba(rgba) { + const match = rgba.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([0-9.]+))?\s*\)$/i); + if (!match) return null; + return [ + parseInt(match[1] ?? "0") / 255, + parseInt(match[2] ?? "0") / 255, + parseInt(match[3] ?? "0") / 255, + match[4] === void 0 ? 1 : parseFloat(match[4]) + ]; +} +function parseHsla(hsla) { + const match = hsla.match(/^hsla?\s*\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*(?:,\s*([0-9.]+))?\s*\)$/i); + if (!match) return null; + return [ + parseInt(match[1] ?? "0"), + parseInt(match[2] ?? "0"), + parseInt(match[3] ?? "0"), + match[4] === void 0 ? 1 : parseFloat(match[4]) + ]; +} +function hslaToRgba(hsla) { + const [h, s, l, a] = hsla; + const hDecimal = h / 360; + const sDecimal = s / 100; + const lDecimal = l / 100; + let r, g, b; + if (s === 0) { + r = g = b = lDecimal; + } else { + const hue2rgb = (p2, q2, t) => { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t; + if (t < 1 / 2) return q2; + if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6; + return p2; + }; + const q = lDecimal < 0.5 ? lDecimal * (1 + sDecimal) : lDecimal + sDecimal - lDecimal * sDecimal; + const p = 2 * lDecimal - q; + r = hue2rgb(p, q, hDecimal + 1 / 3); + g = hue2rgb(p, q, hDecimal); + b = hue2rgb(p, q, hDecimal - 1 / 3); + } + return [r, g, b, a]; +} +const clamp = (n, min, max) => Math.min(Math.max(n, min), max); +const fallbackColor = [0.5, 0.5, 0.5, 1]; +export { + clamp, + getShaderColorFromString +}; +//# sourceMappingURL=get-shader-color-from-string.js.map diff --git a/public/vendor/paper-shaders/get-shader-color-from-string.js.map b/public/vendor/paper-shaders/get-shader-color-from-string.js.map new file mode 100644 index 0000000..4f89049 --- /dev/null +++ b/public/vendor/paper-shaders/get-shader-color-from-string.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../src/get-shader-color-from-string.ts"], + "sourcesContent": ["/** Convert color string from HSL, RGB, or hex to 0-to-1-range-RGBA array */\nexport function getShaderColorFromString(\n colorString: string | [number, number, number] | [number, number, number, number] | undefined\n): [number, number, number, number] {\n // If the color string is already an array of 3 or 4 numbers, return it (with alpha=1 if needed)\n if (Array.isArray(colorString)) {\n if (colorString.length === 4) return colorString as [number, number, number, number];\n if (colorString.length === 3) return [...colorString, 1];\n return fallbackColor;\n }\n\n // If the color string is not a string, return the fallback\n if (typeof colorString !== 'string') {\n return fallbackColor;\n }\n\n let r: number,\n g: number,\n b: number,\n a = 1;\n if (colorString.startsWith('#')) {\n [r, g, b, a] = hexToRgba(colorString);\n } else if (colorString.startsWith('rgb')) {\n const rgba = parseRgba(colorString);\n if (rgba === null) return fallbackColor;\n [r, g, b, a] = rgba;\n } else if (colorString.startsWith('hsl')) {\n const hsla = parseHsla(colorString);\n if (hsla === null) return fallbackColor;\n [r, g, b, a] = hslaToRgba(hsla);\n } else {\n console.error('Unsupported color format', colorString);\n return fallbackColor;\n }\n\n return [clamp(r, 0, 1), clamp(g, 0, 1), clamp(b, 0, 1), clamp(a, 0, 1)];\n}\n\n/** Convert hex to RGBA (0 to 1 range) */\nfunction hexToRgba(hex: string): [number, number, number, number] {\n // Remove # if present\n hex = hex.replace(/^#/, '');\n\n // Expand shorthand hex (3-letter #rgb or 4-letter #rgba) by doubling each digit\n if (hex.length === 3 || hex.length === 4) {\n hex = hex\n .split('')\n .map((char) => char + char)\n .join('');\n }\n // Expand six-letter hex to eight-letter (add full opacity if no alpha)\n if (hex.length === 6) {\n hex = hex + 'ff';\n }\n\n // Bail out on malformed hex (wrong length or non-hex characters) so callers\n // get the fallback color instead of NaN channels leaking into the shader.\n if (!/^[0-9a-f]{8}$/i.test(hex)) {\n console.warn('Invalid hex color');\n return fallbackColor;\n }\n\n // Parse the components\n const r = parseInt(hex.slice(0, 2), 16) / 255;\n const g = parseInt(hex.slice(2, 4), 16) / 255;\n const b = parseInt(hex.slice(4, 6), 16) / 255;\n const a = parseInt(hex.slice(6, 8), 16) / 255;\n\n return [r, g, b, a];\n}\n\n/** Parse RGBA string to RGBA (0 to 1 range), or null if unparseable */\nfunction parseRgba(rgba: string): [number, number, number, number] | null {\n // Match both rgb and rgba patterns\n const match = rgba.match(/^rgba?\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(?:,\\s*([0-9.]+))?\\s*\\)$/i);\n if (!match) return null;\n\n return [\n parseInt(match[1] ?? '0') / 255,\n parseInt(match[2] ?? '0') / 255,\n parseInt(match[3] ?? '0') / 255,\n match[4] === undefined ? 1 : parseFloat(match[4]),\n ];\n}\n\n/** Parse HSLA string, or null if unparseable */\nfunction parseHsla(hsla: string): [number, number, number, number] | null {\n const match = hsla.match(/^hsla?\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)%\\s*,\\s*(\\d+)%\\s*(?:,\\s*([0-9.]+))?\\s*\\)$/i);\n if (!match) return null;\n\n return [\n parseInt(match[1] ?? '0'),\n parseInt(match[2] ?? '0'),\n parseInt(match[3] ?? '0'),\n match[4] === undefined ? 1 : parseFloat(match[4]),\n ];\n}\n\n/** Convert HSLA to RGBA (0 to 1 range) */\nfunction hslaToRgba(hsla: [number, number, number, number]): [number, number, number, number] {\n const [h, s, l, a] = hsla;\n const hDecimal = h / 360;\n const sDecimal = s / 100;\n const lDecimal = l / 100;\n let r, g, b;\n\n if (s === 0) {\n r = g = b = lDecimal; // achromatic\n } else {\n const hue2rgb = (p: number, q: number, t: number) => {\n if (t < 0) t += 1;\n if (t > 1) t -= 1;\n if (t < 1 / 6) return p + (q - p) * 6 * t;\n if (t < 1 / 2) return q;\n if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n return p;\n };\n\n const q = lDecimal < 0.5 ? lDecimal * (1 + sDecimal) : lDecimal + sDecimal - lDecimal * sDecimal;\n const p = 2 * lDecimal - q;\n r = hue2rgb(p, q, hDecimal + 1 / 3);\n g = hue2rgb(p, q, hDecimal);\n b = hue2rgb(p, q, hDecimal - 1 / 3);\n }\n\n return [r, g, b, a];\n}\n\nexport const clamp = (n: number, min: number, max: number): number => Math.min(Math.max(n, min), max);\n\nconst fallbackColor = [0.5, 0.5, 0.5, 1] as [number, number, number, number];\n"], + "mappings": "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", + "names": ["p", "q"] +} diff --git a/public/vendor/paper-shaders/get-shader-noise-texture.d.ts b/public/vendor/paper-shaders/get-shader-noise-texture.d.ts new file mode 100644 index 0000000..35661e3 --- /dev/null +++ b/public/vendor/paper-shaders/get-shader-noise-texture.d.ts @@ -0,0 +1 @@ +export declare function getShaderNoiseTexture(): HTMLImageElement | undefined; diff --git a/public/vendor/paper-shaders/get-shader-noise-texture.js b/public/vendor/paper-shaders/get-shader-noise-texture.js new file mode 100644 index 0000000..dedc3ec --- /dev/null +++ b/public/vendor/paper-shaders/get-shader-noise-texture.js @@ -0,0 +1,18 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +function getShaderNoiseTexture() { + if (typeof window === "undefined") { + return void 0; + } + const img = new Image(); + img.src = noiseSrc; + return img; +} +const noiseSrc = "data:image/png;base64,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"; +export { + getShaderNoiseTexture +}; +//# sourceMappingURL=get-shader-noise-texture.js.map diff --git a/public/vendor/paper-shaders/get-shader-noise-texture.js.map b/public/vendor/paper-shaders/get-shader-noise-texture.js.map new file mode 100644 index 0000000..5128cdc --- /dev/null +++ b/public/vendor/paper-shaders/get-shader-noise-texture.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../src/get-shader-noise-texture.ts"], + "sourcesContent": ["export function getShaderNoiseTexture(): HTMLImageElement | undefined {\n if (typeof window === 'undefined') {\n return undefined;\n }\n\n const img = new Image();\n img.src = noiseSrc;\n return img;\n}\n\nconst noiseSrc =\n 'data:image/png;base64,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';\n"], + "mappings": ";;;;;AAAO,SAAS,wBAAsD;AACpE,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,IAAI,MAAM;AACtB,MAAI,MAAM;AACV,SAAO;AACT;AAEA,MAAM,WACJ;", + "names": [] +} diff --git a/public/vendor/paper-shaders/index.d.ts b/public/vendor/paper-shaders/index.d.ts new file mode 100644 index 0000000..f438002 --- /dev/null +++ b/public/vendor/paper-shaders/index.d.ts @@ -0,0 +1,68 @@ +/** The core Shader Mount class. Pass it a parent element and a fragment shader to get started. */ +export { ShaderMount, isPaperShaderElement } from './shader-mount.js'; +export type { PaperShaderElement, ShaderMotionParams, ShaderMountUniforms, ShaderPreset, ImageShaderPreset, } from './shader-mount.js'; +/** Shader sizing options and uniforms */ +export { defaultObjectSizing, defaultPatternSizing, ShaderFitOptions, type ShaderFit, type ShaderSizingParams, type ShaderSizingUniforms, } from './shader-sizing.js'; +/** A flowing interplay of color spots, moving along distinct trajectories and transformed by organic distortion */ +export { meshGradientFragmentShader, meshGradientMeta, type MeshGradientParams, type MeshGradientUniforms, } from './shaders/mesh-gradient.js'; +/** Radial multi-colored gradient shaped with layered noise for a natural, smoky aesthetic */ +export { smokeRingMeta, smokeRingFragmentShader, type SmokeRingParams, type SmokeRingUniforms, } from './shaders/smoke-ring.js'; +/** A glowing, web-like structure of fluid lines and soft intersections. Great for creating atmospheric, organic-yet-futuristic visuals */ +export { neuroNoiseFragmentShader, type NeuroNoiseParams, type NeuroNoiseUniforms } from './shaders/neuro-noise.js'; +/** Animated multi-color dots pattern with each dot orbiting around its cell center. Supports up to 40 colors and various shape and motion controls. Great for playful, dynamic backgrounds and UI textures */ +export { dotOrbitMeta, dotOrbitFragmentShader, type DotOrbitParams, type DotOrbitUniforms, } from './shaders/dot-orbit.js'; +/** Static grid pattern made of circles, diamonds, squares or triangles */ +export { dotGridFragmentShader, DotGridShapes, type DotGridShape, type DotGridParams, type DotGridUniforms, } from './shaders/dot-grid.js'; +/** A multi-color gradient mapped into smooth, animated curves, delivering a sleek, futuristic visual */ +export { simplexNoiseMeta, simplexNoiseFragmentShader, type SimplexNoiseParams, type SimplexNoiseUniforms, } from './shaders/simplex-noise.js'; +/** Up to 20 gooey blobs moving around the center and merging into smooth organic shapes */ +export { metaballsMeta, metaballsFragmentShader, type MetaballsParams, type MetaballsUniforms, } from './shaders/metaballs.js'; +/** Animated 3D Perlin noise with exposed controls */ +export { perlinNoiseFragmentShader, type PerlinNoiseParams, type PerlinNoiseUniforms } from './shaders/perlin-noise.js'; +/** Anti-aliased animated Voronoi pattern with smooth and customizable edges */ +export { voronoiMeta, voronoiFragmentShader, type VoronoiParams, type VoronoiUniforms } from './shaders/voronoi.js'; +/** Static line pattern configurable into textures ranging from sharp zigzags to smooth flowing waves */ +export { wavesFragmentShader, type WavesParams, type WavesUniforms } from './shaders/waves.js'; +/** Animated color fields warped by noise and swirls, applied over base patterns (checks, stripes, or split edge). Blends up to 10 colors with adjustable distribution, softness, distortion, and swirl. Great for fluid, smoky, or marbled effects */ +export { warpMeta, warpFragmentShader, WarpPatterns, type WarpParams, type WarpUniforms, type WarpPattern, } from './shaders/warp.js'; +/** Animated rays of light radiating from the center, blended with up to 5 colors. The rays are adjustable by size, density, brightness and center glow. Great for dramatic backgrounds, logo reveals, and VFX like energy bursts or sun shafts */ +export { godRaysMeta, godRaysFragmentShader, type GodRaysParams, type GodRaysUniforms } from './shaders/god-rays.js'; +/** A single-colored animated spiral that morphs across a wide range of shapes - from crisp, thin-lined geometry to flowing whirlpool forms and wavy, abstract rings */ +export { spiralFragmentShader, type SpiralParams, type SpiralUniforms } from './shaders/spiral.js'; +/** Animated bands of color twisting and bending, producing spirals, arcs, and flowing circular patterns */ +export { swirlMeta, swirlFragmentShader, type SwirlParams, type SwirlUniforms } from './shaders/swirl.js'; +/** Animated 2-color dithering over with multiple pattern sources (noise, warp, dots, waves, ripple, swirl, sphere) */ +export { ditheringFragmentShader, DitheringShapes, DitheringTypes, type DitheringParams, type DitheringUniforms, type DitheringShape, type DitheringType, } from './shaders/dithering.js'; +/** Multi-color gradients with grainy, noise-textured distortion available in 7 animated abstract forms */ +export { grainGradientFragmentShader, grainGradientMeta, GrainGradientShapes, type GrainGradientParams, type GrainGradientUniforms, type GrainGradientShape, } from './shaders/grain-gradient.js'; +/** Luminous trails of color merging into a glowing gradient frame */ +export { pulsingBorderMeta, pulsingBorderFragmentShader, PulsingBorderAspectRatios, type PulsingBorderParams, type PulsingBorderUniforms, type PulsingBorderAspectRatio, } from './shaders/pulsing-border.js'; +/** Glowing translucent 3D panels rotating around a central axis */ +export { colorPanelsFragmentShader, colorPanelsMeta, type ColorPanelsParams, type ColorPanelsUniforms, } from './shaders/color-panels.js'; +/** Multi-point mesh gradients with up to 10 color spots, enhanced by two-direction warping, adjustable blend sharpness, and grain controls. Perfect for elegant wallpapers and atmospheric backdrops */ +export { staticMeshGradientFragmentShader, staticMeshGradientMeta, type StaticMeshGradientParams, type StaticMeshGradientUniforms, } from './shaders/static-mesh-gradient.js'; +/** Radial gradient with up to 10 blended colors, featuring advanced focal point control, shape distortion, and grain effects */ +export { staticRadialGradientFragmentShader, staticRadialGradientMeta, type StaticRadialGradientParams, type StaticRadialGradientUniforms, } from './shaders/static-radial-gradient.js'; +/** A static texture built from multiple noise layers, usable for a realistic paper and cardboard surfaces or generating abstract patterns. Can be used as a image filter or as a texture */ +export { paperTextureFragmentShader, type PaperTextureParams, type PaperTextureUniforms, } from './shaders/paper-texture.js'; +/** Water-like surface distortion with natural caustic realism. Works as an image filter or animated texture without image */ +export { waterFragmentShader, type WaterParams, type WaterUniforms } from './shaders/water.js'; +/** Fluted glass image filter transforms an image into streaked, ribbed distortions, giving a mix of clarity and obscurity */ +export { flutedGlassFragmentShader, GlassDistortionShapes, GlassGridShapes, type GlassDistortionShape, type GlassGridShape, type FlutedGlassParams, type FlutedGlassUniforms, } from './shaders/fluted-glass.js'; +/** A dithering image filter with support for 4 dithering modes and multiple color palettes (2-color, 3-color, and multicolor options, using either predefined colors or ones sampled directly from the original image) */ +export { imageDitheringFragmentShader, type ImageDitheringParams, type ImageDitheringUniforms, } from './shaders/image-dithering.js'; +/** Lens Distortion image filter that separates an image into shifting color layers, recreating the chromatic aberration of a lens, and warps the image geometry with barrel or pincushion curvature */ +export { lensDistortionFragmentShader, lensDistortionMeta, type LensDistortionParams, type LensDistortionUniforms } from './shaders/lens-distortion.js'; +/** A glowing gradient of colors flowing through an input image. The effect creates a smoothly animated wave of intensity across the image */ +export { heatmapMeta, heatmapFragmentShader, toProcessedHeatmap, type HeatmapParams, type HeatmapUniforms, } from './shaders/heatmap.js'; +/** Futuristic liquid metal material applied to uploaded logo or one of pre-defined abstract shape */ +export { liquidMetalFragmentShader, LiquidMetalShapes, toProcessedLiquidMetal, type LiquidMetalShape, type LiquidMetalParams, type LiquidMetalUniforms, } from './shaders/liquid-metal.js'; +/** A halftone-dot image filter featuring customizable grids, color palettes, and dot styles */ +export { HalftoneDotsTypes, HalftoneDotsGrids, halftoneDotsFragmentShader, type HalftoneDotsParams, type HalftoneDotsUniforms, type HalftoneDotsType, type HalftoneDotsGrid, } from './shaders/halftone-dots.js'; +/** A halftone CMYK classic algo */ +export { HalftoneCmykTypes, halftoneCmykFragmentShader, type HalftoneCmykParams, type HalftoneCmykUniforms, type HalftoneCmykType, } from './shaders/halftone-cmyk.js'; +/** Animated color fields running around a glassy logo shape */ +export { gemSmokeMeta, gemSmokeFragmentShader, toProcessedGemSmoke, GemSmokeShapes, type GemSmokeShape, type GemSmokeParams, type GemSmokeUniforms, } from './shaders/gem-smoke.js'; +export { getShaderColorFromString } from './get-shader-color-from-string.js'; +export { getShaderNoiseTexture } from './get-shader-noise-texture.js'; +export { emptyPixel } from './empty-pixel.js'; diff --git a/public/vendor/paper-shaders/index.js b/public/vendor/paper-shaders/index.js new file mode 100644 index 0000000..fc9e21b --- /dev/null +++ b/public/vendor/paper-shaders/index.js @@ -0,0 +1,189 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import { ShaderMount, isPaperShaderElement } from "./shader-mount.js"; +import { + defaultObjectSizing, + defaultPatternSizing, + ShaderFitOptions +} from "./shader-sizing.js"; +import { + meshGradientFragmentShader, + meshGradientMeta +} from "./shaders/mesh-gradient.js"; +import { + smokeRingMeta, + smokeRingFragmentShader +} from "./shaders/smoke-ring.js"; +import { neuroNoiseFragmentShader } from "./shaders/neuro-noise.js"; +import { + dotOrbitMeta, + dotOrbitFragmentShader +} from "./shaders/dot-orbit.js"; +import { + dotGridFragmentShader, + DotGridShapes +} from "./shaders/dot-grid.js"; +import { + simplexNoiseMeta, + simplexNoiseFragmentShader +} from "./shaders/simplex-noise.js"; +import { + metaballsMeta, + metaballsFragmentShader +} from "./shaders/metaballs.js"; +import { perlinNoiseFragmentShader } from "./shaders/perlin-noise.js"; +import { voronoiMeta, voronoiFragmentShader } from "./shaders/voronoi.js"; +import { wavesFragmentShader } from "./shaders/waves.js"; +import { + warpMeta, + warpFragmentShader, + WarpPatterns +} from "./shaders/warp.js"; +import { godRaysMeta, godRaysFragmentShader } from "./shaders/god-rays.js"; +import { spiralFragmentShader } from "./shaders/spiral.js"; +import { swirlMeta, swirlFragmentShader } from "./shaders/swirl.js"; +import { + ditheringFragmentShader, + DitheringShapes, + DitheringTypes +} from "./shaders/dithering.js"; +import { + grainGradientFragmentShader, + grainGradientMeta, + GrainGradientShapes +} from "./shaders/grain-gradient.js"; +import { + pulsingBorderMeta, + pulsingBorderFragmentShader, + PulsingBorderAspectRatios +} from "./shaders/pulsing-border.js"; +import { + colorPanelsFragmentShader, + colorPanelsMeta +} from "./shaders/color-panels.js"; +import { + staticMeshGradientFragmentShader, + staticMeshGradientMeta +} from "./shaders/static-mesh-gradient.js"; +import { + staticRadialGradientFragmentShader, + staticRadialGradientMeta +} from "./shaders/static-radial-gradient.js"; +import { + paperTextureFragmentShader +} from "./shaders/paper-texture.js"; +import { waterFragmentShader } from "./shaders/water.js"; +import { + flutedGlassFragmentShader, + GlassDistortionShapes, + GlassGridShapes +} from "./shaders/fluted-glass.js"; +import { + imageDitheringFragmentShader +} from "./shaders/image-dithering.js"; +import { lensDistortionFragmentShader, lensDistortionMeta } from "./shaders/lens-distortion.js"; +import { + heatmapMeta, + heatmapFragmentShader, + toProcessedHeatmap +} from "./shaders/heatmap.js"; +import { + liquidMetalFragmentShader, + LiquidMetalShapes, + toProcessedLiquidMetal +} from "./shaders/liquid-metal.js"; +import { + HalftoneDotsTypes, + HalftoneDotsGrids, + halftoneDotsFragmentShader +} from "./shaders/halftone-dots.js"; +import { + HalftoneCmykTypes, + halftoneCmykFragmentShader +} from "./shaders/halftone-cmyk.js"; +import { + gemSmokeMeta, + gemSmokeFragmentShader, + toProcessedGemSmoke, + GemSmokeShapes +} from "./shaders/gem-smoke.js"; +import { getShaderColorFromString } from "./get-shader-color-from-string.js"; +import { getShaderNoiseTexture } from "./get-shader-noise-texture.js"; +import { emptyPixel } from "./empty-pixel.js"; +export { + DitheringShapes, + DitheringTypes, + DotGridShapes, + GemSmokeShapes, + GlassDistortionShapes, + GlassGridShapes, + GrainGradientShapes, + HalftoneCmykTypes, + HalftoneDotsGrids, + HalftoneDotsTypes, + LiquidMetalShapes, + PulsingBorderAspectRatios, + ShaderFitOptions, + ShaderMount, + WarpPatterns, + colorPanelsFragmentShader, + colorPanelsMeta, + defaultObjectSizing, + defaultPatternSizing, + ditheringFragmentShader, + dotGridFragmentShader, + dotOrbitFragmentShader, + dotOrbitMeta, + emptyPixel, + flutedGlassFragmentShader, + gemSmokeFragmentShader, + gemSmokeMeta, + getShaderColorFromString, + getShaderNoiseTexture, + godRaysFragmentShader, + godRaysMeta, + grainGradientFragmentShader, + grainGradientMeta, + halftoneCmykFragmentShader, + halftoneDotsFragmentShader, + heatmapFragmentShader, + heatmapMeta, + imageDitheringFragmentShader, + isPaperShaderElement, + lensDistortionFragmentShader, + lensDistortionMeta, + liquidMetalFragmentShader, + meshGradientFragmentShader, + meshGradientMeta, + metaballsFragmentShader, + metaballsMeta, + neuroNoiseFragmentShader, + paperTextureFragmentShader, + perlinNoiseFragmentShader, + pulsingBorderFragmentShader, + pulsingBorderMeta, + simplexNoiseFragmentShader, + simplexNoiseMeta, + smokeRingFragmentShader, + smokeRingMeta, + spiralFragmentShader, + staticMeshGradientFragmentShader, + staticMeshGradientMeta, + staticRadialGradientFragmentShader, + staticRadialGradientMeta, + swirlFragmentShader, + swirlMeta, + toProcessedGemSmoke, + toProcessedHeatmap, + toProcessedLiquidMetal, + voronoiFragmentShader, + voronoiMeta, + warpFragmentShader, + warpMeta, + waterFragmentShader, + wavesFragmentShader +}; +//# sourceMappingURL=index.js.map diff --git a/public/vendor/paper-shaders/index.js.map b/public/vendor/paper-shaders/index.js.map new file mode 100644 index 0000000..a23db2b --- /dev/null +++ b/public/vendor/paper-shaders/index.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../src/index.ts"], + "sourcesContent": ["/** The core Shader Mount class. Pass it a parent element and a fragment shader to get started. */\nexport { ShaderMount, isPaperShaderElement } from './shader-mount.js';\nexport type {\n PaperShaderElement,\n ShaderMotionParams,\n ShaderMountUniforms,\n ShaderPreset,\n ImageShaderPreset,\n} from './shader-mount.js';\n\n/** Shader sizing options and uniforms */\nexport {\n defaultObjectSizing,\n defaultPatternSizing,\n ShaderFitOptions,\n type ShaderFit,\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from './shader-sizing.js';\n\n/** A flowing interplay of color spots, moving along distinct trajectories and transformed by organic distortion */\nexport {\n meshGradientFragmentShader,\n meshGradientMeta,\n type MeshGradientParams,\n type MeshGradientUniforms,\n} from './shaders/mesh-gradient.js';\n\n/** Radial multi-colored gradient shaped with layered noise for a natural, smoky aesthetic */\nexport {\n smokeRingMeta,\n smokeRingFragmentShader,\n type SmokeRingParams,\n type SmokeRingUniforms,\n} from './shaders/smoke-ring.js';\n\n/** A glowing, web-like structure of fluid lines and soft intersections. Great for creating atmospheric, organic-yet-futuristic visuals */\nexport { neuroNoiseFragmentShader, type NeuroNoiseParams, type NeuroNoiseUniforms } from './shaders/neuro-noise.js';\n\n/** Animated multi-color dots pattern with each dot orbiting around its cell center. Supports up to 40 colors and various shape and motion controls. Great for playful, dynamic backgrounds and UI textures */\nexport {\n dotOrbitMeta,\n dotOrbitFragmentShader,\n type DotOrbitParams,\n type DotOrbitUniforms,\n} from './shaders/dot-orbit.js';\n\n/** Static grid pattern made of circles, diamonds, squares or triangles */\nexport {\n dotGridFragmentShader,\n DotGridShapes,\n type DotGridShape,\n type DotGridParams,\n type DotGridUniforms,\n} from './shaders/dot-grid.js';\n\n/** A multi-color gradient mapped into smooth, animated curves, delivering a sleek, futuristic visual */\nexport {\n simplexNoiseMeta,\n simplexNoiseFragmentShader,\n type SimplexNoiseParams,\n type SimplexNoiseUniforms,\n} from './shaders/simplex-noise.js';\n\n/** Up to 20 gooey blobs moving around the center and merging into smooth organic shapes */\nexport {\n metaballsMeta,\n metaballsFragmentShader,\n type MetaballsParams,\n type MetaballsUniforms,\n} from './shaders/metaballs.js';\n\n/** Animated 3D Perlin noise with exposed controls */\nexport { perlinNoiseFragmentShader, type PerlinNoiseParams, type PerlinNoiseUniforms } from './shaders/perlin-noise.js';\n\n/** Anti-aliased animated Voronoi pattern with smooth and customizable edges */\nexport { voronoiMeta, voronoiFragmentShader, type VoronoiParams, type VoronoiUniforms } from './shaders/voronoi.js';\n\n/** Static line pattern configurable into textures ranging from sharp zigzags to smooth flowing waves */\nexport { wavesFragmentShader, type WavesParams, type WavesUniforms } from './shaders/waves.js';\n\n/** Animated color fields warped by noise and swirls, applied over base patterns (checks, stripes, or split edge). Blends up to 10 colors with adjustable distribution, softness, distortion, and swirl. Great for fluid, smoky, or marbled effects */\nexport {\n warpMeta,\n warpFragmentShader,\n WarpPatterns,\n type WarpParams,\n type WarpUniforms,\n type WarpPattern,\n} from './shaders/warp.js';\n\n/** Animated rays of light radiating from the center, blended with up to 5 colors. The rays are adjustable by size, density, brightness and center glow. Great for dramatic backgrounds, logo reveals, and VFX like energy bursts or sun shafts */\nexport { godRaysMeta, godRaysFragmentShader, type GodRaysParams, type GodRaysUniforms } from './shaders/god-rays.js';\n\n/** A single-colored animated spiral that morphs across a wide range of shapes - from crisp, thin-lined geometry to flowing whirlpool forms and wavy, abstract rings */\nexport { spiralFragmentShader, type SpiralParams, type SpiralUniforms } from './shaders/spiral.js';\n\n/** Animated bands of color twisting and bending, producing spirals, arcs, and flowing circular patterns */\nexport { swirlMeta, swirlFragmentShader, type SwirlParams, type SwirlUniforms } from './shaders/swirl.js';\n\n/** Animated 2-color dithering over with multiple pattern sources (noise, warp, dots, waves, ripple, swirl, sphere) */\nexport {\n ditheringFragmentShader,\n DitheringShapes,\n DitheringTypes,\n type DitheringParams,\n type DitheringUniforms,\n type DitheringShape,\n type DitheringType,\n} from './shaders/dithering.js';\n\n/** Multi-color gradients with grainy, noise-textured distortion available in 7 animated abstract forms */\nexport {\n grainGradientFragmentShader,\n grainGradientMeta,\n GrainGradientShapes,\n type GrainGradientParams,\n type GrainGradientUniforms,\n type GrainGradientShape,\n} from './shaders/grain-gradient.js';\n\n/** Luminous trails of color merging into a glowing gradient frame */\nexport {\n pulsingBorderMeta,\n pulsingBorderFragmentShader,\n PulsingBorderAspectRatios,\n type PulsingBorderParams,\n type PulsingBorderUniforms,\n type PulsingBorderAspectRatio,\n} from './shaders/pulsing-border.js';\n\n/** Glowing translucent 3D panels rotating around a central axis */\nexport {\n colorPanelsFragmentShader,\n colorPanelsMeta,\n type ColorPanelsParams,\n type ColorPanelsUniforms,\n} from './shaders/color-panels.js';\n\n/** Multi-point mesh gradients with up to 10 color spots, enhanced by two-direction warping, adjustable blend sharpness, and grain controls. Perfect for elegant wallpapers and atmospheric backdrops */\nexport {\n staticMeshGradientFragmentShader,\n staticMeshGradientMeta,\n type StaticMeshGradientParams,\n type StaticMeshGradientUniforms,\n} from './shaders/static-mesh-gradient.js';\n\n/** Radial gradient with up to 10 blended colors, featuring advanced focal point control, shape distortion, and grain effects */\nexport {\n staticRadialGradientFragmentShader,\n staticRadialGradientMeta,\n type StaticRadialGradientParams,\n type StaticRadialGradientUniforms,\n} from './shaders/static-radial-gradient.js';\n\n/** A static texture built from multiple noise layers, usable for a realistic paper and cardboard surfaces or generating abstract patterns. Can be used as a image filter or as a texture */\nexport {\n paperTextureFragmentShader,\n type PaperTextureParams,\n type PaperTextureUniforms,\n} from './shaders/paper-texture.js';\n\n/** Water-like surface distortion with natural caustic realism. Works as an image filter or animated texture without image */\nexport { waterFragmentShader, type WaterParams, type WaterUniforms } from './shaders/water.js';\n\n/** Fluted glass image filter transforms an image into streaked, ribbed distortions, giving a mix of clarity and obscurity */\nexport {\n flutedGlassFragmentShader,\n GlassDistortionShapes,\n GlassGridShapes,\n type GlassDistortionShape,\n type GlassGridShape,\n type FlutedGlassParams,\n type FlutedGlassUniforms,\n} from './shaders/fluted-glass.js';\n\n/** A dithering image filter with support for 4 dithering modes and multiple color palettes (2-color, 3-color, and multicolor options, using either predefined colors or ones sampled directly from the original image) */\nexport {\n imageDitheringFragmentShader,\n type ImageDitheringParams,\n type ImageDitheringUniforms,\n} from './shaders/image-dithering.js';\n\n/** Lens Distortion image filter that separates an image into shifting color layers, recreating the chromatic aberration of a lens, and warps the image geometry with barrel or pincushion curvature */\nexport { lensDistortionFragmentShader, lensDistortionMeta, type LensDistortionParams, type LensDistortionUniforms } from './shaders/lens-distortion.js';\n\n/** A glowing gradient of colors flowing through an input image. The effect creates a smoothly animated wave of intensity across the image */\nexport {\n heatmapMeta,\n heatmapFragmentShader,\n toProcessedHeatmap,\n type HeatmapParams,\n type HeatmapUniforms,\n} from './shaders/heatmap.js';\n\n/** Futuristic liquid metal material applied to uploaded logo or one of pre-defined abstract shape */\nexport {\n liquidMetalFragmentShader,\n LiquidMetalShapes,\n toProcessedLiquidMetal,\n type LiquidMetalShape,\n type LiquidMetalParams,\n type LiquidMetalUniforms,\n} from './shaders/liquid-metal.js';\n\n/** A halftone-dot image filter featuring customizable grids, color palettes, and dot styles */\nexport {\n HalftoneDotsTypes,\n HalftoneDotsGrids,\n halftoneDotsFragmentShader,\n type HalftoneDotsParams,\n type HalftoneDotsUniforms,\n type HalftoneDotsType,\n type HalftoneDotsGrid,\n} from './shaders/halftone-dots.js';\n\n/** A halftone CMYK classic algo */\nexport {\n HalftoneCmykTypes,\n halftoneCmykFragmentShader,\n type HalftoneCmykParams,\n type HalftoneCmykUniforms,\n type HalftoneCmykType,\n} from './shaders/halftone-cmyk.js';\n\n/** Animated color fields running around a glassy logo shape */\nexport {\n gemSmokeMeta,\n gemSmokeFragmentShader,\n toProcessedGemSmoke,\n GemSmokeShapes,\n type GemSmokeShape,\n type GemSmokeParams,\n type GemSmokeUniforms,\n} from './shaders/gem-smoke.js';\n\n// ----- Utils ----- //\nexport { getShaderColorFromString } from './get-shader-color-from-string.js';\nexport { getShaderNoiseTexture } from './get-shader-noise-texture.js';\nexport { emptyPixel } from './empty-pixel.js';\n"], + "mappings": ";;;;;AACA,SAAS,aAAa,4BAA4B;AAUlD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP,SAAS,gCAAgF;AAGzF;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP,SAAS,iCAAmF;AAG5F,SAAS,aAAa,6BAAuE;AAG7F,SAAS,2BAAiE;AAG1E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP,SAAS,aAAa,6BAAuE;AAG7F,SAAS,4BAAoE;AAG7E,SAAS,WAAW,2BAAiE;AAGrF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAKK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,OAGK;AAGP,SAAS,2BAAiE;AAG1E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAKK;AAGP;AAAA,EACE;AAAA,OAGK;AAGP,SAAS,8BAA8B,0BAAkF;AAGzH;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAKK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP,SAAS,gCAAgC;AACzC,SAAS,6BAA6B;AACtC,SAAS,kBAAkB;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shader-color-spaces.d.ts b/public/vendor/paper-shaders/shader-color-spaces.d.ts new file mode 100644 index 0000000..99aab8a --- /dev/null +++ b/public/vendor/paper-shaders/shader-color-spaces.d.ts @@ -0,0 +1,6 @@ +export declare const ShaderColorSpaces: { + readonly rgb: 0; + readonly oklch: 1; +}; +export type ShaderColorSpace = keyof typeof ShaderColorSpaces; +export declare const declareOklchTransforms = "\n\n// magic numbers (and magic could be better tbh)\n#define OKLCH_CHROMA_THRESHOLD .001\n#define OKLCH_HUE_NEUTRALIZER - 2.\n\nvec3 srgbToLinear(vec3 srgb) {\n return pow(srgb, vec3(2.2));\n}\n\nvec3 linearToSrgb(vec3 linear) {\n return pow(linear, vec3(1.0 / 2.2));\n}\n\nvec3 LrgbToOklab(vec3 rgb) {\n float L = pow(0.4122214708 * rgb.r + 0.5363325363 * rgb.g + 0.0514459929 * rgb.b, 1.0 / 3.0);\n float M = pow(0.2119034982 * rgb.r + 0.6806995451 * rgb.g + 0.1073969566 * rgb.b, 1.0 / 3.0);\n float S = pow(0.0883024619 * rgb.r + 0.2817188376 * rgb.g + 0.6299787005 * rgb.b, 1.0 / 3.0);\n return vec3(\n 0.2104542553 * L + 0.793617785 * M - 0.0040720468 * S,\n 1.9779984951 * L - 2.428592205 * M + 0.4505937099 * S,\n 0.0259040371 * L + 0.7827717662 * M - 0.808675766 * S\n );\n}\n\nvec3 OklabToLrgb(vec3 oklab) {\n float L = oklab.x;\n float a = oklab.y;\n float b = oklab.z;\n\n float l_ = L + 0.3963377774 * a + 0.2158037573 * b;\n float m_ = L - 0.1055613458 * a - 0.0638541728 * b;\n float s_ = L - 0.0894841775 * a - 1.291485548 * b;\n\n float l = l_ * l_ * l_;\n float m = m_ * m_ * m_;\n float s = s_ * s_ * s_;\n\n return vec3(\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s\n );\n}\n\nvec3 oklabToOklch(vec3 oklab) {\n float C = length(oklab.yz);\n float H = atan(oklab.z, oklab.y);\n if (C < OKLCH_CHROMA_THRESHOLD) {\n H = OKLCH_HUE_NEUTRALIZER;\n }\n return vec3(oklab.x, C, H);\n}\n\nvec3 oklchToOklab(vec3 oklch) {\n float a = oklch.y * cos(oklch.z);\n float b = oklch.y * sin(oklch.z);\n return vec3(oklch.x, a, b);\n}\n\nfloat mixHue(float h1, float h2, float mixer) {\n float delta = mod(h2 - h1 + PI, TWO_PI) - PI;\n return h1 + mixer * delta;\n}\n\nvec3 srgbToOklab(vec3 rgb) {\n return oklabToOklch(LrgbToOklab(srgbToLinear(rgb)));\n}\n\nvec3 oklabToSrgb(vec3 oklab) {\n return linearToSrgb(OklabToLrgb(oklchToOklab(oklab)));\n}\n\nvec3 mixOklabVector(vec3 color1, vec3 color2, float mixer) {\n color1.x = mix(color1.x, color2.x, mixer);\n color1.y = mix(color1.y, color2.y, mixer);\n if (color1.y > OKLCH_CHROMA_THRESHOLD && color2.y > OKLCH_CHROMA_THRESHOLD) {\n color1.z = mixHue(color1.z, color2.z, mixer);\n }\n return color1;\n}\n"; diff --git a/public/vendor/paper-shaders/shader-color-spaces.js b/public/vendor/paper-shaders/shader-color-spaces.js new file mode 100644 index 0000000..7ad1dd1 --- /dev/null +++ b/public/vendor/paper-shaders/shader-color-spaces.js @@ -0,0 +1,96 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +const ShaderColorSpaces = { + rgb: 0, + oklch: 1 +}; +const declareOklchTransforms = ` + +// magic numbers (and magic could be better tbh) +#define OKLCH_CHROMA_THRESHOLD .001 +#define OKLCH_HUE_NEUTRALIZER - 2. + +vec3 srgbToLinear(vec3 srgb) { + return pow(srgb, vec3(2.2)); +} + +vec3 linearToSrgb(vec3 linear) { + return pow(linear, vec3(1.0 / 2.2)); +} + +vec3 LrgbToOklab(vec3 rgb) { + float L = pow(0.4122214708 * rgb.r + 0.5363325363 * rgb.g + 0.0514459929 * rgb.b, 1.0 / 3.0); + float M = pow(0.2119034982 * rgb.r + 0.6806995451 * rgb.g + 0.1073969566 * rgb.b, 1.0 / 3.0); + float S = pow(0.0883024619 * rgb.r + 0.2817188376 * rgb.g + 0.6299787005 * rgb.b, 1.0 / 3.0); + return vec3( + 0.2104542553 * L + 0.793617785 * M - 0.0040720468 * S, + 1.9779984951 * L - 2.428592205 * M + 0.4505937099 * S, + 0.0259040371 * L + 0.7827717662 * M - 0.808675766 * S + ); +} + +vec3 OklabToLrgb(vec3 oklab) { + float L = oklab.x; + float a = oklab.y; + float b = oklab.z; + + float l_ = L + 0.3963377774 * a + 0.2158037573 * b; + float m_ = L - 0.1055613458 * a - 0.0638541728 * b; + float s_ = L - 0.0894841775 * a - 1.291485548 * b; + + float l = l_ * l_ * l_; + float m = m_ * m_ * m_; + float s = s_ * s_ * s_; + + return vec3( + 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, + -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, + -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s + ); +} + +vec3 oklabToOklch(vec3 oklab) { + float C = length(oklab.yz); + float H = atan(oklab.z, oklab.y); + if (C < OKLCH_CHROMA_THRESHOLD) { + H = OKLCH_HUE_NEUTRALIZER; + } + return vec3(oklab.x, C, H); +} + +vec3 oklchToOklab(vec3 oklch) { + float a = oklch.y * cos(oklch.z); + float b = oklch.y * sin(oklch.z); + return vec3(oklch.x, a, b); +} + +float mixHue(float h1, float h2, float mixer) { + float delta = mod(h2 - h1 + PI, TWO_PI) - PI; + return h1 + mixer * delta; +} + +vec3 srgbToOklab(vec3 rgb) { + return oklabToOklch(LrgbToOklab(srgbToLinear(rgb))); +} + +vec3 oklabToSrgb(vec3 oklab) { + return linearToSrgb(OklabToLrgb(oklchToOklab(oklab))); +} + +vec3 mixOklabVector(vec3 color1, vec3 color2, float mixer) { + color1.x = mix(color1.x, color2.x, mixer); + color1.y = mix(color1.y, color2.y, mixer); + if (color1.y > OKLCH_CHROMA_THRESHOLD && color2.y > OKLCH_CHROMA_THRESHOLD) { + color1.z = mixHue(color1.z, color2.z, mixer); + } + return color1; +} +`; +export { + ShaderColorSpaces, + declareOklchTransforms +}; +//# sourceMappingURL=shader-color-spaces.js.map diff --git a/public/vendor/paper-shaders/shader-color-spaces.js.map b/public/vendor/paper-shaders/shader-color-spaces.js.map new file mode 100644 index 0000000..0b5106e --- /dev/null +++ b/public/vendor/paper-shaders/shader-color-spaces.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../src/shader-color-spaces.ts"], + "sourcesContent": ["export const ShaderColorSpaces = {\n rgb: 0,\n oklch: 1,\n} as const;\n\nexport type ShaderColorSpace = keyof typeof ShaderColorSpaces;\n\nexport const declareOklchTransforms = `\n\n// magic numbers (and magic could be better tbh)\n#define OKLCH_CHROMA_THRESHOLD .001\n#define OKLCH_HUE_NEUTRALIZER - 2.\n\nvec3 srgbToLinear(vec3 srgb) {\n return pow(srgb, vec3(2.2));\n}\n\nvec3 linearToSrgb(vec3 linear) {\n return pow(linear, vec3(1.0 / 2.2));\n}\n\nvec3 LrgbToOklab(vec3 rgb) {\n float L = pow(0.4122214708 * rgb.r + 0.5363325363 * rgb.g + 0.0514459929 * rgb.b, 1.0 / 3.0);\n float M = pow(0.2119034982 * rgb.r + 0.6806995451 * rgb.g + 0.1073969566 * rgb.b, 1.0 / 3.0);\n float S = pow(0.0883024619 * rgb.r + 0.2817188376 * rgb.g + 0.6299787005 * rgb.b, 1.0 / 3.0);\n return vec3(\n 0.2104542553 * L + 0.793617785 * M - 0.0040720468 * S,\n 1.9779984951 * L - 2.428592205 * M + 0.4505937099 * S,\n 0.0259040371 * L + 0.7827717662 * M - 0.808675766 * S\n );\n}\n\nvec3 OklabToLrgb(vec3 oklab) {\n float L = oklab.x;\n float a = oklab.y;\n float b = oklab.z;\n\n float l_ = L + 0.3963377774 * a + 0.2158037573 * b;\n float m_ = L - 0.1055613458 * a - 0.0638541728 * b;\n float s_ = L - 0.0894841775 * a - 1.291485548 * b;\n\n float l = l_ * l_ * l_;\n float m = m_ * m_ * m_;\n float s = s_ * s_ * s_;\n\n return vec3(\n 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s\n );\n}\n\nvec3 oklabToOklch(vec3 oklab) {\n float C = length(oklab.yz);\n float H = atan(oklab.z, oklab.y);\n if (C < OKLCH_CHROMA_THRESHOLD) {\n H = OKLCH_HUE_NEUTRALIZER;\n }\n return vec3(oklab.x, C, H);\n}\n\nvec3 oklchToOklab(vec3 oklch) {\n float a = oklch.y * cos(oklch.z);\n float b = oklch.y * sin(oklch.z);\n return vec3(oklch.x, a, b);\n}\n\nfloat mixHue(float h1, float h2, float mixer) {\n float delta = mod(h2 - h1 + PI, TWO_PI) - PI;\n return h1 + mixer * delta;\n}\n\nvec3 srgbToOklab(vec3 rgb) {\n return oklabToOklch(LrgbToOklab(srgbToLinear(rgb)));\n}\n\nvec3 oklabToSrgb(vec3 oklab) {\n return linearToSrgb(OklabToLrgb(oklchToOklab(oklab)));\n}\n\nvec3 mixOklabVector(vec3 color1, vec3 color2, float mixer) {\n color1.x = mix(color1.x, color2.x, mixer);\n color1.y = mix(color1.y, color2.y, mixer);\n if (color1.y > OKLCH_CHROMA_THRESHOLD && color2.y > OKLCH_CHROMA_THRESHOLD) {\n color1.z = mixHue(color1.z, color2.z, mixer);\n }\n return color1;\n}\n`;\n"], + "mappings": ";;;;;AAAO,MAAM,oBAAoB;AAAA,EAC/B,KAAK;AAAA,EACL,OAAO;AACT;AAIO,MAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shader-mount.d.ts b/public/vendor/paper-shaders/shader-mount.d.ts new file mode 100644 index 0000000..5983e8a --- /dev/null +++ b/public/vendor/paper-shaders/shader-mount.d.ts @@ -0,0 +1,140 @@ +export declare class ShaderMount { + parentElement: PaperShaderElement; + canvasElement: HTMLCanvasElement; + private gl; + private program; + private uniformLocations; + /** The fragment shader that we are using */ + private fragmentShader; + /** Stores the RAF for the render loop */ + private rafId; + /** Time of the last rendered frame */ + private lastRenderTime; + /** Total time that we have played any animation, passed as a uniform to the shader for time-based VFX */ + private currentFrame; + /** The speed that we progress through animation time (multiplies by delta time every update). Allows negatives to play in reverse. If set to 0, rAF will stop entirely so static shaders have no recurring performance costs */ + private speed; + /** Actual speed used that accounts for visibility: we pause the shader if the tab is hidden or the element out of the viewport */ + private currentSpeed; + /** Uniforms that are provided by the user for the specific shader being mounted (not including uniforms that this Mount adds, like time and resolution) */ + private providedUniforms; + /** Names of the uniforms that should have mipmaps generated for them */ + private mipmaps; + /** Just a sanity check to make sure frames don't run after we're disposed */ + private hasBeenDisposed; + /** If the resolution of the canvas has changed since the last render */ + private resolutionChanged; + /** Store textures that are provided by the user */ + private textures; + private minPixelRatio; + private maxPixelCount; + private isSafari; + private uniformCache; + private textureUnitMap; + private ownerDocument; + constructor( + /** The div you'd like to mount the shader to. The shader will match its size. */ + parentElement: HTMLElement, fragmentShader: string, uniforms: ShaderMountUniforms, webGlContextAttributes?: WebGLContextAttributes, + /** The speed of the animation, or 0 to stop it. Supports negative values to play in reverse. */ + speed?: number, + /** Pass a frame to offset the starting u_time value and give deterministic results*/ + frame?: number, + /** + * The minimum pixel ratio to render at, defaults to 2. + * May be reduced to improve performance or increased together with `maxPixelCount` to improve antialiasing. + */ + minPixelRatio?: number, + /** + * The maximum amount of physical device pixels to render for the shader, + * by default it's 1920 * 1080 * 2x dpi (per each side) = 8,294,400 pixels of a 4K screen. + * Actual DOM size of the canvas can be larger, it will just lose quality after this. + * + * May be reduced to improve performance or increased to improve quality on high-resolution screens. + */ + maxPixelCount?: number, + /** Names of the uniforms that should have mipmaps generated for them */ + mipmaps?: string[]); + private initProgram; + private setupPositionAttribute; + private setupUniforms; + /** + * The scale that we should render at. + * - Used to target 2x rendering even on 1x screens for better antialiasing + * - Prevents the virtual resolution from going beyond the maximum resolution + * - Accounts for the page zoom level so we render in physical device pixels rather than CSS pixels + */ + private renderScale; + private parentWidth; + private parentHeight; + private parentDevicePixelWidth; + private parentDevicePixelHeight; + private devicePixelsSupported; + private intersectionObserver; + private isInViewport; + private resizeObserver; + private setupResizeObserver; + private setupIntersectionObserver; + private handleVisualViewportChange; + /** Resize handler for when the container div changes size or the max pixel count changes and we want to resize our canvas to match */ + private handleResize; + private render; + private requestRender; + /** Creates a texture from an image and sets it into a uniform value */ + private setTextureUniform; + /** Utility: recursive equality test for all the uniforms */ + private areUniformValuesEqual; + /** Sets the provided uniform values into the WebGL program, can be a partial list of uniforms that have changed */ + private setUniformValues; + /** Gets the current total animation time from 0ms */ + getCurrentFrame: () => number; + /** Set a frame to get a deterministic result, frames are literally just milliseconds from zero since the animation started */ + setFrame: (newFrame: number) => void; + /** Set an animation speed (or 0 to stop animation) */ + setSpeed: (newSpeed?: number) => void; + /** Apply the target speed, pausing (0) while the tab is hidden or the element is out of the viewport */ + private updateCurrentSpeed; + private setCurrentSpeed; + /** Set the maximum pixel count for the shader, this will limit the number of pixels that will be rendered */ + setMaxPixelCount: (newMaxPixelCount?: number) => void; + /** Set the minimum pixel ratio for the shader */ + setMinPixelRatio: (newMinPixelRatio?: number) => void; + /** Update the uniforms that are provided by the outside shader, can be a partial set with only the uniforms that have changed */ + setUniforms: (newUniforms: ShaderMountUniforms) => void; + private handleDocumentVisibilityChange; + /** Dispose of the shader mount, cleaning up all of the WebGL resources */ + dispose: () => void; +} +/** The parent `
` element that has a ShaderMount available on it */ +export interface PaperShaderElement extends HTMLElement { + paperShaderMount: ShaderMount | undefined; +} +/** Check if an element is a Paper shader element */ +export declare function isPaperShaderElement(element: HTMLElement): element is PaperShaderElement; +/** + * Uniform types that we support to be auto-mapped into the fragment shader + * + * We accept undefined as a convenience for server rendering, when some things may be undefined + * We just skip setting the uniform if it's undefined. This allows the shader mount to still take up space during server rendering + */ +export interface ShaderMountUniforms { + [key: string]: boolean | number | number[] | number[][] | HTMLImageElement | undefined; +} +export interface ShaderMotionParams { + speed?: number; + frame?: number; +} +export type ShaderPreset = { + name: string; + params: Required; +}; +export type ImageShaderPreset = { + name: string; + /** + * Params for the shader excluding the image. + * Image is excluded as it isn't considered a preset, + * e.g. when switching between presets it shouldn't switch the image. + * + * While we exclude images from presets they should still be set with a default prop value so the code-first usage of shaders remains great. + */ + params: Required>; +}; diff --git a/public/vendor/paper-shaders/shader-mount.js b/public/vendor/paper-shaders/shader-mount.js new file mode 100644 index 0000000..5cf3dfe --- /dev/null +++ b/public/vendor/paper-shaders/shader-mount.js @@ -0,0 +1,510 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import { vertexShaderSource } from "./vertex-shader.js"; +const DEFAULT_MAX_PIXEL_COUNT = 1920 * 1080 * 4; +class ShaderMount { + parentElement; + canvasElement; + gl; + program = null; + uniformLocations = {}; + /** The fragment shader that we are using */ + fragmentShader; + /** Stores the RAF for the render loop */ + rafId = null; + /** Time of the last rendered frame */ + lastRenderTime = 0; + /** Total time that we have played any animation, passed as a uniform to the shader for time-based VFX */ + currentFrame = 0; + /** The speed that we progress through animation time (multiplies by delta time every update). Allows negatives to play in reverse. If set to 0, rAF will stop entirely so static shaders have no recurring performance costs */ + speed = 0; + /** Actual speed used that accounts for visibility: we pause the shader if the tab is hidden or the element out of the viewport */ + currentSpeed = 0; + /** Uniforms that are provided by the user for the specific shader being mounted (not including uniforms that this Mount adds, like time and resolution) */ + providedUniforms; + /** Names of the uniforms that should have mipmaps generated for them */ + mipmaps = []; + /** Just a sanity check to make sure frames don't run after we're disposed */ + hasBeenDisposed = false; + /** If the resolution of the canvas has changed since the last render */ + resolutionChanged = true; + /** Store textures that are provided by the user */ + textures = /* @__PURE__ */ new Map(); + minPixelRatio; + maxPixelCount; + isSafari = isSafari(); + uniformCache = {}; + textureUnitMap = /* @__PURE__ */ new Map(); + ownerDocument; + constructor(parentElement, fragmentShader, uniforms, webGlContextAttributes, speed = 0, frame = 0, minPixelRatio = 2, maxPixelCount = DEFAULT_MAX_PIXEL_COUNT, mipmaps = []) { + if (parentElement?.nodeType === 1) { + this.parentElement = parentElement; + } else { + throw new Error("Paper Shaders: parent element must be an HTMLElement"); + } + this.ownerDocument = parentElement.ownerDocument; + if (!this.ownerDocument.querySelector("style[data-paper-shader]")) { + const styleElement = this.ownerDocument.createElement("style"); + styleElement.innerHTML = defaultStyle; + styleElement.setAttribute("data-paper-shader", ""); + this.ownerDocument.head.prepend(styleElement); + } + const canvasElement = this.ownerDocument.createElement("canvas"); + this.canvasElement = canvasElement; + this.parentElement.prepend(canvasElement); + this.fragmentShader = fragmentShader; + this.providedUniforms = uniforms; + this.mipmaps = mipmaps; + this.currentFrame = frame; + this.minPixelRatio = minPixelRatio; + this.maxPixelCount = maxPixelCount; + const gl = canvasElement.getContext("webgl2", webGlContextAttributes); + if (!gl) { + throw new Error("Paper Shaders: WebGL is not supported in this browser"); + } + this.gl = gl; + this.initProgram(); + this.setupPositionAttribute(); + this.setupUniforms(); + this.setUniformValues(this.providedUniforms); + this.setupResizeObserver(); + visualViewport?.addEventListener("resize", this.handleVisualViewportChange); + this.setupIntersectionObserver(); + this.setSpeed(speed); + this.parentElement.setAttribute("data-paper-shader", ""); + this.parentElement.paperShaderMount = this; + this.ownerDocument.addEventListener("visibilitychange", this.handleDocumentVisibilityChange); + } + initProgram = () => { + const program = createProgram(this.gl, vertexShaderSource, this.fragmentShader); + if (!program) return; + this.program = program; + }; + setupPositionAttribute = () => { + const positionAttributeLocation = this.gl.getAttribLocation(this.program, "a_position"); + const positionBuffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer); + const positions = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]; + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW); + this.gl.enableVertexAttribArray(positionAttributeLocation); + this.gl.vertexAttribPointer(positionAttributeLocation, 2, this.gl.FLOAT, false, 0, 0); + }; + setupUniforms = () => { + const uniformLocations = { + u_time: this.gl.getUniformLocation(this.program, "u_time"), + u_pixelRatio: this.gl.getUniformLocation(this.program, "u_pixelRatio"), + u_resolution: this.gl.getUniformLocation(this.program, "u_resolution") + }; + Object.entries(this.providedUniforms).forEach(([key, value]) => { + uniformLocations[key] = this.gl.getUniformLocation(this.program, key); + if (value instanceof HTMLImageElement) { + const aspectRatioUniformName = `${key}AspectRatio`; + uniformLocations[aspectRatioUniformName] = this.gl.getUniformLocation(this.program, aspectRatioUniformName); + } + }); + this.uniformLocations = uniformLocations; + }; + /** + * The scale that we should render at. + * - Used to target 2x rendering even on 1x screens for better antialiasing + * - Prevents the virtual resolution from going beyond the maximum resolution + * - Accounts for the page zoom level so we render in physical device pixels rather than CSS pixels + */ + renderScale = 1; + parentWidth = 0; + parentHeight = 0; + parentDevicePixelWidth = 0; + parentDevicePixelHeight = 0; + devicePixelsSupported = false; + intersectionObserver = null; + isInViewport = true; + resizeObserver = null; + setupResizeObserver = () => { + this.resizeObserver = new ResizeObserver(([entry]) => { + if (entry?.borderBoxSize[0]) { + const physicalPixelSize = entry.devicePixelContentBoxSize?.[0]; + if (physicalPixelSize !== void 0) { + this.devicePixelsSupported = true; + this.parentDevicePixelWidth = physicalPixelSize.inlineSize; + this.parentDevicePixelHeight = physicalPixelSize.blockSize; + } + this.parentWidth = entry.borderBoxSize[0].inlineSize; + this.parentHeight = entry.borderBoxSize[0].blockSize; + } + this.handleResize(); + }); + this.resizeObserver.observe(this.parentElement); + }; + setupIntersectionObserver = () => { + const view = this.ownerDocument.defaultView; + if (!view?.IntersectionObserver) return; + this.intersectionObserver = new view.IntersectionObserver(([entry]) => { + this.isInViewport = entry?.isIntersecting ?? true; + this.updateCurrentSpeed(); + }); + this.intersectionObserver.observe(this.parentElement); + }; + // Visual viewport resize handler, mainly used to react to browser zoom changes. + // Resize observer by itself does not react to pinch zoom, and although it usually + // reacts to classic browser zoom, it's not guaranteed in edge cases. + // Since timing between visual viewport changes and resize observer is complex + // and because we'd like to know the device pixel sizes of elements, we just restart + // the observer to get a guaranteed fresh callback regardless if it would have triggered or not. + handleVisualViewportChange = () => { + this.resizeObserver?.disconnect(); + this.setupResizeObserver(); + }; + /** Resize handler for when the container div changes size or the max pixel count changes and we want to resize our canvas to match */ + handleResize = () => { + let targetPixelWidth = 0; + let targetPixelHeight = 0; + const dpr = Math.max(1, window.devicePixelRatio); + const pinchZoom = visualViewport?.scale ?? 1; + if (this.devicePixelsSupported) { + const scaleToMeetMinPixelRatio = Math.max(1, this.minPixelRatio / dpr); + targetPixelWidth = this.parentDevicePixelWidth * scaleToMeetMinPixelRatio * pinchZoom; + targetPixelHeight = this.parentDevicePixelHeight * scaleToMeetMinPixelRatio * pinchZoom; + } else { + let targetRenderScale = Math.max(dpr, this.minPixelRatio) * pinchZoom; + if (this.isSafari) { + const zoomLevel = bestGuessBrowserZoom(this.ownerDocument); + targetRenderScale *= Math.max(1, zoomLevel); + } + targetPixelWidth = Math.round(this.parentWidth) * targetRenderScale; + targetPixelHeight = Math.round(this.parentHeight) * targetRenderScale; + } + const maxPixelCountHeadroom = Math.sqrt(this.maxPixelCount) / Math.sqrt(targetPixelWidth * targetPixelHeight); + const scaleToMeetMaxPixelCount = Math.min(1, maxPixelCountHeadroom); + const newWidth = Math.round(targetPixelWidth * scaleToMeetMaxPixelCount); + const newHeight = Math.round(targetPixelHeight * scaleToMeetMaxPixelCount); + const newRenderScale = newWidth / Math.round(this.parentWidth); + if (this.canvasElement.width !== newWidth || this.canvasElement.height !== newHeight || this.renderScale !== newRenderScale) { + this.renderScale = newRenderScale; + this.canvasElement.width = newWidth; + this.canvasElement.height = newHeight; + this.resolutionChanged = true; + this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height); + this.render(performance.now()); + } + }; + render = (currentTime) => { + if (this.hasBeenDisposed) return; + if (this.program === null) { + console.warn("Tried to render before program or gl was initialized"); + return; + } + const dt = currentTime - this.lastRenderTime; + this.lastRenderTime = currentTime; + if (this.currentSpeed !== 0) { + this.currentFrame += dt * this.currentSpeed; + } + this.gl.clear(this.gl.COLOR_BUFFER_BIT); + this.gl.useProgram(this.program); + this.gl.uniform1f(this.uniformLocations.u_time, this.currentFrame * 1e-3); + if (this.resolutionChanged) { + this.gl.uniform2f(this.uniformLocations.u_resolution, this.gl.canvas.width, this.gl.canvas.height); + this.gl.uniform1f(this.uniformLocations.u_pixelRatio, this.renderScale); + this.resolutionChanged = false; + } + this.gl.drawArrays(this.gl.TRIANGLES, 0, 6); + if (this.currentSpeed !== 0) { + this.requestRender(); + } else { + this.rafId = null; + } + }; + requestRender = () => { + if (this.rafId !== null) { + cancelAnimationFrame(this.rafId); + } + this.rafId = requestAnimationFrame(this.render); + }; + /** Creates a texture from an image and sets it into a uniform value */ + setTextureUniform = (uniformName, image) => { + if (!image.complete || image.naturalWidth === 0) { + throw new Error(`Paper Shaders: image for uniform ${uniformName} must be fully loaded`); + } + const existingTexture = this.textures.get(uniformName); + if (existingTexture) { + this.gl.deleteTexture(existingTexture); + } + if (!this.textureUnitMap.has(uniformName)) { + this.textureUnitMap.set(uniformName, this.textureUnitMap.size); + } + const textureUnit = this.textureUnitMap.get(uniformName); + this.gl.activeTexture(this.gl.TEXTURE0 + textureUnit); + const texture = this.gl.createTexture(); + this.gl.bindTexture(this.gl.TEXTURE_2D, texture); + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); + this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image); + if (this.mipmaps.includes(uniformName)) { + this.gl.generateMipmap(this.gl.TEXTURE_2D); + this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR); + } + const error = this.gl.getError(); + if (error !== this.gl.NO_ERROR || texture === null) { + console.error("Paper Shaders: WebGL error when uploading texture:", error); + return; + } + this.textures.set(uniformName, texture); + const location = this.uniformLocations[uniformName]; + if (location) { + this.gl.uniform1i(location, textureUnit); + const aspectRatioUniformName = `${uniformName}AspectRatio`; + const aspectRatioLocation = this.uniformLocations[aspectRatioUniformName]; + if (aspectRatioLocation) { + const aspectRatio = image.naturalWidth / image.naturalHeight; + this.gl.uniform1f(aspectRatioLocation, aspectRatio); + } + } + }; + /** Utility: recursive equality test for all the uniforms */ + areUniformValuesEqual = (a, b) => { + if (a === b) return true; + if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) { + return a.every((val, i) => this.areUniformValuesEqual(val, b[i])); + } + return false; + }; + /** Sets the provided uniform values into the WebGL program, can be a partial list of uniforms that have changed */ + setUniformValues = (updatedUniforms) => { + this.gl.useProgram(this.program); + Object.entries(updatedUniforms).forEach(([key, value]) => { + let cacheValue = value; + if (value instanceof HTMLImageElement) { + cacheValue = `${value.src.slice(0, 200)}|${value.naturalWidth}x${value.naturalHeight}`; + } + if (this.areUniformValuesEqual(this.uniformCache[key], cacheValue)) return; + this.uniformCache[key] = cacheValue; + const location = this.uniformLocations[key]; + if (!location) { + console.warn(`Uniform location for ${key} not found`); + return; + } + if (value instanceof HTMLImageElement) { + this.setTextureUniform(key, value); + } else if (Array.isArray(value)) { + let flatArray = null; + let valueLength = null; + if (value[0] !== void 0 && Array.isArray(value[0])) { + const firstChildLength = value[0].length; + if (value.every((arr) => arr.length === firstChildLength)) { + flatArray = value.flat(); + valueLength = firstChildLength; + } else { + console.warn(`All child arrays must be the same length for ${key}`); + return; + } + } else { + flatArray = value; + valueLength = flatArray.length; + } + switch (valueLength) { + case 2: + this.gl.uniform2fv(location, flatArray); + break; + case 3: + this.gl.uniform3fv(location, flatArray); + break; + case 4: + this.gl.uniform4fv(location, flatArray); + break; + case 9: + this.gl.uniformMatrix3fv(location, false, flatArray); + break; + case 16: + this.gl.uniformMatrix4fv(location, false, flatArray); + break; + default: + console.warn(`Unsupported uniform array length: ${valueLength}`); + } + } else if (typeof value === "number") { + this.gl.uniform1f(location, value); + } else if (typeof value === "boolean") { + this.gl.uniform1i(location, value ? 1 : 0); + } else { + console.warn(`Unsupported uniform type for ${key}: ${typeof value}`); + } + }); + }; + /** Gets the current total animation time from 0ms */ + getCurrentFrame = () => { + return this.currentFrame; + }; + /** Set a frame to get a deterministic result, frames are literally just milliseconds from zero since the animation started */ + setFrame = (newFrame) => { + this.currentFrame = newFrame; + this.lastRenderTime = performance.now(); + this.render(performance.now()); + }; + /** Set an animation speed (or 0 to stop animation) */ + setSpeed = (newSpeed = 1) => { + this.speed = newSpeed; + this.updateCurrentSpeed(); + }; + /** Apply the target speed, pausing (0) while the tab is hidden or the element is out of the viewport */ + updateCurrentSpeed = () => { + this.setCurrentSpeed(this.ownerDocument.hidden || !this.isInViewport ? 0 : this.speed); + }; + setCurrentSpeed = (newSpeed) => { + this.currentSpeed = newSpeed; + if (this.rafId === null && newSpeed !== 0) { + this.lastRenderTime = performance.now(); + this.rafId = requestAnimationFrame(this.render); + } + if (this.rafId !== null && newSpeed === 0) { + cancelAnimationFrame(this.rafId); + this.rafId = null; + } + }; + /** Set the maximum pixel count for the shader, this will limit the number of pixels that will be rendered */ + setMaxPixelCount = (newMaxPixelCount = DEFAULT_MAX_PIXEL_COUNT) => { + this.maxPixelCount = newMaxPixelCount; + this.handleResize(); + }; + /** Set the minimum pixel ratio for the shader */ + setMinPixelRatio = (newMinPixelRatio = 2) => { + this.minPixelRatio = newMinPixelRatio; + this.handleResize(); + }; + /** Update the uniforms that are provided by the outside shader, can be a partial set with only the uniforms that have changed */ + setUniforms = (newUniforms) => { + this.setUniformValues(newUniforms); + this.providedUniforms = { ...this.providedUniforms, ...newUniforms }; + this.render(performance.now()); + }; + handleDocumentVisibilityChange = () => { + this.updateCurrentSpeed(); + }; + /** Dispose of the shader mount, cleaning up all of the WebGL resources */ + dispose = () => { + this.hasBeenDisposed = true; + if (this.rafId !== null) { + cancelAnimationFrame(this.rafId); + this.rafId = null; + } + if (this.gl && this.program) { + this.textures.forEach((texture) => { + this.gl.deleteTexture(texture); + }); + this.textures.clear(); + this.gl.deleteProgram(this.program); + this.program = null; + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null); + this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null); + this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); + this.gl.getError(); + } + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } + if (this.intersectionObserver) { + this.intersectionObserver.disconnect(); + this.intersectionObserver = null; + } + visualViewport?.removeEventListener("resize", this.handleVisualViewportChange); + this.ownerDocument.removeEventListener("visibilitychange", this.handleDocumentVisibilityChange); + this.uniformLocations = {}; + this.canvasElement.remove(); + delete this.parentElement.paperShaderMount; + }; +} +function createShader(gl, type, source) { + const shader = gl.createShader(type); + if (!shader) return null; + gl.shaderSource(shader, source); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + console.error("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader)); + gl.deleteShader(shader); + return null; + } + return shader; +} +function createProgram(gl, vertexShaderSource2, fragmentShaderSource) { + const format = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT); + const precision = format ? format.precision : null; + if (precision && precision < 23) { + vertexShaderSource2 = vertexShaderSource2.replace(/precision\s+(lowp|mediump)\s+float;/g, "precision highp float;"); + fragmentShaderSource = fragmentShaderSource.replace(/precision\s+(lowp|mediump)\s+float/g, "precision highp float").replace(/\b(uniform|varying|attribute)\s+(lowp|mediump)\s+(\w+)/g, "$1 highp $3"); + } + const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource2); + const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource); + if (!vertexShader || !fragmentShader) return null; + const program = gl.createProgram(); + if (!program) return null; + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + console.error("Unable to initialize the shader program: " + gl.getProgramInfoLog(program)); + gl.deleteProgram(program); + gl.deleteShader(vertexShader); + gl.deleteShader(fragmentShader); + return null; + } + gl.detachShader(program, vertexShader); + gl.detachShader(program, fragmentShader); + gl.deleteShader(vertexShader); + gl.deleteShader(fragmentShader); + return program; +} +const defaultStyle = `@layer paper-shaders { + :where([data-paper-shader]) { + isolation: isolate; + position: relative; + + & canvas { + contain: strict; + display: block; + position: absolute; + inset: 0; + z-index: -1; + width: 100%; + height: 100%; + border-radius: inherit; + corner-shape: inherit; + } + } +}`; +function isPaperShaderElement(element) { + return "paperShaderMount" in element; +} +function isSafari() { + const ua = navigator.userAgent.toLowerCase(); + return ua.includes("safari") && !ua.includes("chrome") && !ua.includes("android"); +} +function bestGuessBrowserZoom(doc) { + const viewportScale = visualViewport?.scale ?? 1; + const viewportWidth = visualViewport?.width ?? window.innerWidth; + const scrollbarWidth = window.innerWidth - doc.documentElement.clientWidth; + const innerWidth = viewportScale * viewportWidth + scrollbarWidth; + const ratio = outerWidth / innerWidth; + const zoomPercentageRounded = Math.round(100 * ratio); + if (zoomPercentageRounded % 5 === 0) { + return zoomPercentageRounded / 100; + } + if (zoomPercentageRounded === 33) { + return 1 / 3; + } + if (zoomPercentageRounded === 67) { + return 2 / 3; + } + if (zoomPercentageRounded === 133) { + return 4 / 3; + } + return ratio; +} +export { + ShaderMount, + isPaperShaderElement +}; +//# sourceMappingURL=shader-mount.js.map diff --git a/public/vendor/paper-shaders/shader-mount.js.map b/public/vendor/paper-shaders/shader-mount.js.map new file mode 100644 index 0000000..3e2d6f3 --- /dev/null +++ b/public/vendor/paper-shaders/shader-mount.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../src/shader-mount.ts"], + "sourcesContent": ["import { vertexShaderSource } from './vertex-shader.js';\n\nconst DEFAULT_MAX_PIXEL_COUNT: number = 1920 * 1080 * 4;\n\nexport class ShaderMount {\n public parentElement: PaperShaderElement;\n public canvasElement: HTMLCanvasElement;\n private gl: WebGL2RenderingContext;\n private program: WebGLProgram | null = null;\n private uniformLocations: Record = {};\n /** The fragment shader that we are using */\n private fragmentShader: string;\n /** Stores the RAF for the render loop */\n private rafId: number | null = null;\n /** Time of the last rendered frame */\n private lastRenderTime = 0;\n /** Total time that we have played any animation, passed as a uniform to the shader for time-based VFX */\n private currentFrame = 0;\n /** The speed that we progress through animation time (multiplies by delta time every update). Allows negatives to play in reverse. If set to 0, rAF will stop entirely so static shaders have no recurring performance costs */\n private speed = 0;\n /** Actual speed used that accounts for visibility: we pause the shader if the tab is hidden or the element out of the viewport */\n private currentSpeed = 0;\n /** Uniforms that are provided by the user for the specific shader being mounted (not including uniforms that this Mount adds, like time and resolution) */\n private providedUniforms: ShaderMountUniforms;\n /** Names of the uniforms that should have mipmaps generated for them */\n private mipmaps: string[] = [];\n /** Just a sanity check to make sure frames don't run after we're disposed */\n private hasBeenDisposed = false;\n /** If the resolution of the canvas has changed since the last render */\n private resolutionChanged = true;\n /** Store textures that are provided by the user */\n private textures: Map = new Map();\n private minPixelRatio;\n private maxPixelCount;\n private isSafari = isSafari();\n private uniformCache: Record = {};\n private textureUnitMap: Map = new Map();\n private ownerDocument: Document;\n\n constructor(\n /** The div you'd like to mount the shader to. The shader will match its size. */\n parentElement: HTMLElement,\n fragmentShader: string,\n uniforms: ShaderMountUniforms,\n webGlContextAttributes?: WebGLContextAttributes,\n /** The speed of the animation, or 0 to stop it. Supports negative values to play in reverse. */\n speed = 0,\n /** Pass a frame to offset the starting u_time value and give deterministic results*/\n frame = 0,\n /**\n * The minimum pixel ratio to render at, defaults to 2.\n * May be reduced to improve performance or increased together with `maxPixelCount` to improve antialiasing.\n */\n minPixelRatio = 2,\n /**\n * The maximum amount of physical device pixels to render for the shader,\n * by default it's 1920 * 1080 * 2x dpi (per each side) = 8,294,400 pixels of a 4K screen.\n * Actual DOM size of the canvas can be larger, it will just lose quality after this.\n *\n * May be reduced to improve performance or increased to improve quality on high-resolution screens.\n */\n maxPixelCount: number = DEFAULT_MAX_PIXEL_COUNT,\n /** Names of the uniforms that should have mipmaps generated for them */\n mipmaps: string[] = []\n ) {\n // nodeType check instead of `instanceof` to work across document boundaries (iframes, PiP windows)\n if (parentElement?.nodeType === 1) {\n this.parentElement = parentElement as PaperShaderElement;\n } else {\n throw new Error('Paper Shaders: parent element must be an HTMLElement');\n }\n\n this.ownerDocument = parentElement.ownerDocument;\n\n if (!this.ownerDocument.querySelector('style[data-paper-shader]')) {\n const styleElement = this.ownerDocument.createElement('style');\n styleElement.innerHTML = defaultStyle;\n styleElement.setAttribute('data-paper-shader', '');\n this.ownerDocument.head.prepend(styleElement);\n }\n\n // Create the canvas element and mount it into the provided element\n const canvasElement = this.ownerDocument.createElement('canvas');\n this.canvasElement = canvasElement;\n this.parentElement.prepend(canvasElement);\n this.fragmentShader = fragmentShader;\n this.providedUniforms = uniforms;\n this.mipmaps = mipmaps;\n // Base our starting animation time on the provided frame value\n this.currentFrame = frame;\n this.minPixelRatio = minPixelRatio;\n this.maxPixelCount = maxPixelCount;\n\n const gl = canvasElement.getContext('webgl2', webGlContextAttributes);\n if (!gl) {\n throw new Error('Paper Shaders: WebGL is not supported in this browser');\n }\n this.gl = gl;\n\n this.initProgram();\n this.setupPositionAttribute();\n // Grab the locations of the uniforms in the fragment shader\n this.setupUniforms();\n // Put the user provided values into the uniforms\n this.setUniformValues(this.providedUniforms);\n // Set up the resize observer to handle window resizing and set u_resolution\n this.setupResizeObserver();\n // Set up the visual viewport change listener to handle zoom changes (pinch zoom and classic browser zoom)\n visualViewport?.addEventListener('resize', this.handleVisualViewportChange);\n // Set up the intersection observer to pause animation when the element is scrolled out of view\n this.setupIntersectionObserver();\n\n // Set the animation speed after everything is ready to go\n this.setSpeed(speed);\n\n // Mark parent element as paper shader mount\n this.parentElement.setAttribute('data-paper-shader', '');\n\n // Add the shaderMount instance to the div mount element to make it easily accessible\n this.parentElement.paperShaderMount = this;\n\n // Listen for document visibility changes to pause the shader when the tab is hidden\n this.ownerDocument.addEventListener('visibilitychange', this.handleDocumentVisibilityChange);\n }\n\n private initProgram = () => {\n const program = createProgram(this.gl, vertexShaderSource, this.fragmentShader);\n if (!program) return;\n this.program = program;\n };\n\n private setupPositionAttribute = () => {\n const positionAttributeLocation = this.gl.getAttribLocation(this.program!, 'a_position');\n const positionBuffer = this.gl.createBuffer();\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);\n const positions = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];\n this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW);\n this.gl.enableVertexAttribArray(positionAttributeLocation);\n this.gl.vertexAttribPointer(positionAttributeLocation, 2, this.gl.FLOAT, false, 0, 0);\n };\n\n private setupUniforms = () => {\n // Create a map to store all uniform locations\n const uniformLocations: Record = {\n u_time: this.gl.getUniformLocation(this.program!, 'u_time'),\n u_pixelRatio: this.gl.getUniformLocation(this.program!, 'u_pixelRatio'),\n u_resolution: this.gl.getUniformLocation(this.program!, 'u_resolution'),\n };\n\n // Add locations for all provided uniforms\n Object.entries(this.providedUniforms).forEach(([key, value]) => {\n uniformLocations[key] = this.gl.getUniformLocation(this.program!, key);\n\n // For texture uniforms, also look for the aspect ratio uniform\n if (value instanceof HTMLImageElement) {\n const aspectRatioUniformName = `${key}AspectRatio`;\n uniformLocations[aspectRatioUniformName] = this.gl.getUniformLocation(this.program!, aspectRatioUniformName);\n }\n });\n\n this.uniformLocations = uniformLocations;\n };\n\n /**\n * The scale that we should render at.\n * - Used to target 2x rendering even on 1x screens for better antialiasing\n * - Prevents the virtual resolution from going beyond the maximum resolution\n * - Accounts for the page zoom level so we render in physical device pixels rather than CSS pixels\n */\n private renderScale = 1;\n private parentWidth = 0;\n private parentHeight = 0;\n private parentDevicePixelWidth = 0;\n private parentDevicePixelHeight = 0;\n private devicePixelsSupported = false;\n\n private intersectionObserver: IntersectionObserver | null = null;\n private isInViewport = true;\n\n private resizeObserver: ResizeObserver | null = null;\n private setupResizeObserver = () => {\n this.resizeObserver = new ResizeObserver(([entry]) => {\n if (entry?.borderBoxSize[0]) {\n const physicalPixelSize = entry.devicePixelContentBoxSize?.[0];\n\n if (physicalPixelSize !== undefined) {\n this.devicePixelsSupported = true;\n this.parentDevicePixelWidth = physicalPixelSize.inlineSize;\n this.parentDevicePixelHeight = physicalPixelSize.blockSize;\n }\n\n this.parentWidth = entry.borderBoxSize[0].inlineSize;\n this.parentHeight = entry.borderBoxSize[0].blockSize;\n }\n\n this.handleResize();\n });\n\n this.resizeObserver.observe(this.parentElement);\n };\n\n private setupIntersectionObserver = () => {\n // we check element's own window to support shaders used within iframe/PiP elements\n const view = this.ownerDocument.defaultView;\n if (!view?.IntersectionObserver) return;\n\n this.intersectionObserver = new view.IntersectionObserver(([entry]) => {\n this.isInViewport = entry?.isIntersecting ?? true;\n this.updateCurrentSpeed();\n });\n\n this.intersectionObserver.observe(this.parentElement);\n };\n\n // Visual viewport resize handler, mainly used to react to browser zoom changes.\n // Resize observer by itself does not react to pinch zoom, and although it usually\n // reacts to classic browser zoom, it's not guaranteed in edge cases.\n // Since timing between visual viewport changes and resize observer is complex\n // and because we'd like to know the device pixel sizes of elements, we just restart\n // the observer to get a guaranteed fresh callback regardless if it would have triggered or not.\n private handleVisualViewportChange = () => {\n this.resizeObserver?.disconnect();\n this.setupResizeObserver();\n\n // In case of debugging timing, from here on:\n // - animation frame 1: a paint after the visual viewport resize\n // - animation frame 2: a paint after the resize observer has been handled, if it was ever triggered\n };\n\n /** Resize handler for when the container div changes size or the max pixel count changes and we want to resize our canvas to match */\n private handleResize = () => {\n // Aim to render at least as many pixels as physically displayed\n // This will overshoot when the user zooms out, but that's acceptable\n\n let targetPixelWidth = 0;\n let targetPixelHeight = 0;\n\n // If window.devicePixelRatio is below 1, it's safe to say the browser is just zoomed out\n // We can use 1 as the minimum value not to upscale it needlessly to meet the min pixel ratio param\n const dpr = Math.max(1, window.devicePixelRatio);\n const pinchZoom = visualViewport?.scale ?? 1;\n\n if (this.devicePixelsSupported) {\n // Use the real pixel size if we know it, plus meet the min pixel ratio requirement and add in pinch zoom\n const scaleToMeetMinPixelRatio = Math.max(1, this.minPixelRatio / dpr);\n targetPixelWidth = this.parentDevicePixelWidth * scaleToMeetMinPixelRatio * pinchZoom;\n targetPixelHeight = this.parentDevicePixelHeight * scaleToMeetMinPixelRatio * pinchZoom;\n } else {\n // Otherwise try to approximate the element size in device pixels using devicePixelRatio.\n // (devicePixelRatio is imprecise and element's width/height may be fractional CSS sizes, not real pixels).\n let targetRenderScale = Math.max(dpr, this.minPixelRatio) * pinchZoom;\n\n if (this.isSafari) {\n // As of 2025, Safari reports physical devicePixelRatio, but other browsers add the current zoom level:\n // https://bugs.webkit.org/show_bug.cgi?id=124862\n //\n // In Safari we need to factor in the zoom level manually in order to set the target resolution.\n // To avoid sidebars upscaling the target resolution, set a minimum zoom level of 1.\n // This will render at higher resolution when zoomed out, but that's fine.\n // (We mostly care about maintaining good quality when zoomed in).\n const zoomLevel = bestGuessBrowserZoom(this.ownerDocument);\n targetRenderScale *= Math.max(1, zoomLevel);\n }\n\n // Rounding the client width/height since they may be fractional in CSS layout values\n targetPixelWidth = Math.round(this.parentWidth) * targetRenderScale;\n targetPixelHeight = Math.round(this.parentHeight) * targetRenderScale;\n }\n\n // Prevent the total rendered pixel count from exceeding maxPixelCount\n const maxPixelCountHeadroom = Math.sqrt(this.maxPixelCount) / Math.sqrt(targetPixelWidth * targetPixelHeight);\n const scaleToMeetMaxPixelCount = Math.min(1, maxPixelCountHeadroom);\n const newWidth = Math.round(targetPixelWidth * scaleToMeetMaxPixelCount);\n const newHeight = Math.round(targetPixelHeight * scaleToMeetMaxPixelCount);\n const newRenderScale = newWidth / Math.round(this.parentWidth);\n\n if (\n this.canvasElement.width !== newWidth ||\n this.canvasElement.height !== newHeight ||\n this.renderScale !== newRenderScale // Usually, only render scale changes when the user zooms in/out\n ) {\n this.renderScale = newRenderScale;\n this.canvasElement.width = newWidth;\n this.canvasElement.height = newHeight;\n this.resolutionChanged = true;\n this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);\n\n // this is necessary to avoid flashes while resizing (the next scheduled render will set uniforms)\n this.render(performance.now());\n }\n };\n\n private render = (currentTime: number) => {\n if (this.hasBeenDisposed) return;\n\n if (this.program === null) {\n console.warn('Tried to render before program or gl was initialized');\n return;\n }\n\n // Calculate the delta time\n const dt = currentTime - this.lastRenderTime;\n this.lastRenderTime = currentTime;\n // Increase the total animation time by dt * animationSpeed\n if (this.currentSpeed !== 0) {\n this.currentFrame += dt * this.currentSpeed;\n }\n\n // Clear the canvas\n this.gl.clear(this.gl.COLOR_BUFFER_BIT);\n\n // Update uniforms\n this.gl.useProgram(this.program);\n\n // Update the time uniform\n this.gl.uniform1f(this.uniformLocations.u_time!, this.currentFrame * 0.001);\n\n // If the resolution has changed, we need to update the uniform\n if (this.resolutionChanged) {\n this.gl.uniform2f(this.uniformLocations.u_resolution!, this.gl.canvas.width, this.gl.canvas.height);\n this.gl.uniform1f(this.uniformLocations.u_pixelRatio!, this.renderScale);\n this.resolutionChanged = false;\n }\n\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);\n\n // Loop if we're animating\n if (this.currentSpeed !== 0) {\n this.requestRender();\n } else {\n this.rafId = null;\n }\n };\n\n private requestRender = () => {\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n }\n this.rafId = requestAnimationFrame(this.render);\n };\n\n /** Creates a texture from an image and sets it into a uniform value */\n private setTextureUniform = (uniformName: string, image: HTMLImageElement): void => {\n if (!image.complete || image.naturalWidth === 0) {\n throw new Error(`Paper Shaders: image for uniform ${uniformName} must be fully loaded`);\n }\n\n // Clean up existing texture if present\n const existingTexture = this.textures.get(uniformName);\n if (existingTexture) {\n this.gl.deleteTexture(existingTexture);\n }\n\n // Get texture unit\n if (!this.textureUnitMap.has(uniformName)) {\n this.textureUnitMap.set(uniformName, this.textureUnitMap.size);\n }\n const textureUnit = this.textureUnitMap.get(uniformName)!;\n // Activate correct texture unit before creating the texture\n this.gl.activeTexture(this.gl.TEXTURE0 + textureUnit);\n\n // Create and set up the new texture\n const texture = this.gl.createTexture();\n this.gl.bindTexture(this.gl.TEXTURE_2D, texture);\n\n // Set texture parameters\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);\n\n // Upload image to texture\n this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image);\n\n // Generate mipmaps if the uniform is in the mipmaps list\n if (this.mipmaps.includes(uniformName)) {\n this.gl.generateMipmap(this.gl.TEXTURE_2D);\n this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_LINEAR);\n }\n\n const error = this.gl.getError();\n if (error !== this.gl.NO_ERROR || texture === null) {\n console.error('Paper Shaders: WebGL error when uploading texture:', error);\n return;\n }\n\n // Store the texture\n this.textures.set(uniformName, texture);\n\n // Set up texture unit and uniform\n const location = this.uniformLocations[uniformName];\n if (location) {\n this.gl.uniform1i(location, textureUnit);\n\n // Calculate and set the aspect ratio uniform\n const aspectRatioUniformName = `${uniformName}AspectRatio`;\n const aspectRatioLocation = this.uniformLocations[aspectRatioUniformName];\n if (aspectRatioLocation) {\n const aspectRatio = image.naturalWidth / image.naturalHeight;\n this.gl.uniform1f(aspectRatioLocation, aspectRatio);\n }\n }\n };\n\n /** Utility: recursive equality test for all the uniforms */\n private areUniformValuesEqual = (a: any, b: any): boolean => {\n if (a === b) return true;\n if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {\n return a.every((val, i) => this.areUniformValuesEqual(val, (b as any)[i]));\n }\n return false;\n };\n\n /** Sets the provided uniform values into the WebGL program, can be a partial list of uniforms that have changed */\n private setUniformValues = (updatedUniforms: ShaderMountUniforms) => {\n this.gl.useProgram(this.program);\n Object.entries(updatedUniforms).forEach(([key, value]) => {\n // Grab the value to use in the uniform cache\n let cacheValue: ShaderMountUniforms[keyof ShaderMountUniforms] | string = value;\n if (value instanceof HTMLImageElement) {\n // Images use their src for the cache value to save memory\n cacheValue = `${value.src.slice(0, 200)}|${value.naturalWidth}x${value.naturalHeight}`;\n }\n\n // Check if the uniform value has changed and, if not, bail early to avoid extra work\n if (this.areUniformValuesEqual(this.uniformCache[key], cacheValue)) return;\n // Update the uniform cache if we are still here\n this.uniformCache[key] = cacheValue;\n\n const location = this.uniformLocations[key];\n if (!location) {\n console.warn(`Uniform location for ${key} not found`);\n return;\n }\n\n if (value instanceof HTMLImageElement) {\n // Texture case, requires a good amount of code so it gets its own function:\n this.setTextureUniform(key, value);\n } else if (Array.isArray(value)) {\n // Array case\n let flatArray: number[] | null = null;\n let valueLength: number | null = null;\n\n // If it's an array of same-sized arrays, flatten it down so we can set the uniform\n if (value[0] !== undefined && Array.isArray(value[0])) {\n const firstChildLength = value[0].length;\n if (value.every((arr) => (arr as number[]).length === firstChildLength)) {\n // Array of same-sized arrays case, flattens the array sets it\n flatArray = value.flat();\n valueLength = firstChildLength;\n } else {\n console.warn(`All child arrays must be the same length for ${key}`);\n return;\n }\n } else {\n // Array of primitive values case, supports 2, 3, 4, 9, 16 length arrays\n flatArray = value as number[];\n valueLength = flatArray.length;\n }\n\n // Set the uniform based on array length... supports 2, 3, 4, 9, 16 length arrays of primitive values\n // or arbitrary length arrays of arrays\n switch (valueLength) {\n case 2:\n this.gl.uniform2fv(location, flatArray);\n break;\n case 3:\n this.gl.uniform3fv(location, flatArray);\n break;\n case 4:\n this.gl.uniform4fv(location, flatArray);\n break;\n case 9:\n this.gl.uniformMatrix3fv(location, false, flatArray);\n break;\n case 16:\n this.gl.uniformMatrix4fv(location, false, flatArray);\n break;\n default:\n console.warn(`Unsupported uniform array length: ${valueLength}`);\n }\n } else if (typeof value === 'number') {\n // Number case, supports floats and ints\n this.gl.uniform1f(location, value);\n } else if (typeof value === 'boolean') {\n // Boolean case, supports true and false\n this.gl.uniform1i(location, value ? 1 : 0);\n } else {\n // May happen on the server for SSR when undefined images are passed in\n console.warn(`Unsupported uniform type for ${key}: ${typeof value}`);\n }\n });\n };\n\n /** Gets the current total animation time from 0ms */\n public getCurrentFrame = (): number => {\n return this.currentFrame;\n };\n\n /** Set a frame to get a deterministic result, frames are literally just milliseconds from zero since the animation started */\n public setFrame = (newFrame: number): void => {\n this.currentFrame = newFrame;\n this.lastRenderTime = performance.now();\n this.render(performance.now());\n };\n\n /** Set an animation speed (or 0 to stop animation) */\n public setSpeed = (newSpeed = 1): void => {\n // Set the new animation speed\n this.speed = newSpeed;\n this.updateCurrentSpeed();\n };\n\n /** Apply the target speed, pausing (0) while the tab is hidden or the element is out of the viewport */\n private updateCurrentSpeed = (): void => {\n this.setCurrentSpeed(this.ownerDocument.hidden || !this.isInViewport ? 0 : this.speed);\n };\n\n private setCurrentSpeed = (newSpeed: number): void => {\n this.currentSpeed = newSpeed;\n\n if (this.rafId === null && newSpeed !== 0) {\n // Moving from 0 to animating, kick off a new rAF loop\n this.lastRenderTime = performance.now();\n this.rafId = requestAnimationFrame(this.render);\n }\n\n if (this.rafId !== null && newSpeed === 0) {\n // Moving from animating to not animating, cancel the rAF loop\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n };\n\n /** Set the maximum pixel count for the shader, this will limit the number of pixels that will be rendered */\n public setMaxPixelCount = (newMaxPixelCount: number = DEFAULT_MAX_PIXEL_COUNT): void => {\n this.maxPixelCount = newMaxPixelCount;\n\n this.handleResize();\n };\n\n /** Set the minimum pixel ratio for the shader */\n public setMinPixelRatio = (newMinPixelRatio: number = 2): void => {\n this.minPixelRatio = newMinPixelRatio;\n\n this.handleResize();\n };\n\n /** Update the uniforms that are provided by the outside shader, can be a partial set with only the uniforms that have changed */\n public setUniforms = (newUniforms: ShaderMountUniforms): void => {\n this.setUniformValues(newUniforms);\n this.providedUniforms = { ...this.providedUniforms, ...newUniforms };\n\n this.render(performance.now());\n };\n\n private handleDocumentVisibilityChange = () => {\n this.updateCurrentSpeed();\n };\n\n /** Dispose of the shader mount, cleaning up all of the WebGL resources */\n public dispose = (): void => {\n // Immediately mark as disposed to prevent future renders from leaking in\n this.hasBeenDisposed = true;\n\n // Cancel the rAF loop\n if (this.rafId !== null) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n if (this.gl && this.program) {\n // Clean up all textures\n this.textures.forEach((texture) => {\n this.gl.deleteTexture(texture);\n });\n this.textures.clear();\n\n this.gl.deleteProgram(this.program);\n this.program = null;\n\n // Reset the WebGL context\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);\n this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);\n this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null);\n this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);\n\n // Clear any errors\n this.gl.getError();\n }\n\n if (this.resizeObserver) {\n this.resizeObserver.disconnect();\n this.resizeObserver = null;\n }\n\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n\n visualViewport?.removeEventListener('resize', this.handleVisualViewportChange);\n this.ownerDocument.removeEventListener('visibilitychange', this.handleDocumentVisibilityChange);\n\n this.uniformLocations = {};\n\n // Remove the shader from the div wrapper element\n this.canvasElement.remove();\n // Free up the reference to self to enable garbage collection\n delete this.parentElement.paperShaderMount;\n };\n}\n\nfunction createShader(gl: WebGL2RenderingContext, type: number, source: string): WebGLShader | null {\n const shader = gl.createShader(type);\n if (!shader) return null;\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));\n gl.deleteShader(shader);\n return null;\n }\n\n return shader;\n}\n\nfunction createProgram(\n gl: WebGL2RenderingContext,\n vertexShaderSource: string,\n fragmentShaderSource: string\n): WebGLProgram | null {\n const format = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT);\n const precision = format ? format.precision : null;\n // MEDIUM_FLOAT precision can be 10, 16 or 23 bits depending on device;\n // Shaders fail on 10 bit (and 16 bit is hard to test) => we force 23-bit by switching to highp\n if (precision && precision < 23) {\n vertexShaderSource = vertexShaderSource.replace(/precision\\s+(lowp|mediump)\\s+float;/g, 'precision highp float;');\n fragmentShaderSource = fragmentShaderSource\n .replace(/precision\\s+(lowp|mediump)\\s+float/g, 'precision highp float')\n .replace(/\\b(uniform|varying|attribute)\\s+(lowp|mediump)\\s+(\\w+)/g, '$1 highp $3');\n }\n\n const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);\n const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);\n\n if (!vertexShader || !fragmentShader) return null;\n\n const program = gl.createProgram();\n if (!program) return null;\n\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));\n gl.deleteProgram(program);\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n return null;\n }\n\n // Clean up shaders after successful linking\n gl.detachShader(program, vertexShader);\n gl.detachShader(program, fragmentShader);\n gl.deleteShader(vertexShader);\n gl.deleteShader(fragmentShader);\n\n return program;\n}\n\nconst defaultStyle = `@layer paper-shaders {\n :where([data-paper-shader]) {\n isolation: isolate;\n position: relative;\n\n & canvas {\n contain: strict;\n display: block;\n position: absolute;\n inset: 0;\n z-index: -1;\n width: 100%;\n height: 100%;\n border-radius: inherit;\n corner-shape: inherit;\n }\n }\n}`;\n\n/** The parent `
` element that has a ShaderMount available on it */\nexport interface PaperShaderElement extends HTMLElement {\n paperShaderMount: ShaderMount | undefined;\n}\n\n/** Check if an element is a Paper shader element */\nexport function isPaperShaderElement(element: HTMLElement): element is PaperShaderElement {\n return 'paperShaderMount' in element;\n}\n\n/**\n * Uniform types that we support to be auto-mapped into the fragment shader\n *\n * We accept undefined as a convenience for server rendering, when some things may be undefined\n * We just skip setting the uniform if it's undefined. This allows the shader mount to still take up space during server rendering\n */\nexport interface ShaderMountUniforms {\n [key: string]: boolean | number | number[] | number[][] | HTMLImageElement | undefined;\n}\n\nexport interface ShaderMotionParams {\n speed?: number;\n frame?: number;\n}\n\nexport type ShaderPreset = {\n name: string;\n params: Required;\n};\n\nexport type ImageShaderPreset = {\n name: string;\n /**\n * Params for the shader excluding the image.\n * Image is excluded as it isn't considered a preset,\n * e.g. when switching between presets it shouldn't switch the image.\n *\n * While we exclude images from presets they should still be set with a default prop value so the code-first usage of shaders remains great.\n */\n params: Required>;\n};\n\nfunction isSafari() {\n const ua = navigator.userAgent.toLowerCase();\n return ua.includes('safari') && !ua.includes('chrome') && !ua.includes('android');\n}\n\n// Zoom level can be estimated comparing the browser's outerWidth and the viewport width.\n// It's nowhere near perfect because it's affected by the presence of browser sidebars,\n// like a vertical web inspector or Arc's sidebar. Also, both outerWidth and innerWidth\n// are integers, which would almost never give us a perfect ratio at face values.\n//\n// Still, this is pretty accurate in the vast majority of cases.\n//\n// Note 1:\n// Avoid innerWidth, use visualViewport.width instead.\n// - innerWidth is affected by pinch zoom in Safari, but not other browsers.\n// visualViewport.width works consistently in all browsers.\n// - innerWidth is rounded to integer, but not visualViewport.width.\n// - visualViewport.width is affected by hard scrollbars, so they need to be added manually\n//\n// Note 2:\n// Opening a sidebar in Safari like web inspector or bookmarks will throw off the zoom\n// level detection and result in a larger target resolution. Not a concern in real-world usage\n// with Safari, but we'd rather not try to detect zoom levels with other browsers\n// (e.g. Arc always has a sidebar, which affects outerWidth vs visualViewport.width).\nfunction bestGuessBrowserZoom(doc: Document) {\n const viewportScale = visualViewport?.scale ?? 1;\n const viewportWidth = visualViewport?.width ?? window.innerWidth;\n const scrollbarWidth = window.innerWidth - doc.documentElement.clientWidth;\n const innerWidth = viewportScale * viewportWidth + scrollbarWidth;\n\n // outerWidth and innerWidth are always integers so we won't often get the original zoom ratio\n // E.g. given a 125% zoom, outerWidth = 1657, innerWidth = 1325, 1657 / 1325 = 1.2505660377\n // We check for common zoom levels and return the closest one if found.\n\n const ratio = outerWidth / innerWidth;\n const zoomPercentageRounded = Math.round(100 * ratio);\n\n // All zoom levels divisible by 5%\n if (zoomPercentageRounded % 5 === 0) {\n return zoomPercentageRounded / 100;\n }\n\n // 33% zoom\n if (zoomPercentageRounded === 33) {\n return 1 / 3;\n }\n\n // 67% zoom\n if (zoomPercentageRounded === 67) {\n return 2 / 3;\n }\n\n // 133% zoom\n if (zoomPercentageRounded === 133) {\n return 4 / 3;\n }\n\n return ratio;\n}\n"], + "mappings": "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", + "names": ["vertexShaderSource"] +} diff --git a/public/vendor/paper-shaders/shader-sizing.d.ts b/public/vendor/paper-shaders/shader-sizing.d.ts new file mode 100644 index 0000000..5640031 --- /dev/null +++ b/public/vendor/paper-shaders/shader-sizing.d.ts @@ -0,0 +1,30 @@ +export interface ShaderSizingUniforms { + u_fit: (typeof ShaderFitOptions)[ShaderFit]; + u_scale: number; + u_rotation: number; + u_originX: number; + u_originY: number; + u_offsetX: number; + u_offsetY: number; + u_worldWidth: number; + u_worldHeight: number; +} +export interface ShaderSizingParams { + fit?: 'none' | 'contain' | 'cover'; + scale?: number; + rotation?: number; + originX?: number; + originY?: number; + offsetX?: number; + offsetY?: number; + worldWidth?: number; + worldHeight?: number; +} +export declare const defaultObjectSizing: Required; +export declare const defaultPatternSizing: Required; +export declare const ShaderFitOptions: { + readonly none: 0; + readonly contain: 1; + readonly cover: 2; +}; +export type ShaderFit = keyof typeof ShaderFitOptions; diff --git a/public/vendor/paper-shaders/shader-sizing.js b/public/vendor/paper-shaders/shader-sizing.js new file mode 100644 index 0000000..bf7a798 --- /dev/null +++ b/public/vendor/paper-shaders/shader-sizing.js @@ -0,0 +1,38 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +const defaultObjectSizing = { + fit: "contain", + scale: 1, + rotation: 0, + offsetX: 0, + offsetY: 0, + originX: 0.5, + originY: 0.5, + worldWidth: 0, + worldHeight: 0 +}; +const defaultPatternSizing = { + fit: "none", + scale: 1, + rotation: 0, + offsetX: 0, + offsetY: 0, + originX: 0.5, + originY: 0.5, + worldWidth: 0, + worldHeight: 0 +}; +const ShaderFitOptions = { + none: 0, + contain: 1, + cover: 2 +}; +export { + ShaderFitOptions, + defaultObjectSizing, + defaultPatternSizing +}; +//# sourceMappingURL=shader-sizing.js.map diff --git a/public/vendor/paper-shaders/shader-sizing.js.map b/public/vendor/paper-shaders/shader-sizing.js.map new file mode 100644 index 0000000..a082fe9 --- /dev/null +++ b/public/vendor/paper-shaders/shader-sizing.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../src/shader-sizing.ts"], + "sourcesContent": ["export interface ShaderSizingUniforms {\n u_fit: (typeof ShaderFitOptions)[ShaderFit];\n u_scale: number;\n u_rotation: number;\n u_originX: number;\n u_originY: number;\n u_offsetX: number;\n u_offsetY: number;\n u_worldWidth: number;\n u_worldHeight: number;\n}\n\nexport interface ShaderSizingParams {\n fit?: 'none' | 'contain' | 'cover';\n scale?: number;\n rotation?: number;\n originX?: number;\n originY?: number;\n offsetX?: number;\n offsetY?: number;\n worldWidth?: number;\n worldHeight?: number;\n}\n\nexport const defaultObjectSizing: Required = {\n fit: 'contain',\n scale: 1,\n rotation: 0,\n offsetX: 0,\n offsetY: 0,\n originX: 0.5,\n originY: 0.5,\n worldWidth: 0,\n worldHeight: 0,\n};\n\nexport const defaultPatternSizing: Required = {\n fit: 'none',\n scale: 1,\n rotation: 0,\n offsetX: 0,\n offsetY: 0,\n originX: 0.5,\n originY: 0.5,\n worldWidth: 0,\n worldHeight: 0,\n};\n\nexport const ShaderFitOptions = {\n none: 0,\n contain: 1,\n cover: 2,\n} as const;\n\nexport type ShaderFit = keyof typeof ShaderFitOptions;\n"], + "mappings": ";;;;;AAwBO,MAAM,sBAAoD;AAAA,EAC/D,KAAK;AAAA,EACL,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,aAAa;AACf;AAEO,MAAM,uBAAqD;AAAA,EAChE,KAAK;AAAA,EACL,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,aAAa;AACf;AAEO,MAAM,mBAAmB;AAAA,EAC9B,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AACT;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shader-utils.d.ts b/public/vendor/paper-shaders/shader-utils.d.ts new file mode 100644 index 0000000..07a242b --- /dev/null +++ b/public/vendor/paper-shaders/shader-utils.d.ts @@ -0,0 +1,10 @@ +export declare const declarePI = "\n#define TWO_PI 6.28318530718\n#define PI 3.14159265358979323846\n"; +export declare const rotation2 = "\nvec2 rotate(vec2 uv, float th) {\n return mat2(cos(th), sin(th), -sin(th), cos(th)) * uv;\n}\n"; +export declare const proceduralHash11 = "\n float hash11(float p) {\n p = fract(p * 0.3183099) + 0.1;\n p *= p + 19.19;\n return fract(p * p);\n }\n"; +export declare const proceduralHash21 = "\n float hash21(vec2 p) {\n p = fract(p * vec2(0.3183099, 0.3678794)) + 0.1;\n p += dot(p, p + 19.19);\n return fract(p.x * p.y);\n }\n"; +export declare const proceduralHash22 = "\n vec2 hash22(vec2 p) {\n p = fract(p * vec2(0.3183099, 0.3678794)) + 0.1;\n p += dot(p, p.yx + 19.19);\n return fract(vec2(p.x * p.y, p.x + p.y));\n }\n"; +export declare const textureRandomizerR = "\n float randomR(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).r;\n }\n"; +export declare const textureRandomizerGB = "\n vec2 randomGB(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).gb;\n }\n"; +export declare const colorBandingFix = "\n color += 1. / 256. * (fract(sin(dot(.014 * gl_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453123) - .5);\n"; +export declare const simplexNoise = "\nvec3 permute(vec3 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); }\nfloat snoise(vec2 v) {\n const vec4 C = vec4(0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439);\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1;\n i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0));\n vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy),\n dot(x12.zw, x12.zw)), 0.0);\n m = m * m;\n m = m * m;\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n"; +export declare const fiberNoise = "\nfloat fiberRandom(vec2 p) {\n vec2 uv = floor(p) / 100.;\n return texture(u_noiseTexture, fract(uv)).b;\n}\n\nfloat fiberValueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = fiberRandom(i);\n float b = fiberRandom(i + vec2(1.0, 0.0));\n float c = fiberRandom(i + vec2(0.0, 1.0));\n float d = fiberRandom(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat fiberNoiseFbm(in vec2 n, vec2 seedOffset) {\n float total = 0.0, amplitude = 1.;\n for (int i = 0; i < 4; i++) {\n n = rotate(n, .7);\n total += fiberValueNoise(n + seedOffset) * amplitude;\n n *= 2.;\n amplitude *= 0.6;\n }\n return total;\n}\n\nfloat fiberNoise(vec2 uv, vec2 seedOffset) {\n float epsilon = 0.001;\n float n1 = fiberNoiseFbm(uv + vec2(epsilon, 0.0), seedOffset);\n float n2 = fiberNoiseFbm(uv - vec2(epsilon, 0.0), seedOffset);\n float n3 = fiberNoiseFbm(uv + vec2(0.0, epsilon), seedOffset);\n float n4 = fiberNoiseFbm(uv - vec2(0.0, epsilon), seedOffset);\n return length(vec2(n1 - n2, n3 - n4)) / (2.0 * epsilon);\n}\n"; diff --git a/public/vendor/paper-shaders/shader-utils.js b/public/vendor/paper-shaders/shader-utils.js new file mode 100644 index 0000000..262fcd4 --- /dev/null +++ b/public/vendor/paper-shaders/shader-utils.js @@ -0,0 +1,131 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +const declarePI = ` +#define TWO_PI 6.28318530718 +#define PI 3.14159265358979323846 +`; +const rotation2 = ` +vec2 rotate(vec2 uv, float th) { + return mat2(cos(th), sin(th), -sin(th), cos(th)) * uv; +} +`; +const proceduralHash11 = ` + float hash11(float p) { + p = fract(p * 0.3183099) + 0.1; + p *= p + 19.19; + return fract(p * p); + } +`; +const proceduralHash21 = ` + float hash21(vec2 p) { + p = fract(p * vec2(0.3183099, 0.3678794)) + 0.1; + p += dot(p, p + 19.19); + return fract(p.x * p.y); + } +`; +const proceduralHash22 = ` + vec2 hash22(vec2 p) { + p = fract(p * vec2(0.3183099, 0.3678794)) + 0.1; + p += dot(p, p.yx + 19.19); + return fract(vec2(p.x * p.y, p.x + p.y)); + } +`; +const textureRandomizerR = ` + float randomR(vec2 p) { + vec2 uv = floor(p) / 100. + .5; + return texture(u_noiseTexture, fract(uv)).r; + } +`; +const textureRandomizerGB = ` + vec2 randomGB(vec2 p) { + vec2 uv = floor(p) / 100. + .5; + return texture(u_noiseTexture, fract(uv)).gb; + } +`; +const colorBandingFix = ` + color += 1. / 256. * (fract(sin(dot(.014 * gl_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453123) - .5); +`; +const simplexNoise = ` +vec3 permute(vec3 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); } +float snoise(vec2 v) { + const vec4 C = vec4(0.211324865405187, 0.366025403784439, + -0.577350269189626, 0.024390243902439); + vec2 i = floor(v + dot(v, C.yy)); + vec2 x0 = v - i + dot(i, C.xx); + vec2 i1; + i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); + vec4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + i = mod(i, 289.0); + vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + + i.x + vec3(0.0, i1.x, 1.0)); + vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), + dot(x12.zw, x12.zw)), 0.0); + m = m * m; + m = m * m; + vec3 x = 2.0 * fract(p * C.www) - 1.0; + vec3 h = abs(x) - 0.5; + vec3 ox = floor(x + 0.5); + vec3 a0 = x - ox; + m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h); + vec3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + return 130.0 * dot(m, g); +} +`; +const fiberNoise = ` +float fiberRandom(vec2 p) { + vec2 uv = floor(p) / 100.; + return texture(u_noiseTexture, fract(uv)).b; +} + +float fiberValueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = fiberRandom(i); + float b = fiberRandom(i + vec2(1.0, 0.0)); + float c = fiberRandom(i + vec2(0.0, 1.0)); + float d = fiberRandom(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} + +float fiberNoiseFbm(in vec2 n, vec2 seedOffset) { + float total = 0.0, amplitude = 1.; + for (int i = 0; i < 4; i++) { + n = rotate(n, .7); + total += fiberValueNoise(n + seedOffset) * amplitude; + n *= 2.; + amplitude *= 0.6; + } + return total; +} + +float fiberNoise(vec2 uv, vec2 seedOffset) { + float epsilon = 0.001; + float n1 = fiberNoiseFbm(uv + vec2(epsilon, 0.0), seedOffset); + float n2 = fiberNoiseFbm(uv - vec2(epsilon, 0.0), seedOffset); + float n3 = fiberNoiseFbm(uv + vec2(0.0, epsilon), seedOffset); + float n4 = fiberNoiseFbm(uv - vec2(0.0, epsilon), seedOffset); + return length(vec2(n1 - n2, n3 - n4)) / (2.0 * epsilon); +} +`; +export { + colorBandingFix, + declarePI, + fiberNoise, + proceduralHash11, + proceduralHash21, + proceduralHash22, + rotation2, + simplexNoise, + textureRandomizerGB, + textureRandomizerR +}; +//# sourceMappingURL=shader-utils.js.map diff --git a/public/vendor/paper-shaders/shader-utils.js.map b/public/vendor/paper-shaders/shader-utils.js.map new file mode 100644 index 0000000..d0cdc67 --- /dev/null +++ b/public/vendor/paper-shaders/shader-utils.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../src/shader-utils.ts"], + "sourcesContent": ["// language=GLSL\nexport const declarePI = `\n#define TWO_PI 6.28318530718\n#define PI 3.14159265358979323846\n`;\n\n// language=GLSL\nexport const rotation2 = `\nvec2 rotate(vec2 uv, float th) {\n return mat2(cos(th), sin(th), -sin(th), cos(th)) * uv;\n}\n`;\n\n// language=GLSL\nexport const proceduralHash11 = `\n float hash11(float p) {\n p = fract(p * 0.3183099) + 0.1;\n p *= p + 19.19;\n return fract(p * p);\n }\n`;\n\n// language=GLSL\nexport const proceduralHash21 = `\n float hash21(vec2 p) {\n p = fract(p * vec2(0.3183099, 0.3678794)) + 0.1;\n p += dot(p, p + 19.19);\n return fract(p.x * p.y);\n }\n`;\n\n// language=GLSL\nexport const proceduralHash22 = `\n vec2 hash22(vec2 p) {\n p = fract(p * vec2(0.3183099, 0.3678794)) + 0.1;\n p += dot(p, p.yx + 19.19);\n return fract(vec2(p.x * p.y, p.x + p.y));\n }\n`;\n\n// language=GLSL\nexport const textureRandomizerR = `\n float randomR(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).r;\n }\n`;\n\n// language=GLSL\nexport const textureRandomizerGB = `\n vec2 randomGB(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).gb;\n }\n`;\n\n// language=GLSL\nexport const colorBandingFix = `\n color += 1. / 256. * (fract(sin(dot(.014 * gl_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453123) - .5);\n`;\n\n// language=GLSL\nexport const simplexNoise = `\nvec3 permute(vec3 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); }\nfloat snoise(vec2 v) {\n const vec4 C = vec4(0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439);\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1;\n i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0));\n vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy),\n dot(x12.zw, x12.zw)), 0.0);\n m = m * m;\n m = m * m;\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n`;\n\n// language=GLSL\nexport const fiberNoise = `\nfloat fiberRandom(vec2 p) {\n vec2 uv = floor(p) / 100.;\n return texture(u_noiseTexture, fract(uv)).b;\n}\n\nfloat fiberValueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = fiberRandom(i);\n float b = fiberRandom(i + vec2(1.0, 0.0));\n float c = fiberRandom(i + vec2(0.0, 1.0));\n float d = fiberRandom(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat fiberNoiseFbm(in vec2 n, vec2 seedOffset) {\n float total = 0.0, amplitude = 1.;\n for (int i = 0; i < 4; i++) {\n n = rotate(n, .7);\n total += fiberValueNoise(n + seedOffset) * amplitude;\n n *= 2.;\n amplitude *= 0.6;\n }\n return total;\n}\n\nfloat fiberNoise(vec2 uv, vec2 seedOffset) {\n float epsilon = 0.001;\n float n1 = fiberNoiseFbm(uv + vec2(epsilon, 0.0), seedOffset);\n float n2 = fiberNoiseFbm(uv - vec2(epsilon, 0.0), seedOffset);\n float n3 = fiberNoiseFbm(uv + vec2(0.0, epsilon), seedOffset);\n float n4 = fiberNoiseFbm(uv - vec2(0.0, epsilon), seedOffset);\n return length(vec2(n1 - n2, n3 - n4)) / (2.0 * epsilon);\n}\n`;\n"], + "mappings": ";;;;;AACO,MAAM,YAAY;AAAA;AAAA;AAAA;AAMlB,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAOlB,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASzB,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASzB,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASzB,MAAM,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAQ3B,MAAM,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAQ5B,MAAM,kBAAkB;AAAA;AAAA;AAKxB,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BrB,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/color-panels.d.ts b/public/vendor/paper-shaders/shaders/color-panels.d.ts new file mode 100644 index 0000000..20868f1 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/color-panels.d.ts @@ -0,0 +1,70 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const colorPanelsMeta: { + readonly maxColorCount: 7; +}; +/** + * Pseudo-3D semi-transparent panels rotating around a central axis + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_scale (float): Overall zoom level, used for anti-aliasing calculations + * - u_colors (vec4[]): Up to 7 RGBA colors used to color the panels + * - u_colorsCount (float): Number of active colors + * - u_colorBack (vec4): Background color in RGBA + * - u_density (float): Angle between every 2 panels (0.25 to 7) + * - u_angle1 (float): Skew angle applied to all panes (-1 to 1) + * - u_angle2 (float): Skew angle applied to all panes (-1 to 1) + * - u_length (float): Panel length relative to total height (0 to 3) + * - u_edges (bool): Color highlight on the panels edges + * - u_blur (float): Side blur, 0 for sharp edges (0 to 0.5) + * - u_fadeIn (float): Transparency near central axis (0 to 1) + * - u_fadeOut (float): Transparency near viewer (0 to 1) + * - u_gradient (float): Color mixing within a panel, 0 = solid, 1 = gradient (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const colorPanelsFragmentShader: string; +export interface ColorPanelsUniforms extends ShaderSizingUniforms { + u_colors: vec4[]; + u_colorsCount: number; + u_colorBack: [number, number, number, number]; + u_angle1: number; + u_angle2: number; + u_length: number; + u_edges: boolean; + u_blur: number; + u_fadeIn: number; + u_fadeOut: number; + u_density: number; + u_gradient: number; +} +export interface ColorPanelsParams extends ShaderSizingParams, ShaderMotionParams { + colors?: string[]; + colorBack?: string; + angle1?: number; + angle2?: number; + length?: number; + edges?: boolean; + blur?: number; + fadeIn?: number; + fadeOut?: number; + density?: number; + gradient?: number; +} diff --git a/public/vendor/paper-shaders/shaders/color-panels.js b/public/vendor/paper-shaders/shaders/color-panels.js new file mode 100644 index 0000000..ed8fcbd --- /dev/null +++ b/public/vendor/paper-shaders/shaders/color-panels.js @@ -0,0 +1,224 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, colorBandingFix } from "../shader-utils.js"; +const colorPanelsMeta = { + maxColorCount: 7 +}; +const colorPanelsFragmentShader = `#version 300 es +precision lowp float; + +uniform float u_time; +uniform mediump float u_scale; + +uniform vec4 u_colors[${colorPanelsMeta.maxColorCount}]; +uniform float u_colorsCount; +uniform vec4 u_colorBack; +uniform float u_density; +uniform float u_angle1; +uniform float u_angle2; +uniform float u_length; +uniform bool u_edges; +uniform float u_blur; +uniform float u_fadeIn; +uniform float u_fadeOut; +uniform float u_gradient; + +in vec2 v_objectUV; + +out vec4 fragColor; + +${declarePI} + +const float zLimit = .5; + +vec2 getPanel(float angle, vec2 uv, float invLength, float aa) { + float sinA = sin(angle); + float cosA = cos(angle); + + float denom = sinA - uv.y * cosA; + if (abs(denom) < .01) return vec2(0.); + + float z = uv.y / denom; + + if (z <= 0. || z > zLimit) return vec2(0.); + + float zRatio = z / zLimit; + float panelMap = 1. - zRatio; + float x = uv.x * (cosA * z + 1.) * invLength; + + float zOffset = zRatio - .5; + float left = -.5 + zOffset * u_angle1; + float right = .5 - zOffset * u_angle2; + float blurX = aa + 2. * panelMap * u_blur; + + float leftEdge1 = left - blurX; + float leftEdge2 = left + .25 * blurX; + float rightEdge1 = right - .25 * blurX; + float rightEdge2 = right + blurX; + + float panel = smoothstep(leftEdge1, leftEdge2, x) * (1.0 - smoothstep(rightEdge1, rightEdge2, x)); + panel *= mix(0., panel, smoothstep(0., .01 / max(u_scale, 1e-6), panelMap)); + + float midScreen = abs(sinA); + if (u_edges == true) { + panelMap = mix(.99, panelMap, panel * clamp(panelMap / (.15 * (1. - pow(midScreen, .1))), 0.0, 1.0)); + } else if (midScreen < .07) { + panel *= (midScreen * 15.); + } + + return vec2(panel, panelMap); +} + +vec4 blendColor(vec4 colorA, float panelMask, float panelMap) { + float fade = 1. - smoothstep(.97 - .97 * u_fadeIn, 1., panelMap); + + fade *= smoothstep(-.2 * (1. - u_fadeOut), u_fadeOut, panelMap); + + vec3 blendedRGB = mix(vec3(0.), colorA.rgb, fade); + float blendedAlpha = mix(0., colorA.a, fade); + + return vec4(blendedRGB, blendedAlpha) * panelMask; +} + +void main() { + vec2 uv = v_objectUV; + uv *= 1.25; + + float t = .02 * u_time; + t = fract(t); + bool reverseTime = (t < 0.5); + + vec3 color = vec3(0.); + float opacity = 0.; + + float aa = .005 / u_scale; + int colorsCount = int(u_colorsCount); + + vec4 premultipliedColors[${colorPanelsMeta.maxColorCount}]; + for (int i = 0; i < ${colorPanelsMeta.maxColorCount}; i++) { + if (i >= colorsCount) break; + vec4 c = u_colors[i]; + c.rgb *= c.a; + premultipliedColors[i] = c; + } + + float invLength = 1.5 / max(u_length, .001); + + float totalColorWeight = 0.; + int panelsNumber = 12; + + float densityNormalizer = 1.; + if (colorsCount == 4) { + panelsNumber = 16; + densityNormalizer = 1.34; + } else if (colorsCount == 5) { + panelsNumber = 20; + densityNormalizer = 1.67; + } else if (colorsCount == 7) { + panelsNumber = 14; + densityNormalizer = 1.17; + } + + float fPanelsNumber = float(panelsNumber); + + float totalPanelsShape = 0.; + float panelGrad = 1. - clamp(u_gradient, 0., 1.); + + for (int set = 0; set < 2; set++) { + bool isForward = (set == 0 && !reverseTime) || (set == 1 && reverseTime); + if (!isForward) continue; + + for (int i = 0; i <= 20; i++) { + if (i >= panelsNumber) break; + + int idx = panelsNumber - 1 - i; + + float offset = float(idx) / fPanelsNumber; + if (set == 1) { + offset += .5; + } + + float densityFract = densityNormalizer * fract(t + offset); + float angleNorm = densityFract / u_density; + if (densityFract >= .5 || angleNorm >= .3) continue; + + float smoothDensity = clamp((.5 - densityFract) / .1, 0., 1.) * clamp(densityFract / .01, 0., 1.); + float smoothAngle = clamp((.3 - angleNorm) / .05, 0., 1.); + if (smoothDensity * smoothAngle < .001) continue; + + if (angleNorm > .5) { + angleNorm = 0.5; + } + vec2 panel = getPanel(angleNorm * TWO_PI + PI, uv, invLength, aa); + if (panel[0] <= .001) continue; + float panelMask = panel[0] * smoothDensity * smoothAngle; + float panelMap = panel[1]; + + int colorIdx = idx % colorsCount; + int nextColorIdx = (idx + 1) % colorsCount; + + vec4 colorA = premultipliedColors[colorIdx]; + vec4 colorB = premultipliedColors[nextColorIdx]; + + colorA = mix(colorA, colorB, max(0., smoothstep(.0, .45, panelMap) - panelGrad)); + vec4 blended = blendColor(colorA, panelMask, panelMap); + color = blended.rgb + color * (1. - blended.a); + opacity = blended.a + opacity * (1. - blended.a); + } + + + for (int i = 0; i <= 20; i++) { + if (i >= panelsNumber) break; + + int idx = panelsNumber - 1 - i; + + float offset = float(idx) / fPanelsNumber; + if (set == 0) { + offset += .5; + } + + float densityFract = densityNormalizer * fract(-t + offset); + float angleNorm = -densityFract / u_density; + if (densityFract >= .5 || angleNorm < -.3) continue; + + float smoothDensity = clamp((.5 - densityFract) / .1, 0., 1.) * clamp(densityFract / .01, 0., 1.); + float smoothAngle = clamp((angleNorm + .3) / .05, 0., 1.); + if (smoothDensity * smoothAngle < .001) continue; + + vec2 panel = getPanel(angleNorm * TWO_PI + PI, uv, invLength, aa); + float panelMask = panel[0] * smoothDensity * smoothAngle; + if (panelMask <= .001) continue; + float panelMap = panel[1]; + + int colorIdx = (colorsCount - (idx % colorsCount)) % colorsCount; + if (colorIdx < 0) colorIdx += colorsCount; + int nextColorIdx = (colorIdx + 1) % colorsCount; + + vec4 colorA = premultipliedColors[colorIdx]; + vec4 colorB = premultipliedColors[nextColorIdx]; + + colorA = mix(colorA, colorB, max(0., smoothstep(.0, .45, panelMap) - panelGrad)); + vec4 blended = blendColor(colorA, panelMask, panelMap); + color = blended.rgb + color * (1. - blended.a); + opacity = blended.a + opacity * (1. - blended.a); + } + } + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1.0 - opacity); + opacity = opacity + u_colorBack.a * (1.0 - opacity); + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +export { + colorPanelsFragmentShader, + colorPanelsMeta +}; +//# sourceMappingURL=color-panels.js.map diff --git a/public/vendor/paper-shaders/shaders/color-panels.js.map b/public/vendor/paper-shaders/shaders/color-panels.js.map new file mode 100644 index 0000000..286797e --- /dev/null +++ b/public/vendor/paper-shaders/shaders/color-panels.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/color-panels.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, colorBandingFix } from '../shader-utils.js';\n\nexport const colorPanelsMeta = {\n maxColorCount: 7,\n} as const;\n\n/**\n * Pseudo-3D semi-transparent panels rotating around a central axis\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_scale (float): Overall zoom level, used for anti-aliasing calculations\n * - u_colors (vec4[]): Up to 7 RGBA colors used to color the panels\n * - u_colorsCount (float): Number of active colors\n * - u_colorBack (vec4): Background color in RGBA\n * - u_density (float): Angle between every 2 panels (0.25 to 7)\n * - u_angle1 (float): Skew angle applied to all panes (-1 to 1)\n * - u_angle2 (float): Skew angle applied to all panes (-1 to 1)\n * - u_length (float): Panel length relative to total height (0 to 3)\n * - u_edges (bool): Color highlight on the panels edges\n * - u_blur (float): Side blur, 0 for sharp edges (0 to 0.5)\n * - u_fadeIn (float): Transparency near central axis (0 to 1)\n * - u_fadeOut (float): Transparency near viewer (0 to 1)\n * - u_gradient (float): Color mixing within a panel, 0 = solid, 1 = gradient (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const colorPanelsFragmentShader: string = `#version 300 es\nprecision lowp float;\n\nuniform float u_time;\nuniform mediump float u_scale;\n\nuniform vec4 u_colors[${ colorPanelsMeta.maxColorCount }];\nuniform float u_colorsCount;\nuniform vec4 u_colorBack;\nuniform float u_density;\nuniform float u_angle1;\nuniform float u_angle2;\nuniform float u_length;\nuniform bool u_edges;\nuniform float u_blur;\nuniform float u_fadeIn;\nuniform float u_fadeOut;\nuniform float u_gradient;\n\nin vec2 v_objectUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n\nconst float zLimit = .5;\n\nvec2 getPanel(float angle, vec2 uv, float invLength, float aa) {\n float sinA = sin(angle);\n float cosA = cos(angle);\n\n float denom = sinA - uv.y * cosA;\n if (abs(denom) < .01) return vec2(0.);\n\n float z = uv.y / denom;\n\n if (z <= 0. || z > zLimit) return vec2(0.);\n\n float zRatio = z / zLimit;\n float panelMap = 1. - zRatio;\n float x = uv.x * (cosA * z + 1.) * invLength;\n\n float zOffset = zRatio - .5;\n float left = -.5 + zOffset * u_angle1;\n float right = .5 - zOffset * u_angle2;\n float blurX = aa + 2. * panelMap * u_blur;\n\n float leftEdge1 = left - blurX;\n float leftEdge2 = left + .25 * blurX;\n float rightEdge1 = right - .25 * blurX;\n float rightEdge2 = right + blurX;\n\n float panel = smoothstep(leftEdge1, leftEdge2, x) * (1.0 - smoothstep(rightEdge1, rightEdge2, x));\n panel *= mix(0., panel, smoothstep(0., .01 / max(u_scale, 1e-6), panelMap));\n\n float midScreen = abs(sinA);\n if (u_edges == true) {\n panelMap = mix(.99, panelMap, panel * clamp(panelMap / (.15 * (1. - pow(midScreen, .1))), 0.0, 1.0));\n } else if (midScreen < .07) {\n panel *= (midScreen * 15.);\n }\n\n return vec2(panel, panelMap);\n}\n\nvec4 blendColor(vec4 colorA, float panelMask, float panelMap) {\n float fade = 1. - smoothstep(.97 - .97 * u_fadeIn, 1., panelMap);\n\n fade *= smoothstep(-.2 * (1. - u_fadeOut), u_fadeOut, panelMap);\n\n vec3 blendedRGB = mix(vec3(0.), colorA.rgb, fade);\n float blendedAlpha = mix(0., colorA.a, fade);\n\n return vec4(blendedRGB, blendedAlpha) * panelMask;\n}\n\nvoid main() {\n vec2 uv = v_objectUV;\n uv *= 1.25;\n\n float t = .02 * u_time;\n t = fract(t);\n bool reverseTime = (t < 0.5);\n\n vec3 color = vec3(0.);\n float opacity = 0.;\n\n float aa = .005 / u_scale;\n int colorsCount = int(u_colorsCount);\n\n vec4 premultipliedColors[${ colorPanelsMeta.maxColorCount }];\n for (int i = 0; i < ${ colorPanelsMeta.maxColorCount }; i++) {\n if (i >= colorsCount) break;\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n premultipliedColors[i] = c;\n }\n\n float invLength = 1.5 / max(u_length, .001);\n\n float totalColorWeight = 0.;\n int panelsNumber = 12;\n\n float densityNormalizer = 1.;\n if (colorsCount == 4) {\n panelsNumber = 16;\n densityNormalizer = 1.34;\n } else if (colorsCount == 5) {\n panelsNumber = 20;\n densityNormalizer = 1.67;\n } else if (colorsCount == 7) {\n panelsNumber = 14;\n densityNormalizer = 1.17;\n }\n\n float fPanelsNumber = float(panelsNumber);\n\n float totalPanelsShape = 0.;\n float panelGrad = 1. - clamp(u_gradient, 0., 1.);\n\n for (int set = 0; set < 2; set++) {\n bool isForward = (set == 0 && !reverseTime) || (set == 1 && reverseTime);\n if (!isForward) continue;\n\n for (int i = 0; i <= 20; i++) {\n if (i >= panelsNumber) break;\n\n int idx = panelsNumber - 1 - i;\n\n float offset = float(idx) / fPanelsNumber;\n if (set == 1) {\n offset += .5;\n }\n\n float densityFract = densityNormalizer * fract(t + offset);\n float angleNorm = densityFract / u_density;\n if (densityFract >= .5 || angleNorm >= .3) continue;\n\n float smoothDensity = clamp((.5 - densityFract) / .1, 0., 1.) * clamp(densityFract / .01, 0., 1.);\n float smoothAngle = clamp((.3 - angleNorm) / .05, 0., 1.);\n if (smoothDensity * smoothAngle < .001) continue;\n\n if (angleNorm > .5) {\n angleNorm = 0.5;\n }\n vec2 panel = getPanel(angleNorm * TWO_PI + PI, uv, invLength, aa);\n if (panel[0] <= .001) continue;\n float panelMask = panel[0] * smoothDensity * smoothAngle;\n float panelMap = panel[1];\n\n int colorIdx = idx % colorsCount;\n int nextColorIdx = (idx + 1) % colorsCount;\n\n vec4 colorA = premultipliedColors[colorIdx];\n vec4 colorB = premultipliedColors[nextColorIdx];\n\n colorA = mix(colorA, colorB, max(0., smoothstep(.0, .45, panelMap) - panelGrad));\n vec4 blended = blendColor(colorA, panelMask, panelMap);\n color = blended.rgb + color * (1. - blended.a);\n opacity = blended.a + opacity * (1. - blended.a);\n }\n\n\n for (int i = 0; i <= 20; i++) {\n if (i >= panelsNumber) break;\n\n int idx = panelsNumber - 1 - i;\n\n float offset = float(idx) / fPanelsNumber;\n if (set == 0) {\n offset += .5;\n }\n\n float densityFract = densityNormalizer * fract(-t + offset);\n float angleNorm = -densityFract / u_density;\n if (densityFract >= .5 || angleNorm < -.3) continue;\n\n float smoothDensity = clamp((.5 - densityFract) / .1, 0., 1.) * clamp(densityFract / .01, 0., 1.);\n float smoothAngle = clamp((angleNorm + .3) / .05, 0., 1.);\n if (smoothDensity * smoothAngle < .001) continue;\n\n vec2 panel = getPanel(angleNorm * TWO_PI + PI, uv, invLength, aa);\n float panelMask = panel[0] * smoothDensity * smoothAngle;\n if (panelMask <= .001) continue;\n float panelMap = panel[1];\n\n int colorIdx = (colorsCount - (idx % colorsCount)) % colorsCount;\n if (colorIdx < 0) colorIdx += colorsCount;\n int nextColorIdx = (colorIdx + 1) % colorsCount;\n\n vec4 colorA = premultipliedColors[colorIdx];\n vec4 colorB = premultipliedColors[nextColorIdx];\n\n colorA = mix(colorA, colorB, max(0., smoothstep(.0, .45, panelMap) - panelGrad));\n vec4 blended = blendColor(colorA, panelMask, panelMap);\n color = blended.rgb + color * (1. - blended.a);\n opacity = blended.a + opacity * (1. - blended.a);\n }\n }\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1.0 - opacity);\n opacity = opacity + u_colorBack.a * (1.0 - opacity);\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface ColorPanelsUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_colorBack: [number, number, number, number];\n u_angle1: number;\n u_angle2: number;\n u_length: number;\n u_edges: boolean;\n u_blur: number;\n u_fadeIn: number;\n u_fadeOut: number;\n u_density: number;\n u_gradient: number;\n}\n\nexport interface ColorPanelsParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n colorBack?: string;\n angle1?: number;\n angle2?: number;\n length?: number;\n edges?: boolean;\n blur?: number;\n fadeIn?: number;\n fadeOut?: number;\n density?: number;\n gradient?: number;\n}\n"], + "mappings": ";;;;;AAEA,eAAmE;AACnE,SAAS,WAAW,uBAAuB;AAEpC,MAAM,kBAAkB;AAAA,EAC7B,eAAe;AACjB;AAwCO,MAAM,4BAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMxB,gBAAgB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBpD,SAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAmEiB,gBAAgB,aAAc;AAAA,wBACnC,gBAAgB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAiHlD,eAAgB;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/dithering.d.ts b/public/vendor/paper-shaders/shaders/dithering.d.ts new file mode 100644 index 0000000..d9f8dc8 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/dithering.d.ts @@ -0,0 +1,62 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * Animated 2-color dithering over multiple pattern sources (noise, warp, dots, waves, ripple, swirl, sphere). + * + * SIZING NOTE: This shader performs sizing in the fragment shader (not vertex shader) to keep + * u_pxSize in consistent actual pixels. The pixel grid is computed from gl_FragCoord before any + * transforms, so scaling/rotating only affects the underlying pattern shape. + * No vertex shader outputs (v_objectUV, v_patternUV, etc.) are used. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_colorBack (vec4): Background color in RGBA + * - u_colorFront (vec4): Foreground (ink) color in RGBA + * - u_shape (float): Shape pattern type (1 = simplex, 2 = warp, 3 = dots, 4 = wave, 5 = ripple, 6 = swirl, 7 = sphere) + * - u_type (float): Dithering type (1 = random, 2 = 2x2 Bayer, 3 = 4x4 Bayer, 4 = 8x8 Bayer) + * - u_pxSize (float): Pixel size of dithering grid (0.5 to 20) + * + * */ +export declare const ditheringFragmentShader: string; +export interface DitheringUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colorFront: [number, number, number, number]; + u_shape: (typeof DitheringShapes)[DitheringShape]; + u_type: (typeof DitheringTypes)[DitheringType]; + u_pxSize: number; +} +export interface DitheringParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colorFront?: string; + shape?: DitheringShape; + type?: DitheringType; + size?: number; +} +export declare const DitheringShapes: { + readonly simplex: 1; + readonly warp: 2; + readonly dots: 3; + readonly wave: 4; + readonly ripple: 5; + readonly swirl: 6; + readonly sphere: 7; +}; +export type DitheringShape = keyof typeof DitheringShapes; +export declare const DitheringTypes: { + readonly random: 1; + readonly '2x2': 2; + readonly '4x4': 3; + readonly '8x8': 4; +}; +export type DitheringType = keyof typeof DitheringTypes; diff --git a/public/vendor/paper-shaders/shaders/dithering.js b/public/vendor/paper-shaders/shaders/dithering.js new file mode 100644 index 0000000..9802517 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/dithering.js @@ -0,0 +1,271 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import { +} from "../shader-sizing.js"; +import { simplexNoise, declarePI, proceduralHash11, proceduralHash21 } from "../shader-utils.js"; +const ditheringFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform vec2 u_resolution; +uniform float u_pixelRatio; +uniform float u_originX; +uniform float u_originY; +uniform float u_worldWidth; +uniform float u_worldHeight; +uniform float u_fit; +uniform float u_scale; +uniform float u_rotation; +uniform float u_offsetX; +uniform float u_offsetY; + +uniform float u_pxSize; +uniform vec4 u_colorBack; +uniform vec4 u_colorFront; +uniform float u_shape; +uniform float u_type; + +out vec4 fragColor; + +${simplexNoise} +${declarePI} +${proceduralHash11} +${proceduralHash21} + +float getSimplexNoise(vec2 uv, float t) { + float noise = .5 * snoise(uv - vec2(0., .3 * t)); + noise += .5 * snoise(2. * uv + vec2(0., .32 * t)); + + return noise; +} + +const int bayer2x2[4] = int[4](0, 2, 3, 1); +const int bayer4x4[16] = int[16]( +0, 8, 2, 10, +12, 4, 14, 6, +3, 11, 1, 9, +15, 7, 13, 5 +); + +const int bayer8x8[64] = int[64]( +0, 32, 8, 40, 2, 34, 10, 42, +48, 16, 56, 24, 50, 18, 58, 26, +12, 44, 4, 36, 14, 46, 6, 38, +60, 28, 52, 20, 62, 30, 54, 22, +3, 35, 11, 43, 1, 33, 9, 41, +51, 19, 59, 27, 49, 17, 57, 25, +15, 47, 7, 39, 13, 45, 5, 37, +63, 31, 55, 23, 61, 29, 53, 21 +); + +float getBayerValue(vec2 uv, int size) { + ivec2 pos = ivec2(fract(uv / float(size)) * float(size)); + int index = pos.y * size + pos.x; + + if (size == 2) { + return float(bayer2x2[index]) / 4.0; + } else if (size == 4) { + return float(bayer4x4[index]) / 16.0; + } else if (size == 8) { + return float(bayer8x8[index]) / 64.0; + } + return 0.0; +} + + +void main() { + float t = .5 * u_time; + + float pxSize = u_pxSize * u_pixelRatio; + vec2 pxSizeUV = gl_FragCoord.xy - .5 * u_resolution; + pxSizeUV /= pxSize; + vec2 canvasPixelizedUV = (floor(pxSizeUV) + .5) * pxSize; + vec2 normalizedUV = canvasPixelizedUV / u_resolution; + + vec2 ditheringNoiseUV = canvasPixelizedUV; + vec2 shapeUV = normalizedUV; + + vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5); + vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight); + givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio; + float r = u_rotation * PI / 180.; + mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r)); + vec2 graphicOffset = vec2(-u_offsetX, u_offsetY); + + float patternBoxRatio = givenBoxSize.x / givenBoxSize.y; + vec2 boxSize = vec2( + (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x, + (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y + ); + + if (u_shape > 3.5) { + vec2 objectBoxSize = vec2(0.); + // fit = none + objectBoxSize.x = min(boxSize.x, boxSize.y); + if (u_fit == 1.) { // fit = contain + objectBoxSize.x = min(u_resolution.x, u_resolution.y); + } else if (u_fit == 2.) { // fit = cover + objectBoxSize.x = max(u_resolution.x, u_resolution.y); + } + objectBoxSize.y = objectBoxSize.x; + vec2 objectWorldScale = u_resolution.xy / objectBoxSize; + + shapeUV *= objectWorldScale; + shapeUV += boxOrigin * (objectWorldScale - 1.); + shapeUV += vec2(-u_offsetX, u_offsetY); + shapeUV /= u_scale; + shapeUV = graphicRotation * shapeUV; + } else { + vec2 patternBoxSize = vec2(0.); + // fit = none + patternBoxSize.x = patternBoxRatio * min(boxSize.x / patternBoxRatio, boxSize.y); + float patternWorldNoFitBoxWidth = patternBoxSize.x; + if (u_fit == 1.) { // fit = contain + patternBoxSize.x = patternBoxRatio * min(u_resolution.x / patternBoxRatio, u_resolution.y); + } else if (u_fit == 2.) { // fit = cover + patternBoxSize.x = patternBoxRatio * max(u_resolution.x / patternBoxRatio, u_resolution.y); + } + patternBoxSize.y = patternBoxSize.x / patternBoxRatio; + vec2 patternWorldScale = u_resolution.xy / patternBoxSize; + + shapeUV += vec2(-u_offsetX, u_offsetY) / patternWorldScale; + shapeUV += boxOrigin; + shapeUV -= boxOrigin / patternWorldScale; + shapeUV *= u_resolution.xy; + shapeUV /= u_pixelRatio; + if (u_fit > 0.) { + shapeUV *= (patternWorldNoFitBoxWidth / patternBoxSize.x); + } + shapeUV /= u_scale; + shapeUV = graphicRotation * shapeUV; + shapeUV += boxOrigin / patternWorldScale; + shapeUV -= boxOrigin; + shapeUV += .5; + } + + float shape = 0.; + if (u_shape < 1.5) { + // Simplex noise + shapeUV *= .001; + + shape = 0.5 + 0.5 * getSimplexNoise(shapeUV, t); + shape = smoothstep(0.3, 0.9, shape); + + } else if (u_shape < 2.5) { + // Warp + shapeUV *= .003; + + for (float i = 1.0; i < 6.0; i++) { + shapeUV.x += 0.6 / i * cos(i * 2.5 * shapeUV.y + t); + shapeUV.y += 0.6 / i * cos(i * 1.5 * shapeUV.x + t); + } + + shape = .15 / max(0.001, abs(sin(t - shapeUV.y - shapeUV.x))); + shape = smoothstep(0.02, 1., shape); + + } else if (u_shape < 3.5) { + // Dots + shapeUV *= .05; + + float stripeIdx = floor(2. * shapeUV.x / TWO_PI); + float rand = hash11(stripeIdx * 10.); + rand = sign(rand - .5) * pow(.1 + abs(rand), .4); + shape = sin(shapeUV.x) * cos(shapeUV.y - 5. * rand * t); + shape = pow(abs(shape), 6.); + + } else if (u_shape < 4.5) { + // Sine wave + shapeUV *= 4.; + + float wave = cos(.5 * shapeUV.x - 2. * t) * sin(1.5 * shapeUV.x + t) * (.75 + .25 * cos(3. * t)); + shape = 1. - smoothstep(-1., 1., shapeUV.y + wave); + + } else if (u_shape < 5.5) { + // Ripple + + float dist = length(shapeUV); + float waves = sin(pow(dist, 1.7) * 7. - 3. * t) * .5 + .5; + shape = waves; + + } else if (u_shape < 6.5) { + // Swirl + + float l = length(shapeUV); + float angle = 6. * atan(shapeUV.y, shapeUV.x) + 4. * t; + float twist = 1.2; + float offset = 1. / pow(max(l, 1e-6), twist) + angle / TWO_PI; + float mid = smoothstep(0., 1., pow(l, twist)); + shape = mix(0., fract(offset), mid); + + } else { + // Sphere + shapeUV *= 2.; + + float d = 1. - pow(length(shapeUV), 2.); + vec3 pos = vec3(shapeUV, sqrt(max(0., d))); + vec3 lightPos = normalize(vec3(cos(1.5 * t), .8, sin(1.25 * t))); + shape = .5 + .5 * dot(lightPos, pos); + shape *= step(0., d); + } + + + int type = int(floor(u_type)); + float dithering = 0.0; + + switch (type) { + case 1: { + dithering = step(hash21(ditheringNoiseUV), shape); + } break; + case 2: + dithering = getBayerValue(pxSizeUV, 2); + break; + case 3: + dithering = getBayerValue(pxSizeUV, 4); + break; + default : + dithering = getBayerValue(pxSizeUV, 8); + break; + } + + dithering -= .5; + float res = step(.5, shape + dithering); + + vec3 fgColor = u_colorFront.rgb * u_colorFront.a; + float fgOpacity = u_colorFront.a; + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + float bgOpacity = u_colorBack.a; + + vec3 color = fgColor * res; + float opacity = fgOpacity * res; + + color += bgColor * (1. - opacity); + opacity += bgOpacity * (1. - opacity); + + fragColor = vec4(color, opacity); +} +`; +const DitheringShapes = { + simplex: 1, + warp: 2, + dots: 3, + wave: 4, + ripple: 5, + swirl: 6, + sphere: 7 +}; +const DitheringTypes = { + "random": 1, + "2x2": 2, + "4x4": 3, + "8x8": 4 +}; +export { + DitheringShapes, + DitheringTypes, + ditheringFragmentShader +}; +//# sourceMappingURL=dithering.js.map diff --git a/public/vendor/paper-shaders/shaders/dithering.js.map b/public/vendor/paper-shaders/shaders/dithering.js.map new file mode 100644 index 0000000..322a46e --- /dev/null +++ b/public/vendor/paper-shaders/shaders/dithering.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/dithering.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport { simplexNoise, declarePI, proceduralHash11, proceduralHash21 } from '../shader-utils.js';\n\n/**\n * Animated 2-color dithering over multiple pattern sources (noise, warp, dots, waves, ripple, swirl, sphere).\n *\n * SIZING NOTE: This shader performs sizing in the fragment shader (not vertex shader) to keep\n * u_pxSize in consistent actual pixels. The pixel grid is computed from gl_FragCoord before any\n * transforms, so scaling/rotating only affects the underlying pattern shape.\n * No vertex shader outputs (v_objectUV, v_patternUV, etc.) are used.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colorFront (vec4): Foreground (ink) color in RGBA\n * - u_shape (float): Shape pattern type (1 = simplex, 2 = warp, 3 = dots, 4 = wave, 5 = ripple, 6 = swirl, 7 = sphere)\n * - u_type (float): Dithering type (1 = random, 2 = 2x2 Bayer, 3 = 4x4 Bayer, 4 = 8x8 Bayer)\n * - u_pxSize (float): Pixel size of dithering grid (0.5 to 20)\n *\n * */\n\n// language=GLSL\nexport const ditheringFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\nuniform float u_originX;\nuniform float u_originY;\nuniform float u_worldWidth;\nuniform float u_worldHeight;\nuniform float u_fit;\nuniform float u_scale;\nuniform float u_rotation;\nuniform float u_offsetX;\nuniform float u_offsetY;\n\nuniform float u_pxSize;\nuniform vec4 u_colorBack;\nuniform vec4 u_colorFront;\nuniform float u_shape;\nuniform float u_type;\n\nout vec4 fragColor;\n\n${ simplexNoise }\n${ declarePI }\n${ proceduralHash11 }\n${ proceduralHash21 }\n\nfloat getSimplexNoise(vec2 uv, float t) {\n float noise = .5 * snoise(uv - vec2(0., .3 * t));\n noise += .5 * snoise(2. * uv + vec2(0., .32 * t));\n\n return noise;\n}\n\nconst int bayer2x2[4] = int[4](0, 2, 3, 1);\nconst int bayer4x4[16] = int[16](\n0, 8, 2, 10,\n12, 4, 14, 6,\n3, 11, 1, 9,\n15, 7, 13, 5\n);\n\nconst int bayer8x8[64] = int[64](\n0, 32, 8, 40, 2, 34, 10, 42,\n48, 16, 56, 24, 50, 18, 58, 26,\n12, 44, 4, 36, 14, 46, 6, 38,\n60, 28, 52, 20, 62, 30, 54, 22,\n3, 35, 11, 43, 1, 33, 9, 41,\n51, 19, 59, 27, 49, 17, 57, 25,\n15, 47, 7, 39, 13, 45, 5, 37,\n63, 31, 55, 23, 61, 29, 53, 21\n);\n\nfloat getBayerValue(vec2 uv, int size) {\n ivec2 pos = ivec2(fract(uv / float(size)) * float(size));\n int index = pos.y * size + pos.x;\n\n if (size == 2) {\n return float(bayer2x2[index]) / 4.0;\n } else if (size == 4) {\n return float(bayer4x4[index]) / 16.0;\n } else if (size == 8) {\n return float(bayer8x8[index]) / 64.0;\n }\n return 0.0;\n}\n\n\nvoid main() {\n float t = .5 * u_time;\n\n float pxSize = u_pxSize * u_pixelRatio;\n vec2 pxSizeUV = gl_FragCoord.xy - .5 * u_resolution;\n pxSizeUV /= pxSize;\n vec2 canvasPixelizedUV = (floor(pxSizeUV) + .5) * pxSize;\n vec2 normalizedUV = canvasPixelizedUV / u_resolution;\n\n vec2 ditheringNoiseUV = canvasPixelizedUV;\n vec2 shapeUV = normalizedUV;\n\n vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);\n vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight);\n givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio;\n float r = u_rotation * PI / 180.;\n mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n\n float patternBoxRatio = givenBoxSize.x / givenBoxSize.y;\n vec2 boxSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n \n if (u_shape > 3.5) {\n vec2 objectBoxSize = vec2(0.);\n // fit = none\n objectBoxSize.x = min(boxSize.x, boxSize.y);\n if (u_fit == 1.) { // fit = contain\n objectBoxSize.x = min(u_resolution.x, u_resolution.y);\n } else if (u_fit == 2.) { // fit = cover\n objectBoxSize.x = max(u_resolution.x, u_resolution.y);\n }\n objectBoxSize.y = objectBoxSize.x;\n vec2 objectWorldScale = u_resolution.xy / objectBoxSize;\n\n shapeUV *= objectWorldScale;\n shapeUV += boxOrigin * (objectWorldScale - 1.);\n shapeUV += vec2(-u_offsetX, u_offsetY);\n shapeUV /= u_scale;\n shapeUV = graphicRotation * shapeUV;\n } else {\n vec2 patternBoxSize = vec2(0.);\n // fit = none\n patternBoxSize.x = patternBoxRatio * min(boxSize.x / patternBoxRatio, boxSize.y);\n float patternWorldNoFitBoxWidth = patternBoxSize.x;\n if (u_fit == 1.) { // fit = contain\n patternBoxSize.x = patternBoxRatio * min(u_resolution.x / patternBoxRatio, u_resolution.y);\n } else if (u_fit == 2.) { // fit = cover\n patternBoxSize.x = patternBoxRatio * max(u_resolution.x / patternBoxRatio, u_resolution.y);\n }\n patternBoxSize.y = patternBoxSize.x / patternBoxRatio;\n vec2 patternWorldScale = u_resolution.xy / patternBoxSize;\n\n shapeUV += vec2(-u_offsetX, u_offsetY) / patternWorldScale;\n shapeUV += boxOrigin;\n shapeUV -= boxOrigin / patternWorldScale;\n shapeUV *= u_resolution.xy;\n shapeUV /= u_pixelRatio;\n if (u_fit > 0.) {\n shapeUV *= (patternWorldNoFitBoxWidth / patternBoxSize.x);\n }\n shapeUV /= u_scale;\n shapeUV = graphicRotation * shapeUV;\n shapeUV += boxOrigin / patternWorldScale;\n shapeUV -= boxOrigin;\n shapeUV += .5;\n }\n\n float shape = 0.;\n if (u_shape < 1.5) {\n // Simplex noise\n shapeUV *= .001;\n\n shape = 0.5 + 0.5 * getSimplexNoise(shapeUV, t);\n shape = smoothstep(0.3, 0.9, shape);\n\n } else if (u_shape < 2.5) {\n // Warp\n shapeUV *= .003;\n\n for (float i = 1.0; i < 6.0; i++) {\n shapeUV.x += 0.6 / i * cos(i * 2.5 * shapeUV.y + t);\n shapeUV.y += 0.6 / i * cos(i * 1.5 * shapeUV.x + t);\n }\n\n shape = .15 / max(0.001, abs(sin(t - shapeUV.y - shapeUV.x)));\n shape = smoothstep(0.02, 1., shape);\n\n } else if (u_shape < 3.5) {\n // Dots\n shapeUV *= .05;\n\n float stripeIdx = floor(2. * shapeUV.x / TWO_PI);\n float rand = hash11(stripeIdx * 10.);\n rand = sign(rand - .5) * pow(.1 + abs(rand), .4);\n shape = sin(shapeUV.x) * cos(shapeUV.y - 5. * rand * t);\n shape = pow(abs(shape), 6.);\n\n } else if (u_shape < 4.5) {\n // Sine wave\n shapeUV *= 4.;\n\n float wave = cos(.5 * shapeUV.x - 2. * t) * sin(1.5 * shapeUV.x + t) * (.75 + .25 * cos(3. * t));\n shape = 1. - smoothstep(-1., 1., shapeUV.y + wave);\n\n } else if (u_shape < 5.5) {\n // Ripple\n\n float dist = length(shapeUV);\n float waves = sin(pow(dist, 1.7) * 7. - 3. * t) * .5 + .5;\n shape = waves;\n\n } else if (u_shape < 6.5) {\n // Swirl\n\n float l = length(shapeUV);\n float angle = 6. * atan(shapeUV.y, shapeUV.x) + 4. * t;\n float twist = 1.2;\n float offset = 1. / pow(max(l, 1e-6), twist) + angle / TWO_PI;\n float mid = smoothstep(0., 1., pow(l, twist));\n shape = mix(0., fract(offset), mid);\n\n } else {\n // Sphere\n shapeUV *= 2.;\n\n float d = 1. - pow(length(shapeUV), 2.);\n vec3 pos = vec3(shapeUV, sqrt(max(0., d)));\n vec3 lightPos = normalize(vec3(cos(1.5 * t), .8, sin(1.25 * t)));\n shape = .5 + .5 * dot(lightPos, pos);\n shape *= step(0., d);\n }\n\n\n int type = int(floor(u_type));\n float dithering = 0.0;\n\n switch (type) {\n case 1: {\n dithering = step(hash21(ditheringNoiseUV), shape);\n } break;\n case 2:\n dithering = getBayerValue(pxSizeUV, 2);\n break;\n case 3:\n dithering = getBayerValue(pxSizeUV, 4);\n break;\n default :\n dithering = getBayerValue(pxSizeUV, 8);\n break;\n }\n\n dithering -= .5;\n float res = step(.5, shape + dithering);\n\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n vec3 color = fgColor * res;\n float opacity = fgOpacity * res;\n\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface DitheringUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colorFront: [number, number, number, number];\n u_shape: (typeof DitheringShapes)[DitheringShape];\n u_type: (typeof DitheringTypes)[DitheringType];\n u_pxSize: number;\n}\n\nexport interface DitheringParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colorFront?: string;\n shape?: DitheringShape;\n type?: DitheringType;\n size?: number;\n}\n\nexport const DitheringShapes = {\n simplex: 1,\n warp: 2,\n dots: 3,\n wave: 4,\n ripple: 5,\n swirl: 6,\n sphere: 7,\n} as const;\n\nexport type DitheringShape = keyof typeof DitheringShapes;\n\nexport const DitheringTypes = {\n 'random': 1,\n '2x2': 2,\n '4x4': 3,\n '8x8': 4,\n} as const;\n\nexport type DitheringType = keyof typeof DitheringTypes;\n"], + "mappings": ";;;;;AACA;AAAA,OAGO;AACP,SAAS,cAAc,WAAW,kBAAkB,wBAAwB;AAgCrE,MAAM,0BAAkC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyB5C,YAAa;AAAA,EACb,SAAU;AAAA,EACV,gBAAiB;AAAA,EACjB,gBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuOb,MAAM,kBAAkB;AAAA,EAC7B,SAAS;AAAA,EACT,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AACV;AAIO,MAAM,iBAAiB;AAAA,EAC5B,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/dot-grid.d.ts b/public/vendor/paper-shaders/shaders/dot-grid.d.ts new file mode 100644 index 0000000..31d5d35 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/dot-grid.d.ts @@ -0,0 +1,65 @@ +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * Static grid pattern made of circles, diamonds, squares or triangles. + * + * Fragment shader uniforms: + * - u_colorBack (vec4): Background color in RGBA + * - u_colorFill (vec4): Shape fill color in RGBA + * - u_colorStroke (vec4): Shape stroke color in RGBA + * - u_dotSize (float): Base size of each shape in pixels (1 to 100) + * - u_gapX (float): Pattern horizontal spacing in pixels (2 to 500) + * - u_gapY (float): Pattern vertical spacing in pixels (2 to 500) + * - u_strokeWidth (float): Outline stroke width in pixels (0 to 50) + * - u_sizeRange (float): Random variation in shape size, 0 = uniform, higher = random up to base size (0 to 1) + * - u_opacityRange (float): Random variation in shape opacity, 0 = opaque, higher = semi-transparent (0 to 1) + * - u_shape (float): Shape type (0 = circle, 1 = diamond, 2 = square, 3 = triangle) + * + * Vertex shader outputs (used in fragment shader): + * - v_patternUV (vec2): UV coordinates in pixels (scaled by 0.01 for precision), with scale, rotation and offset applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const dotGridFragmentShader: string; +export interface DotGridUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colorFill: [number, number, number, number]; + u_colorStroke: [number, number, number, number]; + u_dotSize: number; + u_gapX: number; + u_gapY: number; + u_strokeWidth: number; + u_sizeRange: number; + u_opacityRange: number; + u_shape: (typeof DotGridShapes)[DotGridShape]; +} +export interface DotGridParams extends ShaderSizingParams { + colorBack?: string; + colorFill?: string; + colorStroke?: string; + size?: number; + gapX?: number; + gapY?: number; + strokeWidth?: number; + sizeRange?: number; + opacityRange?: number; + shape?: DotGridShape; +} +export declare const DotGridShapes: { + readonly circle: 0; + readonly diamond: 1; + readonly square: 2; + readonly triangle: 3; +}; +export type DotGridShape = keyof typeof DotGridShapes; diff --git a/public/vendor/paper-shaders/shaders/dot-grid.js b/public/vendor/paper-shaders/shaders/dot-grid.js new file mode 100644 index 0000000..e657564 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/dot-grid.js @@ -0,0 +1,110 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, simplexNoise } from "../shader-utils.js"; +const dotGridFragmentShader = `#version 300 es +precision mediump float; + +uniform vec4 u_colorBack; +uniform vec4 u_colorFill; +uniform vec4 u_colorStroke; +uniform float u_dotSize; +uniform float u_gapX; +uniform float u_gapY; +uniform float u_strokeWidth; +uniform float u_sizeRange; +uniform float u_opacityRange; +uniform float u_shape; + +in vec2 v_patternUV; + +out vec4 fragColor; + +${declarePI} +${simplexNoise} + +float polygon(vec2 p, float N, float rot) { + float a = atan(p.x, p.y) + rot; + float r = TWO_PI / float(N); + + return cos(floor(.5 + a / r) * r - a) * length(p); +} + +void main() { + + // x100 is a default multiplier between vertex and fragmant shaders + // we use it to avoid UV presision issues + vec2 shape_uv = 100. * v_patternUV; + + vec2 gap = max(abs(vec2(u_gapX, u_gapY)), vec2(1e-6)); + vec2 grid = fract(shape_uv / gap) + 1e-4; + vec2 grid_idx = floor(shape_uv / gap); + float sizeRandomizer = .5 + .8 * snoise(2. * vec2(grid_idx.x * 100., grid_idx.y)); + float opacity_randomizer = .5 + .7 * snoise(2. * vec2(grid_idx.y, grid_idx.x)); + + vec2 center = vec2(0.5) - 1e-3; + vec2 p = (grid - center) * vec2(u_gapX, u_gapY); + + float baseSize = u_dotSize * (1. - sizeRandomizer * u_sizeRange); + float strokeWidth = u_strokeWidth * (1. - sizeRandomizer * u_sizeRange); + + float dist; + if (u_shape < 0.5) { + // Circle + dist = length(p); + } else if (u_shape < 1.5) { + // Diamond + strokeWidth *= 1.5; + dist = polygon(1.5 * p, 4., .25 * PI); + } else if (u_shape < 2.5) { + // Square + dist = polygon(1.03 * p, 4., 1e-3); + } else { + // Triangle + strokeWidth *= 1.5; + p = p * 2. - 1.; + p *= .9; + p.y = 1. - p.y; + p.y -= .75 * baseSize; + dist = polygon(p, 3., 1e-3); + } + + float edgeWidth = fwidth(dist); + float shapeOuter = 1. - smoothstep(baseSize - edgeWidth, baseSize + edgeWidth, dist - strokeWidth); + float shapeInner = 1. - smoothstep(baseSize - edgeWidth, baseSize + edgeWidth, dist); + float stroke = shapeOuter - shapeInner; + + float dotOpacity = max(0., 1. - opacity_randomizer * u_opacityRange); + stroke *= dotOpacity; + shapeInner *= dotOpacity; + + stroke *= u_colorStroke.a; + shapeInner *= u_colorFill.a; + + vec3 color = vec3(0.); + color += stroke * u_colorStroke.rgb; + color += shapeInner * u_colorFill.rgb; + color += (1. - shapeInner - stroke) * u_colorBack.rgb * u_colorBack.a; + + float opacity = 0.; + opacity += stroke; + opacity += shapeInner; + opacity += (1. - opacity) * u_colorBack.a; + + fragColor = vec4(color, opacity); +} +`; +const DotGridShapes = { + circle: 0, + diamond: 1, + square: 2, + triangle: 3 +}; +export { + DotGridShapes, + dotGridFragmentShader +}; +//# sourceMappingURL=dot-grid.js.map diff --git a/public/vendor/paper-shaders/shaders/dot-grid.js.map b/public/vendor/paper-shaders/shaders/dot-grid.js.map new file mode 100644 index 0000000..3de1213 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/dot-grid.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/dot-grid.ts"], + "sourcesContent": ["import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, simplexNoise } from '../shader-utils.js';\n\n/**\n * Static grid pattern made of circles, diamonds, squares or triangles.\n *\n * Fragment shader uniforms:\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colorFill (vec4): Shape fill color in RGBA\n * - u_colorStroke (vec4): Shape stroke color in RGBA\n * - u_dotSize (float): Base size of each shape in pixels (1 to 100)\n * - u_gapX (float): Pattern horizontal spacing in pixels (2 to 500)\n * - u_gapY (float): Pattern vertical spacing in pixels (2 to 500)\n * - u_strokeWidth (float): Outline stroke width in pixels (0 to 50)\n * - u_sizeRange (float): Random variation in shape size, 0 = uniform, higher = random up to base size (0 to 1)\n * - u_opacityRange (float): Random variation in shape opacity, 0 = opaque, higher = semi-transparent (0 to 1)\n * - u_shape (float): Shape type (0 = circle, 1 = diamond, 2 = square, 3 = triangle)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_patternUV (vec2): UV coordinates in pixels (scaled by 0.01 for precision), with scale, rotation and offset applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const dotGridFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorFill;\nuniform vec4 u_colorStroke;\nuniform float u_dotSize;\nuniform float u_gapX;\nuniform float u_gapY;\nuniform float u_strokeWidth;\nuniform float u_sizeRange;\nuniform float u_opacityRange;\nuniform float u_shape;\n\nin vec2 v_patternUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ simplexNoise }\n\nfloat polygon(vec2 p, float N, float rot) {\n float a = atan(p.x, p.y) + rot;\n float r = TWO_PI / float(N);\n\n return cos(floor(.5 + a / r) * r - a) * length(p);\n}\n\nvoid main() {\n\n // x100 is a default multiplier between vertex and fragmant shaders\n // we use it to avoid UV presision issues\n vec2 shape_uv = 100. * v_patternUV;\n\n vec2 gap = max(abs(vec2(u_gapX, u_gapY)), vec2(1e-6));\n vec2 grid = fract(shape_uv / gap) + 1e-4;\n vec2 grid_idx = floor(shape_uv / gap);\n float sizeRandomizer = .5 + .8 * snoise(2. * vec2(grid_idx.x * 100., grid_idx.y));\n float opacity_randomizer = .5 + .7 * snoise(2. * vec2(grid_idx.y, grid_idx.x));\n\n vec2 center = vec2(0.5) - 1e-3;\n vec2 p = (grid - center) * vec2(u_gapX, u_gapY);\n\n float baseSize = u_dotSize * (1. - sizeRandomizer * u_sizeRange);\n float strokeWidth = u_strokeWidth * (1. - sizeRandomizer * u_sizeRange);\n\n float dist;\n if (u_shape < 0.5) {\n // Circle\n dist = length(p);\n } else if (u_shape < 1.5) {\n // Diamond\n strokeWidth *= 1.5;\n dist = polygon(1.5 * p, 4., .25 * PI);\n } else if (u_shape < 2.5) {\n // Square\n dist = polygon(1.03 * p, 4., 1e-3);\n } else {\n // Triangle\n strokeWidth *= 1.5;\n p = p * 2. - 1.;\n p *= .9;\n p.y = 1. - p.y;\n p.y -= .75 * baseSize;\n dist = polygon(p, 3., 1e-3);\n }\n\n float edgeWidth = fwidth(dist);\n float shapeOuter = 1. - smoothstep(baseSize - edgeWidth, baseSize + edgeWidth, dist - strokeWidth);\n float shapeInner = 1. - smoothstep(baseSize - edgeWidth, baseSize + edgeWidth, dist);\n float stroke = shapeOuter - shapeInner;\n\n float dotOpacity = max(0., 1. - opacity_randomizer * u_opacityRange);\n stroke *= dotOpacity;\n shapeInner *= dotOpacity;\n\n stroke *= u_colorStroke.a;\n shapeInner *= u_colorFill.a;\n\n vec3 color = vec3(0.);\n color += stroke * u_colorStroke.rgb;\n color += shapeInner * u_colorFill.rgb;\n color += (1. - shapeInner - stroke) * u_colorBack.rgb * u_colorBack.a;\n\n float opacity = 0.;\n opacity += stroke;\n opacity += shapeInner;\n opacity += (1. - opacity) * u_colorBack.a;\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface DotGridUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colorFill: [number, number, number, number];\n u_colorStroke: [number, number, number, number];\n u_dotSize: number;\n u_gapX: number;\n u_gapY: number;\n u_strokeWidth: number;\n u_sizeRange: number;\n u_opacityRange: number;\n u_shape: (typeof DotGridShapes)[DotGridShape];\n}\n\nexport interface DotGridParams extends ShaderSizingParams {\n colorBack?: string;\n colorFill?: string;\n colorStroke?: string;\n size?: number;\n gapX?: number;\n gapY?: number;\n strokeWidth?: number;\n sizeRange?: number;\n opacityRange?: number;\n shape?: DotGridShape;\n}\n\nexport const DotGridShapes = {\n circle: 0,\n diamond: 1,\n square: 2,\n triangle: 3,\n} as const;\n\nexport type DotGridShape = keyof typeof DotGridShapes;\n"], + "mappings": ";;;;;AAAA,eAAmE;AACnE,SAAS,WAAW,oBAAoB;AAoCjC,MAAM,wBAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkB1C,SAAU;AAAA,EACV,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoGT,MAAM,gBAAgB;AAAA,EAC3B,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AACZ;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/dot-orbit.d.ts b/public/vendor/paper-shaders/shaders/dot-orbit.d.ts new file mode 100644 index 0000000..ef3818b --- /dev/null +++ b/public/vendor/paper-shaders/shaders/dot-orbit.d.ts @@ -0,0 +1,57 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const dotOrbitMeta: { + readonly maxColorCount: 10; +}; +/** + * Animated multi-color dots pattern with each dot orbiting around its cell center. + * Supports up to 10 colors and various shape and motion controls. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_colorBack (vec4): Background color in RGBA + * - u_colors (vec4[]): Up to 10 base colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_stepsPerColor (float): Number of extra colors between base colors, 1 = N colors, 2 = 2×N, etc. (1 to 4) + * - u_size (float): Dot radius relative to cell size (0 to 1) + * - u_sizeRange (float): Random variation in shape size, 0 = uniform, higher = random up to base size (0 to 1) + * - u_spreading (float): Maximum orbit distance around cell center (0 to 1) + * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture + * + * Vertex shader outputs (used in fragment shader): + * - v_patternUV (vec2): UV coordinates in pixels (scaled by 0.01 for precision), with rotation and offset applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const dotOrbitFragmentShader: string; +export interface DotOrbitUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colors: vec4[]; + u_colorsCount: number; + u_size: number; + u_sizeRange: number; + u_spreading: number; + u_stepsPerColor: number; + u_noiseTexture?: HTMLImageElement; +} +export interface DotOrbitParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colors?: string[]; + size?: number; + sizeRange?: number; + spreading?: number; + stepsPerColor?: number; +} diff --git a/public/vendor/paper-shaders/shaders/dot-orbit.js b/public/vendor/paper-shaders/shaders/dot-orbit.js new file mode 100644 index 0000000..edee2e7 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/dot-orbit.js @@ -0,0 +1,123 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, rotation2, textureRandomizerR, textureRandomizerGB } from "../shader-utils.js"; +const dotOrbitMeta = { + maxColorCount: 10 +}; +const dotOrbitFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform sampler2D u_noiseTexture; + +uniform vec4 u_colorBack; +uniform vec4 u_colors[${dotOrbitMeta.maxColorCount}]; +uniform float u_colorsCount; +uniform float u_stepsPerColor; +uniform float u_size; +uniform float u_sizeRange; +uniform float u_spreading; + +in vec2 v_patternUV; + +out vec4 fragColor; + +${declarePI} +${rotation2} +${textureRandomizerR} +${textureRandomizerGB} + + +vec3 voronoiShape(vec2 uv, float time) { + vec2 i_uv = floor(uv); + vec2 f_uv = fract(uv); + + float spreading = .25 * clamp(u_spreading, 0., 1.); + + float minDist = 1.; + vec2 randomizer = vec2(0.); + for (int y = -1; y <= 1; y++) { + for (int x = -1; x <= 1; x++) { + vec2 tileOffset = vec2(float(x), float(y)); + vec2 rand = randomGB(i_uv + tileOffset); + vec2 cellCenter = vec2(.5 + 1e-4); + cellCenter += spreading * cos(time + TWO_PI * rand); + cellCenter -= .5; + cellCenter = rotate(cellCenter, randomR(vec2(rand.x, rand.y)) + .1 * time); + cellCenter += .5; + float dist = length(tileOffset + cellCenter - f_uv); + if (dist < minDist) { + minDist = dist; + randomizer = rand; + } + } + } + + return vec3(minDist, randomizer); +} + +void main() { + + vec2 shape_uv = v_patternUV; + shape_uv *= 1.5; + + const float firstFrameOffset = -10.; + float t = u_time + firstFrameOffset; + + vec3 voronoi = voronoiShape(shape_uv, t) + 1e-4; + + float radius = .25 * clamp(u_size, 0., 1.) - .5 * clamp(u_sizeRange, 0., 1.) * voronoi[2]; + float dist = voronoi[0]; + float edgeWidth = fwidth(dist); + float dots = 1. - smoothstep(radius - edgeWidth, radius + edgeWidth, dist); + + float shape = voronoi[1]; + + float mixer = shape * (u_colorsCount - 1.); + mixer = (shape - .5 / u_colorsCount) * u_colorsCount; + float steps = max(1., u_stepsPerColor); + + vec4 gradient = u_colors[0]; + gradient.rgb *= gradient.a; + for (int i = 1; i < ${dotOrbitMeta.maxColorCount}; i++) { + if (i >= int(u_colorsCount)) break; + float localT = clamp(mixer - float(i - 1), 0.0, 1.0); + localT = round(localT * steps) / steps; + vec4 c = u_colors[i]; + c.rgb *= c.a; + gradient = mix(gradient, c, localT); + } + + if ((mixer < 0.) || (mixer > (u_colorsCount - 1.))) { + float localT = mixer + 1.; + if (mixer > (u_colorsCount - 1.)) { + localT = mixer - (u_colorsCount - 1.); + } + localT = round(localT * steps) / steps; + vec4 cFst = u_colors[0]; + cFst.rgb *= cFst.a; + vec4 cLast = u_colors[int(u_colorsCount - 1.)]; + cLast.rgb *= cLast.a; + gradient = mix(cLast, cFst, localT); + } + + vec3 color = gradient.rgb * dots; + float opacity = gradient.a * dots; + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1. - opacity); + opacity = opacity + u_colorBack.a * (1. - opacity); + + fragColor = vec4(color, opacity); +} +`; +export { + dotOrbitFragmentShader, + dotOrbitMeta +}; +//# sourceMappingURL=dot-orbit.js.map diff --git a/public/vendor/paper-shaders/shaders/dot-orbit.js.map b/public/vendor/paper-shaders/shaders/dot-orbit.js.map new file mode 100644 index 0000000..e0a18c9 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/dot-orbit.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/dot-orbit.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, textureRandomizerR, textureRandomizerGB } from '../shader-utils.js';\n\nexport const dotOrbitMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Animated multi-color dots pattern with each dot orbiting around its cell center.\n * Supports up to 10 colors and various shape and motion controls.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colors (vec4[]): Up to 10 base colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_stepsPerColor (float): Number of extra colors between base colors, 1 = N colors, 2 = 2\u00D7N, etc. (1 to 4)\n * - u_size (float): Dot radius relative to cell size (0 to 1)\n * - u_sizeRange (float): Random variation in shape size, 0 = uniform, higher = random up to base size (0 to 1)\n * - u_spreading (float): Maximum orbit distance around cell center (0 to 1)\n * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_patternUV (vec2): UV coordinates in pixels (scaled by 0.01 for precision), with rotation and offset applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const dotOrbitFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${ dotOrbitMeta.maxColorCount }];\nuniform float u_colorsCount;\nuniform float u_stepsPerColor;\nuniform float u_size;\nuniform float u_sizeRange;\nuniform float u_spreading;\n\nin vec2 v_patternUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\n${ textureRandomizerR }\n${ textureRandomizerGB }\n\n\nvec3 voronoiShape(vec2 uv, float time) {\n vec2 i_uv = floor(uv);\n vec2 f_uv = fract(uv);\n\n float spreading = .25 * clamp(u_spreading, 0., 1.);\n\n float minDist = 1.;\n vec2 randomizer = vec2(0.);\n for (int y = -1; y <= 1; y++) {\n for (int x = -1; x <= 1; x++) {\n vec2 tileOffset = vec2(float(x), float(y));\n vec2 rand = randomGB(i_uv + tileOffset);\n vec2 cellCenter = vec2(.5 + 1e-4);\n cellCenter += spreading * cos(time + TWO_PI * rand);\n cellCenter -= .5;\n cellCenter = rotate(cellCenter, randomR(vec2(rand.x, rand.y)) + .1 * time);\n cellCenter += .5;\n float dist = length(tileOffset + cellCenter - f_uv);\n if (dist < minDist) {\n minDist = dist;\n randomizer = rand;\n }\n }\n }\n\n return vec3(minDist, randomizer);\n}\n\nvoid main() {\n\n vec2 shape_uv = v_patternUV;\n shape_uv *= 1.5;\n\n const float firstFrameOffset = -10.;\n float t = u_time + firstFrameOffset;\n\n vec3 voronoi = voronoiShape(shape_uv, t) + 1e-4;\n\n float radius = .25 * clamp(u_size, 0., 1.) - .5 * clamp(u_sizeRange, 0., 1.) * voronoi[2];\n float dist = voronoi[0];\n float edgeWidth = fwidth(dist);\n float dots = 1. - smoothstep(radius - edgeWidth, radius + edgeWidth, dist);\n\n float shape = voronoi[1];\n\n float mixer = shape * (u_colorsCount - 1.);\n mixer = (shape - .5 / u_colorsCount) * u_colorsCount;\n float steps = max(1., u_stepsPerColor);\n\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n for (int i = 1; i < ${ dotOrbitMeta.maxColorCount }; i++) {\n if (i >= int(u_colorsCount)) break;\n float localT = clamp(mixer - float(i - 1), 0.0, 1.0);\n localT = round(localT * steps) / steps;\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, localT);\n }\n\n if ((mixer < 0.) || (mixer > (u_colorsCount - 1.))) {\n float localT = mixer + 1.;\n if (mixer > (u_colorsCount - 1.)) {\n localT = mixer - (u_colorsCount - 1.);\n }\n localT = round(localT * steps) / steps;\n vec4 cFst = u_colors[0];\n cFst.rgb *= cFst.a;\n vec4 cLast = u_colors[int(u_colorsCount - 1.)];\n cLast.rgb *= cLast.a;\n gradient = mix(cLast, cFst, localT);\n }\n\n vec3 color = gradient.rgb * dots;\n float opacity = gradient.a * dots;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface DotOrbitUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_size: number;\n u_sizeRange: number;\n u_spreading: number;\n u_stepsPerColor: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface DotOrbitParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n size?: number;\n sizeRange?: number;\n spreading?: number;\n stepsPerColor?: number;\n}\n"], + "mappings": ";;;;;AAEA,eAAmE;AACnE,SAAS,WAAW,WAAW,oBAAoB,2BAA2B;AAEvE,MAAM,eAAe;AAAA,EAC1B,eAAe;AACjB;AAoCO,MAAM,yBAAiC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAQrB,aAAa,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWjD,SAAU;AAAA,EACV,SAAU;AAAA,EACV,kBAAmB;AAAA,EACnB,mBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAsDE,aAAa,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/fluted-glass.d.ts b/public/vendor/paper-shaders/shaders/fluted-glass.d.ts new file mode 100644 index 0000000..18dc387 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/fluted-glass.d.ts @@ -0,0 +1,114 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * Fluted glass image filter that transforms an image into streaked, ribbed distortions, + * giving a mix of clarity and obscurity. + * + * Fragment shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_image (sampler2D): Source image texture + * - u_imageAspectRatio (float): Aspect ratio of the source image + * - u_colorBack (vec4): Background color in RGBA + * - u_colorShadow (vec4): Shadows color in RGBA + * - u_colorHighlight (vec4): Highlights color in RGBA + * - u_shadows (float): Color gradient added over image and background, following distortion shape (0 to 1) + * - u_highlights (float): Thin strokes along distortion shape, useful for antialiasing on small grid (0 to 1) + * - u_size (float): Size of the distortion shape grid (0 to 1) + * - u_shape (float): Grid shape (1 = lines, 2 = linesIrregular, 3 = wave, 4 = zigzag, 5 = pattern) + * - u_angle (float): Direction of the grid relative to the image in degrees (0 to 180) + * - u_distortionShape (float): Shape of distortion (1 = prism, 2 = lens, 3 = contour, 4 = cascade, 5 = flat) + * - u_distortion (float): Power of distortion applied within each stripe (0 to 1) + * - u_shift (float): Texture shift in direction opposite to the grid (-1 to 1) + * - u_stretch (float): Extra distortion along the grid lines (0 to 1) + * - u_blur (float): One-directional blur over the image and extra blur around edges (0 to 1) + * - u_edges (float): Glass distortion and softness on the image edges (0 to 1) + * - u_marginLeft (float): Distance from the left edge to the effect (0 to 1) + * - u_marginRight (float): Distance from the right edge to the effect (0 to 1) + * - u_marginTop (float): Distance from the top edge to the effect (0 to 1) + * - u_marginBottom (float): Distance from the bottom edge to the effect (0 to 1) + * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1) + * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * + */ +export declare const flutedGlassFragmentShader: string; +export interface FlutedGlassUniforms extends ShaderSizingUniforms { + u_image: HTMLImageElement | string | undefined; + u_colorBack: [number, number, number, number]; + u_colorShadow: [number, number, number, number]; + u_colorHighlight: [number, number, number, number]; + u_shadows: number; + u_size: number; + u_angle: number; + u_distortion: number; + u_shift: number; + u_blur: number; + u_edges: number; + u_marginLeft: number; + u_marginRight: number; + u_marginTop: number; + u_marginBottom: number; + u_stretch: number; + u_distortionShape: (typeof GlassDistortionShapes)[GlassDistortionShape]; + u_highlights: number; + u_shape: (typeof GlassGridShapes)[GlassGridShape]; + u_grainMixer: number; + u_grainOverlay: number; + u_noiseTexture?: HTMLImageElement; +} +export interface FlutedGlassParams extends ShaderSizingParams, ShaderMotionParams { + image?: HTMLImageElement | string; + colorBack?: string; + colorShadow?: string; + colorHighlight?: string; + shadows?: number; + size?: number; + angle?: number; + distortion?: number; + shift?: number; + blur?: number; + edges?: number; + margin?: number; + marginLeft?: number; + marginRight?: number; + marginTop?: number; + marginBottom?: number; + stretch?: number; + distortionShape?: GlassDistortionShape; + highlights?: number; + shape?: GlassGridShape; + grainMixer?: number; + grainOverlay?: number; +} +export declare const GlassGridShapes: { + readonly lines: 1; + readonly linesIrregular: 2; + readonly wave: 3; + readonly zigzag: 4; + readonly pattern: 5; +}; +export declare const GlassDistortionShapes: { + readonly prism: 1; + readonly lens: 2; + readonly contour: 3; + readonly cascade: 4; + readonly flat: 5; +}; +export type GlassDistortionShape = keyof typeof GlassDistortionShapes; +export type GlassGridShape = keyof typeof GlassGridShapes; diff --git a/public/vendor/paper-shaders/shaders/fluted-glass.js b/public/vendor/paper-shaders/shaders/fluted-glass.js new file mode 100644 index 0000000..5f4a788 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/fluted-glass.js @@ -0,0 +1,358 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, rotation2, proceduralHash21 } from "../shader-utils.js"; +const flutedGlassFragmentShader = `#version 300 es +precision mediump float; + +uniform vec2 u_resolution; +uniform float u_pixelRatio; +uniform float u_rotation; + +uniform vec4 u_colorBack; +uniform vec4 u_colorShadow; +uniform vec4 u_colorHighlight; + +uniform sampler2D u_image; +uniform float u_imageAspectRatio; + +uniform float u_size; +uniform float u_shadows; +uniform float u_angle; +uniform float u_stretch; +uniform float u_shape; +uniform float u_distortion; +uniform float u_highlights; +uniform float u_distortionShape; +uniform float u_shift; +uniform float u_blur; +uniform float u_edges; +uniform float u_marginLeft; +uniform float u_marginRight; +uniform float u_marginTop; +uniform float u_marginBottom; +uniform float u_grainMixer; +uniform float u_grainOverlay; + +in vec2 v_imageUV; + +out vec4 fragColor; + +${declarePI} +${rotation2} +${proceduralHash21} + +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = hash21(i); + float b = hash21(i + vec2(1.0, 0.0)); + float c = hash21(i + vec2(0.0, 1.0)); + float d = hash21(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} + +float getUvFrame(vec2 uv, float softness) { + float aax = 2. * fwidth(uv.x); + float aay = 2. * fwidth(uv.y); + float left = smoothstep(0., aax + softness, uv.x); + float right = 1. - smoothstep(1. - softness - aax, 1., uv.x); + float bottom = smoothstep(0., aay + softness, uv.y); + float top = 1. - smoothstep(1. - softness - aay, 1., uv.y); + return left * right * bottom * top; +} + +const int MAX_RADIUS = 50; +vec4 samplePremultiplied(sampler2D tex, vec2 uv) { + vec4 c = texture(tex, uv); + c.rgb *= c.a; + return c; +} +vec4 getBlur(sampler2D tex, vec2 uv, vec2 texelSize, vec2 dir, float sigma) { + if (sigma <= .5) return texture(tex, uv); + int radius = int(min(float(MAX_RADIUS), ceil(3.0 * sigma))); + + float twoSigma2 = 2.0 * sigma * sigma; + float gaussianNorm = 1.0 / sqrt(TWO_PI * sigma * sigma); + + vec4 sum = samplePremultiplied(tex, uv) * gaussianNorm; + float weightSum = gaussianNorm; + + for (int i = 1; i <= MAX_RADIUS; i++) { + if (i > radius) break; + + float x = float(i); + float w = exp(-(x * x) / twoSigma2) * gaussianNorm; + + vec2 offset = dir * texelSize * x; + vec4 s1 = samplePremultiplied(tex, uv + offset); + vec4 s2 = samplePremultiplied(tex, uv - offset); + + sum += (s1 + s2) * w; + weightSum += 2.0 * w; + } + + vec4 result = sum / weightSum; + if (result.a > 0.) { + result.rgb /= result.a; + } + + return result; +} + +vec2 rotateAspect(vec2 p, float a, float aspect) { + p.x *= aspect; + p = rotate(p, a); + p.x /= aspect; + return p; +} + +float smoothFract(float x) { + float f = fract(x); + float w = fwidth(x); + + float edge = abs(f - 0.5) - 0.5; + float band = smoothstep(-w, w, edge); + + return mix(f, 1.0 - f, band); +} + +void main() { + + float patternRotation = -u_angle * PI / 180.; + float patternSize = mix(200., 5., u_size); + + vec2 uv = v_imageUV; + + vec2 uvMask = gl_FragCoord.xy / u_resolution.xy; + vec2 sw = vec2(.005); + vec4 margins = vec4(u_marginLeft, u_marginTop, u_marginRight, u_marginBottom); + float mask = + smoothstep(margins[0], margins[0] + sw.x, uvMask.x + sw.x) * + smoothstep(margins[2], margins[2] + sw.x, 1.0 - uvMask.x + sw.x) * + smoothstep(margins[1], margins[1] + sw.y, uvMask.y + sw.y) * + smoothstep(margins[3], margins[3] + sw.y, 1.0 - uvMask.y + sw.y); + float maskOuter = + smoothstep(margins[0] - sw.x, margins[0], uvMask.x + sw.x) * + smoothstep(margins[2] - sw.x, margins[2], 1.0 - uvMask.x + sw.x) * + smoothstep(margins[1] - sw.y, margins[1], uvMask.y + sw.y) * + smoothstep(margins[3] - sw.y, margins[3], 1.0 - uvMask.y + sw.y); + float maskStroke = maskOuter - mask; + float maskInner = + smoothstep(margins[0] - 2. * sw.x, margins[0], uvMask.x) * + smoothstep(margins[2] - 2. * sw.x, margins[2], 1.0 - uvMask.x) * + smoothstep(margins[1] - 2. * sw.y, margins[1], uvMask.y) * + smoothstep(margins[3] - 2. * sw.y, margins[3], 1.0 - uvMask.y); + float maskStrokeInner = maskInner - mask; + + uv -= .5; + uv *= patternSize; + uv = rotateAspect(uv, patternRotation, u_imageAspectRatio); + + float curve = 0.; + float patternY = uv.y / u_imageAspectRatio; + if (u_shape > 4.5) { + // pattern + curve = .5 + .5 * sin(.5 * PI * uv.x) * cos(.5 * PI * patternY); + } else if (u_shape > 3.5) { + // zigzag + curve = 10. * abs(fract(.1 * patternY) - .5); + } else if (u_shape > 2.5) { + // wave + curve = 4. * sin(.23 * patternY); + } else if (u_shape > 1.5) { + // lines irregular + curve = .5 + .5 * sin(.5 * uv.x) * sin(1.7 * uv.x); + } else { + // lines + } + + vec2 UvToFract = uv + curve; + vec2 fractOrigUV = fract(uv); + vec2 floorOrigUV = floor(uv); + + float x = smoothFract(UvToFract.x); + float xNonSmooth = fract(UvToFract.x) + .0001; + + float highlightsWidth = 2. * max(.001, fwidth(UvToFract.x)); + highlightsWidth += 2. * maskStrokeInner; + float highlights = smoothstep(0., highlightsWidth, xNonSmooth); + highlights *= smoothstep(1., 1. - highlightsWidth, xNonSmooth); + highlights = 1. - highlights; + highlights *= u_highlights; + highlights = clamp(highlights, 0., 1.); + highlights *= mask; + + float shadows = pow(x, 1.3); + float distortion = 0.; + float fadeX = 1.; + float frameFade = 0.; + + float aa = fwidth(xNonSmooth); + aa = max(aa, fwidth(uv.x)); + aa = max(aa, fwidth(UvToFract.x)); + aa = max(aa, .0001); + + if (u_distortionShape == 1.) { + distortion = -pow(1.5 * x, 3.); + distortion += (.5 - u_shift); + + frameFade = pow(1.5 * x, 3.); + aa = max(.2, aa); + aa += mix(.2, 0., u_size); + fadeX = smoothstep(0., aa, xNonSmooth) * smoothstep(1., 1. - aa, xNonSmooth); + distortion = mix(.5, distortion, fadeX); + } else if (u_distortionShape == 2.) { + distortion = 2. * pow(x, 2.); + distortion -= (.5 + u_shift); + + frameFade = pow(abs(x - .5), 4.); + aa = max(.2, aa); + aa += mix(.2, 0., u_size); + fadeX = smoothstep(0., aa, xNonSmooth) * smoothstep(1., 1. - aa, xNonSmooth); + distortion = mix(.5, distortion, fadeX); + frameFade = mix(1., frameFade, .5 * fadeX); + } else if (u_distortionShape == 3.) { + distortion = pow(2. * (xNonSmooth - .5), 6.); + distortion -= .25; + distortion -= u_shift; + + frameFade = 1. - 2. * pow(abs(x - .4), 2.); + aa = .15; + aa += mix(.1, 0., u_size); + fadeX = smoothstep(0., aa, xNonSmooth) * smoothstep(1., 1. - aa, xNonSmooth); + frameFade = mix(1., frameFade, fadeX); + + } else if (u_distortionShape == 4.) { + x = xNonSmooth; + distortion = sin((x + .25) * TWO_PI); + shadows = .5 + .5 * asin(distortion) / (.5 * PI); + distortion *= .5; + distortion -= u_shift; + frameFade = .5 + .5 * sin(x * TWO_PI); + } else if (u_distortionShape == 5.) { + distortion -= pow(abs(x), .2) * x; + distortion += .33; + distortion -= 3. * u_shift; + distortion *= .33; + + frameFade = .3 * (smoothstep(.0, 1., x)); + shadows = pow(x, 2.5); + + aa = max(.1, aa); + aa += mix(.1, 0., u_size); + fadeX = smoothstep(0., aa, xNonSmooth) * smoothstep(1., 1. - aa, xNonSmooth); + distortion *= fadeX; + } + + vec2 dudx = dFdx(v_imageUV); + vec2 dudy = dFdy(v_imageUV); + vec2 grainUV = v_imageUV - .5; + grainUV *= (.8 / vec2(length(dudx), length(dudy))); + grainUV += .5; + float grain = valueNoise(grainUV); + grain = smoothstep(.4, .7, grain); + grain *= u_grainMixer; + distortion = mix(distortion, 0., grain); + + shadows = min(shadows, 1.); + shadows += maskStrokeInner; + shadows *= mask; + shadows = min(shadows, 1.); + shadows *= pow(u_shadows, 2.); + shadows = clamp(shadows, 0., 1.); + + distortion *= 3. * u_distortion; + frameFade *= u_distortion; + + fractOrigUV.x += distortion; + floorOrigUV = rotateAspect(floorOrigUV, -patternRotation, u_imageAspectRatio); + fractOrigUV = rotateAspect(fractOrigUV, -patternRotation, u_imageAspectRatio); + + uv = (floorOrigUV + fractOrigUV) / patternSize; + uv += pow(maskStroke, 4.); + + uv += vec2(.5); + + uv = mix(v_imageUV, uv, smoothstep(0., .7, mask)); + float blur = mix(0., 50., u_blur); + blur = mix(0., blur, smoothstep(.5, 1., mask)); + + float edgeDistortion = mix(.0, .04, u_edges); + edgeDistortion += .06 * frameFade * u_edges; + edgeDistortion *= mask; + float frame = getUvFrame(uv, edgeDistortion); + + float stretch = 1. - smoothstep(0., .5, xNonSmooth) * smoothstep(1., 1. - .5, xNonSmooth); + stretch = pow(stretch, 2.); + stretch *= mask; + stretch *= getUvFrame(uv, .1 + .05 * mask * frameFade); + uv.y = mix(uv.y, .5, u_stretch * stretch); + + vec4 image = getBlur(u_image, uv, 1. / u_resolution / u_pixelRatio, vec2(0., 1.), blur); + image.rgb *= image.a; + vec4 backColor = u_colorBack; + backColor.rgb *= backColor.a; + vec4 highlightColor = u_colorHighlight; + highlightColor.rgb *= highlightColor.a; + vec4 shadowColor = u_colorShadow; + + vec3 color = highlightColor.rgb * highlights; + float opacity = highlightColor.a * highlights; + + shadows = mix(shadows * shadowColor.a, 0., highlights); + color = mix(color, shadowColor.rgb * shadowColor.a, .5 * shadows); + color += .5 * pow(shadows, .5) * shadowColor.rgb; + opacity += shadows; + color = clamp(color, vec3(0.), vec3(1.)); + opacity = clamp(opacity, 0., 1.); + + color += image.rgb * (1. - opacity) * frame; + opacity += image.a * (1. - opacity) * frame; + + color += backColor.rgb * (1. - opacity); + opacity += backColor.a * (1. - opacity); + + float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.)); + grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5); + grainOverlay = pow(grainOverlay, 1.3); + + float grainOverlayV = grainOverlay * 2. - 1.; + vec3 grainOverlayColor = vec3(step(0., grainOverlayV)); + float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV); + grainOverlayStrength = pow(grainOverlayStrength, .8); + grainOverlayStrength *= mask; + color = mix(color, grainOverlayColor, .35 * grainOverlayStrength); + + opacity += .5 * grainOverlayStrength; + opacity = clamp(opacity, 0., 1.); + + fragColor = vec4(color, opacity); +} +`; +const GlassGridShapes = { + lines: 1, + linesIrregular: 2, + wave: 3, + zigzag: 4, + pattern: 5 +}; +const GlassDistortionShapes = { + prism: 1, + lens: 2, + contour: 3, + cascade: 4, + flat: 5 +}; +export { + GlassDistortionShapes, + GlassGridShapes, + flutedGlassFragmentShader +}; +//# sourceMappingURL=fluted-glass.js.map diff --git a/public/vendor/paper-shaders/shaders/fluted-glass.js.map b/public/vendor/paper-shaders/shaders/fluted-glass.js.map new file mode 100644 index 0000000..888b1ca --- /dev/null +++ b/public/vendor/paper-shaders/shaders/fluted-glass.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/fluted-glass.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\n/**\n * Fluted glass image filter that transforms an image into streaked, ribbed distortions,\n * giving a mix of clarity and obscurity.\n *\n * Fragment shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_image (sampler2D): Source image texture\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colorShadow (vec4): Shadows color in RGBA\n * - u_colorHighlight (vec4): Highlights color in RGBA\n * - u_shadows (float): Color gradient added over image and background, following distortion shape (0 to 1)\n * - u_highlights (float): Thin strokes along distortion shape, useful for antialiasing on small grid (0 to 1)\n * - u_size (float): Size of the distortion shape grid (0 to 1)\n * - u_shape (float): Grid shape (1 = lines, 2 = linesIrregular, 3 = wave, 4 = zigzag, 5 = pattern)\n * - u_angle (float): Direction of the grid relative to the image in degrees (0 to 180)\n * - u_distortionShape (float): Shape of distortion (1 = prism, 2 = lens, 3 = contour, 4 = cascade, 5 = flat)\n * - u_distortion (float): Power of distortion applied within each stripe (0 to 1)\n * - u_shift (float): Texture shift in direction opposite to the grid (-1 to 1)\n * - u_stretch (float): Extra distortion along the grid lines (0 to 1)\n * - u_blur (float): One-directional blur over the image and extra blur around edges (0 to 1)\n * - u_edges (float): Glass distortion and softness on the image edges (0 to 1)\n * - u_marginLeft (float): Distance from the left edge to the effect (0 to 1)\n * - u_marginRight (float): Distance from the right edge to the effect (0 to 1)\n * - u_marginTop (float): Distance from the top edge to the effect (0 to 1)\n * - u_marginBottom (float): Distance from the bottom edge to the effect (0 to 1)\n * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1)\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const flutedGlassFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\nuniform float u_rotation;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorShadow;\nuniform vec4 u_colorHighlight;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_size;\nuniform float u_shadows;\nuniform float u_angle;\nuniform float u_stretch;\nuniform float u_shape;\nuniform float u_distortion;\nuniform float u_highlights;\nuniform float u_distortionShape;\nuniform float u_shift;\nuniform float u_blur;\nuniform float u_edges;\nuniform float u_marginLeft;\nuniform float u_marginRight;\nuniform float u_marginTop;\nuniform float u_marginBottom;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\n\nin vec2 v_imageUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\n${ proceduralHash21 }\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat getUvFrame(vec2 uv, float softness) {\n float aax = 2. * fwidth(uv.x);\n float aay = 2. * fwidth(uv.y);\n float left = smoothstep(0., aax + softness, uv.x);\n float right = 1. - smoothstep(1. - softness - aax, 1., uv.x);\n float bottom = smoothstep(0., aay + softness, uv.y);\n float top = 1. - smoothstep(1. - softness - aay, 1., uv.y);\n return left * right * bottom * top;\n}\n\nconst int MAX_RADIUS = 50;\nvec4 samplePremultiplied(sampler2D tex, vec2 uv) {\n vec4 c = texture(tex, uv);\n c.rgb *= c.a;\n return c;\n}\nvec4 getBlur(sampler2D tex, vec2 uv, vec2 texelSize, vec2 dir, float sigma) {\n if (sigma <= .5) return texture(tex, uv);\n int radius = int(min(float(MAX_RADIUS), ceil(3.0 * sigma)));\n\n float twoSigma2 = 2.0 * sigma * sigma;\n float gaussianNorm = 1.0 / sqrt(TWO_PI * sigma * sigma);\n\n vec4 sum = samplePremultiplied(tex, uv) * gaussianNorm;\n float weightSum = gaussianNorm;\n\n for (int i = 1; i <= MAX_RADIUS; i++) {\n if (i > radius) break;\n\n float x = float(i);\n float w = exp(-(x * x) / twoSigma2) * gaussianNorm;\n\n vec2 offset = dir * texelSize * x;\n vec4 s1 = samplePremultiplied(tex, uv + offset);\n vec4 s2 = samplePremultiplied(tex, uv - offset);\n\n sum += (s1 + s2) * w;\n weightSum += 2.0 * w;\n }\n\n vec4 result = sum / weightSum;\n if (result.a > 0.) {\n result.rgb /= result.a;\n }\n\n return result;\n}\n\nvec2 rotateAspect(vec2 p, float a, float aspect) {\n p.x *= aspect;\n p = rotate(p, a);\n p.x /= aspect;\n return p;\n}\n\nfloat smoothFract(float x) {\n float f = fract(x);\n float w = fwidth(x);\n\n float edge = abs(f - 0.5) - 0.5;\n float band = smoothstep(-w, w, edge);\n\n return mix(f, 1.0 - f, band);\n}\n\nvoid main() {\n\n float patternRotation = -u_angle * PI / 180.;\n float patternSize = mix(200., 5., u_size);\n\n vec2 uv = v_imageUV;\n\n vec2 uvMask = gl_FragCoord.xy / u_resolution.xy;\n vec2 sw = vec2(.005);\n vec4 margins = vec4(u_marginLeft, u_marginTop, u_marginRight, u_marginBottom);\n float mask =\n smoothstep(margins[0], margins[0] + sw.x, uvMask.x + sw.x) *\n smoothstep(margins[2], margins[2] + sw.x, 1.0 - uvMask.x + sw.x) *\n smoothstep(margins[1], margins[1] + sw.y, uvMask.y + sw.y) *\n smoothstep(margins[3], margins[3] + sw.y, 1.0 - uvMask.y + sw.y);\n float maskOuter =\n smoothstep(margins[0] - sw.x, margins[0], uvMask.x + sw.x) *\n smoothstep(margins[2] - sw.x, margins[2], 1.0 - uvMask.x + sw.x) *\n smoothstep(margins[1] - sw.y, margins[1], uvMask.y + sw.y) *\n smoothstep(margins[3] - sw.y, margins[3], 1.0 - uvMask.y + sw.y);\n float maskStroke = maskOuter - mask;\n float maskInner =\n smoothstep(margins[0] - 2. * sw.x, margins[0], uvMask.x) *\n smoothstep(margins[2] - 2. * sw.x, margins[2], 1.0 - uvMask.x) *\n smoothstep(margins[1] - 2. * sw.y, margins[1], uvMask.y) *\n smoothstep(margins[3] - 2. * sw.y, margins[3], 1.0 - uvMask.y);\n float maskStrokeInner = maskInner - mask;\n\n uv -= .5;\n uv *= patternSize;\n uv = rotateAspect(uv, patternRotation, u_imageAspectRatio);\n\n float curve = 0.;\n float patternY = uv.y / u_imageAspectRatio;\n if (u_shape > 4.5) {\n // pattern\n curve = .5 + .5 * sin(.5 * PI * uv.x) * cos(.5 * PI * patternY);\n } else if (u_shape > 3.5) {\n // zigzag\n curve = 10. * abs(fract(.1 * patternY) - .5);\n } else if (u_shape > 2.5) {\n // wave\n curve = 4. * sin(.23 * patternY);\n } else if (u_shape > 1.5) {\n // lines irregular\n curve = .5 + .5 * sin(.5 * uv.x) * sin(1.7 * uv.x);\n } else {\n // lines\n }\n\n vec2 UvToFract = uv + curve;\n vec2 fractOrigUV = fract(uv);\n vec2 floorOrigUV = floor(uv);\n\n float x = smoothFract(UvToFract.x);\n float xNonSmooth = fract(UvToFract.x) + .0001;\n\n float highlightsWidth = 2. * max(.001, fwidth(UvToFract.x));\n highlightsWidth += 2. * maskStrokeInner;\n float highlights = smoothstep(0., highlightsWidth, xNonSmooth);\n highlights *= smoothstep(1., 1. - highlightsWidth, xNonSmooth);\n highlights = 1. - highlights;\n highlights *= u_highlights;\n highlights = clamp(highlights, 0., 1.);\n highlights *= mask;\n\n float shadows = pow(x, 1.3);\n float distortion = 0.;\n float fadeX = 1.;\n float frameFade = 0.;\n\n float aa = fwidth(xNonSmooth);\n aa = max(aa, fwidth(uv.x));\n aa = max(aa, fwidth(UvToFract.x));\n aa = max(aa, .0001);\n\n if (u_distortionShape == 1.) {\n distortion = -pow(1.5 * x, 3.);\n distortion += (.5 - u_shift);\n\n frameFade = pow(1.5 * x, 3.);\n aa = max(.2, aa);\n aa += mix(.2, 0., u_size);\n fadeX = smoothstep(0., aa, xNonSmooth) * smoothstep(1., 1. - aa, xNonSmooth);\n distortion = mix(.5, distortion, fadeX);\n } else if (u_distortionShape == 2.) {\n distortion = 2. * pow(x, 2.);\n distortion -= (.5 + u_shift);\n\n frameFade = pow(abs(x - .5), 4.);\n aa = max(.2, aa);\n aa += mix(.2, 0., u_size);\n fadeX = smoothstep(0., aa, xNonSmooth) * smoothstep(1., 1. - aa, xNonSmooth);\n distortion = mix(.5, distortion, fadeX);\n frameFade = mix(1., frameFade, .5 * fadeX);\n } else if (u_distortionShape == 3.) {\n distortion = pow(2. * (xNonSmooth - .5), 6.);\n distortion -= .25;\n distortion -= u_shift;\n\n frameFade = 1. - 2. * pow(abs(x - .4), 2.);\n aa = .15;\n aa += mix(.1, 0., u_size);\n fadeX = smoothstep(0., aa, xNonSmooth) * smoothstep(1., 1. - aa, xNonSmooth);\n frameFade = mix(1., frameFade, fadeX);\n\n } else if (u_distortionShape == 4.) {\n x = xNonSmooth;\n distortion = sin((x + .25) * TWO_PI);\n shadows = .5 + .5 * asin(distortion) / (.5 * PI);\n distortion *= .5;\n distortion -= u_shift;\n frameFade = .5 + .5 * sin(x * TWO_PI);\n } else if (u_distortionShape == 5.) {\n distortion -= pow(abs(x), .2) * x;\n distortion += .33;\n distortion -= 3. * u_shift;\n distortion *= .33;\n\n frameFade = .3 * (smoothstep(.0, 1., x));\n shadows = pow(x, 2.5);\n\n aa = max(.1, aa);\n aa += mix(.1, 0., u_size);\n fadeX = smoothstep(0., aa, xNonSmooth) * smoothstep(1., 1. - aa, xNonSmooth);\n distortion *= fadeX;\n }\n\n vec2 dudx = dFdx(v_imageUV);\n vec2 dudy = dFdy(v_imageUV);\n vec2 grainUV = v_imageUV - .5;\n grainUV *= (.8 / vec2(length(dudx), length(dudy)));\n grainUV += .5;\n float grain = valueNoise(grainUV);\n grain = smoothstep(.4, .7, grain);\n grain *= u_grainMixer;\n distortion = mix(distortion, 0., grain);\n\n shadows = min(shadows, 1.);\n shadows += maskStrokeInner;\n shadows *= mask;\n shadows = min(shadows, 1.);\n shadows *= pow(u_shadows, 2.);\n shadows = clamp(shadows, 0., 1.);\n\n distortion *= 3. * u_distortion;\n frameFade *= u_distortion;\n\n fractOrigUV.x += distortion;\n floorOrigUV = rotateAspect(floorOrigUV, -patternRotation, u_imageAspectRatio);\n fractOrigUV = rotateAspect(fractOrigUV, -patternRotation, u_imageAspectRatio);\n\n uv = (floorOrigUV + fractOrigUV) / patternSize;\n uv += pow(maskStroke, 4.);\n\n uv += vec2(.5);\n\n uv = mix(v_imageUV, uv, smoothstep(0., .7, mask));\n float blur = mix(0., 50., u_blur);\n blur = mix(0., blur, smoothstep(.5, 1., mask));\n\n float edgeDistortion = mix(.0, .04, u_edges);\n edgeDistortion += .06 * frameFade * u_edges;\n edgeDistortion *= mask;\n float frame = getUvFrame(uv, edgeDistortion);\n\n float stretch = 1. - smoothstep(0., .5, xNonSmooth) * smoothstep(1., 1. - .5, xNonSmooth);\n stretch = pow(stretch, 2.);\n stretch *= mask;\n stretch *= getUvFrame(uv, .1 + .05 * mask * frameFade);\n uv.y = mix(uv.y, .5, u_stretch * stretch);\n\n vec4 image = getBlur(u_image, uv, 1. / u_resolution / u_pixelRatio, vec2(0., 1.), blur);\n image.rgb *= image.a;\n vec4 backColor = u_colorBack;\n backColor.rgb *= backColor.a;\n vec4 highlightColor = u_colorHighlight;\n highlightColor.rgb *= highlightColor.a;\n vec4 shadowColor = u_colorShadow;\n\n vec3 color = highlightColor.rgb * highlights;\n float opacity = highlightColor.a * highlights;\n\n shadows = mix(shadows * shadowColor.a, 0., highlights);\n color = mix(color, shadowColor.rgb * shadowColor.a, .5 * shadows);\n color += .5 * pow(shadows, .5) * shadowColor.rgb;\n opacity += shadows;\n color = clamp(color, vec3(0.), vec3(1.));\n opacity = clamp(opacity, 0., 1.);\n\n color += image.rgb * (1. - opacity) * frame;\n opacity += image.a * (1. - opacity) * frame;\n\n color += backColor.rgb * (1. - opacity);\n opacity += backColor.a * (1. - opacity);\n\n float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.));\n grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\n grainOverlay = pow(grainOverlay, 1.3);\n\n float grainOverlayV = grainOverlay * 2. - 1.;\n vec3 grainOverlayColor = vec3(step(0., grainOverlayV));\n float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV);\n grainOverlayStrength = pow(grainOverlayStrength, .8);\n grainOverlayStrength *= mask;\n color = mix(color, grainOverlayColor, .35 * grainOverlayStrength);\n\n opacity += .5 * grainOverlayStrength;\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface FlutedGlassUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_colorBack: [number, number, number, number];\n u_colorShadow: [number, number, number, number];\n u_colorHighlight: [number, number, number, number];\n u_shadows: number;\n u_size: number;\n u_angle: number;\n u_distortion: number;\n u_shift: number;\n u_blur: number;\n u_edges: number;\n u_marginLeft: number;\n u_marginRight: number;\n u_marginTop: number;\n u_marginBottom: number;\n u_stretch: number;\n u_distortionShape: (typeof GlassDistortionShapes)[GlassDistortionShape];\n u_highlights: number;\n u_shape: (typeof GlassGridShapes)[GlassGridShape];\n u_grainMixer: number;\n u_grainOverlay: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface FlutedGlassParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n colorBack?: string;\n colorShadow?: string;\n colorHighlight?: string;\n shadows?: number;\n size?: number;\n angle?: number;\n distortion?: number;\n shift?: number;\n blur?: number;\n edges?: number;\n margin?: number;\n marginLeft?: number;\n marginRight?: number;\n marginTop?: number;\n marginBottom?: number;\n stretch?: number;\n distortionShape?: GlassDistortionShape;\n highlights?: number;\n shape?: GlassGridShape;\n grainMixer?: number;\n grainOverlay?: number;\n}\n\nexport const GlassGridShapes = {\n lines: 1,\n linesIrregular: 2,\n wave: 3,\n zigzag: 4,\n pattern: 5,\n} as const;\n\nexport const GlassDistortionShapes = {\n prism: 1,\n lens: 2,\n contour: 3,\n cascade: 4,\n flat: 5,\n} as const;\n\nexport type GlassDistortionShape = keyof typeof GlassDistortionShapes;\nexport type GlassGridShape = keyof typeof GlassGridShapes;\n"], + "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,WAAW,WAAW,wBAAwB;AAmDhD,MAAM,4BAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoC9C,SAAU;AAAA,EACV,SAAU;AAAA,EACV,gBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwVb,MAAM,kBAAkB;AAAA,EAC7B,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,SAAS;AACX;AAEO,MAAM,wBAAwB;AAAA,EACnC,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,MAAM;AACR;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/gem-smoke.d.ts b/public/vendor/paper-shaders/shaders/gem-smoke.d.ts new file mode 100644 index 0000000..eaca83e --- /dev/null +++ b/public/vendor/paper-shaders/shaders/gem-smoke.d.ts @@ -0,0 +1,94 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import type { ShaderSizingParams, ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const gemSmokeMeta: { + readonly maxColorCount: 6; +}; +/** + * Animated color fields placed over uploaded logo shape; gives the illusion of smoky noise + * behind the glassy shape. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_resolution (vec2): Canvas resolution in pixels + * - u_image (sampler2D): Pre-processed source image texture (R = edge gradient, G = alpha) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * - u_colors (vec4[]): Up to 6 smoke colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_colorBack (vec4): Background color in RGBA + * - u_innerDistortion (float): Power of smoke distortion inside the input shape (0 to 1) + * - u_outerDistortion (float): Power of smoke distortion outside the input shape (0 to 1) + * - u_outerGlow (float): Visibility of smoke shape outside the input shape (0 to 1) + * - u_innerGlow (float): Visibility of smoke shape inside the input shape (0 to 1) + * - u_colorInner (vec4): Additional color inside the input shape, mixing with smoke (RGBA) + * - u_offset (float): Vertical offset of smoke inside the shape (-1 to 1) + * - u_angle (float): Smoke direction in degrees (0 to 360) + * - u_size (float): Size of smoke shape relative to the image box (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied + * - v_objectUV (vec2): Normalized UV coordinates with scale, rotation, and offset applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * + */ +export declare const gemSmokeFragmentShader: string; +export declare const POISSON_CONFIG_OPTIMIZED: { + measurePerformance: boolean; + workingSize: number; + iterations: number; +}; +export declare function toProcessedGemSmoke(file: File | string): Promise<{ + imageData: ImageData; + pngBlob: Blob; +}>; +export interface GemSmokeUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colors: vec4[]; + u_colorsCount: number; + u_image: HTMLImageElement | string | undefined; + u_innerDistortion: number; + u_outerDistortion: number; + u_outerGlow: number; + u_innerGlow: number; + u_colorInner: [number, number, number, number]; + u_offset: number; + u_angle: number; + u_size: number; + u_shape: (typeof GemSmokeShapes)[GemSmokeShape]; + u_isImage: boolean; +} +export interface GemSmokeParams extends ShaderSizingParams, ShaderMotionParams { + colors?: string[]; + colorBack?: string; + image?: HTMLImageElement | string | undefined; + innerDistortion?: number; + outerDistortion?: number; + outerGlow?: number; + innerGlow?: number; + colorInner?: string; + offset?: number; + angle?: number; + size?: number; + shape?: GemSmokeShape; +} +export declare const GemSmokeShapes: { + readonly none: 0; + readonly circle: 1; + readonly daisy: 2; + readonly diamond: 3; + readonly metaballs: 4; +}; +export type GemSmokeShape = keyof typeof GemSmokeShapes; diff --git a/public/vendor/paper-shaders/shaders/gem-smoke.js b/public/vendor/paper-shaders/shaders/gem-smoke.js new file mode 100644 index 0000000..327958c --- /dev/null +++ b/public/vendor/paper-shaders/shaders/gem-smoke.js @@ -0,0 +1,585 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import { rotation2, declarePI } from "../shader-utils.js"; +const gemSmokeMeta = { + maxColorCount: 6 +}; +const gemSmokeFragmentShader = `#version 300 es +precision mediump float; + +in mediump vec2 v_imageUV; +in mediump vec2 v_objectUV; +in mediump vec2 v_responsiveUV; +in mediump vec2 v_responsiveBoxGivenSize; +out vec4 fragColor; + +// Image +uniform sampler2D u_image; +uniform float u_imageAspectRatio; + +// Canvas +uniform vec2 u_resolution; +uniform float u_time; + +// Colors +uniform vec4 u_colors[${gemSmokeMeta.maxColorCount}]; +uniform float u_colorsCount; +uniform vec4 u_colorBack; +uniform vec4 u_colorInner; + +// Effect controls +uniform float u_innerDistortion; +uniform float u_outerDistortion; +uniform float u_outerGlow; +uniform float u_innerGlow; +uniform float u_offset; +uniform float u_angle; +uniform float u_size; + +// Shape controls +uniform float u_shape; +uniform bool u_isImage; + +${declarePI} +${rotation2} + +// 9x9 Gaussian blur on R and G channels +vec2 gaussBlur9x9RG(sampler2D tex, vec2 uv, vec2 dudx, vec2 dudy, float radius) { + vec2 texel = 1.0 / vec2(textureSize(tex, 0)); + vec2 r = max(radius, 0.0) * texel; + // Pascal's row 8: sum = 256, 2D norm = 65536 + const float k[9] = float[9](1.0, 8.0, 28.0, 56.0, 70.0, 56.0, 28.0, 8.0, 1.0); + vec2 sum = vec2(0.0); + + for (int j = -4; j <= 4; ++j) { + float wy = k[j + 4]; + for (int i = -4; i <= 4; ++i) { + float w = k[i + 4] * wy; + vec2 off = vec2(float(i) * r.x, float(j) * r.y); + sum += w * texture(tex, uv + off).rg; + } + } + + return sum / 65536.0; +} + +float sst(float a, float b, float x) { + return smoothstep(a, b, x); +} + +void main() { + float time = u_time; + + float roundness = 0.; + float imgAlpha = 0.; + + if (u_isImage == true) { + // Image sampling (UV scaled inward to account for padding) + vec2 imageUV = v_imageUV; + imageUV -= .5; + imageUV *= .95; + imageUV += .5; + + vec2 dudx = dFdx(v_imageUV); + vec2 dudy = dFdy(v_imageUV); + + // Blurred image: x = roundness, y = alpha + vec2 blurred = gaussBlur9x9RG(u_image, imageUV, dudx, dudy, 10.); + roundness = 1. - blurred.x; + vec2 texelA = 1.0 / vec2(textureSize(u_image, 0)); + const float k3[3] = float[3](1.0, 2.0, 1.0); + for (int j = -1; j <= 1; ++j) { + for (int i = -1; i <= 1; ++i) { + imgAlpha += k3[i + 1] * k3[j + 1] * texture(u_image, imageUV + vec2(float(i) * texelA.x, float(j) * texelA.y)).g; + } + } + imgAlpha /= 16.0; + } else { + vec2 uv = v_objectUV + .5; + uv.y = 1. - uv.y; + float edge = 0.; + + if (u_shape < 1.) { + // full-fill on canvas + vec2 borderUV = v_responsiveUV + .5; + vec2 mask = min(borderUV, 1. - borderUV); + vec2 pixel_thickness = min(250. / v_responsiveBoxGivenSize, vec2(.5)); + float maskX = smoothstep(0.0, pixel_thickness.x, mask.x); + float maskY = smoothstep(0.0, pixel_thickness.y, mask.y); + maskX = pow(maskX, .25); + maskY = pow(maskY, .25); + edge = clamp(1. - maskX * maskY, 0., 1.); + } else if (u_shape < 2.) { + // circle + vec2 shapeUV = uv - .5; + shapeUV *= .67; + edge = pow(clamp(3. * length(shapeUV), 0., 1.), 18.); + } else if (u_shape < 3.) { + // daisy + vec2 shapeUV = uv - .5; + shapeUV *= 1.68; + + float r = length(shapeUV) * 2.; + float a = atan(shapeUV.y, shapeUV.x) + .2; + r *= (1. + .05 * sin(3. * a + 2. * time)); + float f = abs(cos(a * 3.)); + edge = smoothstep(f, f + .7, r); + edge *= edge; + } else if (u_shape < 4.) { + // diamond + vec2 shapeUV = uv - .5; + shapeUV = rotate(shapeUV, .25 * PI); + shapeUV *= 1.42; + shapeUV += .5; + vec2 mask = min(shapeUV, 1. - shapeUV); + vec2 pixel_thickness = vec2(.15); + float maskX = smoothstep(0.0, pixel_thickness.x, mask.x); + float maskY = smoothstep(0.0, pixel_thickness.y, mask.y); + maskX = pow(maskX, .25); + maskY = pow(maskY, .25); + edge = clamp(1. - maskX * maskY, 0., 1.); + } else if (u_shape < 5.) { + // metaballs + vec2 shapeUV = uv - .5; + shapeUV *= 1.3; + edge = 0.; + for (int i = 0; i < 5; i++) { + float fi = float(i); + float speed = 1.5 + 2./3. * sin(fi * 12.345); + float angle = -fi * 1.5; + vec2 dir1 = vec2(cos(angle), sin(angle)); + vec2 dir2 = vec2(cos(angle + 1.57), sin(angle + 1.)); + vec2 traj = .4 * (dir1 * sin(time * speed + fi * 1.23) + dir2 * cos(time * (speed * 0.7) + fi * 2.17)); + float d = length(shapeUV + traj); + edge += pow(1.0 - clamp(d, 0.0, 1.0), 4.0); + } + edge = 1. - smoothstep(.65, .9, edge); + edge = pow(edge, 4.); + } + + imgAlpha = 1. - smoothstep(.9 - 2. * fwidth(edge), .9, edge); + roundness = 1. - edge; + } + +// Smoke UV setup + vec2 smokeUV = v_objectUV; + smokeUV = rotate(smokeUV, u_angle * PI / 180.); + smokeUV *= mix(4., 1., u_size); + + // Two swirl paths: inner (shape-masked) and outer (free), each with independent distortion + vec2 innerUV = smokeUV; + vec2 outerUV = smokeUV; + + // Vertical displacement \u2014 applied independently to inner and outer + innerUV.y += u_innerDistortion * (1. - sst(0., 1., length(.4 * innerUV))); + innerUV.y -= .4 * u_innerDistortion; + innerUV.y += .7 * u_offset * roundness; + + outerUV.y += u_outerDistortion * (1. - sst(0., 1., length(.4 * outerUV))); + outerUV.y -= .4 * u_outerDistortion; + + float innerSwirl = u_innerDistortion * roundness; + float outerSwirl = u_outerDistortion; + + for (int i = 1; i < 5; i++) { + float fi = float(i); + + float stretchIn = max(length(dFdx(innerUV)), length(dFdy(innerUV))); + float dampenIn = 1. / (1. + stretchIn * 8.); + float sIn = innerSwirl * dampenIn; + innerUV.x += sIn / fi * cos(time + fi * 2.9 * innerUV.y); + innerUV.y += sIn / fi * cos(time + fi * 1.5 * innerUV.x); + + float stretchOut = max(length(dFdx(outerUV)), length(dFdy(outerUV))); + float dampenOut = 1. / (1. + stretchOut * 8.); + float sOut = outerSwirl * dampenOut; + outerUV.x += sOut / fi * cos(time + fi * 2.9 * outerUV.y); + outerUV.y += sOut / fi * cos(time + fi * 1.5 * outerUV.x); + } + + // Smoke shapes from swirl fields + float innerShape = exp(-1.5 * dot(innerUV, innerUV)); + float outerShape = exp(-1.5 * dot(outerUV, outerUV)); + + // Visibility masks + float outerMask = pow(u_outerGlow, 2.) * (1. - imgAlpha); + float innerMask = (.01 + .99 * u_innerGlow) * imgAlpha; + + innerShape *= innerMask; + outerShape *= outerMask; + + // Color gradient + float mixer = (innerShape + outerShape) * u_colorsCount; + vec4 gradient = u_colors[0]; + gradient.rgb *= gradient.a; + + float smokeMask = 0.; + for (int i = 1; i < ${gemSmokeMeta.maxColorCount + 1}; i++) { + if (i > int(u_colorsCount)) break; + + float m = sst(0., 1., clamp(mixer - float(i - 1), 0., 1.)); + if (i == 1) smokeMask = m; + + vec4 c = u_colors[i - 1]; + c.rgb *= c.a; + gradient = mix(gradient, c, m); + } + + // Compositing (premultiplied alpha, front-to-back) + vec3 color = gradient.rgb * smokeMask; + float opacity = gradient.a * smokeMask; + + float innerOpacity = u_colorInner.a * imgAlpha; + vec3 innerColor = u_colorInner.rgb * innerOpacity; + color += innerColor * (1.0 - opacity); + opacity += innerOpacity * (1.0 - opacity); + + vec3 backColor = u_colorBack.rgb * u_colorBack.a; + color += backColor * (1.0 - opacity); + opacity += u_colorBack.a * (1.0 - opacity); + + fragColor = vec4(color, opacity); +} +`; +const POISSON_CONFIG_OPTIMIZED = { + measurePerformance: false, + // Set to true to see performance metrics + workingSize: 512, + // Size to solve Poisson at (will upscale to original size) + iterations: 32 + // SOR converges ~2-20x faster than standard Gauss-Seidel +}; +function toProcessedGemSmoke(file) { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + const isBlob = typeof file === "string" && file.startsWith("blob:"); + return new Promise((resolve, reject) => { + if (!file || !ctx) { + reject(new Error("Invalid file or canvas context")); + return; + } + const blobContentTypePromise = isBlob && fetch(file).then((res) => res.headers.get("Content-Type")); + const img = new Image(); + img.crossOrigin = "anonymous"; + const totalStartTime = performance.now(); + img.onload = async () => { + let isSVG; + const blobContentType = await blobContentTypePromise; + if (blobContentType) { + isSVG = blobContentType === "image/svg+xml"; + } else if (typeof file === "string") { + isSVG = file.endsWith(".svg") || file.startsWith("data:image/svg+xml"); + } else { + isSVG = file.type === "image/svg+xml"; + } + let originalWidth = img.width || img.naturalWidth; + let originalHeight = img.height || img.naturalHeight; + if (isSVG) { + const svgMaxSize = 4096; + const aspectRatio = originalWidth / originalHeight; + if (originalWidth > originalHeight) { + originalWidth = svgMaxSize; + originalHeight = svgMaxSize / aspectRatio; + } else { + originalHeight = svgMaxSize; + originalWidth = svgMaxSize * aspectRatio; + } + img.width = originalWidth; + img.height = originalHeight; + } + const minDimension = Math.min(originalWidth, originalHeight); + const targetSize = POISSON_CONFIG_OPTIMIZED.workingSize; + const scaleFactor = targetSize / minDimension; + const width = Math.round(originalWidth * scaleFactor); + const height = Math.round(originalHeight * scaleFactor); + if (POISSON_CONFIG_OPTIMIZED.measurePerformance) { + console.log(`[Processing Mode]`); + console.log(` Original: ${originalWidth}\xD7${originalHeight}`); + console.log(` Working: ${width}\xD7${height} (${(scaleFactor * 100).toFixed(1)}% scale)`); + if (scaleFactor < 1) { + console.log(` Speedup: ~${Math.round(1 / (scaleFactor * scaleFactor))}\xD7`); + } + } + canvas.width = originalWidth; + canvas.height = originalHeight; + const paddingSize = 0.025; + const padX = Math.ceil(width * paddingSize); + const padY = Math.ceil(height * paddingSize); + const imgW = width - 2 * padX; + const imgH = height - 2 * padY; + const shapeCanvas = document.createElement("canvas"); + shapeCanvas.width = width; + shapeCanvas.height = height; + const shapeCtx = shapeCanvas.getContext("2d"); + shapeCtx.drawImage(img, padX, padY, imgW, imgH); + const startMask = performance.now(); + const shapeImageData = shapeCtx.getImageData(0, 0, width, height); + const data = shapeImageData.data; + const shapeMask = new Uint8Array(width * height); + const boundaryMask = new Uint8Array(width * height); + let shapePixelCount = 0; + for (let i = 0, idx = 0; i < data.length; i += 4, idx++) { + const a = data[i + 3]; + const isShape = a === 0 ? 0 : 1; + shapeMask[idx] = isShape; + shapePixelCount += isShape; + } + const boundaryIndices = []; + const interiorIndices = []; + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const idx = y * width + x; + if (!shapeMask[idx]) continue; + let isBoundary = false; + if (x === 0 || x === width - 1 || y === 0 || y === height - 1) { + isBoundary = true; + } else { + isBoundary = !shapeMask[idx - 1] || // left + !shapeMask[idx + 1] || // right + !shapeMask[idx - width] || // top + !shapeMask[idx + width] || // bottom + !shapeMask[idx - width - 1] || // top-left + !shapeMask[idx - width + 1] || // top-right + !shapeMask[idx + width - 1] || // bottom-left + !shapeMask[idx + width + 1]; + } + if (isBoundary) { + boundaryMask[idx] = 1; + boundaryIndices.push(idx); + } else { + interiorIndices.push(idx); + } + } + } + if (POISSON_CONFIG_OPTIMIZED.measurePerformance) { + console.log(`[Mask Building] Time: ${(performance.now() - startMask).toFixed(2)}ms`); + console.log( + ` Shape pixels: ${shapePixelCount} / ${width * height} (${(shapePixelCount / (width * height) * 100).toFixed(1)}%)` + ); + console.log(` Interior pixels: ${interiorIndices.length}`); + console.log(` Boundary pixels: ${boundaryIndices.length}`); + } + const sparseData = buildSparseData( + shapeMask, + boundaryMask, + new Uint32Array(interiorIndices), + new Uint32Array(boundaryIndices), + width, + height + ); + const startSolve = performance.now(); + const u = solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height); + if (POISSON_CONFIG_OPTIMIZED.measurePerformance) { + console.log(`[Poisson Solve] Time: ${(performance.now() - startSolve).toFixed(2)}ms`); + } + let maxVal = 0; + let finalImageData; + for (let i = 0; i < interiorIndices.length; i++) { + const idx = interiorIndices[i]; + if (u[idx] > maxVal) maxVal = u[idx]; + } + const tempCanvas = document.createElement("canvas"); + tempCanvas.width = width; + tempCanvas.height = height; + const tempCtx = tempCanvas.getContext("2d"); + const tempImg = tempCtx.createImageData(width, height); + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const idx = y * width + x; + const px = idx * 4; + if (!shapeMask[idx]) { + tempImg.data[px] = 255; + tempImg.data[px + 1] = 255; + tempImg.data[px + 2] = 255; + tempImg.data[px + 3] = 0; + } else { + const poissonRatio = u[idx] / maxVal; + let gray = 255 * (1 - poissonRatio); + tempImg.data[px] = gray; + tempImg.data[px + 1] = gray; + tempImg.data[px + 2] = gray; + tempImg.data[px + 3] = 255; + } + } + } + tempCtx.putImageData(tempImg, 0, 0); + ctx.imageSmoothingEnabled = true; + ctx.imageSmoothingQuality = "high"; + ctx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, originalWidth, originalHeight); + const outImg = ctx.getImageData(0, 0, originalWidth, originalHeight); + const origPadX = Math.ceil(originalWidth * paddingSize); + const origPadY = Math.ceil(originalHeight * paddingSize); + const originalCanvas = document.createElement("canvas"); + originalCanvas.width = originalWidth; + originalCanvas.height = originalHeight; + const originalCtx = originalCanvas.getContext("2d"); + originalCtx.drawImage(img, origPadX, origPadY, originalWidth - 2 * origPadX, originalHeight - 2 * origPadY); + const originalData = originalCtx.getImageData(0, 0, originalWidth, originalHeight); + for (let i = 0; i < outImg.data.length; i += 4) { + const a = originalData.data[i + 3]; + const upscaledAlpha = outImg.data[i + 3]; + if (a === 0) { + outImg.data[i] = 255; + outImg.data[i + 1] = 0; + } else { + outImg.data[i] = upscaledAlpha === 0 ? 0 : outImg.data[i]; + outImg.data[i + 1] = a; + } + outImg.data[i + 2] = 255; + outImg.data[i + 3] = 255; + } + ctx.putImageData(outImg, 0, 0); + finalImageData = outImg; + canvas.toBlob((blob) => { + if (!blob) { + reject(new Error("Failed to create PNG blob")); + return; + } + if (POISSON_CONFIG_OPTIMIZED.measurePerformance) { + const totalTime = performance.now() - totalStartTime; + console.log(`[Total Processing Time] ${totalTime.toFixed(2)}ms`); + if (scaleFactor < 1) { + const estimatedFullResTime = totalTime * Math.pow(originalWidth * originalHeight / (width * height), 1.5); + console.log(`[Estimated time at full resolution] ~${estimatedFullResTime.toFixed(0)}ms`); + console.log( + `[Time saved] ~${(estimatedFullResTime - totalTime).toFixed(0)}ms (${Math.round(estimatedFullResTime / totalTime)}\xD7 faster)` + ); + } + } + resolve({ + imageData: finalImageData, + pngBlob: blob + }); + }, "image/png"); + }; + img.onerror = () => reject(new Error("Failed to load image")); + img.src = typeof file === "string" ? file : URL.createObjectURL(file); + }); +} +function buildSparseData(shapeMask, boundaryMask, interiorPixels, boundaryPixels, width, height) { + const pixelCount = interiorPixels.length; + const neighborIndices = new Int32Array(pixelCount * 4); + for (let i = 0; i < pixelCount; i++) { + const idx = interiorPixels[i]; + const x = idx % width; + const y = Math.floor(idx / width); + neighborIndices[i * 4 + 0] = x < width - 1 && shapeMask[idx + 1] ? idx + 1 : -1; + neighborIndices[i * 4 + 1] = x > 0 && shapeMask[idx - 1] ? idx - 1 : -1; + neighborIndices[i * 4 + 2] = y > 0 && shapeMask[idx - width] ? idx - width : -1; + neighborIndices[i * 4 + 3] = y < height - 1 && shapeMask[idx + width] ? idx + width : -1; + } + return { + interiorPixels, + boundaryPixels, + pixelCount, + neighborIndices + }; +} +function solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height) { + const ITERATIONS = POISSON_CONFIG_OPTIMIZED.iterations; + const C = 0.01; + const u = new Float32Array(width * height); + const { interiorPixels, neighborIndices, pixelCount } = sparseData; + const startTime = performance.now(); + const omega = 1.9; + const redPixels = []; + const blackPixels = []; + for (let i = 0; i < pixelCount; i++) { + const idx = interiorPixels[i]; + const x = idx % width; + const y = Math.floor(idx / width); + if ((x + y) % 2 === 0) { + redPixels.push(i); + } else { + blackPixels.push(i); + } + } + for (let iter = 0; iter < ITERATIONS; iter++) { + for (const i of redPixels) { + const idx = interiorPixels[i]; + const eastIdx = neighborIndices[i * 4 + 0]; + const westIdx = neighborIndices[i * 4 + 1]; + const northIdx = neighborIndices[i * 4 + 2]; + const southIdx = neighborIndices[i * 4 + 3]; + let sumN = 0; + if (eastIdx >= 0) sumN += u[eastIdx]; + if (westIdx >= 0) sumN += u[westIdx]; + if (northIdx >= 0) sumN += u[northIdx]; + if (southIdx >= 0) sumN += u[southIdx]; + const newValue = (C + sumN) / 4; + u[idx] = omega * newValue + (1 - omega) * u[idx]; + } + for (const i of blackPixels) { + const idx = interiorPixels[i]; + const eastIdx = neighborIndices[i * 4 + 0]; + const westIdx = neighborIndices[i * 4 + 1]; + const northIdx = neighborIndices[i * 4 + 2]; + const southIdx = neighborIndices[i * 4 + 3]; + let sumN = 0; + if (eastIdx >= 0) sumN += u[eastIdx]; + if (westIdx >= 0) sumN += u[westIdx]; + if (northIdx >= 0) sumN += u[northIdx]; + if (southIdx >= 0) sumN += u[southIdx]; + const newValue = (C + sumN) / 4; + u[idx] = omega * newValue + (1 - omega) * u[idx]; + } + } + const tmp = new Float32Array(width * height); + for (let smooth = 0; smooth < 3; smooth++) { + tmp.set(u); + for (let i = 0; i < pixelCount; i++) { + const idx = interiorPixels[i]; + const eastIdx = neighborIndices[i * 4 + 0]; + const westIdx = neighborIndices[i * 4 + 1]; + const northIdx = neighborIndices[i * 4 + 2]; + const southIdx = neighborIndices[i * 4 + 3]; + let sum = 0; + let count = 0; + if (eastIdx >= 0) { + sum += tmp[eastIdx]; + count++; + } + if (westIdx >= 0) { + sum += tmp[westIdx]; + count++; + } + if (northIdx >= 0) { + sum += tmp[northIdx]; + count++; + } + if (southIdx >= 0) { + sum += tmp[southIdx]; + count++; + } + u[idx] = count > 0 ? (tmp[idx] + sum / count) * 0.5 : tmp[idx]; + } + } + if (POISSON_CONFIG_OPTIMIZED.measurePerformance) { + const elapsed = performance.now() - startTime; + console.log(`[Optimized Poisson Solver (SOR \u03C9=${omega})]`); + console.log(` Working size: ${width}\xD7${height}`); + console.log(` Iterations: ${ITERATIONS}`); + console.log(` Time: ${elapsed.toFixed(2)}ms`); + console.log(` Interior pixels processed: ${pixelCount}`); + console.log(` Speed: ${(ITERATIONS * pixelCount / (elapsed * 1e3)).toFixed(2)} Mpixels/sec`); + } + return u; +} +const GemSmokeShapes = { + none: 0, + circle: 1, + daisy: 2, + diamond: 3, + metaballs: 4 +}; +export { + GemSmokeShapes, + POISSON_CONFIG_OPTIMIZED, + gemSmokeFragmentShader, + gemSmokeMeta, + toProcessedGemSmoke +}; +//# sourceMappingURL=gem-smoke.js.map diff --git a/public/vendor/paper-shaders/shaders/gem-smoke.js.map b/public/vendor/paper-shaders/shaders/gem-smoke.js.map new file mode 100644 index 0000000..cec8dac --- /dev/null +++ b/public/vendor/paper-shaders/shaders/gem-smoke.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/gem-smoke.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport type { ShaderSizingParams, ShaderSizingUniforms } from '../shader-sizing.js';\nimport { rotation2, declarePI } from '../shader-utils.js';\n\nexport const gemSmokeMeta = {\n maxColorCount: 6,\n} as const;\n\n/**\n * Animated color fields placed over uploaded logo shape; gives the illusion of smoky noise\n * behind the glassy shape.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_image (sampler2D): Pre-processed source image texture (R = edge gradient, G = alpha)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colors (vec4[]): Up to 6 smoke colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_colorBack (vec4): Background color in RGBA\n * - u_innerDistortion (float): Power of smoke distortion inside the input shape (0 to 1)\n * - u_outerDistortion (float): Power of smoke distortion outside the input shape (0 to 1)\n * - u_outerGlow (float): Visibility of smoke shape outside the input shape (0 to 1)\n * - u_innerGlow (float): Visibility of smoke shape inside the input shape (0 to 1)\n * - u_colorInner (vec4): Additional color inside the input shape, mixing with smoke (RGBA)\n * - u_offset (float): Vertical offset of smoke inside the shape (-1 to 1)\n * - u_angle (float): Smoke direction in degrees (0 to 360)\n * - u_size (float): Size of smoke shape relative to the image box (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied\n * - v_objectUV (vec2): Normalized UV coordinates with scale, rotation, and offset applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const gemSmokeFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nin mediump vec2 v_imageUV;\nin mediump vec2 v_objectUV;\nin mediump vec2 v_responsiveUV;\nin mediump vec2 v_responsiveBoxGivenSize;\nout vec4 fragColor;\n\n// Image\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\n// Canvas\nuniform vec2 u_resolution;\nuniform float u_time;\n\n// Colors\nuniform vec4 u_colors[${gemSmokeMeta.maxColorCount}];\nuniform float u_colorsCount;\nuniform vec4 u_colorBack;\nuniform vec4 u_colorInner;\n\n// Effect controls\nuniform float u_innerDistortion;\nuniform float u_outerDistortion;\nuniform float u_outerGlow;\nuniform float u_innerGlow;\nuniform float u_offset;\nuniform float u_angle;\nuniform float u_size;\n\n// Shape controls\nuniform float u_shape;\nuniform bool u_isImage;\n\n${ declarePI }\n${ rotation2 }\n\n// 9x9 Gaussian blur on R and G channels\nvec2 gaussBlur9x9RG(sampler2D tex, vec2 uv, vec2 dudx, vec2 dudy, float radius) {\n vec2 texel = 1.0 / vec2(textureSize(tex, 0));\n vec2 r = max(radius, 0.0) * texel;\n // Pascal's row 8: sum = 256, 2D norm = 65536\n const float k[9] = float[9](1.0, 8.0, 28.0, 56.0, 70.0, 56.0, 28.0, 8.0, 1.0);\n vec2 sum = vec2(0.0);\n\n for (int j = -4; j <= 4; ++j) {\n float wy = k[j + 4];\n for (int i = -4; i <= 4; ++i) {\n float w = k[i + 4] * wy;\n vec2 off = vec2(float(i) * r.x, float(j) * r.y);\n sum += w * texture(tex, uv + off).rg;\n }\n }\n\n return sum / 65536.0;\n}\n\nfloat sst(float a, float b, float x) {\n return smoothstep(a, b, x);\n}\n\nvoid main() {\n float time = u_time;\n\n float roundness = 0.;\n float imgAlpha = 0.;\n\n if (u_isImage == true) {\n // Image sampling (UV scaled inward to account for padding)\n vec2 imageUV = v_imageUV;\n imageUV -= .5;\n imageUV *= .95;\n imageUV += .5;\n\n vec2 dudx = dFdx(v_imageUV);\n vec2 dudy = dFdy(v_imageUV);\n\n // Blurred image: x = roundness, y = alpha\n vec2 blurred = gaussBlur9x9RG(u_image, imageUV, dudx, dudy, 10.);\n roundness = 1. - blurred.x;\n vec2 texelA = 1.0 / vec2(textureSize(u_image, 0));\n const float k3[3] = float[3](1.0, 2.0, 1.0);\n for (int j = -1; j <= 1; ++j) {\n for (int i = -1; i <= 1; ++i) {\n imgAlpha += k3[i + 1] * k3[j + 1] * texture(u_image, imageUV + vec2(float(i) * texelA.x, float(j) * texelA.y)).g;\n }\n }\n imgAlpha /= 16.0;\n } else {\n vec2 uv = v_objectUV + .5;\n uv.y = 1. - uv.y;\n float edge = 0.;\n\n if (u_shape < 1.) {\n // full-fill on canvas\n vec2 borderUV = v_responsiveUV + .5;\n vec2 mask = min(borderUV, 1. - borderUV);\n vec2 pixel_thickness = min(250. / v_responsiveBoxGivenSize, vec2(.5));\n float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);\n float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);\n maskX = pow(maskX, .25);\n maskY = pow(maskY, .25);\n edge = clamp(1. - maskX * maskY, 0., 1.);\n } else if (u_shape < 2.) {\n // circle\n vec2 shapeUV = uv - .5;\n shapeUV *= .67;\n edge = pow(clamp(3. * length(shapeUV), 0., 1.), 18.);\n } else if (u_shape < 3.) {\n // daisy\n vec2 shapeUV = uv - .5;\n shapeUV *= 1.68;\n\n float r = length(shapeUV) * 2.;\n float a = atan(shapeUV.y, shapeUV.x) + .2;\n r *= (1. + .05 * sin(3. * a + 2. * time));\n float f = abs(cos(a * 3.));\n edge = smoothstep(f, f + .7, r);\n edge *= edge;\n } else if (u_shape < 4.) {\n // diamond\n vec2 shapeUV = uv - .5;\n shapeUV = rotate(shapeUV, .25 * PI);\n shapeUV *= 1.42;\n shapeUV += .5;\n vec2 mask = min(shapeUV, 1. - shapeUV);\n vec2 pixel_thickness = vec2(.15);\n float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);\n float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);\n maskX = pow(maskX, .25);\n maskY = pow(maskY, .25);\n edge = clamp(1. - maskX * maskY, 0., 1.);\n } else if (u_shape < 5.) {\n // metaballs\n vec2 shapeUV = uv - .5;\n shapeUV *= 1.3;\n edge = 0.;\n for (int i = 0; i < 5; i++) {\n float fi = float(i);\n float speed = 1.5 + 2./3. * sin(fi * 12.345);\n float angle = -fi * 1.5;\n vec2 dir1 = vec2(cos(angle), sin(angle));\n vec2 dir2 = vec2(cos(angle + 1.57), sin(angle + 1.));\n vec2 traj = .4 * (dir1 * sin(time * speed + fi * 1.23) + dir2 * cos(time * (speed * 0.7) + fi * 2.17));\n float d = length(shapeUV + traj);\n edge += pow(1.0 - clamp(d, 0.0, 1.0), 4.0);\n }\n edge = 1. - smoothstep(.65, .9, edge);\n edge = pow(edge, 4.);\n }\n\n imgAlpha = 1. - smoothstep(.9 - 2. * fwidth(edge), .9, edge);\n roundness = 1. - edge;\n }\n\n// Smoke UV setup\n vec2 smokeUV = v_objectUV;\n smokeUV = rotate(smokeUV, u_angle * PI / 180.);\n smokeUV *= mix(4., 1., u_size);\n\n // Two swirl paths: inner (shape-masked) and outer (free), each with independent distortion\n vec2 innerUV = smokeUV;\n vec2 outerUV = smokeUV;\n\n // Vertical displacement \u2014 applied independently to inner and outer\n innerUV.y += u_innerDistortion * (1. - sst(0., 1., length(.4 * innerUV)));\n innerUV.y -= .4 * u_innerDistortion;\n innerUV.y += .7 * u_offset * roundness;\n\n outerUV.y += u_outerDistortion * (1. - sst(0., 1., length(.4 * outerUV)));\n outerUV.y -= .4 * u_outerDistortion;\n\n float innerSwirl = u_innerDistortion * roundness;\n float outerSwirl = u_outerDistortion;\n\n for (int i = 1; i < 5; i++) {\n float fi = float(i);\n\n float stretchIn = max(length(dFdx(innerUV)), length(dFdy(innerUV)));\n float dampenIn = 1. / (1. + stretchIn * 8.);\n float sIn = innerSwirl * dampenIn;\n innerUV.x += sIn / fi * cos(time + fi * 2.9 * innerUV.y);\n innerUV.y += sIn / fi * cos(time + fi * 1.5 * innerUV.x);\n\n float stretchOut = max(length(dFdx(outerUV)), length(dFdy(outerUV)));\n float dampenOut = 1. / (1. + stretchOut * 8.);\n float sOut = outerSwirl * dampenOut;\n outerUV.x += sOut / fi * cos(time + fi * 2.9 * outerUV.y);\n outerUV.y += sOut / fi * cos(time + fi * 1.5 * outerUV.x);\n }\n\n // Smoke shapes from swirl fields\n float innerShape = exp(-1.5 * dot(innerUV, innerUV));\n float outerShape = exp(-1.5 * dot(outerUV, outerUV));\n\n // Visibility masks\n float outerMask = pow(u_outerGlow, 2.) * (1. - imgAlpha);\n float innerMask = (.01 + .99 * u_innerGlow) * imgAlpha;\n\n innerShape *= innerMask;\n outerShape *= outerMask;\n\n // Color gradient\n float mixer = (innerShape + outerShape) * u_colorsCount;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n\n float smokeMask = 0.;\n for (int i = 1; i < ${gemSmokeMeta.maxColorCount + 1}; i++) {\n if (i > int(u_colorsCount)) break;\n\n float m = sst(0., 1., clamp(mixer - float(i - 1), 0., 1.));\n if (i == 1) smokeMask = m;\n\n vec4 c = u_colors[i - 1];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n // Compositing (premultiplied alpha, front-to-back)\n vec3 color = gradient.rgb * smokeMask;\n float opacity = gradient.a * smokeMask;\n\n float innerOpacity = u_colorInner.a * imgAlpha;\n vec3 innerColor = u_colorInner.rgb * innerOpacity;\n color += innerColor * (1.0 - opacity);\n opacity += innerOpacity * (1.0 - opacity);\n\n vec3 backColor = u_colorBack.rgb * u_colorBack.a;\n color += backColor * (1.0 - opacity);\n opacity += u_colorBack.a * (1.0 - opacity);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\n// Configuration for Poisson solver\nexport const POISSON_CONFIG_OPTIMIZED = {\n measurePerformance: false, // Set to true to see performance metrics\n workingSize: 512, // Size to solve Poisson at (will upscale to original size)\n iterations: 32, // SOR converges ~2-20x faster than standard Gauss-Seidel\n};\n\n// Precomputed pixel data for sparse processing\ninterface SparsePixelData {\n interiorPixels: Uint32Array; // Indices of interior pixels\n boundaryPixels: Uint32Array; // Indices of boundary pixels\n pixelCount: number;\n // Neighbor indices for each interior pixel (4 neighbors per pixel)\n // Layout: [east, west, north, south] for each pixel\n neighborIndices: Int32Array;\n}\n\nexport function toProcessedGemSmoke(file: File | string): Promise<{ imageData: ImageData; pngBlob: Blob }> {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n const isBlob = typeof file === 'string' && file.startsWith('blob:');\n\n return new Promise((resolve, reject) => {\n if (!file || !ctx) {\n reject(new Error('Invalid file or canvas context'));\n return;\n }\n\n const blobContentTypePromise = isBlob && fetch(file).then((res) => res.headers.get('Content-Type'));\n const img = new Image();\n img.crossOrigin = 'anonymous';\n const totalStartTime = performance.now();\n\n img.onload = async () => {\n // Force SVG to load at a high fidelity size if it's an SVG\n let isSVG;\n\n const blobContentType = await blobContentTypePromise;\n\n if (blobContentType) {\n isSVG = blobContentType === 'image/svg+xml';\n } else if (typeof file === 'string') {\n isSVG = file.endsWith('.svg') || file.startsWith('data:image/svg+xml');\n } else {\n isSVG = file.type === 'image/svg+xml';\n }\n\n let originalWidth = img.width || img.naturalWidth;\n let originalHeight = img.height || img.naturalHeight;\n\n if (isSVG) {\n // Scale SVG to max dimension while preserving aspect ratio\n const svgMaxSize = 4096;\n const aspectRatio = originalWidth / originalHeight;\n\n if (originalWidth > originalHeight) {\n originalWidth = svgMaxSize;\n originalHeight = svgMaxSize / aspectRatio;\n } else {\n originalHeight = svgMaxSize;\n originalWidth = svgMaxSize * aspectRatio;\n }\n\n img.width = originalWidth;\n img.height = originalHeight;\n }\n\n // Always scale to working resolution for consistency\n const minDimension = Math.min(originalWidth, originalHeight);\n const targetSize = POISSON_CONFIG_OPTIMIZED.workingSize;\n\n // Calculate scale to fit within workingSize\n const scaleFactor = targetSize / minDimension;\n const width = Math.round(originalWidth * scaleFactor);\n const height = Math.round(originalHeight * scaleFactor);\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Processing Mode]`);\n console.log(` Original: ${originalWidth}\u00D7${originalHeight}`);\n console.log(` Working: ${width}\u00D7${height} (${(scaleFactor * 100).toFixed(1)}% scale)`);\n if (scaleFactor < 1) {\n console.log(` Speedup: ~${Math.round(1 / (scaleFactor * scaleFactor))}\u00D7`);\n }\n }\n\n canvas.width = originalWidth;\n canvas.height = originalHeight;\n\n const paddingSize = 0.025;\n const padX = Math.ceil(width * paddingSize);\n const padY = Math.ceil(height * paddingSize);\n const imgW = width - 2 * padX;\n const imgH = height - 2 * padY;\n\n // \u2500\u2500 1. Shape canvas (working res): image centered with padding \u2500\u2500\n const shapeCanvas = document.createElement('canvas');\n shapeCanvas.width = width;\n shapeCanvas.height = height;\n\n const shapeCtx = shapeCanvas.getContext('2d')!;\n shapeCtx.drawImage(img, padX, padY, imgW, imgH);\n\n // 1) Build optimized masks using TypedArrays\n const startMask = performance.now();\n\n const shapeImageData = shapeCtx.getImageData(0, 0, width, height);\n const data = shapeImageData.data;\n\n // Use Uint8Array for masks (1 byte per pixel vs 8+ bytes for boolean array)\n const shapeMask = new Uint8Array(width * height);\n const boundaryMask = new Uint8Array(width * height);\n\n // First pass: identify shape pixels\n let shapePixelCount = 0;\n for (let i = 0, idx = 0; i < data.length; i += 4, idx++) {\n const a = data[i + 3];\n const isShape = a === 0 ? 0 : 1;\n shapeMask[idx] = isShape;\n shapePixelCount += isShape;\n }\n\n // 2) Optimized boundary detection using sparse approach\n // Only check shape pixels, not all pixels\n const boundaryIndices: number[] = [];\n const interiorIndices: number[] = [];\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n if (!shapeMask[idx]) continue;\n\n // Check if pixel is on boundary (optimized: early exit)\n let isBoundary = false;\n\n // Check 4-connected neighbors first (most common case)\n if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {\n isBoundary = true;\n } else {\n // Check all 8 neighbors (including diagonals) for comprehensive boundary detection\n isBoundary =\n !shapeMask[idx - 1] || // left\n !shapeMask[idx + 1] || // right\n !shapeMask[idx - width] || // top\n !shapeMask[idx + width] || // bottom\n !shapeMask[idx - width - 1] || // top-left\n !shapeMask[idx - width + 1] || // top-right\n !shapeMask[idx + width - 1] || // bottom-left\n !shapeMask[idx + width + 1]; // bottom-right\n }\n\n if (isBoundary) {\n boundaryMask[idx] = 1;\n boundaryIndices.push(idx);\n } else {\n interiorIndices.push(idx);\n }\n }\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Mask Building] Time: ${(performance.now() - startMask).toFixed(2)}ms`);\n console.log(\n ` Shape pixels: ${shapePixelCount} / ${width * height} (${((shapePixelCount / (width * height)) * 100).toFixed(1)}%)`\n );\n console.log(` Interior pixels: ${interiorIndices.length}`);\n console.log(` Boundary pixels: ${boundaryIndices.length}`);\n }\n\n // 3) Precompute sparse data structure for solver\n const sparseData = buildSparseData(\n shapeMask,\n boundaryMask,\n new Uint32Array(interiorIndices),\n new Uint32Array(boundaryIndices),\n width,\n height\n );\n\n // 4) Solve Poisson equation with optimized sparse solver\n const startSolve = performance.now();\n const u = solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height);\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Poisson Solve] Time: ${(performance.now() - startSolve).toFixed(2)}ms`);\n }\n\n // 5) Generate output image\n let maxVal = 0;\n let finalImageData: ImageData;\n\n // Only check shape pixels for max value\n for (let i = 0; i < interiorIndices.length; i++) {\n const idx = interiorIndices[i]!;\n if (u[idx]! > maxVal) maxVal = u[idx]!;\n }\n\n // Create roundness image at working resolution\n const tempCanvas = document.createElement('canvas');\n tempCanvas.width = width;\n tempCanvas.height = height;\n const tempCtx = tempCanvas.getContext('2d')!;\n\n const tempImg = tempCtx.createImageData(width, height);\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const px = idx * 4;\n\n if (!shapeMask[idx]) {\n tempImg.data[px] = 255;\n tempImg.data[px + 1] = 255;\n tempImg.data[px + 2] = 255;\n tempImg.data[px + 3] = 0; // Alpha = 0 for background\n } else {\n const poissonRatio = u[idx]! / maxVal;\n let gray = 255 * (1 - poissonRatio);\n tempImg.data[px] = gray;\n tempImg.data[px + 1] = gray;\n tempImg.data[px + 2] = gray;\n tempImg.data[px + 3] = 255; // Alpha = 255 for shape\n }\n }\n }\n tempCtx.putImageData(tempImg, 0, 0);\n\n // Upscale to original resolution with smooth interpolation\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, originalWidth, originalHeight);\n\n // Now get the upscaled image data for final output\n const outImg = ctx.getImageData(0, 0, originalWidth, originalHeight);\n\n // Re-apply edges from original resolution with anti-aliasing\n // This ensures edges are pixel-perfect while roundness is smooth\n const origPadX = Math.ceil(originalWidth * paddingSize);\n const origPadY = Math.ceil(originalHeight * paddingSize);\n const originalCanvas = document.createElement('canvas');\n originalCanvas.width = originalWidth;\n originalCanvas.height = originalHeight;\n const originalCtx = originalCanvas.getContext('2d')!;\n originalCtx.drawImage(img, origPadX, origPadY, originalWidth - 2 * origPadX, originalHeight - 2 * origPadY);\n const originalData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);\n\n // Process each pixel: Red channel = roundness, Alpha channel = original alpha\n for (let i = 0; i < outImg.data.length; i += 4) {\n const a = originalData.data[i + 3]!;\n // Use only alpha to determine background vs shape\n const upscaledAlpha = outImg.data[i + 3]!;\n if (a === 0) {\n // Background pixel\n outImg.data[i] = 255;\n outImg.data[i + 1] = 0;\n } else {\n // Red channel carries the roundness\n // Check if upscale missed this pixel by looking at alpha channel\n // If upscaled alpha is 0, the low-res version thought this was background\n outImg.data[i] = upscaledAlpha === 0 ? 0 : outImg.data[i]!; // roundness or 0\n outImg.data[i + 1] = a; // original alpha\n }\n\n // Unused channels fixed\n outImg.data[i + 2] = 255;\n outImg.data[i + 3] = 255;\n }\n\n ctx.putImageData(outImg, 0, 0);\n finalImageData = outImg;\n canvas.toBlob((blob) => {\n if (!blob) {\n reject(new Error('Failed to create PNG blob'));\n return;\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n const totalTime = performance.now() - totalStartTime;\n console.log(`[Total Processing Time] ${totalTime.toFixed(2)}ms`);\n if (scaleFactor < 1) {\n const estimatedFullResTime = totalTime * Math.pow((originalWidth * originalHeight) / (width * height), 1.5);\n console.log(`[Estimated time at full resolution] ~${estimatedFullResTime.toFixed(0)}ms`);\n console.log(\n `[Time saved] ~${(estimatedFullResTime - totalTime).toFixed(0)}ms (${Math.round(estimatedFullResTime / totalTime)}\u00D7 faster)`\n );\n }\n }\n\n resolve({\n imageData: finalImageData,\n pngBlob: blob,\n });\n }, 'image/png');\n };\n\n img.onerror = () => reject(new Error('Failed to load image'));\n img.src = typeof file === 'string' ? file : URL.createObjectURL(file);\n });\n}\n\nfunction buildSparseData(\n shapeMask: Uint8Array,\n boundaryMask: Uint8Array,\n interiorPixels: Uint32Array,\n boundaryPixels: Uint32Array,\n width: number,\n height: number\n): SparsePixelData {\n const pixelCount = interiorPixels.length;\n\n // Build neighbor indices for sparse processing\n // For each interior pixel, store indices of its 4 neighbors\n // Use -1 for out-of-bounds or non-shape neighbors\n const neighborIndices = new Int32Array(pixelCount * 4);\n\n for (let i = 0; i < pixelCount; i++) {\n const idx = interiorPixels[i]!;\n const x = idx % width;\n const y = Math.floor(idx / width);\n\n // East neighbor\n neighborIndices[i * 4 + 0] = x < width - 1 && shapeMask[idx + 1] ? idx + 1 : -1;\n // West neighbor\n neighborIndices[i * 4 + 1] = x > 0 && shapeMask[idx - 1] ? idx - 1 : -1;\n // North neighbor\n neighborIndices[i * 4 + 2] = y > 0 && shapeMask[idx - width] ? idx - width : -1;\n // South neighbor\n neighborIndices[i * 4 + 3] = y < height - 1 && shapeMask[idx + width] ? idx + width : -1;\n }\n\n return {\n interiorPixels,\n boundaryPixels,\n pixelCount,\n neighborIndices,\n };\n}\n\nfunction solvePoissonSparse(\n sparseData: SparsePixelData,\n shapeMask: Uint8Array,\n boundaryMask: Uint8Array,\n width: number,\n height: number\n): Float32Array {\n // This controls how smooth the falloff roundness will be and extend into the shape\n const ITERATIONS = POISSON_CONFIG_OPTIMIZED.iterations;\n\n // Keep C constant - only iterations control roundness spread\n const C = 0.01;\n\n const u = new Float32Array(width * height);\n const { interiorPixels, neighborIndices, pixelCount } = sparseData;\n\n // Performance tracking\n const startTime = performance.now();\n\n // Red-Black SOR for better symmetry with fewer iterations\n // omega between 1.8-1.95 typically gives best convergence for Poisson\n const omega = 1.9;\n\n // Pre-classify pixels as red or black for efficient processing\n const redPixels: number[] = [];\n const blackPixels: number[] = [];\n\n for (let i = 0; i < pixelCount; i++) {\n const idx = interiorPixels[i]!;\n const x = idx % width;\n const y = Math.floor(idx / width);\n\n if ((x + y) % 2 === 0) {\n redPixels.push(i);\n } else {\n blackPixels.push(i);\n }\n }\n\n for (let iter = 0; iter < ITERATIONS; iter++) {\n // Red pass: update red pixels\n for (const i of redPixels) {\n const idx = interiorPixels[i]!;\n\n // Get precomputed neighbor indices\n const eastIdx = neighborIndices[i * 4 + 0]!;\n const westIdx = neighborIndices[i * 4 + 1]!;\n const northIdx = neighborIndices[i * 4 + 2]!;\n const southIdx = neighborIndices[i * 4 + 3]!;\n\n // Sum neighbors (use 0 for out-of-bounds)\n let sumN = 0;\n if (eastIdx >= 0) sumN += u[eastIdx]!;\n if (westIdx >= 0) sumN += u[westIdx]!;\n if (northIdx >= 0) sumN += u[northIdx]!;\n if (southIdx >= 0) sumN += u[southIdx]!;\n\n // SOR update: blend new value with old value\n const newValue = (C + sumN) / 4;\n u[idx] = omega * newValue + (1 - omega) * u[idx]!;\n }\n\n // Black pass: update black pixels\n for (const i of blackPixels) {\n const idx = interiorPixels[i]!;\n\n // Get precomputed neighbor indices\n const eastIdx = neighborIndices[i * 4 + 0]!;\n const westIdx = neighborIndices[i * 4 + 1]!;\n const northIdx = neighborIndices[i * 4 + 2]!;\n const southIdx = neighborIndices[i * 4 + 3]!;\n\n // Sum neighbors (use 0 for out-of-bounds)\n let sumN = 0;\n if (eastIdx >= 0) sumN += u[eastIdx]!;\n if (westIdx >= 0) sumN += u[westIdx]!;\n if (northIdx >= 0) sumN += u[northIdx]!;\n if (southIdx >= 0) sumN += u[southIdx]!;\n\n // SOR update: blend new value with old value\n const newValue = (C + sumN) / 4;\n u[idx] = omega * newValue + (1 - omega) * u[idx]!;\n }\n }\n\n // Jacobi smoothing passes to remove Red-Black checkerboard artifacts\n const tmp = new Float32Array(width * height);\n for (let smooth = 0; smooth < 3; smooth++) {\n tmp.set(u);\n for (let i = 0; i < pixelCount; i++) {\n const idx = interiorPixels[i]!;\n const eastIdx = neighborIndices[i * 4 + 0]!;\n const westIdx = neighborIndices[i * 4 + 1]!;\n const northIdx = neighborIndices[i * 4 + 2]!;\n const southIdx = neighborIndices[i * 4 + 3]!;\n\n let sum = 0;\n let count = 0;\n if (eastIdx >= 0) { sum += tmp[eastIdx]!; count++; }\n if (westIdx >= 0) { sum += tmp[westIdx]!; count++; }\n if (northIdx >= 0) { sum += tmp[northIdx]!; count++; }\n if (southIdx >= 0) { sum += tmp[southIdx]!; count++; }\n\n u[idx] = count > 0 ? (tmp[idx]! + sum / count) * 0.5 : tmp[idx]!;\n }\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n const elapsed = performance.now() - startTime;\n\n console.log(`[Optimized Poisson Solver (SOR \u03C9=${omega})]`);\n console.log(` Working size: ${width}\u00D7${height}`);\n console.log(` Iterations: ${ITERATIONS}`);\n console.log(` Time: ${elapsed.toFixed(2)}ms`);\n console.log(` Interior pixels processed: ${pixelCount}`);\n console.log(` Speed: ${((ITERATIONS * pixelCount) / (elapsed * 1000)).toFixed(2)} Mpixels/sec`);\n }\n\n return u;\n}\n\nexport interface GemSmokeUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_image: HTMLImageElement | string | undefined;\n u_innerDistortion: number;\n u_outerDistortion: number;\n u_outerGlow: number;\n u_innerGlow: number;\n u_colorInner: [number, number, number, number];\n u_offset: number;\n u_angle: number;\n u_size: number;\n u_shape: (typeof GemSmokeShapes)[GemSmokeShape];\n u_isImage: boolean;\n}\n\nexport interface GemSmokeParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n colorBack?: string;\n image?: HTMLImageElement | string | undefined;\n innerDistortion?: number;\n outerDistortion?: number;\n outerGlow?: number;\n innerGlow?: number;\n colorInner?: string;\n offset?: number;\n angle?: number;\n size?: number;\n shape?: GemSmokeShape;\n}\n\nexport const GemSmokeShapes = {\n none: 0,\n circle: 1,\n daisy: 2,\n diamond: 3,\n metaballs: 4,\n} as const;\n\nexport type GemSmokeShape = keyof typeof GemSmokeShapes;\n"], + "mappings": "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", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/god-rays.d.ts b/public/vendor/paper-shaders/shaders/god-rays.d.ts new file mode 100644 index 0000000..860ffd5 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/god-rays.d.ts @@ -0,0 +1,67 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const godRaysMeta: { + readonly maxColorCount: 5; +}; +/** + * Animated rays of light radiating from the center, blended with up to 5 colors. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_colorBack (vec4): Background color in RGBA + * - u_colorBloom (vec4): Color overlay blended with the rays in RGBA + * - u_colors (vec4[]): Up to 5 ray colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_bloom (float): Strength of the bloom/overlay effect, 0 = alpha blend, 1 = additive blend (0 to 1) + * - u_intensity (float): Visibility/strength of the rays (0 to 1) + * - u_density (float): The number of rays (0 to 1) + * - u_spotty (float): The length of the rays, higher = more spots/shorter rays (0 to 1) + * - u_midSize (float): Size of the circular glow shape in the center (0 to 1) + * - u_midIntensity (float): Brightness/intensity of the central glow (0 to 1) + * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture + * + * Vertex shader outputs (used in fragment shader): + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied + * + * The rays are adjustable by size, density, brightness and center glow. + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const godRaysFragmentShader: string; +export interface GodRaysUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colorBloom: [number, number, number, number]; + u_colors: vec4[]; + u_colorsCount: number; + u_spotty: number; + u_midSize: number; + u_midIntensity: number; + u_density: number; + u_intensity: number; + u_bloom: number; + u_noiseTexture?: HTMLImageElement; +} +export interface GodRaysParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colorBloom?: string; + colors?: string[]; + spotty?: number; + midSize?: number; + midIntensity?: number; + density?: number; + intensity?: number; + bloom?: number; +} diff --git a/public/vendor/paper-shaders/shaders/god-rays.js b/public/vendor/paper-shaders/shaders/god-rays.js new file mode 100644 index 0000000..a4c3891 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/god-rays.js @@ -0,0 +1,133 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, rotation2, textureRandomizerR, colorBandingFix, proceduralHash11 } from "../shader-utils.js"; +const godRaysMeta = { + maxColorCount: 5 +}; +const godRaysFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform sampler2D u_noiseTexture; + +uniform vec4 u_colorBack; +uniform vec4 u_colorBloom; +uniform vec4 u_colors[${godRaysMeta.maxColorCount}]; +uniform float u_colorsCount; + +uniform float u_density; +uniform float u_spotty; +uniform float u_midSize; +uniform float u_midIntensity; +uniform float u_intensity; +uniform float u_bloom; + +in vec2 v_objectUV; + +out vec4 fragColor; + +${declarePI} +${rotation2} +${textureRandomizerR} +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = randomR(i); + float b = randomR(i + vec2(1.0, 0.0)); + float c = randomR(i + vec2(0.0, 1.0)); + float d = randomR(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} + +${proceduralHash11} + +float raysShape(vec2 uv, float r, float freq, float intensity, float radius) { + float a = atan(uv.y, uv.x); + vec2 left = vec2(a * freq, r); + vec2 right = vec2(fract(a / TWO_PI) * TWO_PI * freq, r); + float n_left = pow(valueNoise(left), intensity); + float n_right = pow(valueNoise(right), intensity); + float shape = mix(n_right, n_left, smoothstep(-.15, .15, uv.x)); + return shape; +} + +void main() { + vec2 shape_uv = v_objectUV; + + float t = .2 * u_time; + + float radius = length(shape_uv); + float spots = 6.5 * abs(u_spotty); + + float intensity = 4. - 3. * clamp(u_intensity, 0., 1.); + + float delta = 1. - smoothstep(0., 1., radius); + + float midSize = 10. * abs(u_midSize); + float ms_lo = 0.02 * midSize; + float ms_hi = max(midSize, 1e-6); + float middleShape = pow(u_midIntensity, 0.3) * (1. - smoothstep(ms_lo, ms_hi, 3.0 * radius)); + middleShape = pow(middleShape, 5.0); + + vec3 accumColor = vec3(0.0); + float accumAlpha = 0.0; + + for (int i = 0; i < ${godRaysMeta.maxColorCount}; i++) { + if (i >= int(u_colorsCount)) break; + + vec2 rotatedUV = rotate(shape_uv, float(i) + 1.0); + + float r1 = radius * (1.0 + 0.4 * float(i)) - 3.0 * t; + float r2 = 0.5 * radius * (1.0 + spots) - 2.0 * t; + float density = 6. * u_density + step(.5, u_density) * pow(4.5 * (u_density - .5), 4.); + float f = mix(1.0, 3.0 + 0.5 * float(i), hash11(float(i) * 15.)) * density; + + float ray = raysShape(rotatedUV, r1, 5.0 * f, intensity, radius); + ray *= raysShape(rotatedUV, r2, 4.0 * f, intensity, radius); + ray += (1. + 4. * ray) * middleShape; + ray = clamp(ray, 0.0, 1.0); + + float srcAlpha = u_colors[i].a * ray; + vec3 srcColor = u_colors[i].rgb * srcAlpha; + + vec3 alphaBlendColor = accumColor + (1.0 - accumAlpha) * srcColor; + float alphaBlendAlpha = accumAlpha + (1.0 - accumAlpha) * srcAlpha; + + vec3 addBlendColor = accumColor + srcColor; + float addBlendAlpha = accumAlpha + srcAlpha; + + accumColor = mix(alphaBlendColor, addBlendColor, u_bloom); + accumAlpha = mix(alphaBlendAlpha, addBlendAlpha, u_bloom); + } + + float overlayAlpha = u_colorBloom.a; + vec3 overlayColor = u_colorBloom.rgb * overlayAlpha; + + vec3 colorWithOverlay = accumColor + accumAlpha * overlayColor; + accumColor = mix(accumColor, colorWithOverlay, u_bloom); + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + + vec3 color = accumColor + (1. - accumAlpha) * bgColor; + float opacity = accumAlpha + (1. - accumAlpha) * u_colorBack.a; + color = clamp(color, 0., 1.); + opacity = clamp(opacity, 0., 1.); + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +export { + godRaysFragmentShader, + godRaysMeta +}; +//# sourceMappingURL=god-rays.js.map diff --git a/public/vendor/paper-shaders/shaders/god-rays.js.map b/public/vendor/paper-shaders/shaders/god-rays.js.map new file mode 100644 index 0000000..a6a5bea --- /dev/null +++ b/public/vendor/paper-shaders/shaders/god-rays.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/god-rays.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, textureRandomizerR, colorBandingFix, proceduralHash11 } from '../shader-utils.js';\n\nexport const godRaysMeta = {\n maxColorCount: 5,\n} as const;\n\n/**\n * Animated rays of light radiating from the center, blended with up to 5 colors.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colorBloom (vec4): Color overlay blended with the rays in RGBA\n * - u_colors (vec4[]): Up to 5 ray colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_bloom (float): Strength of the bloom/overlay effect, 0 = alpha blend, 1 = additive blend (0 to 1)\n * - u_intensity (float): Visibility/strength of the rays (0 to 1)\n * - u_density (float): The number of rays (0 to 1)\n * - u_spotty (float): The length of the rays, higher = more spots/shorter rays (0 to 1)\n * - u_midSize (float): Size of the circular glow shape in the center (0 to 1)\n * - u_midIntensity (float): Brightness/intensity of the central glow (0 to 1)\n * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * The rays are adjustable by size, density, brightness and center glow.\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const godRaysFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorBloom;\nuniform vec4 u_colors[${ godRaysMeta.maxColorCount }];\nuniform float u_colorsCount;\n\nuniform float u_density;\nuniform float u_spotty;\nuniform float u_midSize;\nuniform float u_midIntensity;\nuniform float u_intensity;\nuniform float u_bloom;\n\nin vec2 v_objectUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\n${ textureRandomizerR }\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomR(i);\n float b = randomR(i + vec2(1.0, 0.0));\n float c = randomR(i + vec2(0.0, 1.0));\n float d = randomR(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\n${ proceduralHash11 }\n\nfloat raysShape(vec2 uv, float r, float freq, float intensity, float radius) {\n float a = atan(uv.y, uv.x);\n vec2 left = vec2(a * freq, r);\n vec2 right = vec2(fract(a / TWO_PI) * TWO_PI * freq, r);\n float n_left = pow(valueNoise(left), intensity);\n float n_right = pow(valueNoise(right), intensity);\n float shape = mix(n_right, n_left, smoothstep(-.15, .15, uv.x));\n return shape;\n}\n\nvoid main() {\n vec2 shape_uv = v_objectUV;\n\n float t = .2 * u_time;\n\n float radius = length(shape_uv);\n float spots = 6.5 * abs(u_spotty);\n\n float intensity = 4. - 3. * clamp(u_intensity, 0., 1.);\n\n float delta = 1. - smoothstep(0., 1., radius);\n\n float midSize = 10. * abs(u_midSize);\n float ms_lo = 0.02 * midSize;\n float ms_hi = max(midSize, 1e-6);\n float middleShape = pow(u_midIntensity, 0.3) * (1. - smoothstep(ms_lo, ms_hi, 3.0 * radius));\n middleShape = pow(middleShape, 5.0);\n\n vec3 accumColor = vec3(0.0);\n float accumAlpha = 0.0;\n\n for (int i = 0; i < ${ godRaysMeta.maxColorCount }; i++) {\n if (i >= int(u_colorsCount)) break;\n\n vec2 rotatedUV = rotate(shape_uv, float(i) + 1.0);\n\n float r1 = radius * (1.0 + 0.4 * float(i)) - 3.0 * t;\n float r2 = 0.5 * radius * (1.0 + spots) - 2.0 * t;\n float density = 6. * u_density + step(.5, u_density) * pow(4.5 * (u_density - .5), 4.);\n float f = mix(1.0, 3.0 + 0.5 * float(i), hash11(float(i) * 15.)) * density;\n\n float ray = raysShape(rotatedUV, r1, 5.0 * f, intensity, radius);\n ray *= raysShape(rotatedUV, r2, 4.0 * f, intensity, radius);\n ray += (1. + 4. * ray) * middleShape;\n ray = clamp(ray, 0.0, 1.0);\n\n float srcAlpha = u_colors[i].a * ray;\n vec3 srcColor = u_colors[i].rgb * srcAlpha;\n\n vec3 alphaBlendColor = accumColor + (1.0 - accumAlpha) * srcColor;\n float alphaBlendAlpha = accumAlpha + (1.0 - accumAlpha) * srcAlpha;\n\n vec3 addBlendColor = accumColor + srcColor;\n float addBlendAlpha = accumAlpha + srcAlpha;\n\n accumColor = mix(alphaBlendColor, addBlendColor, u_bloom);\n accumAlpha = mix(alphaBlendAlpha, addBlendAlpha, u_bloom);\n }\n\n float overlayAlpha = u_colorBloom.a;\n vec3 overlayColor = u_colorBloom.rgb * overlayAlpha;\n\n vec3 colorWithOverlay = accumColor + accumAlpha * overlayColor;\n accumColor = mix(accumColor, colorWithOverlay, u_bloom);\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n\n vec3 color = accumColor + (1. - accumAlpha) * bgColor;\n float opacity = accumAlpha + (1. - accumAlpha) * u_colorBack.a;\n color = clamp(color, 0., 1.);\n opacity = clamp(opacity, 0., 1.);\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface GodRaysUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colorBloom: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_spotty: number;\n u_midSize: number;\n u_midIntensity: number;\n u_density: number;\n u_intensity: number;\n u_bloom: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface GodRaysParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colorBloom?: string;\n colors?: string[];\n spotty?: number;\n midSize?: number;\n midIntensity?: number;\n density?: number;\n intensity?: number;\n bloom?: number;\n}\n"], + "mappings": ";;;;;AAEA,eAAmE;AACnE,SAAS,WAAW,WAAW,oBAAoB,iBAAiB,wBAAwB;AAErF,MAAM,cAAc;AAAA,EACzB,eAAe;AACjB;AAwCO,MAAM,wBAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBASpB,YAAY,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAchD,SAAU;AAAA,EACV,SAAU;AAAA,EACV,kBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcnB,gBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAiCK,YAAY,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAyC9C,eAAgB;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/grain-gradient.d.ts b/public/vendor/paper-shaders/shaders/grain-gradient.d.ts new file mode 100644 index 0000000..73f1b9f --- /dev/null +++ b/public/vendor/paper-shaders/shaders/grain-gradient.d.ts @@ -0,0 +1,82 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const grainGradientMeta: { + readonly maxColorCount: 7; +}; +/** + * Multi-color gradients with grainy, noise-textured distortion available in 7 animated abstract forms. + * + * Note: grains are calculated using gl_FragCoord & u_resolution, meaning grains don't react to scaling and fit + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning (0 to 1) + * - u_originY (float): Reference point for positioning (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic + * - u_worldHeight (float): Virtual height of the graphic + * - u_fit (float): Fit mode (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level (0.01 to 4) + * - u_rotation (float): Rotation angle in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset (-1 to 1) + * - u_offsetY (float): Vertical offset (-1 to 1) + * - u_colorBack (vec4): Background color in RGBA + * - u_colors (vec4[]): Up to 7 gradient colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1) + * - u_intensity (float): Distortion between color bands (0 to 1) + * - u_noise (float): Grainy noise overlay (0 to 1) + * - u_shape (float): Shape type (1 = wave, 2 = dots, 3 = truchet, 4 = corners, 5 = ripple, 6 = blob, 7 = sphere) + * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture + * + * Vertex shader outputs (used in fragment shader): + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied (used for shapes 4-7) + * - v_objectBoxSize (vec2): Size of the object bounding box in pixels + * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied (used for shapes 1-3) + * - v_patternBoxSize (vec2): Size of the pattern bounding box in pixels + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const grainGradientFragmentShader: string; +export interface GrainGradientUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colors: vec4[]; + u_colorsCount: number; + u_softness: number; + u_intensity: number; + u_noise: number; + u_shape: (typeof GrainGradientShapes)[GrainGradientShape]; + u_noiseTexture?: HTMLImageElement; +} +export interface GrainGradientParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colors?: string[]; + softness?: number; + intensity?: number; + noise?: number; + shape?: GrainGradientShape; +} +export declare const GrainGradientShapes: { + wave: number; + dots: number; + truchet: number; + corners: number; + ripple: number; + blob: number; + sphere: number; +}; +export type GrainGradientShape = keyof typeof GrainGradientShapes; diff --git a/public/vendor/paper-shaders/shaders/grain-gradient.js b/public/vendor/paper-shaders/shaders/grain-gradient.js new file mode 100644 index 0000000..ed7a3fc --- /dev/null +++ b/public/vendor/paper-shaders/shaders/grain-gradient.js @@ -0,0 +1,303 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import { +} from "../shader-sizing.js"; +import { + simplexNoise, + declarePI, + rotation2, + textureRandomizerR, + proceduralHash11 +} from "../shader-utils.js"; +const grainGradientMeta = { + maxColorCount: 7 +}; +const grainGradientFragmentShader = `#version 300 es +precision lowp float; + +uniform mediump float u_time; +uniform mediump vec2 u_resolution; +uniform mediump float u_pixelRatio; + +uniform sampler2D u_noiseTexture; + +uniform vec4 u_colorBack; +uniform vec4 u_colors[${grainGradientMeta.maxColorCount}]; +uniform float u_colorsCount; +uniform float u_softness; +uniform float u_intensity; +uniform float u_noise; +uniform float u_shape; + +uniform mediump float u_originX; +uniform mediump float u_originY; +uniform mediump float u_worldWidth; +uniform mediump float u_worldHeight; +uniform mediump float u_fit; + +uniform mediump float u_scale; +uniform mediump float u_rotation; +uniform mediump float u_offsetX; +uniform mediump float u_offsetY; + +in vec2 v_objectUV; +in vec2 v_patternUV; +in vec2 v_objectBoxSize; +in vec2 v_patternBoxSize; + +out vec4 fragColor; + +${declarePI} +${simplexNoise} +${rotation2} +${textureRandomizerR} + +float valueNoiseR(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = randomR(i); + float b = randomR(i + vec2(1.0, 0.0)); + float c = randomR(i + vec2(0.0, 1.0)); + float d = randomR(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} +vec4 fbmR(vec2 n0, vec2 n1, vec2 n2, vec2 n3) { + float amplitude = 0.2; + vec4 total = vec4(0.); + for (int i = 0; i < 3; i++) { + n0 = rotate(n0, 0.3); + n1 = rotate(n1, 0.3); + n2 = rotate(n2, 0.3); + n3 = rotate(n3, 0.3); + total.x += valueNoiseR(n0) * amplitude; + total.y += valueNoiseR(n1) * amplitude; + total.z += valueNoiseR(n2) * amplitude; + total.z += valueNoiseR(n3) * amplitude; + n0 *= 1.99; + n1 *= 1.99; + n2 *= 1.99; + n3 *= 1.99; + amplitude *= 0.6; + } + return total; +} + +${proceduralHash11} + +vec2 truchet(vec2 uv, float idx){ + idx = fract(((idx - .5) * 2.)); + if (idx > 0.75) { + uv = vec2(1.0) - uv; + } else if (idx > 0.5) { + uv = vec2(1.0 - uv.x, uv.y); + } else if (idx > 0.25) { + uv = 1.0 - vec2(1.0 - uv.x, uv.y); + } + return uv; +} + +void main() { + + const float firstFrameOffset = 7.; + float t = .1 * (u_time + firstFrameOffset); + + vec2 shape_uv = vec2(0.); + vec2 grain_uv = vec2(0.); + + float r = u_rotation * PI / 180.; + float cr = cos(r); + float sr = sin(r); + mat2 graphicRotation = mat2(cr, sr, -sr, cr); + vec2 graphicOffset = vec2(-u_offsetX, u_offsetY); + + if (u_shape > 3.5) { + shape_uv = v_objectUV; + grain_uv = shape_uv; + + // apply inverse transform to grain_uv so it respects the originXY + grain_uv = transpose(graphicRotation) * grain_uv; + grain_uv *= u_scale; + grain_uv -= graphicOffset; + grain_uv *= v_objectBoxSize; + grain_uv *= .7; + } else { + shape_uv = .5 * v_patternUV; + grain_uv = 100. * v_patternUV; + + // apply inverse transform to grain_uv so it respects the originXY + grain_uv = transpose(graphicRotation) * grain_uv; + grain_uv *= u_scale; + if (u_fit > 0.) { + vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight); + givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio; + float patternBoxRatio = givenBoxSize.x / givenBoxSize.y; + vec2 patternBoxGivenSize = vec2( + (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x, + (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y + ); + patternBoxRatio = patternBoxGivenSize.x / patternBoxGivenSize.y; + float patternBoxNoFitBoxWidth = patternBoxRatio * min(patternBoxGivenSize.x / patternBoxRatio, patternBoxGivenSize.y); + grain_uv /= (patternBoxNoFitBoxWidth / v_patternBoxSize.x); + } + vec2 patternBoxScale = u_resolution.xy / v_patternBoxSize; + grain_uv -= graphicOffset / patternBoxScale; + grain_uv *= 1.6; + } + + + float shape = 0.; + + if (u_shape < 1.5) { + // Sine wave + + float wave = cos(.5 * shape_uv.x - 4. * t) * sin(1.5 * shape_uv.x + 2. * t) * (.75 + .25 * cos(6. * t)); + shape = 1. - smoothstep(-1., 1., shape_uv.y + wave); + + } else if (u_shape < 2.5) { + // Grid (dots) + + float stripeIdx = floor(2. * shape_uv.x / TWO_PI); + float rand = hash11(stripeIdx * 100.); + rand = sign(rand - .5) * pow(4. * abs(rand), .3); + shape = sin(shape_uv.x) * cos(shape_uv.y - 5. * rand * t); + shape = pow(abs(shape), 4.); + + } else if (u_shape < 3.5) { + // Truchet pattern + + float n2 = valueNoiseR(shape_uv * .4 - 3.75 * t); + shape_uv.x += 10.; + shape_uv *= .6; + + vec2 tile = truchet(fract(shape_uv), randomR(floor(shape_uv))); + + float distance1 = length(tile); + float distance2 = length(tile - vec2(1.)); + + n2 -= .5; + n2 *= .1; + shape = smoothstep(.2, .55, distance1 + n2) * (1. - smoothstep(.45, .8, distance1 - n2)); + shape += smoothstep(.2, .55, distance2 + n2) * (1. - smoothstep(.45, .8, distance2 - n2)); + + shape = pow(shape, 1.5); + + } else if (u_shape < 4.5) { + // Corners + + shape_uv *= .6; + vec2 outer = vec2(.5); + + vec2 bl = smoothstep(vec2(0.), outer, shape_uv + vec2(.1 + .1 * sin(3. * t), .2 - .1 * sin(5.25 * t))); + vec2 tr = smoothstep(vec2(0.), outer, 1. - shape_uv); + shape = 1. - bl.x * bl.y * tr.x * tr.y; + + shape_uv = -shape_uv; + bl = smoothstep(vec2(0.), outer, shape_uv + vec2(.1 + .1 * sin(3. * t), .2 - .1 * cos(5.25 * t))); + tr = smoothstep(vec2(0.), outer, 1. - shape_uv); + shape -= bl.x * bl.y * tr.x * tr.y; + + shape = 1. - smoothstep(0., 1., shape); + + } else if (u_shape < 5.5) { + // Ripple + + shape_uv *= 2.; + float dist = length(.4 * shape_uv); + float waves = sin(pow(dist, 1.2) * 5. - 3. * t) * .5 + .5; + shape = waves; + + } else if (u_shape < 6.5) { + // Blob + + t *= 2.; + + vec2 f1_traj = .25 * vec2(1.3 * sin(t), .2 + 1.3 * cos(.6 * t + 4.)); + vec2 f2_traj = .2 * vec2(1.2 * sin(-t), 1.3 * sin(1.6 * t)); + vec2 f3_traj = .25 * vec2(1.7 * cos(-.6 * t), cos(-1.6 * t)); + vec2 f4_traj = .3 * vec2(1.4 * cos(.8 * t), 1.2 * sin(-.6 * t - 3.)); + + shape = .5 * pow(1. - clamp(0., 1., length(shape_uv + f1_traj)), 5.); + shape += .5 * pow(1. - clamp(0., 1., length(shape_uv + f2_traj)), 5.); + shape += .5 * pow(1. - clamp(0., 1., length(shape_uv + f3_traj)), 5.); + shape += .5 * pow(1. - clamp(0., 1., length(shape_uv + f4_traj)), 5.); + + shape = smoothstep(.0, .9, shape); + float edge = smoothstep(.25, .3, shape); + shape = mix(.0, shape, edge); + + } else { + // Sphere + + shape_uv *= 2.; + float d = 1. - pow(length(shape_uv), 2.); + vec3 pos = vec3(shape_uv, sqrt(max(d, 0.))); + vec3 lightPos = normalize(vec3(cos(1.5 * t), .8, sin(1.25 * t))); + shape = .5 + .5 * dot(lightPos, pos); + shape *= step(0., d); + } + + float baseNoise = snoise(grain_uv * .5); + vec4 fbmVals = fbmR( + .002 * grain_uv + 10., + .003 * grain_uv, + .001 * grain_uv, + rotate(.4 * grain_uv, 2.) + ); + float grainDist = baseNoise * snoise(grain_uv * .2) - fbmVals.x - fbmVals.y; + float rawNoise = .75 * baseNoise - fbmVals.w - fbmVals.z; + float noise = clamp(rawNoise, 0., 1.); + + shape += u_intensity * 2. / u_colorsCount * (grainDist + .5); + shape += u_noise * 10. / u_colorsCount * noise; + + float aa = fwidth(shape); + + shape = clamp(shape - .5 / u_colorsCount, 0., 1.); + float totalShape = smoothstep(0., u_softness + 2. * aa, clamp(shape * u_colorsCount, 0., 1.)); + float mixer = shape * (u_colorsCount - 1.); + + int cntStop = int(u_colorsCount) - 1; + vec4 gradient = u_colors[0]; + gradient.rgb *= gradient.a; + for (int i = 1; i < ${grainGradientMeta.maxColorCount}; i++) { + if (i > cntStop) break; + + float localT = clamp(mixer - float(i - 1), 0., 1.); + localT = smoothstep(.5 - .5 * u_softness - aa, .5 + .5 * u_softness + aa, localT); + + vec4 c = u_colors[i]; + c.rgb *= c.a; + gradient = mix(gradient, c, localT); + } + + vec3 color = gradient.rgb * totalShape; + float opacity = gradient.a * totalShape; + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1.0 - opacity); + opacity = opacity + u_colorBack.a * (1.0 - opacity); + + fragColor = vec4(color, opacity); +} +`; +const GrainGradientShapes = { + wave: 1, + dots: 2, + truchet: 3, + corners: 4, + ripple: 5, + blob: 6, + sphere: 7 +}; +export { + GrainGradientShapes, + grainGradientFragmentShader, + grainGradientMeta +}; +//# sourceMappingURL=grain-gradient.js.map diff --git a/public/vendor/paper-shaders/shaders/grain-gradient.js.map b/public/vendor/paper-shaders/shaders/grain-gradient.js.map new file mode 100644 index 0000000..27240ed --- /dev/null +++ b/public/vendor/paper-shaders/shaders/grain-gradient.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/grain-gradient.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport {\n simplexNoise,\n declarePI,\n rotation2,\n textureRandomizerR,\n proceduralHash11,\n} from '../shader-utils.js';\n\nexport const grainGradientMeta = {\n maxColorCount: 7,\n} as const;\n\n/**\n * Multi-color gradients with grainy, noise-textured distortion available in 7 animated abstract forms.\n *\n * Note: grains are calculated using gl_FragCoord & u_resolution, meaning grains don't react to scaling and fit\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning (0 to 1)\n * - u_originY (float): Reference point for positioning (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic\n * - u_worldHeight (float): Virtual height of the graphic\n * - u_fit (float): Fit mode (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level (0.01 to 4)\n * - u_rotation (float): Rotation angle in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset (-1 to 1)\n * - u_offsetY (float): Vertical offset (-1 to 1)\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colors (vec4[]): Up to 7 gradient colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)\n * - u_intensity (float): Distortion between color bands (0 to 1)\n * - u_noise (float): Grainy noise overlay (0 to 1)\n * - u_shape (float): Shape type (1 = wave, 2 = dots, 3 = truchet, 4 = corners, 5 = ripple, 6 = blob, 7 = sphere)\n * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied (used for shapes 4-7)\n * - v_objectBoxSize (vec2): Size of the object bounding box in pixels\n * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied (used for shapes 1-3)\n * - v_patternBoxSize (vec2): Size of the pattern bounding box in pixels\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const grainGradientFragmentShader: string = `#version 300 es\nprecision lowp float;\n\nuniform mediump float u_time;\nuniform mediump vec2 u_resolution;\nuniform mediump float u_pixelRatio;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${ grainGradientMeta.maxColorCount }];\nuniform float u_colorsCount;\nuniform float u_softness;\nuniform float u_intensity;\nuniform float u_noise;\nuniform float u_shape;\n\nuniform mediump float u_originX;\nuniform mediump float u_originY;\nuniform mediump float u_worldWidth;\nuniform mediump float u_worldHeight;\nuniform mediump float u_fit;\n\nuniform mediump float u_scale;\nuniform mediump float u_rotation;\nuniform mediump float u_offsetX;\nuniform mediump float u_offsetY;\n\nin vec2 v_objectUV;\nin vec2 v_patternUV;\nin vec2 v_objectBoxSize;\nin vec2 v_patternBoxSize;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ simplexNoise }\n${ rotation2 }\n${ textureRandomizerR }\n\nfloat valueNoiseR(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomR(i);\n float b = randomR(i + vec2(1.0, 0.0));\n float c = randomR(i + vec2(0.0, 1.0));\n float d = randomR(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\nvec4 fbmR(vec2 n0, vec2 n1, vec2 n2, vec2 n3) {\n float amplitude = 0.2;\n vec4 total = vec4(0.);\n for (int i = 0; i < 3; i++) {\n n0 = rotate(n0, 0.3);\n n1 = rotate(n1, 0.3);\n n2 = rotate(n2, 0.3);\n n3 = rotate(n3, 0.3);\n total.x += valueNoiseR(n0) * amplitude;\n total.y += valueNoiseR(n1) * amplitude;\n total.z += valueNoiseR(n2) * amplitude;\n total.z += valueNoiseR(n3) * amplitude;\n n0 *= 1.99;\n n1 *= 1.99;\n n2 *= 1.99;\n n3 *= 1.99;\n amplitude *= 0.6;\n }\n return total;\n}\n\n${ proceduralHash11 }\n\nvec2 truchet(vec2 uv, float idx){\n idx = fract(((idx - .5) * 2.));\n if (idx > 0.75) {\n uv = vec2(1.0) - uv;\n } else if (idx > 0.5) {\n uv = vec2(1.0 - uv.x, uv.y);\n } else if (idx > 0.25) {\n uv = 1.0 - vec2(1.0 - uv.x, uv.y);\n }\n return uv;\n}\n\nvoid main() {\n\n const float firstFrameOffset = 7.;\n float t = .1 * (u_time + firstFrameOffset);\n\n vec2 shape_uv = vec2(0.);\n vec2 grain_uv = vec2(0.);\n\n float r = u_rotation * PI / 180.;\n float cr = cos(r);\n float sr = sin(r);\n mat2 graphicRotation = mat2(cr, sr, -sr, cr);\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n\n if (u_shape > 3.5) {\n shape_uv = v_objectUV;\n grain_uv = shape_uv;\n\n // apply inverse transform to grain_uv so it respects the originXY\n grain_uv = transpose(graphicRotation) * grain_uv;\n grain_uv *= u_scale;\n grain_uv -= graphicOffset;\n grain_uv *= v_objectBoxSize;\n grain_uv *= .7;\n } else {\n shape_uv = .5 * v_patternUV;\n grain_uv = 100. * v_patternUV;\n\n // apply inverse transform to grain_uv so it respects the originXY\n grain_uv = transpose(graphicRotation) * grain_uv;\n grain_uv *= u_scale;\n if (u_fit > 0.) {\n vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight);\n givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio;\n float patternBoxRatio = givenBoxSize.x / givenBoxSize.y;\n vec2 patternBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n patternBoxRatio = patternBoxGivenSize.x / patternBoxGivenSize.y;\n float patternBoxNoFitBoxWidth = patternBoxRatio * min(patternBoxGivenSize.x / patternBoxRatio, patternBoxGivenSize.y);\n grain_uv /= (patternBoxNoFitBoxWidth / v_patternBoxSize.x);\n }\n vec2 patternBoxScale = u_resolution.xy / v_patternBoxSize;\n grain_uv -= graphicOffset / patternBoxScale;\n grain_uv *= 1.6;\n }\n\n\n float shape = 0.;\n\n if (u_shape < 1.5) {\n // Sine wave\n\n float wave = cos(.5 * shape_uv.x - 4. * t) * sin(1.5 * shape_uv.x + 2. * t) * (.75 + .25 * cos(6. * t));\n shape = 1. - smoothstep(-1., 1., shape_uv.y + wave);\n\n } else if (u_shape < 2.5) {\n // Grid (dots)\n\n float stripeIdx = floor(2. * shape_uv.x / TWO_PI);\n float rand = hash11(stripeIdx * 100.);\n rand = sign(rand - .5) * pow(4. * abs(rand), .3);\n shape = sin(shape_uv.x) * cos(shape_uv.y - 5. * rand * t);\n shape = pow(abs(shape), 4.);\n\n } else if (u_shape < 3.5) {\n // Truchet pattern\n\n float n2 = valueNoiseR(shape_uv * .4 - 3.75 * t);\n shape_uv.x += 10.;\n shape_uv *= .6;\n\n vec2 tile = truchet(fract(shape_uv), randomR(floor(shape_uv)));\n\n float distance1 = length(tile);\n float distance2 = length(tile - vec2(1.));\n\n n2 -= .5;\n n2 *= .1;\n shape = smoothstep(.2, .55, distance1 + n2) * (1. - smoothstep(.45, .8, distance1 - n2));\n shape += smoothstep(.2, .55, distance2 + n2) * (1. - smoothstep(.45, .8, distance2 - n2));\n\n shape = pow(shape, 1.5);\n\n } else if (u_shape < 4.5) {\n // Corners\n\n shape_uv *= .6;\n vec2 outer = vec2(.5);\n\n vec2 bl = smoothstep(vec2(0.), outer, shape_uv + vec2(.1 + .1 * sin(3. * t), .2 - .1 * sin(5.25 * t)));\n vec2 tr = smoothstep(vec2(0.), outer, 1. - shape_uv);\n shape = 1. - bl.x * bl.y * tr.x * tr.y;\n\n shape_uv = -shape_uv;\n bl = smoothstep(vec2(0.), outer, shape_uv + vec2(.1 + .1 * sin(3. * t), .2 - .1 * cos(5.25 * t)));\n tr = smoothstep(vec2(0.), outer, 1. - shape_uv);\n shape -= bl.x * bl.y * tr.x * tr.y;\n\n shape = 1. - smoothstep(0., 1., shape);\n\n } else if (u_shape < 5.5) {\n // Ripple\n\n shape_uv *= 2.;\n float dist = length(.4 * shape_uv);\n float waves = sin(pow(dist, 1.2) * 5. - 3. * t) * .5 + .5;\n shape = waves;\n\n } else if (u_shape < 6.5) {\n // Blob\n\n t *= 2.;\n\n vec2 f1_traj = .25 * vec2(1.3 * sin(t), .2 + 1.3 * cos(.6 * t + 4.));\n vec2 f2_traj = .2 * vec2(1.2 * sin(-t), 1.3 * sin(1.6 * t));\n vec2 f3_traj = .25 * vec2(1.7 * cos(-.6 * t), cos(-1.6 * t));\n vec2 f4_traj = .3 * vec2(1.4 * cos(.8 * t), 1.2 * sin(-.6 * t - 3.));\n\n shape = .5 * pow(1. - clamp(0., 1., length(shape_uv + f1_traj)), 5.);\n shape += .5 * pow(1. - clamp(0., 1., length(shape_uv + f2_traj)), 5.);\n shape += .5 * pow(1. - clamp(0., 1., length(shape_uv + f3_traj)), 5.);\n shape += .5 * pow(1. - clamp(0., 1., length(shape_uv + f4_traj)), 5.);\n\n shape = smoothstep(.0, .9, shape);\n float edge = smoothstep(.25, .3, shape);\n shape = mix(.0, shape, edge);\n\n } else {\n // Sphere\n\n shape_uv *= 2.;\n float d = 1. - pow(length(shape_uv), 2.);\n vec3 pos = vec3(shape_uv, sqrt(max(d, 0.)));\n vec3 lightPos = normalize(vec3(cos(1.5 * t), .8, sin(1.25 * t)));\n shape = .5 + .5 * dot(lightPos, pos);\n shape *= step(0., d);\n }\n\n float baseNoise = snoise(grain_uv * .5);\n vec4 fbmVals = fbmR(\n .002 * grain_uv + 10.,\n .003 * grain_uv,\n .001 * grain_uv,\n rotate(.4 * grain_uv, 2.)\n );\n float grainDist = baseNoise * snoise(grain_uv * .2) - fbmVals.x - fbmVals.y;\n float rawNoise = .75 * baseNoise - fbmVals.w - fbmVals.z;\n float noise = clamp(rawNoise, 0., 1.);\n\n shape += u_intensity * 2. / u_colorsCount * (grainDist + .5);\n shape += u_noise * 10. / u_colorsCount * noise;\n\n float aa = fwidth(shape);\n\n shape = clamp(shape - .5 / u_colorsCount, 0., 1.);\n float totalShape = smoothstep(0., u_softness + 2. * aa, clamp(shape * u_colorsCount, 0., 1.));\n float mixer = shape * (u_colorsCount - 1.);\n\n int cntStop = int(u_colorsCount) - 1;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n for (int i = 1; i < ${ grainGradientMeta.maxColorCount }; i++) {\n if (i > cntStop) break;\n\n float localT = clamp(mixer - float(i - 1), 0., 1.);\n localT = smoothstep(.5 - .5 * u_softness - aa, .5 + .5 * u_softness + aa, localT);\n\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, localT);\n }\n\n vec3 color = gradient.rgb * totalShape;\n float opacity = gradient.a * totalShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1.0 - opacity);\n opacity = opacity + u_colorBack.a * (1.0 - opacity);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface GrainGradientUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_softness: number;\n u_intensity: number;\n u_noise: number;\n u_shape: (typeof GrainGradientShapes)[GrainGradientShape];\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface GrainGradientParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n softness?: number;\n intensity?: number;\n noise?: number;\n shape?: GrainGradientShape;\n}\n\nexport const GrainGradientShapes = {\n wave: 1,\n dots: 2,\n truchet: 3,\n corners: 4,\n ripple: 5,\n blob: 6,\n sphere: 7,\n};\n\nexport type GrainGradientShape = keyof typeof GrainGradientShapes;\n"], + "mappings": ";;;;;AAEA;AAAA,OAGO;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,MAAM,oBAAoB;AAAA,EAC/B,eAAe;AACjB;AAmDO,MAAM,8BAAsC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAU1B,kBAAkB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBtD,SAAU;AAAA,EACV,YAAa;AAAA,EACb,SAAU;AAAA,EACV,kBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCnB,gBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAiLK,kBAAkB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0ClD,MAAM,sBAAsB;AAAA,EACjC,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,QAAQ;AACV;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/halftone-cmyk.d.ts b/public/vendor/paper-shaders/shaders/halftone-cmyk.d.ts new file mode 100644 index 0000000..c77f09b --- /dev/null +++ b/public/vendor/paper-shaders/shaders/halftone-cmyk.d.ts @@ -0,0 +1,105 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * CMYK halftone printing effect applied to images with customizable dot patterns + * and ink colors for each channel (Cyan, Magenta, Yellow, Black). + * + * Fragment shader uniforms: + * - u_image (sampler2D): Source image texture + * - u_imageAspectRatio (float): Aspect ratio of the source image + * - u_colorBack (vec4): Background (paper) color in RGBA + * - u_colorC (vec4): Cyan ink color in RGBA + * - u_colorM (vec4): Magenta ink color in RGBA + * - u_colorY (vec4): Yellow ink color in RGBA + * - u_colorK (vec4): Black ink color in RGBA + * - u_size (float): Halftone cell size (0 to 1) + * - u_minDot (float): Minimum dot thickness (0 to 1) + * - u_contrast (float): Image contrast adjustment (0 to 2) + * - u_softness (float): Edge softness of dots (0 to 1) + * - u_grainSize (float): Size of grain overlay texture (0 to 1) + * - u_grainMixer (float): Strength of grain affecting dot size (0 to 1) + * - u_grainOverlay (float): Strength of grain overlay on final output (0 to 1) + * - u_gridNoise (float): Strength of smooth noise applied to both dot positions and color sampling (0 to 1) + * - u_floodC (float): Flat cyan dot size adjustment applied uniformly (-1 to 1) + * - u_floodM (float): Flat magenta dot size adjustment applied uniformly (-1 to 1) + * - u_floodY (float): Flat yellow dot size adjustment applied uniformly (-1 to 1) + * - u_floodK (float): Flat black dot size adjustment applied uniformly (-1 to 1) + * - u_gainC (float): Proportional cyan dot size gain (enhances existing dots, -1 to 1) + * - u_gainM (float): Proportional magenta dot size gain (enhances existing dots, -1 to 1) + * - u_gainY (float): Proportional yellow dot size gain (enhances existing dots, -1 to 1) + * - u_gainK (float): Proportional black dot size gain (enhances existing dots, -1 to 1) + * - u_type (float): Dot shape style (0 = dots, 1 = ink, 2 = sharp) + * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture + * + * Vertex shader outputs (used in fragment shader): + * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * + */ +export declare const halftoneCmykFragmentShader: string; +export interface HalftoneCmykUniforms extends ShaderSizingUniforms { + u_image: HTMLImageElement | string | undefined; + u_noiseTexture?: HTMLImageElement; + u_colorBack: [number, number, number, number]; + u_colorC: [number, number, number, number]; + u_colorM: [number, number, number, number]; + u_colorY: [number, number, number, number]; + u_colorK: [number, number, number, number]; + u_size: number; + u_contrast: number; + u_softness: number; + u_grainSize: number; + u_grainMixer: number; + u_grainOverlay: number; + u_gridNoise: number; + u_floodC: number; + u_floodM: number; + u_floodY: number; + u_floodK: number; + u_gainC: number; + u_gainM: number; + u_gainY: number; + u_gainK: number; + u_type: (typeof HalftoneCmykTypes)[HalftoneCmykType]; +} +export interface HalftoneCmykParams extends ShaderSizingParams, ShaderMotionParams { + image?: HTMLImageElement | string; + colorBack?: string; + colorC?: string; + colorM?: string; + colorY?: string; + colorK?: string; + size?: number; + contrast?: number; + softness?: number; + grainSize?: number; + grainMixer?: number; + grainOverlay?: number; + gridNoise?: number; + floodC?: number; + floodM?: number; + floodY?: number; + floodK?: number; + gainC?: number; + gainM?: number; + gainY?: number; + gainK?: number; + type?: HalftoneCmykType; +} +export declare const HalftoneCmykTypes: { + readonly dots: 0; + readonly ink: 1; + readonly sharp: 2; +}; +export type HalftoneCmykType = keyof typeof HalftoneCmykTypes; diff --git a/public/vendor/paper-shaders/shaders/halftone-cmyk.js b/public/vendor/paper-shaders/shaders/halftone-cmyk.js new file mode 100644 index 0000000..e361a7c --- /dev/null +++ b/public/vendor/paper-shaders/shaders/halftone-cmyk.js @@ -0,0 +1,292 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI } from "../shader-utils.js"; +const halftoneCmykFragmentShader = `#version 300 es +precision mediump float; + +uniform sampler2D u_image; +uniform float u_imageAspectRatio; + +uniform vec4 u_colorBack; +uniform vec4 u_colorC; +uniform vec4 u_colorM; +uniform vec4 u_colorY; +uniform vec4 u_colorK; +uniform float u_size; +uniform float u_minDot; +uniform float u_contrast; +uniform float u_grainSize; +uniform float u_grainMixer; +uniform float u_grainOverlay; +uniform float u_gridNoise; +uniform float u_softness; +uniform float u_floodC; +uniform float u_floodM; +uniform float u_floodY; +uniform float u_floodK; +uniform float u_gainC; +uniform float u_gainM; +uniform float u_gainY; +uniform float u_gainK; +uniform float u_type; +uniform sampler2D u_noiseTexture; + +in vec2 v_imageUV; +out vec4 fragColor; + +const float shiftC = -.5; +const float shiftM = -.25; +const float shiftY = .2; +const float shiftK = 0.; + +// Precomputed sin/cos for rotation angles (15\xB0, 75\xB0, 0\xB0, 45\xB0) +const float cosC = 0.9659258; const float sinC = 0.2588190; // 15\xB0 +const float cosM = 0.2588190; const float sinM = 0.9659258; // 75\xB0 +const float cosY = 1.0; const float sinY = 0.0; // 0\xB0 +const float cosK = 0.7071068; const float sinK = 0.7071068; // 45\xB0 + +${declarePI} + +vec2 randomRG(vec2 p) { + vec2 uv = floor(p) / 100. + .5; + return texture(u_noiseTexture, fract(uv)).rg; +} +vec3 hash23(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * vec3(0.3183099, 0.3678794, 0.3141592)) + 0.1; + p3 += dot(p3, p3.yzx + 19.19); + return fract(vec3(p3.x * p3.y, p3.y * p3.z, p3.z * p3.x)); +} + +float sst(float edge0, float edge1, float x) { + return smoothstep(edge0, edge1, x); +} + +vec3 valueNoise3(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + vec3 a = hash23(i); + vec3 b = hash23(i + vec2(1.0, 0.0)); + vec3 c = hash23(i + vec2(0.0, 1.0)); + vec3 d = hash23(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + vec3 x1 = mix(a, b, u.x); + vec3 x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} + +float getUvFrame(vec2 uv, vec2 pad) { + float left = smoothstep(-pad.x, 0., uv.x); + float right = smoothstep(1. + pad.x, 1., uv.x); + float bottom = smoothstep(-pad.y, 0., uv.y); + float top = smoothstep(1. + pad.y, 1., uv.y); + + return left * right * bottom * top; +} + +vec4 RGBAtoCMYK(vec4 rgba) { + float k = 1. - max(max(rgba.r, rgba.g), rgba.b); + float denom = 1. - k; + vec3 cmy = vec3(0.); + if (denom > 1e-5) { + cmy = (1. - rgba.rgb - vec3(k)) / denom; + } + return vec4(cmy, k) * rgba.a; +} + +vec3 applyContrast(vec3 rgb) { + return clamp((rgb - 0.5) * u_contrast + 0.5, 0.0, 1.0); +} + +// Single-component CMYK extractors with contrast built-in, alpha-aware +float getCyan(vec4 rgba) { + vec3 c = clamp((rgba.rgb - 0.5) * u_contrast + 0.5, 0.0, 1.0); + float maxRGB = max(max(c.r, c.g), c.b); + return (maxRGB > 1e-5 ? (maxRGB - c.r) / maxRGB : 0.) * rgba.a; +} +float getMagenta(vec4 rgba) { + vec3 c = clamp((rgba.rgb - 0.5) * u_contrast + 0.5, 0.0, 1.0); + float maxRGB = max(max(c.r, c.g), c.b); + return (maxRGB > 1e-5 ? (maxRGB - c.g) / maxRGB : 0.) * rgba.a; +} +float getYellow(vec4 rgba) { + vec3 c = clamp((rgba.rgb - 0.5) * u_contrast + 0.5, 0.0, 1.0); + float maxRGB = max(max(c.r, c.g), c.b); + return (maxRGB > 1e-5 ? (maxRGB - c.b) / maxRGB : 0.) * rgba.a; +} +float getBlack(vec4 rgba) { + vec3 c = clamp((rgba.rgb - 0.5) * u_contrast + 0.5, 0.0, 1.0); + return (1. - max(max(c.r, c.g), c.b)) * rgba.a; +} + +vec2 cellCenterPos(vec2 uv, vec2 cellOffset, float channelIdx) { + vec2 cellCenter = floor(uv) + .5 + cellOffset; + return cellCenter + (randomRG(cellCenter + channelIdx * 50.) - .5) * u_gridNoise; +} + +vec2 gridToImageUV(vec2 cellCenter, float cosA, float sinA, float shift, vec2 pad) { + vec2 uvGrid = mat2(cosA, -sinA, sinA, cosA) * (cellCenter - shift); + return uvGrid * pad + 0.5; +} + +void colorMask(vec2 pos, vec2 cellCenter, float rad, float transparency, float grain, float channelAddon, float channelgain, float generalComp, bool isJoined, inout float outMask) { + float dist = length(pos - cellCenter); + + float radius = rad; + radius *= (1. + generalComp); + radius += (.15 + channelgain * radius); + radius = max(0., radius); + radius = mix(0., radius, transparency); + radius += channelAddon; + radius *= (1. - grain); + + float mask = 1. - sst(0., radius, dist); + if (isJoined) { + // ink or sharp (joined) + mask = pow(mask, 1.2); + } else { + // dots (separate) + mask = sst(.5 - .5 * u_softness, .51 + .49 * u_softness, mask); + } + + mask *= mix(1., mix(.5, 1., 1.5 * radius), u_softness); + outMask += mask; +} + +vec3 applyInk(vec3 paper, vec3 inkColor, float cov) { + vec3 inkEffect = mix(vec3(1.0), inkColor, clamp(cov, 0.0, 1.0)); + return paper * inkEffect; +} + +void main() { + vec2 uv = v_imageUV; + + float cellsPerSide = mix(400.0, 7.0, pow(u_size, 0.7)); + float cellSizeY = 1.0 / cellsPerSide; + vec2 pad = cellSizeY * vec2(1.0 / u_imageAspectRatio, 1.0); + vec2 uvGrid = (uv - .5) / pad; + float insideImageBox = getUvFrame(uv, pad); + + float generalComp = .1 * u_softness + .1 * u_gridNoise + .1 * (1. - step(0.5, u_type)) * (1.5 - u_softness); + + vec2 uvC = mat2(cosC, sinC, -sinC, cosC) * uvGrid + shiftC; + vec2 uvM = mat2(cosM, sinM, -sinM, cosM) * uvGrid + shiftM; + vec2 uvY = mat2(cosY, sinY, -sinY, cosY) * uvGrid + shiftY; + vec2 uvK = mat2(cosK, sinK, -sinK, cosK) * uvGrid + shiftK; + + vec2 grainSize = mix(2000., 200., u_grainSize) * vec2(1., 1. / u_imageAspectRatio); + vec2 grainUV = (v_imageUV - .5) * grainSize + .5; + vec3 noiseValues = valueNoise3(grainUV); + float grain = sst(.55, 1., noiseValues.r); + grain *= u_grainMixer; + + vec4 outMask = vec4(0.); + bool isJoined = u_type > 0.5; + + if (u_type < 1.5) { + // dots or ink: per-cell color sampling + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + vec2 cellOffset = vec2(float(dx), float(dy)); + + vec2 cellCenterC = cellCenterPos(uvC, cellOffset, 0.); + vec4 texC = texture(u_image, gridToImageUV(cellCenterC, cosC, sinC, shiftC, pad)); + colorMask(uvC, cellCenterC, getCyan(texC), insideImageBox * texC.a, grain, u_floodC, u_gainC, generalComp, isJoined, outMask[0]); + + vec2 cellCenterM = cellCenterPos(uvM, cellOffset, 1.); + vec4 texM = texture(u_image, gridToImageUV(cellCenterM, cosM, sinM, shiftM, pad)); + colorMask(uvM, cellCenterM, getMagenta(texM), insideImageBox * texM.a, grain, u_floodM, u_gainM, generalComp, isJoined, outMask[1]); + + vec2 cellCenterY = cellCenterPos(uvY, cellOffset, 2.); + vec4 texY = texture(u_image, gridToImageUV(cellCenterY, cosY, sinY, shiftY, pad)); + colorMask(uvY, cellCenterY, getYellow(texY), insideImageBox * texY.a, grain, u_floodY, u_gainY, generalComp, isJoined, outMask[2]); + + vec2 cellCenterK = cellCenterPos(uvK, cellOffset, 3.); + vec4 texK = texture(u_image, gridToImageUV(cellCenterK, cosK, sinK, shiftK, pad)); + colorMask(uvK, cellCenterK, getBlack(texK), insideImageBox * texK.a, grain, u_floodK, u_gainK, generalComp, isJoined, outMask[3]); + } + } + } else { + // sharp: direct px color sampling + vec4 tex = texture(u_image, uv); + tex.rgb = applyContrast(tex.rgb); + insideImageBox *= tex.a; + vec4 cmykOriginal = RGBAtoCMYK(tex); + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + vec2 cellOffset = vec2(float(dx), float(dy)); + + colorMask(uvC, cellCenterPos(uvC, cellOffset, 0.), cmykOriginal.x, insideImageBox, grain, u_floodC, u_gainC, generalComp, isJoined, outMask[0]); + colorMask(uvM, cellCenterPos(uvM, cellOffset, 1.), cmykOriginal.y, insideImageBox, grain, u_floodM, u_gainM, generalComp, isJoined, outMask[1]); + colorMask(uvY, cellCenterPos(uvY, cellOffset, 2.), cmykOriginal.z, insideImageBox, grain, u_floodY, u_gainY, generalComp, isJoined, outMask[2]); + colorMask(uvK, cellCenterPos(uvK, cellOffset, 3.), cmykOriginal.w, insideImageBox, grain, u_floodK, u_gainK, generalComp, isJoined, outMask[3]); + } + } + } + + float shape; + + float C = outMask[0]; + float M = outMask[1]; + float Y = outMask[2]; + float K = outMask[3]; + + if (isJoined) { + // ink or sharp: apply threshold for joined dots + float th = .5; + float sLeft = th * u_softness; + float sRight = (1. - th) * u_softness + .01; + C = smoothstep(th - sLeft - fwidth(C), th + sRight, C); + M = smoothstep(th - sLeft - fwidth(M), th + sRight, M); + Y = smoothstep(th - sLeft - fwidth(Y), th + sRight, Y); + K = smoothstep(th - sLeft - fwidth(K), th + sRight, K); + } + + C *= u_colorC.a; + M *= u_colorM.a; + Y *= u_colorY.a; + K *= u_colorK.a; + + vec3 ink = vec3(1.); + ink = applyInk(ink, u_colorK.rgb, K); + ink = applyInk(ink, u_colorC.rgb, C); + ink = applyInk(ink, u_colorM.rgb, M); + ink = applyInk(ink, u_colorY.rgb, Y); + + shape = clamp(max(max(C, M), max(Y, K)), 0., 1.); + + vec3 color = u_colorBack.rgb * u_colorBack.a; + + float opacity = u_colorBack.a; + color = mix(color, ink, shape); + opacity += shape; + opacity = clamp(opacity, 0., 1.); + + float grainOverlay = mix(noiseValues.g, noiseValues.b, .5); + grainOverlay = pow(grainOverlay, 1.3); + + float grainOverlayV = grainOverlay * 2. - 1.; + vec3 grainOverlayColor = vec3(step(0., grainOverlayV)); + float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV); + grainOverlayStrength = pow(grainOverlayStrength, .8); + color = mix(color, grainOverlayColor, .5 * grainOverlayStrength); + + opacity += .5 * grainOverlayStrength; + opacity = clamp(opacity, 0., 1.); + + fragColor = vec4(color, opacity); +} +`; +const HalftoneCmykTypes = { + dots: 0, + ink: 1, + sharp: 2 +}; +export { + HalftoneCmykTypes, + halftoneCmykFragmentShader +}; +//# sourceMappingURL=halftone-cmyk.js.map diff --git a/public/vendor/paper-shaders/shaders/halftone-cmyk.js.map b/public/vendor/paper-shaders/shaders/halftone-cmyk.js.map new file mode 100644 index 0000000..3dc3d4e --- /dev/null +++ b/public/vendor/paper-shaders/shaders/halftone-cmyk.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/halftone-cmyk.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI } from '../shader-utils.js';\n\n/**\n * CMYK halftone printing effect applied to images with customizable dot patterns\n * and ink colors for each channel (Cyan, Magenta, Yellow, Black).\n *\n * Fragment shader uniforms:\n * - u_image (sampler2D): Source image texture\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorBack (vec4): Background (paper) color in RGBA\n * - u_colorC (vec4): Cyan ink color in RGBA\n * - u_colorM (vec4): Magenta ink color in RGBA\n * - u_colorY (vec4): Yellow ink color in RGBA\n * - u_colorK (vec4): Black ink color in RGBA\n * - u_size (float): Halftone cell size (0 to 1)\n * - u_minDot (float): Minimum dot thickness (0 to 1)\n * - u_contrast (float): Image contrast adjustment (0 to 2)\n * - u_softness (float): Edge softness of dots (0 to 1)\n * - u_grainSize (float): Size of grain overlay texture (0 to 1)\n * - u_grainMixer (float): Strength of grain affecting dot size (0 to 1)\n * - u_grainOverlay (float): Strength of grain overlay on final output (0 to 1)\n * - u_gridNoise (float): Strength of smooth noise applied to both dot positions and color sampling (0 to 1)\n * - u_floodC (float): Flat cyan dot size adjustment applied uniformly (-1 to 1)\n * - u_floodM (float): Flat magenta dot size adjustment applied uniformly (-1 to 1)\n * - u_floodY (float): Flat yellow dot size adjustment applied uniformly (-1 to 1)\n * - u_floodK (float): Flat black dot size adjustment applied uniformly (-1 to 1)\n * - u_gainC (float): Proportional cyan dot size gain (enhances existing dots, -1 to 1)\n * - u_gainM (float): Proportional magenta dot size gain (enhances existing dots, -1 to 1)\n * - u_gainY (float): Proportional yellow dot size gain (enhances existing dots, -1 to 1)\n * - u_gainK (float): Proportional black dot size gain (enhances existing dots, -1 to 1)\n * - u_type (float): Dot shape style (0 = dots, 1 = ink, 2 = sharp)\n * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const halftoneCmykFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorC;\nuniform vec4 u_colorM;\nuniform vec4 u_colorY;\nuniform vec4 u_colorK;\nuniform float u_size;\nuniform float u_minDot;\nuniform float u_contrast;\nuniform float u_grainSize;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\nuniform float u_gridNoise;\nuniform float u_softness;\nuniform float u_floodC;\nuniform float u_floodM;\nuniform float u_floodY;\nuniform float u_floodK;\nuniform float u_gainC;\nuniform float u_gainM;\nuniform float u_gainY;\nuniform float u_gainK;\nuniform float u_type;\nuniform sampler2D u_noiseTexture;\n\nin vec2 v_imageUV;\nout vec4 fragColor;\n\nconst float shiftC = -.5;\nconst float shiftM = -.25;\nconst float shiftY = .2;\nconst float shiftK = 0.;\n\n// Precomputed sin/cos for rotation angles (15\u00B0, 75\u00B0, 0\u00B0, 45\u00B0)\nconst float cosC = 0.9659258; const float sinC = 0.2588190; // 15\u00B0\nconst float cosM = 0.2588190; const float sinM = 0.9659258; // 75\u00B0\nconst float cosY = 1.0; const float sinY = 0.0; // 0\u00B0\nconst float cosK = 0.7071068; const float sinK = 0.7071068; // 45\u00B0\n\n${ declarePI }\n\nvec2 randomRG(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).rg;\n}\nvec3 hash23(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * vec3(0.3183099, 0.3678794, 0.3141592)) + 0.1;\n p3 += dot(p3, p3.yzx + 19.19);\n return fract(vec3(p3.x * p3.y, p3.y * p3.z, p3.z * p3.x));\n}\n\nfloat sst(float edge0, float edge1, float x) {\n return smoothstep(edge0, edge1, x);\n}\n\nvec3 valueNoise3(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n vec3 a = hash23(i);\n vec3 b = hash23(i + vec2(1.0, 0.0));\n vec3 c = hash23(i + vec2(0.0, 1.0));\n vec3 d = hash23(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n vec3 x1 = mix(a, b, u.x);\n vec3 x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat getUvFrame(vec2 uv, vec2 pad) {\n float left = smoothstep(-pad.x, 0., uv.x);\n float right = smoothstep(1. + pad.x, 1., uv.x);\n float bottom = smoothstep(-pad.y, 0., uv.y);\n float top = smoothstep(1. + pad.y, 1., uv.y);\n\n return left * right * bottom * top;\n}\n\nvec4 RGBAtoCMYK(vec4 rgba) {\n float k = 1. - max(max(rgba.r, rgba.g), rgba.b);\n float denom = 1. - k;\n vec3 cmy = vec3(0.);\n if (denom > 1e-5) {\n cmy = (1. - rgba.rgb - vec3(k)) / denom;\n }\n return vec4(cmy, k) * rgba.a;\n}\n\nvec3 applyContrast(vec3 rgb) {\n return clamp((rgb - 0.5) * u_contrast + 0.5, 0.0, 1.0);\n}\n\n// Single-component CMYK extractors with contrast built-in, alpha-aware\nfloat getCyan(vec4 rgba) {\n vec3 c = clamp((rgba.rgb - 0.5) * u_contrast + 0.5, 0.0, 1.0);\n float maxRGB = max(max(c.r, c.g), c.b);\n return (maxRGB > 1e-5 ? (maxRGB - c.r) / maxRGB : 0.) * rgba.a;\n}\nfloat getMagenta(vec4 rgba) {\n vec3 c = clamp((rgba.rgb - 0.5) * u_contrast + 0.5, 0.0, 1.0);\n float maxRGB = max(max(c.r, c.g), c.b);\n return (maxRGB > 1e-5 ? (maxRGB - c.g) / maxRGB : 0.) * rgba.a;\n}\nfloat getYellow(vec4 rgba) {\n vec3 c = clamp((rgba.rgb - 0.5) * u_contrast + 0.5, 0.0, 1.0);\n float maxRGB = max(max(c.r, c.g), c.b);\n return (maxRGB > 1e-5 ? (maxRGB - c.b) / maxRGB : 0.) * rgba.a;\n}\nfloat getBlack(vec4 rgba) {\n vec3 c = clamp((rgba.rgb - 0.5) * u_contrast + 0.5, 0.0, 1.0);\n return (1. - max(max(c.r, c.g), c.b)) * rgba.a;\n}\n\nvec2 cellCenterPos(vec2 uv, vec2 cellOffset, float channelIdx) {\n vec2 cellCenter = floor(uv) + .5 + cellOffset;\n return cellCenter + (randomRG(cellCenter + channelIdx * 50.) - .5) * u_gridNoise;\n}\n\nvec2 gridToImageUV(vec2 cellCenter, float cosA, float sinA, float shift, vec2 pad) {\n vec2 uvGrid = mat2(cosA, -sinA, sinA, cosA) * (cellCenter - shift);\n return uvGrid * pad + 0.5;\n}\n\nvoid colorMask(vec2 pos, vec2 cellCenter, float rad, float transparency, float grain, float channelAddon, float channelgain, float generalComp, bool isJoined, inout float outMask) {\n float dist = length(pos - cellCenter);\n\n float radius = rad;\n radius *= (1. + generalComp);\n radius += (.15 + channelgain * radius);\n radius = max(0., radius);\n radius = mix(0., radius, transparency);\n radius += channelAddon;\n radius *= (1. - grain);\n\n float mask = 1. - sst(0., radius, dist);\n if (isJoined) {\n // ink or sharp (joined)\n mask = pow(mask, 1.2);\n } else {\n // dots (separate)\n mask = sst(.5 - .5 * u_softness, .51 + .49 * u_softness, mask);\n }\n\n mask *= mix(1., mix(.5, 1., 1.5 * radius), u_softness);\n outMask += mask;\n}\n\nvec3 applyInk(vec3 paper, vec3 inkColor, float cov) {\n vec3 inkEffect = mix(vec3(1.0), inkColor, clamp(cov, 0.0, 1.0));\n return paper * inkEffect;\n}\n\nvoid main() {\n vec2 uv = v_imageUV;\n\n float cellsPerSide = mix(400.0, 7.0, pow(u_size, 0.7));\n float cellSizeY = 1.0 / cellsPerSide;\n vec2 pad = cellSizeY * vec2(1.0 / u_imageAspectRatio, 1.0);\n vec2 uvGrid = (uv - .5) / pad;\n float insideImageBox = getUvFrame(uv, pad);\n\n float generalComp = .1 * u_softness + .1 * u_gridNoise + .1 * (1. - step(0.5, u_type)) * (1.5 - u_softness);\n\n vec2 uvC = mat2(cosC, sinC, -sinC, cosC) * uvGrid + shiftC;\n vec2 uvM = mat2(cosM, sinM, -sinM, cosM) * uvGrid + shiftM;\n vec2 uvY = mat2(cosY, sinY, -sinY, cosY) * uvGrid + shiftY;\n vec2 uvK = mat2(cosK, sinK, -sinK, cosK) * uvGrid + shiftK;\n\n vec2 grainSize = mix(2000., 200., u_grainSize) * vec2(1., 1. / u_imageAspectRatio);\n vec2 grainUV = (v_imageUV - .5) * grainSize + .5;\n vec3 noiseValues = valueNoise3(grainUV);\n float grain = sst(.55, 1., noiseValues.r);\n grain *= u_grainMixer;\n\n vec4 outMask = vec4(0.);\n bool isJoined = u_type > 0.5;\n\n if (u_type < 1.5) {\n // dots or ink: per-cell color sampling\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cellOffset = vec2(float(dx), float(dy));\n\n vec2 cellCenterC = cellCenterPos(uvC, cellOffset, 0.);\n vec4 texC = texture(u_image, gridToImageUV(cellCenterC, cosC, sinC, shiftC, pad));\n colorMask(uvC, cellCenterC, getCyan(texC), insideImageBox * texC.a, grain, u_floodC, u_gainC, generalComp, isJoined, outMask[0]);\n\n vec2 cellCenterM = cellCenterPos(uvM, cellOffset, 1.);\n vec4 texM = texture(u_image, gridToImageUV(cellCenterM, cosM, sinM, shiftM, pad));\n colorMask(uvM, cellCenterM, getMagenta(texM), insideImageBox * texM.a, grain, u_floodM, u_gainM, generalComp, isJoined, outMask[1]);\n\n vec2 cellCenterY = cellCenterPos(uvY, cellOffset, 2.);\n vec4 texY = texture(u_image, gridToImageUV(cellCenterY, cosY, sinY, shiftY, pad));\n colorMask(uvY, cellCenterY, getYellow(texY), insideImageBox * texY.a, grain, u_floodY, u_gainY, generalComp, isJoined, outMask[2]);\n\n vec2 cellCenterK = cellCenterPos(uvK, cellOffset, 3.);\n vec4 texK = texture(u_image, gridToImageUV(cellCenterK, cosK, sinK, shiftK, pad));\n colorMask(uvK, cellCenterK, getBlack(texK), insideImageBox * texK.a, grain, u_floodK, u_gainK, generalComp, isJoined, outMask[3]);\n }\n }\n } else {\n // sharp: direct px color sampling\n vec4 tex = texture(u_image, uv);\n tex.rgb = applyContrast(tex.rgb);\n insideImageBox *= tex.a;\n vec4 cmykOriginal = RGBAtoCMYK(tex);\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cellOffset = vec2(float(dx), float(dy));\n\n colorMask(uvC, cellCenterPos(uvC, cellOffset, 0.), cmykOriginal.x, insideImageBox, grain, u_floodC, u_gainC, generalComp, isJoined, outMask[0]);\n colorMask(uvM, cellCenterPos(uvM, cellOffset, 1.), cmykOriginal.y, insideImageBox, grain, u_floodM, u_gainM, generalComp, isJoined, outMask[1]);\n colorMask(uvY, cellCenterPos(uvY, cellOffset, 2.), cmykOriginal.z, insideImageBox, grain, u_floodY, u_gainY, generalComp, isJoined, outMask[2]);\n colorMask(uvK, cellCenterPos(uvK, cellOffset, 3.), cmykOriginal.w, insideImageBox, grain, u_floodK, u_gainK, generalComp, isJoined, outMask[3]);\n }\n }\n }\n\n float shape;\n\n float C = outMask[0];\n float M = outMask[1];\n float Y = outMask[2];\n float K = outMask[3];\n\n if (isJoined) {\n // ink or sharp: apply threshold for joined dots\n float th = .5;\n float sLeft = th * u_softness;\n float sRight = (1. - th) * u_softness + .01;\n C = smoothstep(th - sLeft - fwidth(C), th + sRight, C);\n M = smoothstep(th - sLeft - fwidth(M), th + sRight, M);\n Y = smoothstep(th - sLeft - fwidth(Y), th + sRight, Y);\n K = smoothstep(th - sLeft - fwidth(K), th + sRight, K);\n }\n\n C *= u_colorC.a;\n M *= u_colorM.a;\n Y *= u_colorY.a;\n K *= u_colorK.a;\n\n vec3 ink = vec3(1.);\n ink = applyInk(ink, u_colorK.rgb, K);\n ink = applyInk(ink, u_colorC.rgb, C);\n ink = applyInk(ink, u_colorM.rgb, M);\n ink = applyInk(ink, u_colorY.rgb, Y);\n\n shape = clamp(max(max(C, M), max(Y, K)), 0., 1.);\n\n vec3 color = u_colorBack.rgb * u_colorBack.a;\n\n float opacity = u_colorBack.a;\n color = mix(color, ink, shape);\n opacity += shape;\n opacity = clamp(opacity, 0., 1.);\n\n float grainOverlay = mix(noiseValues.g, noiseValues.b, .5);\n grainOverlay = pow(grainOverlay, 1.3);\n\n float grainOverlayV = grainOverlay * 2. - 1.;\n vec3 grainOverlayColor = vec3(step(0., grainOverlayV));\n float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV);\n grainOverlayStrength = pow(grainOverlayStrength, .8);\n color = mix(color, grainOverlayColor, .5 * grainOverlayStrength);\n\n opacity += .5 * grainOverlayStrength;\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface HalftoneCmykUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_noiseTexture?: HTMLImageElement;\n u_colorBack: [number, number, number, number];\n u_colorC: [number, number, number, number];\n u_colorM: [number, number, number, number];\n u_colorY: [number, number, number, number];\n u_colorK: [number, number, number, number];\n u_size: number;\n u_contrast: number;\n u_softness: number;\n u_grainSize: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n u_gridNoise: number;\n u_floodC: number;\n u_floodM: number;\n u_floodY: number;\n u_floodK: number;\n u_gainC: number;\n u_gainM: number;\n u_gainY: number;\n u_gainK: number;\n u_type: (typeof HalftoneCmykTypes)[HalftoneCmykType];\n}\n\nexport interface HalftoneCmykParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n colorBack?: string;\n colorC?: string;\n colorM?: string;\n colorY?: string;\n colorK?: string;\n size?: number;\n contrast?: number;\n softness?: number;\n grainSize?: number;\n grainMixer?: number;\n grainOverlay?: number;\n gridNoise?: number;\n floodC?: number;\n floodM?: number;\n floodY?: number;\n floodK?: number;\n gainC?: number;\n gainM?: number;\n gainY?: number;\n gainK?: number;\n type?: HalftoneCmykType;\n}\n\nexport const HalftoneCmykTypes = {\n dots: 0,\n ink: 1,\n sharp: 2,\n} as const;\n\nexport type HalftoneCmykType = keyof typeof HalftoneCmykTypes;\n\n"], + "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,iBAAiB;AAmDnB,MAAM,6BAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4C/C,SAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2RN,MAAM,oBAAoB;AAAA,EAC/B,MAAM;AAAA,EACN,KAAK;AAAA,EACL,OAAO;AACT;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/halftone-dots.d.ts b/public/vendor/paper-shaders/shaders/halftone-dots.d.ts new file mode 100644 index 0000000..24f6a95 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/halftone-dots.d.ts @@ -0,0 +1,82 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * A halftone-dot image filter featuring customizable grids, color palettes, and dot styles. + * + * Fragment shader uniforms: + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_time (float): Animation time + * - u_image (sampler2D): Source image texture + * - u_imageAspectRatio (float): Aspect ratio of the source image + * - u_colorFront (vec4): Foreground color in RGBA + * - u_colorBack (vec4): Background color in RGBA + * - u_originalColors (bool): Use sampled image's original colors instead of colorFront + * - u_type (float): Dot style (0 = classic, 1 = gooey, 2 = holes, 3 = soft) + * - u_inverted (bool): Inverts the image luminance, doesn't affect the color scheme; not effective at zero contrast + * - u_grid (float): Grid type (0 = square, 1 = hex) + * - u_size (float): Grid size relative to the image box (0 to 1) + * - u_radius (float): Maximum dot size relative to grid cell (0 to 2) + * - u_contrast (float): Contrast applied to the sampled image (0 to 1) + * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1) + * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1) + * - u_grainSize (float): Scale applied to both grain distortion and grain overlay (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * + */ +export declare const halftoneDotsFragmentShader: string; +export interface HalftoneDotsUniforms extends ShaderSizingUniforms { + u_image: HTMLImageElement | string | undefined; + u_colorFront: [number, number, number, number]; + u_colorBack: [number, number, number, number]; + u_size: number; + u_grid: (typeof HalftoneDotsGrids)[HalftoneDotsGrid]; + u_radius: number; + u_contrast: number; + u_originalColors: boolean; + u_inverted: boolean; + u_grainMixer: number; + u_grainOverlay: number; + u_grainSize: number; + u_type: (typeof HalftoneDotsTypes)[HalftoneDotsType]; +} +export interface HalftoneDotsParams extends ShaderSizingParams, ShaderMotionParams { + image?: HTMLImageElement | string; + colorFront?: string; + colorBack?: string; + size?: number; + grid?: HalftoneDotsGrid; + radius?: number; + contrast?: number; + originalColors?: boolean; + inverted?: boolean; + grainMixer?: number; + grainOverlay?: number; + grainSize?: number; + type?: HalftoneDotsType; +} +export declare const HalftoneDotsTypes: { + readonly classic: 0; + readonly gooey: 1; + readonly holes: 2; + readonly soft: 3; +}; +export type HalftoneDotsType = keyof typeof HalftoneDotsTypes; +export declare const HalftoneDotsGrids: { + readonly square: 0; + readonly hex: 1; +}; +export type HalftoneDotsGrid = keyof typeof HalftoneDotsGrids; diff --git a/public/vendor/paper-shaders/shaders/halftone-dots.js b/public/vendor/paper-shaders/shaders/halftone-dots.js new file mode 100644 index 0000000..ffdedcf --- /dev/null +++ b/public/vendor/paper-shaders/shaders/halftone-dots.js @@ -0,0 +1,321 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, rotation2, proceduralHash21 } from "../shader-utils.js"; +const halftoneDotsFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_rotation; + +uniform float u_time; + +uniform vec4 u_colorFront; +uniform vec4 u_colorBack; +uniform float u_radius; +uniform float u_contrast; + +uniform sampler2D u_image; +uniform float u_imageAspectRatio; + +uniform float u_size; +uniform float u_grainMixer; +uniform float u_grainOverlay; +uniform float u_grainSize; +uniform float u_grid; +uniform bool u_originalColors; +uniform bool u_inverted; +uniform float u_type; + +in vec2 v_imageUV; + +out vec4 fragColor; + +${declarePI} +${rotation2} +${proceduralHash21} + +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = hash21(i); + float b = hash21(i + vec2(1.0, 0.0)); + float c = hash21(i + vec2(0.0, 1.0)); + float d = hash21(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} + +float lst(float edge0, float edge1, float x) { + return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); +} + +float sst(float edge0, float edge1, float x) { + return smoothstep(edge0, edge1, x); +} + +float getCircle(vec2 uv, float r, float baseR) { + r = mix(.25 * baseR, 0., r); + float d = length(uv - .5); + float aa = fwidth(d); + return 1. - smoothstep(r - aa, r + aa, d); +} + +float getCell(vec2 uv) { + float insideX = step(0.0, uv.x) * (1.0 - step(1.0, uv.x)); + float insideY = step(0.0, uv.y) * (1.0 - step(1.0, uv.y)); + return insideX * insideY; +} + +float getCircleWithHole(vec2 uv, float r, float baseR) { + float cell = getCell(uv); + + r = mix(.75 * baseR, 0., r); + float rMod = mod(r, .5); + + float d = length(uv - .5); + float aa = fwidth(d); + float circle = 1. - smoothstep(rMod - aa, rMod + aa, d); + if (r < .5) { + return circle; + } else { + return cell - circle; + } +} + +float getGooeyBall(vec2 uv, float r, float baseR) { + float d = length(uv - .5); + float sizeRadius = .3; + if (u_grid == 1.) { + sizeRadius = .42; + } + sizeRadius = mix(sizeRadius * baseR, 0., r); + d = 1. - sst(0., sizeRadius, d); + + d = pow(d, 2. + baseR); + return d; +} + +float getSoftBall(vec2 uv, float r, float baseR) { + float d = length(uv - .5); + float sizeRadius = clamp(baseR, 0., 1.); + sizeRadius = mix(.5 * sizeRadius, 0., r); + d = 1. - lst(0., sizeRadius, d); + float powRadius = 1. - lst(0., 2., baseR); + d = pow(d, 4. + 3. * powRadius); + return d; +} + +float getUvFrame(vec2 uv, vec2 pad) { + float aa = 0.0001; + + float left = smoothstep(-pad.x, -pad.x + aa, uv.x); + float right = smoothstep(1.0 + pad.x, 1.0 + pad.x - aa, uv.x); + float bottom = smoothstep(-pad.y, -pad.y + aa, uv.y); + float top = smoothstep(1.0 + pad.y, 1.0 + pad.y - aa, uv.y); + + return left * right * bottom * top; +} + +float sigmoid(float x, float k) { + return 1.0 / (1.0 + exp(-k * (x - 0.5))); +} + +float getLumAtPx(vec2 uv, float contrast) { + vec4 tex = texture(u_image, uv); + vec3 color = vec3( + sigmoid(tex.r, contrast), + sigmoid(tex.g, contrast), + sigmoid(tex.b, contrast) + ); + float lum = dot(vec3(0.2126, 0.7152, 0.0722), color); + lum = mix(1., lum, tex.a); + lum = u_inverted ? (1. - lum) : lum; + return lum; +} + +float getLumBall(vec2 p, vec2 pad, vec2 inCellOffset, float contrast, float baseR, float stepSize, out vec4 ballColor) { + p += inCellOffset; + vec2 uv_i = floor(p); + vec2 uv_f = fract(p); + vec2 samplingUV = (uv_i + .5 - inCellOffset) * pad + vec2(.5); + float outOfFrame = getUvFrame(samplingUV, pad * stepSize); + + float lum = getLumAtPx(samplingUV, contrast); + ballColor = texture(u_image, samplingUV); + ballColor.rgb *= ballColor.a; + ballColor *= outOfFrame; + + float ball = 0.; + if (u_type == 0.) { + // classic + ball = getCircle(uv_f, lum, baseR); + } else if (u_type == 1.) { + // gooey + ball = getGooeyBall(uv_f, lum, baseR); + } else if (u_type == 2.) { + // holes + ball = getCircleWithHole(uv_f, lum, baseR); + } else if (u_type == 3.) { + // soft + ball = getSoftBall(uv_f, lum, baseR); + } + + return ball * outOfFrame; +} + + +void main() { + + float stepMultiplier = 1.; + if (u_type == 0.) { + // classic + stepMultiplier = 2.; + } else if (u_type == 1. || u_type == 3.) { + // gooey & soft + stepMultiplier = 6.; + } + + float cellsPerSide = mix(300., 7., pow(u_size, .7)); + cellsPerSide /= stepMultiplier; + float cellSizeY = 1. / cellsPerSide; + vec2 pad = cellSizeY * vec2(1. / u_imageAspectRatio, 1.); + if (u_type == 1. && u_grid == 1.) { + // gooey diagonal grid works differently + pad *= .7; + } + + vec2 uv = v_imageUV; + uv -= vec2(.5); + uv /= pad; + + float contrast = mix(0., 15., pow(u_contrast, 1.5)); + float baseRadius = u_radius; + if (u_originalColors == true) { + contrast = mix(.1, 4., pow(u_contrast, 2.)); + baseRadius = 2. * pow(.5 * u_radius, .3); + } + + float totalShape = 0.; + vec3 totalColor = vec3(0.); + float totalOpacity = 0.; + + vec4 ballColor; + float shape; + float stepSize = 1. / stepMultiplier; + for (float x = -0.5; x < 0.5; x += stepSize) { + for (float y = -0.5; y < 0.5; y += stepSize) { + vec2 offset = vec2(x, y); + + if (u_grid == 1.) { + float rowIndex = floor((y + .5) / stepSize); + float colIndex = floor((x + .5) / stepSize); + if (stepSize == 1.) { + rowIndex = floor(uv.y + y + 1.); + if (u_type == 1.) { + colIndex = floor(uv.x + x + 1.); + } + } + if (u_type == 1.) { + if (mod(rowIndex + colIndex, 2.) == 1.) { + continue; + } + } else { + if (mod(rowIndex, 2.) == 1.) { + offset.x += .5 * stepSize; + } + } + } + + shape = getLumBall(uv, pad, offset, contrast, baseRadius, stepSize, ballColor); + totalColor += ballColor.rgb * shape; + totalShape += shape; + totalOpacity += shape; + } + } + + const float eps = 1e-4; + + totalColor /= max(totalShape, eps); + totalOpacity /= max(totalShape, eps); + + float finalShape = 0.; + if (u_type == 0.) { + finalShape = min(1., totalShape); + } else if (u_type == 1.) { + float aa = fwidth(totalShape); + float th = .5; + finalShape = smoothstep(th - aa, th + aa, totalShape); + } else if (u_type == 2.) { + finalShape = min(1., totalShape); + } else if (u_type == 3.) { + finalShape = totalShape; + } + + vec2 grainSize = mix(2000., 200., u_grainSize) * vec2(1., 1. / u_imageAspectRatio); + vec2 grainUV = v_imageUV - .5; + grainUV *= grainSize; + grainUV += .5; + float grain = valueNoise(grainUV); + grain = smoothstep(.55, .7 + .2 * u_grainMixer, grain); + grain *= u_grainMixer; + finalShape = mix(finalShape, 0., grain); + + vec3 color = vec3(0.); + float opacity = 0.; + + if (u_originalColors == true) { + color = totalColor * finalShape; + opacity = totalOpacity * finalShape; + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1. - opacity); + opacity = opacity + u_colorBack.a * (1. - opacity); + } else { + vec3 fgColor = u_colorFront.rgb * u_colorFront.a; + float fgOpacity = u_colorFront.a; + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + float bgOpacity = u_colorBack.a; + + color = fgColor * finalShape; + opacity = fgOpacity * finalShape; + color += bgColor * (1. - opacity); + opacity += bgOpacity * (1. - opacity); + } + + float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.)); + grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5); + grainOverlay = pow(grainOverlay, 1.3); + + float grainOverlayV = grainOverlay * 2. - 1.; + vec3 grainOverlayColor = vec3(step(0., grainOverlayV)); + float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV); + grainOverlayStrength = pow(grainOverlayStrength, .8); + color = mix(color, grainOverlayColor, .5 * grainOverlayStrength); + + opacity += .5 * grainOverlayStrength; + opacity = clamp(opacity, 0., 1.); + + fragColor = vec4(color, opacity); +} +`; +const HalftoneDotsTypes = { + classic: 0, + gooey: 1, + holes: 2, + soft: 3 +}; +const HalftoneDotsGrids = { + square: 0, + hex: 1 +}; +export { + HalftoneDotsGrids, + HalftoneDotsTypes, + halftoneDotsFragmentShader +}; +//# sourceMappingURL=halftone-dots.js.map diff --git a/public/vendor/paper-shaders/shaders/halftone-dots.js.map b/public/vendor/paper-shaders/shaders/halftone-dots.js.map new file mode 100644 index 0000000..1d1a973 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/halftone-dots.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/halftone-dots.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\n/**\n * A halftone-dot image filter featuring customizable grids, color palettes, and dot styles.\n *\n * Fragment shader uniforms:\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_time (float): Animation time\n * - u_image (sampler2D): Source image texture\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorFront (vec4): Foreground color in RGBA\n * - u_colorBack (vec4): Background color in RGBA\n * - u_originalColors (bool): Use sampled image's original colors instead of colorFront\n * - u_type (float): Dot style (0 = classic, 1 = gooey, 2 = holes, 3 = soft)\n * - u_inverted (bool): Inverts the image luminance, doesn't affect the color scheme; not effective at zero contrast\n * - u_grid (float): Grid type (0 = square, 1 = hex)\n * - u_size (float): Grid size relative to the image box (0 to 1)\n * - u_radius (float): Maximum dot size relative to grid cell (0 to 2)\n * - u_contrast (float): Contrast applied to the sampled image (0 to 1)\n * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1)\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\n * - u_grainSize (float): Scale applied to both grain distortion and grain overlay (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const halftoneDotsFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_rotation;\n\nuniform float u_time;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\nuniform float u_radius;\nuniform float u_contrast;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_size;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\nuniform float u_grainSize;\nuniform float u_grid;\nuniform bool u_originalColors;\nuniform bool u_inverted;\nuniform float u_type;\n\nin vec2 v_imageUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\n${ proceduralHash21 }\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat lst(float edge0, float edge1, float x) {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\nfloat sst(float edge0, float edge1, float x) {\n return smoothstep(edge0, edge1, x);\n}\n\nfloat getCircle(vec2 uv, float r, float baseR) {\n r = mix(.25 * baseR, 0., r);\n float d = length(uv - .5);\n float aa = fwidth(d);\n return 1. - smoothstep(r - aa, r + aa, d);\n}\n\nfloat getCell(vec2 uv) {\n float insideX = step(0.0, uv.x) * (1.0 - step(1.0, uv.x));\n float insideY = step(0.0, uv.y) * (1.0 - step(1.0, uv.y));\n return insideX * insideY;\n}\n\nfloat getCircleWithHole(vec2 uv, float r, float baseR) {\n float cell = getCell(uv);\n\n r = mix(.75 * baseR, 0., r);\n float rMod = mod(r, .5);\n\n float d = length(uv - .5);\n float aa = fwidth(d);\n float circle = 1. - smoothstep(rMod - aa, rMod + aa, d);\n if (r < .5) {\n return circle;\n } else {\n return cell - circle;\n }\n}\n\nfloat getGooeyBall(vec2 uv, float r, float baseR) {\n float d = length(uv - .5);\n float sizeRadius = .3;\n if (u_grid == 1.) {\n sizeRadius = .42;\n }\n sizeRadius = mix(sizeRadius * baseR, 0., r);\n d = 1. - sst(0., sizeRadius, d);\n\n d = pow(d, 2. + baseR);\n return d;\n}\n\nfloat getSoftBall(vec2 uv, float r, float baseR) {\n float d = length(uv - .5);\n float sizeRadius = clamp(baseR, 0., 1.);\n sizeRadius = mix(.5 * sizeRadius, 0., r);\n d = 1. - lst(0., sizeRadius, d);\n float powRadius = 1. - lst(0., 2., baseR);\n d = pow(d, 4. + 3. * powRadius);\n return d;\n}\n\nfloat getUvFrame(vec2 uv, vec2 pad) {\n float aa = 0.0001;\n\n float left = smoothstep(-pad.x, -pad.x + aa, uv.x);\n float right = smoothstep(1.0 + pad.x, 1.0 + pad.x - aa, uv.x);\n float bottom = smoothstep(-pad.y, -pad.y + aa, uv.y);\n float top = smoothstep(1.0 + pad.y, 1.0 + pad.y - aa, uv.y);\n\n return left * right * bottom * top;\n}\n\nfloat sigmoid(float x, float k) {\n return 1.0 / (1.0 + exp(-k * (x - 0.5)));\n}\n\nfloat getLumAtPx(vec2 uv, float contrast) {\n vec4 tex = texture(u_image, uv);\n vec3 color = vec3(\n sigmoid(tex.r, contrast),\n sigmoid(tex.g, contrast),\n sigmoid(tex.b, contrast)\n );\n float lum = dot(vec3(0.2126, 0.7152, 0.0722), color);\n lum = mix(1., lum, tex.a);\n lum = u_inverted ? (1. - lum) : lum;\n return lum;\n}\n\nfloat getLumBall(vec2 p, vec2 pad, vec2 inCellOffset, float contrast, float baseR, float stepSize, out vec4 ballColor) {\n p += inCellOffset;\n vec2 uv_i = floor(p);\n vec2 uv_f = fract(p);\n vec2 samplingUV = (uv_i + .5 - inCellOffset) * pad + vec2(.5);\n float outOfFrame = getUvFrame(samplingUV, pad * stepSize);\n\n float lum = getLumAtPx(samplingUV, contrast);\n ballColor = texture(u_image, samplingUV);\n ballColor.rgb *= ballColor.a;\n ballColor *= outOfFrame;\n\n float ball = 0.;\n if (u_type == 0.) {\n // classic\n ball = getCircle(uv_f, lum, baseR);\n } else if (u_type == 1.) {\n // gooey\n ball = getGooeyBall(uv_f, lum, baseR);\n } else if (u_type == 2.) {\n // holes\n ball = getCircleWithHole(uv_f, lum, baseR);\n } else if (u_type == 3.) {\n // soft\n ball = getSoftBall(uv_f, lum, baseR);\n }\n\n return ball * outOfFrame;\n}\n\n\nvoid main() {\n\n float stepMultiplier = 1.;\n if (u_type == 0.) {\n // classic\n stepMultiplier = 2.;\n } else if (u_type == 1. || u_type == 3.) {\n // gooey & soft\n stepMultiplier = 6.;\n }\n\n float cellsPerSide = mix(300., 7., pow(u_size, .7));\n cellsPerSide /= stepMultiplier;\n float cellSizeY = 1. / cellsPerSide;\n vec2 pad = cellSizeY * vec2(1. / u_imageAspectRatio, 1.);\n if (u_type == 1. && u_grid == 1.) {\n // gooey diagonal grid works differently\n pad *= .7;\n }\n\n vec2 uv = v_imageUV;\n uv -= vec2(.5);\n uv /= pad;\n\n float contrast = mix(0., 15., pow(u_contrast, 1.5));\n float baseRadius = u_radius;\n if (u_originalColors == true) {\n contrast = mix(.1, 4., pow(u_contrast, 2.));\n baseRadius = 2. * pow(.5 * u_radius, .3);\n }\n\n float totalShape = 0.;\n vec3 totalColor = vec3(0.);\n float totalOpacity = 0.;\n\n vec4 ballColor;\n float shape;\n float stepSize = 1. / stepMultiplier;\n for (float x = -0.5; x < 0.5; x += stepSize) {\n for (float y = -0.5; y < 0.5; y += stepSize) {\n vec2 offset = vec2(x, y);\n\n if (u_grid == 1.) {\n float rowIndex = floor((y + .5) / stepSize);\n float colIndex = floor((x + .5) / stepSize);\n if (stepSize == 1.) {\n rowIndex = floor(uv.y + y + 1.);\n if (u_type == 1.) {\n colIndex = floor(uv.x + x + 1.);\n }\n }\n if (u_type == 1.) {\n if (mod(rowIndex + colIndex, 2.) == 1.) {\n continue;\n }\n } else {\n if (mod(rowIndex, 2.) == 1.) {\n offset.x += .5 * stepSize;\n }\n }\n }\n\n shape = getLumBall(uv, pad, offset, contrast, baseRadius, stepSize, ballColor);\n totalColor += ballColor.rgb * shape;\n totalShape += shape;\n totalOpacity += shape;\n }\n }\n\n const float eps = 1e-4;\n\n totalColor /= max(totalShape, eps);\n totalOpacity /= max(totalShape, eps);\n\n float finalShape = 0.;\n if (u_type == 0.) {\n finalShape = min(1., totalShape);\n } else if (u_type == 1.) {\n float aa = fwidth(totalShape);\n float th = .5;\n finalShape = smoothstep(th - aa, th + aa, totalShape);\n } else if (u_type == 2.) {\n finalShape = min(1., totalShape);\n } else if (u_type == 3.) {\n finalShape = totalShape;\n }\n\n vec2 grainSize = mix(2000., 200., u_grainSize) * vec2(1., 1. / u_imageAspectRatio);\n vec2 grainUV = v_imageUV - .5;\n grainUV *= grainSize;\n grainUV += .5;\n float grain = valueNoise(grainUV);\n grain = smoothstep(.55, .7 + .2 * u_grainMixer, grain);\n grain *= u_grainMixer;\n finalShape = mix(finalShape, 0., grain);\n\n vec3 color = vec3(0.);\n float opacity = 0.;\n\n if (u_originalColors == true) {\n color = totalColor * finalShape;\n opacity = totalOpacity * finalShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n } else {\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n color = fgColor * finalShape;\n opacity = fgOpacity * finalShape;\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n }\n\n float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.));\n grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\n grainOverlay = pow(grainOverlay, 1.3);\n\n float grainOverlayV = grainOverlay * 2. - 1.;\n vec3 grainOverlayColor = vec3(step(0., grainOverlayV));\n float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV);\n grainOverlayStrength = pow(grainOverlayStrength, .8);\n color = mix(color, grainOverlayColor, .5 * grainOverlayStrength);\n\n opacity += .5 * grainOverlayStrength;\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface HalftoneDotsUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_size: number;\n u_grid: (typeof HalftoneDotsGrids)[HalftoneDotsGrid];\n u_radius: number;\n u_contrast: number;\n u_originalColors: boolean;\n u_inverted: boolean;\n u_grainMixer: number;\n u_grainOverlay: number;\n u_grainSize: number;\n u_type: (typeof HalftoneDotsTypes)[HalftoneDotsType];\n}\n\nexport interface HalftoneDotsParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n colorFront?: string;\n colorBack?: string;\n size?: number;\n grid?: HalftoneDotsGrid;\n radius?: number;\n contrast?: number;\n originalColors?: boolean;\n inverted?: boolean;\n grainMixer?: number;\n grainOverlay?: number;\n grainSize?: number;\n type?: HalftoneDotsType;\n}\n\nexport const HalftoneDotsTypes = {\n classic: 0,\n gooey: 1,\n holes: 2,\n soft: 3,\n} as const;\n\nexport type HalftoneDotsType = keyof typeof HalftoneDotsTypes;\n\nexport const HalftoneDotsGrids = {\n square: 0,\n hex: 1,\n} as const;\n\nexport type HalftoneDotsGrid = keyof typeof HalftoneDotsGrids;\n"], + "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,WAAW,WAAW,wBAAwB;AAyChD,MAAM,6BAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4B/C,SAAU;AAAA,EACV,SAAU;AAAA,EACV,gBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6Sb,MAAM,oBAAoB;AAAA,EAC/B,SAAS;AAAA,EACT,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AACR;AAIO,MAAM,oBAAoB;AAAA,EAC/B,QAAQ;AAAA,EACR,KAAK;AACP;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/heatmap.d.ts b/public/vendor/paper-shaders/shaders/heatmap.d.ts new file mode 100644 index 0000000..0e8b624 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/heatmap.d.ts @@ -0,0 +1,67 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import type { ShaderSizingParams, ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const heatmapMeta: { + readonly maxColorCount: 10; +}; +/** + * A glowing gradient of colors flowing through an input shape. + * The effect creates a smoothly animated wave of intensity across the image. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_image (sampler2D): Pre-processed source image texture (R = contour, G = outer blur, B = inner blur) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * - u_colorBack (vec4): Background color in RGBA + * - u_colors (vec4[]): Up to 10 heatmap colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_contour (float): Heat intensity near the edges of the input shape (0 to 1) + * - u_angle (float): Direction of the heatwaves in degrees (0 to 360) + * - u_noise (float): Grain applied across the entire graphic (0 to 1) + * - u_innerGlow (float): Size of the heated area inside the input shape (0 to 1) + * - u_outerGlow (float): Size of the heated area outside the input shape (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * + */ +export declare const heatmapFragmentShader: string; +export declare function toProcessedHeatmap(file: File | string): Promise<{ + blob: Blob; +}>; +export interface HeatmapUniforms extends ShaderSizingUniforms { + u_image: HTMLImageElement | string; + u_contour: number; + u_angle: number; + u_noise: number; + u_innerGlow: number; + u_outerGlow: number; + u_colorBack: [number, number, number, number]; + u_colors: vec4[]; + u_colorsCount: number; +} +export interface HeatmapParams extends ShaderSizingParams, ShaderMotionParams { + image: HTMLImageElement | string; + contour?: number; + angle?: number; + noise?: number; + innerGlow?: number; + outerGlow?: number; + colorBack?: string; + colors?: string[]; +} diff --git a/public/vendor/paper-shaders/shaders/heatmap.js b/public/vendor/paper-shaders/shaders/heatmap.js new file mode 100644 index 0000000..2e2441b --- /dev/null +++ b/public/vendor/paper-shaders/shaders/heatmap.js @@ -0,0 +1,385 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +const heatmapMeta = { + maxColorCount: 10 +}; +const heatmapFragmentShader = `#version 300 es +precision highp float; + +in mediump vec2 v_imageUV; +in mediump vec2 v_objectUV; +out vec4 fragColor; + +uniform sampler2D u_image; +uniform float u_time; +uniform mediump float u_imageAspectRatio; + +uniform vec4 u_colorBack; +uniform vec4 u_colors[${heatmapMeta.maxColorCount}]; +uniform float u_colorsCount; + +uniform float u_angle; +uniform float u_noise; +uniform float u_innerGlow; +uniform float u_outerGlow; +uniform float u_contour; + +#define TWO_PI 6.28318530718 +#define PI 3.14159265358979323846 + +float getImgFrame(vec2 uv, float th) { + float frame = 1.; + frame *= smoothstep(0., th, uv.y); + frame *= 1. - smoothstep(1. - th, 1., uv.y); + frame *= smoothstep(0., th, uv.x); + frame *= 1. - smoothstep(1. - th, 1., uv.x); + return frame; +} + +float circle(vec2 uv, vec2 c, vec2 r) { + return 1. - smoothstep(r[0], r[1], length(uv - c)); +} + +float lst(float edge0, float edge1, float x) { + return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); +} + +float sst(float edge0, float edge1, float x) { + return smoothstep(edge0, edge1, x); +} + +float shadowShape(vec2 uv, float t, float contour) { + vec2 scaledUV = uv; + + // base shape tranjectory + float posY = mix(-1., 2., t); + + // scaleX when it's moving down + scaledUV.y -= .5; + float mainCircleScale = sst(0., .8, posY) * lst(1.4, .9, posY); + scaledUV *= vec2(1., 1. + 1.5 * mainCircleScale); + scaledUV.y += .5; + + // base shape + float innerR = .4; + float outerR = 1. - .3 * (sst(.1, .2, t) * (1. - sst(.2, .5, t))); + float s = circle(scaledUV, vec2(.5, posY - .2), vec2(innerR, outerR)); + float shapeSizing = sst(.2, .3, t) * sst(.6, .3, t); + s = pow(s, 1.4); + s *= 1.2; + + // flat gradient to take over the shadow shape + float topFlattener = 0.; + { + float pos = posY - uv.y; + float edge = 1.2; + topFlattener = lst(-.4, 0., pos) * (1. - sst(.0, edge, pos)); + topFlattener = pow(topFlattener, 3.); + float topFlattenerMixer = (1. - sst(.0, .3, pos)); + s = mix(topFlattener, s, topFlattenerMixer); + } + + // apple right circle + { + float visibility = sst(.6, .7, t) * (1. - sst(.8, .9, t)); + float angle = -2. -t * TWO_PI; + float rightCircle = circle(uv, vec2(.95 - .2 * cos(angle), .4 - .1 * sin(angle)), vec2(.15, .3)); + rightCircle *= visibility; + s = mix(s, 0., rightCircle); + } + + // apple top circle + { + float topCircle = circle(uv, vec2(.5, .19), vec2(.05, .25)); + topCircle += 2. * contour * circle(uv, vec2(.5, .19), vec2(.2, .5)); + float visibility = .55 * sst(.2, .3, t) * (1. - sst(.3, .45, t)); + topCircle *= visibility; + s = mix(s, 0., topCircle); + } + + float leafMask = circle(uv, vec2(.53, .13), vec2(.08, .19)); + leafMask = mix(leafMask, 0., 1. - sst(.4, .54, uv.x)); + leafMask = mix(0., leafMask, sst(.0, .2, uv.y)); + leafMask *= (sst(.5, 1.1, posY) * sst(1.5, 1.3, posY)); + s += leafMask; + + // apple bottom circle + { + float visibility = sst(.0, .4, t) * (1. - sst(.6, .8, t)); + s = mix(s, 0., visibility * circle(uv, vec2(.52, .92), vec2(.09, .25))); + } + + // random balls that are invisible if apple logo is selected + { + float pos = sst(.0, .6, t) * (1. - sst(.6, 1., t)); + s = mix(s, .5, circle(uv, vec2(.0, 1.2 - .5 * pos), vec2(.1, .3))); + s = mix(s, .0, circle(uv, vec2(1., .5 + .5 * pos), vec2(.1, .3))); + + s = mix(s, 1., circle(uv, vec2(.95, .2 + .2 * sst(.3, .4, t) * sst(.7, .5, t)), vec2(.07, .22))); + s = mix(s, 1., circle(uv, vec2(.95, .2 + .2 * sst(.3, .4, t) * (1. - sst(.5, .7, t))), vec2(.07, .22))); + s /= max(1e-4, sst(1., .85, uv.y)); + } + + s = clamp(0., 1., s); + return s; +} + +float blurEdge3x3(sampler2D tex, vec2 uv, vec2 dudx, vec2 dudy, float radius, float centerSample) { + vec2 texel = 1.0 / vec2(textureSize(tex, 0)); + vec2 r = radius * texel; + + float w1 = 1.0, w2 = 2.0, w4 = 4.0; + float norm = 16.0; + float sum = w4 * centerSample; + + sum += w2 * textureGrad(tex, uv + vec2(0.0, -r.y), dudx, dudy).g; + sum += w2 * textureGrad(tex, uv + vec2(0.0, r.y), dudx, dudy).g; + sum += w2 * textureGrad(tex, uv + vec2(-r.x, 0.0), dudx, dudy).g; + sum += w2 * textureGrad(tex, uv + vec2(r.x, 0.0), dudx, dudy).g; + + sum += w1 * textureGrad(tex, uv + vec2(-r.x, -r.y), dudx, dudy).g; + sum += w1 * textureGrad(tex, uv + vec2(r.x, -r.y), dudx, dudy).g; + sum += w1 * textureGrad(tex, uv + vec2(-r.x, r.y), dudx, dudy).g; + sum += w1 * textureGrad(tex, uv + vec2(r.x, r.y), dudx, dudy).g; + + return sum / norm; +} + +void main() { + vec2 uv = v_objectUV + .5; + uv.y = 1. - uv.y; + + vec2 imgUV = v_imageUV; + imgUV -= .5; + imgUV *= 0.5714285714285714; + imgUV += .5; + float imgSoftFrame = getImgFrame(imgUV, .03); + + vec4 img = texture(u_image, imgUV); + vec2 dudx = dFdx(imgUV); + vec2 dudy = dFdy(imgUV); + + if (img.a == 0.) { + fragColor = u_colorBack; + return; + } + + float t = .1 * u_time; + t -= .3; + + float tCopy = t + 1. / 3.; + float tCopy2 = t + 2. / 3.; + + t = mod(t, 1.); + tCopy = mod(tCopy, 1.); + tCopy2 = mod(tCopy2, 1.); + + vec2 animationUV = imgUV - vec2(.5); + float angle = -u_angle * PI / 180.; + float cosA = cos(angle); + float sinA = sin(angle); + animationUV = vec2( + animationUV.x * cosA - animationUV.y * sinA, + animationUV.x * sinA + animationUV.y * cosA + ) + vec2(.5); + + float shape = img[0]; + + img[1] = blurEdge3x3(u_image, imgUV, dudx, dudy, 8., img[1]); + + float outerBlur = 1. - mix(1., img[1], shape); + float innerBlur = mix(img[1], 0., shape); + float contour = mix(img[2], 0., shape); + + outerBlur *= imgSoftFrame; + + float shadow = shadowShape(animationUV, t, innerBlur); + float shadowCopy = shadowShape(animationUV, tCopy, innerBlur); + float shadowCopy2 = shadowShape(animationUV, tCopy2, innerBlur); + + float inner = .8 + .8 * innerBlur; + inner = mix(inner, 0., shadow); + inner = mix(inner, 0., shadowCopy); + inner = mix(inner, 0., shadowCopy2); + + inner *= mix(0., 2., u_innerGlow); + + inner += (u_contour * 2.) * contour; + inner = min(1., inner); + inner *= (1. - shape); + + float outer = 0.; + { + t *= 3.; + t = mod(t - .1, 1.); + + outer = .9 * pow(outerBlur, .8); + float y = mod(animationUV.y - t, 1.); + float animatedMask = sst(.3, .65, y) * (1. - sst(.65, 1., y)); + animatedMask = .5 + animatedMask; + outer *= animatedMask; + outer *= mix(0., 5., pow(u_outerGlow, 2.)); + outer *= imgSoftFrame; + } + + inner = pow(inner, 1.2); + float heat = clamp(inner + outer, 0., 1.); + + heat += (.005 + .35 * u_noise) * (fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123) - .5); + + float mixer = heat * u_colorsCount; + vec4 gradient = u_colors[0]; + gradient.rgb *= gradient.a; + float outerShape = 0.; + for (int i = 1; i < ${heatmapMeta.maxColorCount + 1}; i++) { + if (i > int(u_colorsCount)) break; + float m = clamp(mixer - float(i - 1), 0., 1.); + if (i == 1) { + outerShape = m; + } + vec4 c = u_colors[i - 1]; + c.rgb *= c.a; + gradient = mix(gradient, c, m); + } + + vec3 color = gradient.rgb * outerShape; + float opacity = gradient.a * outerShape; + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1.0 - opacity); + opacity = opacity + u_colorBack.a * (1.0 - opacity); + + color += .02 * (fract(sin(dot(uv + 1., vec2(12.9898, 78.233))) * 43758.5453123) - .5); + + fragColor = vec4(color, opacity); +} +`; +function toProcessedHeatmap(file) { + const canvas = document.createElement("canvas"); + const canvasSize = 1e3; + return new Promise((resolve, reject) => { + const image = new Image(); + image.crossOrigin = "anonymous"; + image.addEventListener("load", () => { + if (typeof file === "string" ? file.endsWith(".svg") : file.type === "image/svg+xml") { + image.width = canvasSize; + image.height = canvasSize; + } + const ratio = image.naturalWidth / image.naturalHeight; + const maxBlur = Math.floor(canvasSize * 0.15); + const padding = Math.ceil(maxBlur * 2.5); + let imgWidth = canvasSize; + let imgHeight = canvasSize; + if (ratio > 1) { + imgHeight = Math.floor(canvasSize / ratio); + } else { + imgWidth = Math.floor(canvasSize * ratio); + } + canvas.width = imgWidth + 2 * padding; + canvas.height = imgHeight + 2 * padding; + const ctx = canvas.getContext("2d", { willReadFrequently: true }); + if (!ctx) { + throw new Error("Failed to get canvas 2d context"); + } + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(image, padding, padding, imgWidth, imgHeight); + const { width, height } = canvas; + const srcImageData = ctx.getImageData(0, 0, width, height); + const src = srcImageData.data; + const totalPixels = width * height; + const gray = new Uint8ClampedArray(totalPixels); + for (let i = 0; i < totalPixels; i++) { + const px = i * 4; + const r = src[px] ?? 0; + const g = src[px + 1] ?? 0; + const b = src[px + 2] ?? 0; + gray[i] = 0.299 * r + 0.587 * g + 0.114 * b | 0; + } + const bigBlurRadius = maxBlur; + const innerBlurRadius = Math.max(1, Math.round(0.12 * maxBlur)); + const contourRadius = 5; + const bigBlurGray = multiPassBlurGray(gray, width, height, bigBlurRadius, 3); + const innerBlurGray = multiPassBlurGray(gray, width, height, innerBlurRadius, 3); + const contourGray = multiPassBlurGray(gray, width, height, contourRadius, 1); + const processedImageData = ctx.createImageData(width, height); + const dst = processedImageData.data; + for (let i = 0; i < totalPixels; i++) { + const px = i * 4; + dst[px] = contourGray[i] ?? 0; + dst[px + 1] = bigBlurGray[i] ?? 0; + dst[px + 2] = innerBlurGray[i] ?? 0; + dst[px + 3] = 255; + } + ctx.putImageData(processedImageData, 0, 0); + canvas.toBlob((blob) => { + if (!blob) { + reject(new Error("Failed to create PNG blob")); + return; + } + resolve({ blob }); + }, "image/png"); + }); + image.addEventListener("error", () => { + reject(new Error("Failed to load image")); + }); + image.src = typeof file === "string" ? file : URL.createObjectURL(file); + }); +} +function blurGray(gray, width, height, radius) { + if (radius <= 0) { + return gray.slice(); + } + const out = new Uint8ClampedArray(width * height); + const integral = new Uint32Array(width * height); + for (let y = 0; y < height; y++) { + let rowSum = 0; + for (let x = 0; x < width; x++) { + const idx = y * width + x; + const v = gray[idx] ?? 0; + rowSum += v; + integral[idx] = rowSum + (y > 0 ? integral[idx - width] ?? 0 : 0); + } + } + for (let y = 0; y < height; y++) { + const y1 = Math.max(0, y - radius); + const y2 = Math.min(height - 1, y + radius); + for (let x = 0; x < width; x++) { + const x1 = Math.max(0, x - radius); + const x2 = Math.min(width - 1, x + radius); + const idxA = y2 * width + x2; + const idxB = y2 * width + (x1 - 1); + const idxC = (y1 - 1) * width + x2; + const idxD = (y1 - 1) * width + (x1 - 1); + const A = integral[idxA] ?? 0; + const B = x1 > 0 ? integral[idxB] ?? 0 : 0; + const C = y1 > 0 ? integral[idxC] ?? 0 : 0; + const D = x1 > 0 && y1 > 0 ? integral[idxD] ?? 0 : 0; + const sum = A - B - C + D; + const area = (x2 - x1 + 1) * (y2 - y1 + 1); + out[y * width + x] = Math.round(sum / area); + } + } + return out; +} +function multiPassBlurGray(gray, width, height, radius, passes) { + if (radius <= 0 || passes <= 1) { + return blurGray(gray, width, height, radius); + } + let input = gray; + let tmp = gray; + for (let p = 0; p < passes; p++) { + tmp = blurGray(input, width, height, radius); + input = tmp; + } + return tmp; +} +export { + heatmapFragmentShader, + heatmapMeta, + toProcessedHeatmap +}; +//# sourceMappingURL=heatmap.js.map diff --git a/public/vendor/paper-shaders/shaders/heatmap.js.map b/public/vendor/paper-shaders/shaders/heatmap.js.map new file mode 100644 index 0000000..36f6d7a --- /dev/null +++ b/public/vendor/paper-shaders/shaders/heatmap.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/heatmap.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport type { ShaderSizingParams, ShaderSizingUniforms } from '../shader-sizing.js';\n\nexport const heatmapMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * A glowing gradient of colors flowing through an input shape.\n * The effect creates a smoothly animated wave of intensity across the image.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_image (sampler2D): Pre-processed source image texture (R = contour, G = outer blur, B = inner blur)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colors (vec4[]): Up to 10 heatmap colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_contour (float): Heat intensity near the edges of the input shape (0 to 1)\n * - u_angle (float): Direction of the heatwaves in degrees (0 to 360)\n * - u_noise (float): Grain applied across the entire graphic (0 to 1)\n * - u_innerGlow (float): Size of the heated area inside the input shape (0 to 1)\n * - u_outerGlow (float): Size of the heated area outside the input shape (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const heatmapFragmentShader: string = `#version 300 es\nprecision highp float;\n\nin mediump vec2 v_imageUV;\nin mediump vec2 v_objectUV;\nout vec4 fragColor;\n\nuniform sampler2D u_image;\nuniform float u_time;\nuniform mediump float u_imageAspectRatio;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${ heatmapMeta.maxColorCount }];\nuniform float u_colorsCount;\n\nuniform float u_angle;\nuniform float u_noise;\nuniform float u_innerGlow;\nuniform float u_outerGlow;\nuniform float u_contour;\n\n#define TWO_PI 6.28318530718\n#define PI 3.14159265358979323846\n\nfloat getImgFrame(vec2 uv, float th) {\n float frame = 1.;\n frame *= smoothstep(0., th, uv.y);\n frame *= 1. - smoothstep(1. - th, 1., uv.y);\n frame *= smoothstep(0., th, uv.x);\n frame *= 1. - smoothstep(1. - th, 1., uv.x);\n return frame;\n}\n\nfloat circle(vec2 uv, vec2 c, vec2 r) {\n return 1. - smoothstep(r[0], r[1], length(uv - c));\n}\n\nfloat lst(float edge0, float edge1, float x) {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\nfloat sst(float edge0, float edge1, float x) {\n return smoothstep(edge0, edge1, x);\n}\n\nfloat shadowShape(vec2 uv, float t, float contour) {\n vec2 scaledUV = uv;\n\n // base shape tranjectory\n float posY = mix(-1., 2., t);\n\n // scaleX when it's moving down\n scaledUV.y -= .5;\n float mainCircleScale = sst(0., .8, posY) * lst(1.4, .9, posY);\n scaledUV *= vec2(1., 1. + 1.5 * mainCircleScale);\n scaledUV.y += .5;\n\n // base shape\n float innerR = .4;\n float outerR = 1. - .3 * (sst(.1, .2, t) * (1. - sst(.2, .5, t)));\n float s = circle(scaledUV, vec2(.5, posY - .2), vec2(innerR, outerR));\n float shapeSizing = sst(.2, .3, t) * sst(.6, .3, t);\n s = pow(s, 1.4);\n s *= 1.2;\n\n // flat gradient to take over the shadow shape\n float topFlattener = 0.;\n {\n float pos = posY - uv.y;\n float edge = 1.2;\n topFlattener = lst(-.4, 0., pos) * (1. - sst(.0, edge, pos));\n topFlattener = pow(topFlattener, 3.);\n float topFlattenerMixer = (1. - sst(.0, .3, pos));\n s = mix(topFlattener, s, topFlattenerMixer);\n }\n\n // apple right circle\n {\n float visibility = sst(.6, .7, t) * (1. - sst(.8, .9, t));\n float angle = -2. -t * TWO_PI;\n float rightCircle = circle(uv, vec2(.95 - .2 * cos(angle), .4 - .1 * sin(angle)), vec2(.15, .3));\n rightCircle *= visibility;\n s = mix(s, 0., rightCircle);\n }\n\n // apple top circle\n {\n float topCircle = circle(uv, vec2(.5, .19), vec2(.05, .25));\n topCircle += 2. * contour * circle(uv, vec2(.5, .19), vec2(.2, .5));\n float visibility = .55 * sst(.2, .3, t) * (1. - sst(.3, .45, t));\n topCircle *= visibility;\n s = mix(s, 0., topCircle);\n }\n\n float leafMask = circle(uv, vec2(.53, .13), vec2(.08, .19));\n leafMask = mix(leafMask, 0., 1. - sst(.4, .54, uv.x));\n leafMask = mix(0., leafMask, sst(.0, .2, uv.y));\n leafMask *= (sst(.5, 1.1, posY) * sst(1.5, 1.3, posY));\n s += leafMask;\n\n // apple bottom circle\n {\n float visibility = sst(.0, .4, t) * (1. - sst(.6, .8, t));\n s = mix(s, 0., visibility * circle(uv, vec2(.52, .92), vec2(.09, .25)));\n }\n\n // random balls that are invisible if apple logo is selected\n {\n float pos = sst(.0, .6, t) * (1. - sst(.6, 1., t));\n s = mix(s, .5, circle(uv, vec2(.0, 1.2 - .5 * pos), vec2(.1, .3)));\n s = mix(s, .0, circle(uv, vec2(1., .5 + .5 * pos), vec2(.1, .3)));\n\n s = mix(s, 1., circle(uv, vec2(.95, .2 + .2 * sst(.3, .4, t) * sst(.7, .5, t)), vec2(.07, .22)));\n s = mix(s, 1., circle(uv, vec2(.95, .2 + .2 * sst(.3, .4, t) * (1. - sst(.5, .7, t))), vec2(.07, .22)));\n s /= max(1e-4, sst(1., .85, uv.y));\n }\n\n s = clamp(0., 1., s);\n return s;\n}\n\nfloat blurEdge3x3(sampler2D tex, vec2 uv, vec2 dudx, vec2 dudy, float radius, float centerSample) {\n vec2 texel = 1.0 / vec2(textureSize(tex, 0));\n vec2 r = radius * texel;\n\n float w1 = 1.0, w2 = 2.0, w4 = 4.0;\n float norm = 16.0;\n float sum = w4 * centerSample;\n\n sum += w2 * textureGrad(tex, uv + vec2(0.0, -r.y), dudx, dudy).g;\n sum += w2 * textureGrad(tex, uv + vec2(0.0, r.y), dudx, dudy).g;\n sum += w2 * textureGrad(tex, uv + vec2(-r.x, 0.0), dudx, dudy).g;\n sum += w2 * textureGrad(tex, uv + vec2(r.x, 0.0), dudx, dudy).g;\n\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, -r.y), dudx, dudy).g;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, -r.y), dudx, dudy).g;\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, r.y), dudx, dudy).g;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, r.y), dudx, dudy).g;\n\n return sum / norm;\n}\n\nvoid main() {\n vec2 uv = v_objectUV + .5;\n uv.y = 1. - uv.y;\n\n vec2 imgUV = v_imageUV;\n imgUV -= .5;\n imgUV *= 0.5714285714285714;\n imgUV += .5;\n float imgSoftFrame = getImgFrame(imgUV, .03);\n\n vec4 img = texture(u_image, imgUV);\n vec2 dudx = dFdx(imgUV);\n vec2 dudy = dFdy(imgUV);\n\n if (img.a == 0.) {\n fragColor = u_colorBack;\n return;\n }\n\n float t = .1 * u_time;\n t -= .3;\n\n float tCopy = t + 1. / 3.;\n float tCopy2 = t + 2. / 3.;\n\n t = mod(t, 1.);\n tCopy = mod(tCopy, 1.);\n tCopy2 = mod(tCopy2, 1.);\n\n vec2 animationUV = imgUV - vec2(.5);\n float angle = -u_angle * PI / 180.;\n float cosA = cos(angle);\n float sinA = sin(angle);\n animationUV = vec2(\n animationUV.x * cosA - animationUV.y * sinA,\n animationUV.x * sinA + animationUV.y * cosA\n ) + vec2(.5);\n\n float shape = img[0];\n\n img[1] = blurEdge3x3(u_image, imgUV, dudx, dudy, 8., img[1]);\n\n float outerBlur = 1. - mix(1., img[1], shape);\n float innerBlur = mix(img[1], 0., shape);\n float contour = mix(img[2], 0., shape);\n\n outerBlur *= imgSoftFrame;\n\n float shadow = shadowShape(animationUV, t, innerBlur);\n float shadowCopy = shadowShape(animationUV, tCopy, innerBlur);\n float shadowCopy2 = shadowShape(animationUV, tCopy2, innerBlur);\n\n float inner = .8 + .8 * innerBlur;\n inner = mix(inner, 0., shadow);\n inner = mix(inner, 0., shadowCopy);\n inner = mix(inner, 0., shadowCopy2);\n\n inner *= mix(0., 2., u_innerGlow);\n\n inner += (u_contour * 2.) * contour;\n inner = min(1., inner);\n inner *= (1. - shape);\n\n float outer = 0.;\n {\n t *= 3.;\n t = mod(t - .1, 1.);\n\n outer = .9 * pow(outerBlur, .8);\n float y = mod(animationUV.y - t, 1.);\n float animatedMask = sst(.3, .65, y) * (1. - sst(.65, 1., y));\n animatedMask = .5 + animatedMask;\n outer *= animatedMask;\n outer *= mix(0., 5., pow(u_outerGlow, 2.));\n outer *= imgSoftFrame;\n }\n\n inner = pow(inner, 1.2);\n float heat = clamp(inner + outer, 0., 1.);\n\n heat += (.005 + .35 * u_noise) * (fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123) - .5);\n\n float mixer = heat * u_colorsCount;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n float outerShape = 0.;\n for (int i = 1; i < ${ heatmapMeta.maxColorCount + 1 }; i++) {\n if (i > int(u_colorsCount)) break;\n float m = clamp(mixer - float(i - 1), 0., 1.);\n if (i == 1) {\n outerShape = m;\n }\n vec4 c = u_colors[i - 1];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n vec3 color = gradient.rgb * outerShape;\n float opacity = gradient.a * outerShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1.0 - opacity);\n opacity = opacity + u_colorBack.a * (1.0 - opacity);\n\n color += .02 * (fract(sin(dot(uv + 1., vec2(12.9898, 78.233))) * 43758.5453123) - .5);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport function toProcessedHeatmap(file: File | string): Promise<{ blob: Blob }> {\n const canvas = document.createElement('canvas');\n const canvasSize = 1000;\n\n return new Promise((resolve, reject) => {\n const image = new Image();\n image.crossOrigin = 'anonymous';\n\n image.addEventListener('load', () => {\n if (typeof file === 'string' ? file.endsWith('.svg') : file.type === 'image/svg+xml') {\n // Force SVG to load at a high fidelity size if it's an SVG\n image.width = canvasSize;\n image.height = canvasSize;\n }\n\n const ratio = image.naturalWidth / image.naturalHeight;\n\n const maxBlur = Math.floor(canvasSize * 0.15);\n const padding = Math.ceil(maxBlur * 2.5);\n let imgWidth = canvasSize;\n let imgHeight = canvasSize;\n if (ratio > 1) {\n imgHeight = Math.floor(canvasSize / ratio);\n } else {\n imgWidth = Math.floor(canvasSize * ratio);\n }\n\n canvas.width = imgWidth + 2 * padding;\n canvas.height = imgHeight + 2 * padding;\n\n const ctx = canvas.getContext('2d', { willReadFrequently: true });\n if (!ctx) {\n throw new Error('Failed to get canvas 2d context');\n }\n\n // 1) Draw original image once, no filters\n ctx.fillStyle = 'white';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(image, padding, padding, imgWidth, imgHeight);\n\n const { width, height } = canvas;\n const srcImageData = ctx.getImageData(0, 0, width, height);\n const src = srcImageData.data; // RGBA\n\n // 2) Build grayscale array (luma)\n const totalPixels = width * height;\n const gray = new Uint8ClampedArray(totalPixels);\n for (let i = 0; i < totalPixels; i++) {\n const px = i * 4;\n const r = src[px] ?? 0;\n const g = src[px + 1] ?? 0;\n const b = src[px + 2] ?? 0;\n // Standard luma conversion\n gray[i] = (0.299 * r + 0.587 * g + 0.114 * b) | 0;\n }\n\n // 3) Blur grayscale for each \"filter\" you previously used\n const bigBlurRadius = maxBlur;\n const innerBlurRadius = Math.max(1, Math.round(0.12 * maxBlur));\n const contourRadius = 5;\n\n const bigBlurGray = multiPassBlurGray(gray, width, height, bigBlurRadius, 3);\n const innerBlurGray = multiPassBlurGray(gray, width, height, innerBlurRadius, 3);\n const contourGray = multiPassBlurGray(gray, width, height, contourRadius, 1);\n\n // 4) Combine into final ImageData\n const processedImageData = ctx.createImageData(width, height);\n const dst = processedImageData.data;\n\n for (let i = 0; i < totalPixels; i++) {\n const px = i * 4;\n dst[px] = contourGray[i] ?? 0;\n dst[px + 1] = bigBlurGray[i] ?? 0;\n dst[px + 2] = innerBlurGray[i] ?? 0;\n dst[px + 3] = 255;\n }\n\n ctx.putImageData(processedImageData, 0, 0);\n\n canvas.toBlob((blob) => {\n if (!blob) {\n reject(new Error('Failed to create PNG blob'));\n return;\n }\n resolve({ blob });\n }, 'image/png');\n });\n\n image.addEventListener('error', () => {\n reject(new Error('Failed to load image'));\n });\n\n image.src = typeof file === 'string' ? file : URL.createObjectURL(file);\n });\n}\n\n/**\n * Fast box blur for grayscale images using an integral image.\n * gray: Uint8ClampedArray of length width * height\n * radius: blur radius in pixels\n */\nfunction blurGray(gray: Uint8ClampedArray, width: number, height: number, radius: number): Uint8ClampedArray {\n if (radius <= 0) {\n return gray.slice();\n }\n\n const out = new Uint8ClampedArray(width * height);\n const integral = new Uint32Array(width * height);\n\n // Build integral image\n for (let y = 0; y < height; y++) {\n let rowSum = 0;\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const v = gray[idx] ?? 0;\n rowSum += v;\n integral[idx] = rowSum + (y > 0 ? (integral[idx - width] ?? 0) : 0);\n }\n }\n\n // Blur using integral image\n for (let y = 0; y < height; y++) {\n const y1 = Math.max(0, y - radius);\n const y2 = Math.min(height - 1, y + radius);\n for (let x = 0; x < width; x++) {\n const x1 = Math.max(0, x - radius);\n const x2 = Math.min(width - 1, x + radius);\n\n const idxA = y2 * width + x2;\n const idxB = y2 * width + (x1 - 1);\n const idxC = (y1 - 1) * width + x2;\n const idxD = (y1 - 1) * width + (x1 - 1);\n\n const A = integral[idxA] ?? 0;\n const B = x1 > 0 ? (integral[idxB] ?? 0) : 0;\n const C = y1 > 0 ? (integral[idxC] ?? 0) : 0;\n const D = x1 > 0 && y1 > 0 ? (integral[idxD] ?? 0) : 0;\n\n const sum = A - B - C + D;\n const area = (x2 - x1 + 1) * (y2 - y1 + 1);\n out[y * width + x] = Math.round(sum / area);\n }\n }\n\n return out;\n}\n\nfunction multiPassBlurGray(\n gray: Uint8ClampedArray,\n width: number,\n height: number,\n radius: number,\n passes: number\n): Uint8ClampedArray {\n if (radius <= 0 || passes <= 1) {\n return blurGray(gray, width, height, radius);\n }\n\n let input = gray;\n let tmp: Uint8ClampedArray = gray;\n\n for (let p = 0; p < passes; p++) {\n tmp = blurGray(input, width, height, radius);\n input = tmp;\n }\n\n return tmp;\n}\n\nexport interface HeatmapUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string;\n u_contour: number;\n u_angle: number;\n u_noise: number;\n u_innerGlow: number;\n u_outerGlow: number;\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n}\n\nexport interface HeatmapParams extends ShaderSizingParams, ShaderMotionParams {\n image: HTMLImageElement | string;\n contour?: number;\n angle?: number;\n noise?: number;\n innerGlow?: number;\n outerGlow?: number;\n colorBack?: string;\n colors?: string[];\n}\n"], + "mappings": "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", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/image-dithering.d.ts b/public/vendor/paper-shaders/shaders/image-dithering.d.ts new file mode 100644 index 0000000..3392dfa --- /dev/null +++ b/public/vendor/paper-shaders/shaders/image-dithering.d.ts @@ -0,0 +1,66 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * A dithering image filter with support for 4 dithering modes and multiple color palettes + * (2-color, 3-color, and multicolor options, using either predefined colors or colors sampled + * from the original image). + * + * SIZING NOTE: This shader performs sizing in the fragment shader (not vertex shader) to keep + * u_pxSize in consistent actual pixels. The pixel grid is computed from gl_FragCoord before any + * transforms, so scaling/rotating only affects the underlying image. + * No vertex shader outputs (v_imageUV, v_objectUV, etc.) are used. + * + * Fragment shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_image (sampler2D): Source image texture + * - u_imageAspectRatio (float): Aspect ratio of the source image + * - u_colorFront (vec4): Foreground color in RGBA + * - u_colorBack (vec4): Background color in RGBA + * - u_colorHighlight (vec4): Secondary foreground color in RGBA (set same as colorFront for classic 2-color dithering) + * - u_originalColors (bool): Use the original colors of the image instead of the color palette + * - u_inverted (bool): Inverts the image luminance, doesn't affect the color scheme; not effective at zero contrast + * - u_type (float): Dithering type (1 = random, 2 = 2x2 Bayer, 3 = 4x4 Bayer, 4 = 8x8 Bayer) + * - u_pxSize (float): Pixel size of dithering grid (0.5 to 20) + * - u_colorSteps (float): Number of colors to use, applies to both color modes (1 to 7) + * + */ +export declare const imageDitheringFragmentShader: string; +export interface ImageDitheringUniforms extends ShaderSizingUniforms { + u_image: HTMLImageElement | string; + u_colorFront: [number, number, number, number]; + u_colorBack: [number, number, number, number]; + u_colorHighlight: [number, number, number, number]; + u_type: (typeof DitheringTypes)[DitheringType]; + u_pxSize: number; + u_colorSteps: number; + u_originalColors: boolean; + u_inverted: boolean; +} +export interface ImageDitheringParams extends ShaderSizingParams, ShaderMotionParams { + image: HTMLImageElement | string; + colorFront?: string; + colorBack?: string; + colorHighlight?: string; + type?: DitheringType; + size?: number; + colorSteps?: number; + originalColors?: boolean; + inverted?: boolean; +} +export declare const DitheringTypes: { + readonly random: 1; + readonly '2x2': 2; + readonly '4x4': 3; + readonly '8x8': 4; +}; +export type DitheringType = keyof typeof DitheringTypes; diff --git a/public/vendor/paper-shaders/shaders/image-dithering.js b/public/vendor/paper-shaders/shaders/image-dithering.js new file mode 100644 index 0000000..82f3fcf --- /dev/null +++ b/public/vendor/paper-shaders/shaders/image-dithering.js @@ -0,0 +1,201 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { proceduralHash21, declarePI } from "../shader-utils.js"; +const imageDitheringFragmentShader = `#version 300 es +precision mediump float; + +uniform vec2 u_resolution; +uniform float u_pixelRatio; +uniform float u_originX; +uniform float u_originY; +uniform float u_worldWidth; +uniform float u_worldHeight; +uniform float u_fit; + +uniform float u_scale; +uniform float u_rotation; +uniform float u_offsetX; +uniform float u_offsetY; + +uniform vec4 u_colorFront; +uniform vec4 u_colorBack; +uniform vec4 u_colorHighlight; + +uniform sampler2D u_image; +uniform float u_imageAspectRatio; + +uniform float u_type; +uniform float u_pxSize; +uniform bool u_originalColors; +uniform bool u_inverted; +uniform float u_colorSteps; + +out vec4 fragColor; + + +${proceduralHash21} +${declarePI} + +float getUvFrame(vec2 uv, vec2 pad) { + float aa = 0.0001; + + float left = smoothstep(-pad.x, -pad.x + aa, uv.x); + float right = smoothstep(1.0 + pad.x, 1.0 + pad.x - aa, uv.x); + float bottom = smoothstep(-pad.y, -pad.y + aa, uv.y); + float top = smoothstep(1.0 + pad.y, 1.0 + pad.y - aa, uv.y); + + return left * right * bottom * top; +} + +vec2 getImageUV(vec2 uv) { + vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5); + float r = u_rotation * PI / 180.; + mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r)); + vec2 graphicOffset = vec2(-u_offsetX, u_offsetY); + + vec2 imageBoxSize; + if (u_fit == 1.) { // contain + imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio; + } else if (u_fit == 2.) { // cover + imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio; + } else { + imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio); + } + imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio; + vec2 imageBoxScale = u_resolution.xy / imageBoxSize; + + vec2 imageUV = uv; + imageUV *= imageBoxScale; + imageUV += boxOrigin * (imageBoxScale - 1.); + imageUV += graphicOffset; + imageUV /= u_scale; + imageUV.x *= u_imageAspectRatio; + imageUV = graphicRotation * imageUV; + imageUV.x /= u_imageAspectRatio; + + imageUV += .5; + imageUV.y = 1. - imageUV.y; + + return imageUV; +} + +const int bayer2x2[4] = int[4](0, 2, 3, 1); +const int bayer4x4[16] = int[16]( +0, 8, 2, 10, +12, 4, 14, 6, +3, 11, 1, 9, +15, 7, 13, 5 +); + +const int bayer8x8[64] = int[64]( +0, 32, 8, 40, 2, 34, 10, 42, +48, 16, 56, 24, 50, 18, 58, 26, +12, 44, 4, 36, 14, 46, 6, 38, +60, 28, 52, 20, 62, 30, 54, 22, +3, 35, 11, 43, 1, 33, 9, 41, +51, 19, 59, 27, 49, 17, 57, 25, +15, 47, 7, 39, 13, 45, 5, 37, +63, 31, 55, 23, 61, 29, 53, 21 +); + +float getBayerValue(vec2 uv, int size) { + ivec2 pos = ivec2(fract(uv / float(size)) * float(size)); + int index = pos.y * size + pos.x; + + if (size == 2) { + return float(bayer2x2[index]) / 4.0; + } else if (size == 4) { + return float(bayer4x4[index]) / 16.0; + } else if (size == 8) { + return float(bayer8x8[index]) / 64.0; + } + return 0.0; +} + + +void main() { + + float pxSize = u_pxSize * u_pixelRatio; + vec2 pxSizeUV = gl_FragCoord.xy - .5 * u_resolution; + pxSizeUV /= pxSize; + vec2 canvasPixelizedUV = (floor(pxSizeUV) + .5) * pxSize; + vec2 normalizedUV = canvasPixelizedUV / u_resolution; + + vec2 imageUV = getImageUV(normalizedUV); + vec2 ditheringNoiseUV = canvasPixelizedUV; + vec4 image = texture(u_image, imageUV); + float frame = getUvFrame(imageUV, pxSize / u_resolution); + + int type = int(floor(u_type)); + float dithering = 0.0; + + float lum = dot(vec3(.2126, .7152, .0722), image.rgb); + lum = u_inverted ? (1. - lum) : lum; + + switch (type) { + case 1: { + dithering = step(hash21(ditheringNoiseUV), lum); + } break; + case 2: + dithering = getBayerValue(pxSizeUV, 2); + break; + case 3: + dithering = getBayerValue(pxSizeUV, 4); + break; + default : + dithering = getBayerValue(pxSizeUV, 8); + break; + } + + float colorSteps = max(floor(u_colorSteps), 1.); + vec3 color = vec3(0.0); + float opacity = 1.; + + dithering -= .5; + float brightness = clamp(lum + dithering / colorSteps, 0.0, 1.0); + brightness = mix(0.0, brightness, frame); + brightness = mix(0.0, brightness, image.a); + float quantLum = floor(brightness * colorSteps + 0.5) / colorSteps; + quantLum = mix(0.0, quantLum, frame); + + if (u_originalColors == true) { + vec3 normColor = image.rgb / max(lum, 0.001); + color = normColor * quantLum; + + float quantAlpha = floor(image.a * colorSteps + 0.5) / colorSteps; + opacity = mix(quantLum, 1., quantAlpha); + } else { + vec3 fgColor = u_colorFront.rgb * u_colorFront.a; + float fgOpacity = u_colorFront.a; + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + float bgOpacity = u_colorBack.a; + vec3 hlColor = u_colorHighlight.rgb * u_colorHighlight.a; + float hlOpacity = u_colorHighlight.a; + + fgColor = mix(fgColor, hlColor, step(1.02 - .02 * u_colorSteps, brightness)); + fgOpacity = mix(fgOpacity, hlOpacity, step(1.02 - .02 * u_colorSteps, brightness)); + + color = fgColor * quantLum; + opacity = fgOpacity * quantLum; + color += bgColor * (1.0 - opacity); + opacity += bgOpacity * (1.0 - opacity); + } + + fragColor = vec4(color, opacity); +} +`; +const DitheringTypes = { + "random": 1, + "2x2": 2, + "4x4": 3, + "8x8": 4 +}; +export { + DitheringTypes, + imageDitheringFragmentShader +}; +//# sourceMappingURL=image-dithering.js.map diff --git a/public/vendor/paper-shaders/shaders/image-dithering.js.map b/public/vendor/paper-shaders/shaders/image-dithering.js.map new file mode 100644 index 0000000..eb53c89 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/image-dithering.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/image-dithering.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { proceduralHash21, declarePI } from '../shader-utils.js';\n\n/**\n * A dithering image filter with support for 4 dithering modes and multiple color palettes\n * (2-color, 3-color, and multicolor options, using either predefined colors or colors sampled\n * from the original image).\n *\n * SIZING NOTE: This shader performs sizing in the fragment shader (not vertex shader) to keep\n * u_pxSize in consistent actual pixels. The pixel grid is computed from gl_FragCoord before any\n * transforms, so scaling/rotating only affects the underlying image.\n * No vertex shader outputs (v_imageUV, v_objectUV, etc.) are used.\n *\n * Fragment shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_image (sampler2D): Source image texture\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorFront (vec4): Foreground color in RGBA\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colorHighlight (vec4): Secondary foreground color in RGBA (set same as colorFront for classic 2-color dithering)\n * - u_originalColors (bool): Use the original colors of the image instead of the color palette\n * - u_inverted (bool): Inverts the image luminance, doesn't affect the color scheme; not effective at zero contrast\n * - u_type (float): Dithering type (1 = random, 2 = 2x2 Bayer, 3 = 4x4 Bayer, 4 = 8x8 Bayer)\n * - u_pxSize (float): Pixel size of dithering grid (0.5 to 20)\n * - u_colorSteps (float): Number of colors to use, applies to both color modes (1 to 7)\n *\n */\n\n// language=GLSL\nexport const imageDitheringFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\nuniform float u_originX;\nuniform float u_originY;\nuniform float u_worldWidth;\nuniform float u_worldHeight;\nuniform float u_fit;\n\nuniform float u_scale;\nuniform float u_rotation;\nuniform float u_offsetX;\nuniform float u_offsetY;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\nuniform vec4 u_colorHighlight;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_type;\nuniform float u_pxSize;\nuniform bool u_originalColors;\nuniform bool u_inverted;\nuniform float u_colorSteps;\n\nout vec4 fragColor;\n\n\n${ proceduralHash21 }\n${ declarePI }\n\nfloat getUvFrame(vec2 uv, vec2 pad) {\n float aa = 0.0001;\n\n float left = smoothstep(-pad.x, -pad.x + aa, uv.x);\n float right = smoothstep(1.0 + pad.x, 1.0 + pad.x - aa, uv.x);\n float bottom = smoothstep(-pad.y, -pad.y + aa, uv.y);\n float top = smoothstep(1.0 + pad.y, 1.0 + pad.y - aa, uv.y);\n\n return left * right * bottom * top;\n}\n\nvec2 getImageUV(vec2 uv) {\n vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);\n float r = u_rotation * PI / 180.;\n mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n\n vec2 imageBoxSize;\n if (u_fit == 1.) { // contain\n imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else if (u_fit == 2.) { // cover\n imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else {\n imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio);\n }\n imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio;\n vec2 imageBoxScale = u_resolution.xy / imageBoxSize;\n\n vec2 imageUV = uv;\n imageUV *= imageBoxScale;\n imageUV += boxOrigin * (imageBoxScale - 1.);\n imageUV += graphicOffset;\n imageUV /= u_scale;\n imageUV.x *= u_imageAspectRatio;\n imageUV = graphicRotation * imageUV;\n imageUV.x /= u_imageAspectRatio;\n\n imageUV += .5;\n imageUV.y = 1. - imageUV.y;\n\n return imageUV;\n}\n\nconst int bayer2x2[4] = int[4](0, 2, 3, 1);\nconst int bayer4x4[16] = int[16](\n0, 8, 2, 10,\n12, 4, 14, 6,\n3, 11, 1, 9,\n15, 7, 13, 5\n);\n\nconst int bayer8x8[64] = int[64](\n0, 32, 8, 40, 2, 34, 10, 42,\n48, 16, 56, 24, 50, 18, 58, 26,\n12, 44, 4, 36, 14, 46, 6, 38,\n60, 28, 52, 20, 62, 30, 54, 22,\n3, 35, 11, 43, 1, 33, 9, 41,\n51, 19, 59, 27, 49, 17, 57, 25,\n15, 47, 7, 39, 13, 45, 5, 37,\n63, 31, 55, 23, 61, 29, 53, 21\n);\n\nfloat getBayerValue(vec2 uv, int size) {\n ivec2 pos = ivec2(fract(uv / float(size)) * float(size));\n int index = pos.y * size + pos.x;\n\n if (size == 2) {\n return float(bayer2x2[index]) / 4.0;\n } else if (size == 4) {\n return float(bayer4x4[index]) / 16.0;\n } else if (size == 8) {\n return float(bayer8x8[index]) / 64.0;\n }\n return 0.0;\n}\n\n\nvoid main() {\n\n float pxSize = u_pxSize * u_pixelRatio;\n vec2 pxSizeUV = gl_FragCoord.xy - .5 * u_resolution;\n pxSizeUV /= pxSize;\n vec2 canvasPixelizedUV = (floor(pxSizeUV) + .5) * pxSize;\n vec2 normalizedUV = canvasPixelizedUV / u_resolution;\n\n vec2 imageUV = getImageUV(normalizedUV);\n vec2 ditheringNoiseUV = canvasPixelizedUV;\n vec4 image = texture(u_image, imageUV);\n float frame = getUvFrame(imageUV, pxSize / u_resolution);\n\n int type = int(floor(u_type));\n float dithering = 0.0;\n\n float lum = dot(vec3(.2126, .7152, .0722), image.rgb);\n lum = u_inverted ? (1. - lum) : lum;\n\n switch (type) {\n case 1: {\n dithering = step(hash21(ditheringNoiseUV), lum);\n } break;\n case 2:\n dithering = getBayerValue(pxSizeUV, 2);\n break;\n case 3:\n dithering = getBayerValue(pxSizeUV, 4);\n break;\n default :\n dithering = getBayerValue(pxSizeUV, 8);\n break;\n }\n\n float colorSteps = max(floor(u_colorSteps), 1.);\n vec3 color = vec3(0.0);\n float opacity = 1.;\n\n dithering -= .5;\n float brightness = clamp(lum + dithering / colorSteps, 0.0, 1.0);\n brightness = mix(0.0, brightness, frame);\n brightness = mix(0.0, brightness, image.a);\n float quantLum = floor(brightness * colorSteps + 0.5) / colorSteps;\n quantLum = mix(0.0, quantLum, frame);\n\n if (u_originalColors == true) {\n vec3 normColor = image.rgb / max(lum, 0.001);\n color = normColor * quantLum;\n\n float quantAlpha = floor(image.a * colorSteps + 0.5) / colorSteps;\n opacity = mix(quantLum, 1., quantAlpha);\n } else {\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n vec3 hlColor = u_colorHighlight.rgb * u_colorHighlight.a;\n float hlOpacity = u_colorHighlight.a;\n\n fgColor = mix(fgColor, hlColor, step(1.02 - .02 * u_colorSteps, brightness));\n fgOpacity = mix(fgOpacity, hlOpacity, step(1.02 - .02 * u_colorSteps, brightness));\n\n color = fgColor * quantLum;\n opacity = fgOpacity * quantLum;\n color += bgColor * (1.0 - opacity);\n opacity += bgOpacity * (1.0 - opacity);\n }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface ImageDitheringUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string;\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_colorHighlight: [number, number, number, number];\n u_type: (typeof DitheringTypes)[DitheringType];\n u_pxSize: number;\n u_colorSteps: number;\n u_originalColors: boolean;\n u_inverted: boolean;\n}\n\nexport interface ImageDitheringParams extends ShaderSizingParams, ShaderMotionParams {\n image: HTMLImageElement | string;\n colorFront?: string;\n colorBack?: string;\n colorHighlight?: string;\n type?: DitheringType;\n size?: number;\n colorSteps?: number;\n originalColors?: boolean;\n inverted?: boolean;\n}\n\nexport const DitheringTypes = {\n 'random': 1,\n '2x2': 2,\n '4x4': 3,\n '8x8': 4,\n} as const;\n\nexport type DitheringType = keyof typeof DitheringTypes;\n"], + "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,kBAAkB,iBAAiB;AAsCrC,MAAM,+BAAuC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgCjD,gBAAiB;AAAA,EACjB,SAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+KN,MAAM,iBAAiB;AAAA,EAC5B,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/lens-distortion.d.ts b/public/vendor/paper-shaders/shaders/lens-distortion.d.ts new file mode 100644 index 0000000..8a1970c --- /dev/null +++ b/public/vendor/paper-shaders/shaders/lens-distortion.d.ts @@ -0,0 +1,97 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const lensDistortionMeta: { + readonly maxSamples: 50; +}; +/** + * + * Lens Distortion image filter separates an image into shifting color layers + * (recreating the chromatic aberration of a lens) and warps the image geometry, + * curving it outward or inward like barrel and pincushion distortion. + * + * Fragment shader uniforms: + * - u_image (sampler2D): Source image texture + * - u_spread (float): Strength of the color split; how far the color layers are pushed apart; 0 is off (0 to 1) + * - u_bias (float): Shifts the colors toward one end of the spread; 0 spaces them evenly (-1 to 1) + * - u_angle (float): Direction of the spread in degrees (0 to 360) + * - u_perspective (float): Shapes the spread direction from a straight line (0) to a radial burst out from the centre (1) (0 to 1) + * - u_count (float): Number of sampled color layers along the spread; higher is smoother and costlier (2 to 50) + * - u_dispersion (float): Overall amount of color dispersion; 1 gives each layer its own color from the spectrum, 0 keeps the original image color (0 to 1) + * - u_dispersionShift (float): Balance of the dispersion between a soft circular zone at the centre and the rest of the image; 0 applies it evenly, -1 keeps it in the centre only, 1 keeps it at the edges only (-1 to 1) + * - u_dispersionColor (float): Rotates the colors around the hue wheel, 0 to 1 for a full turn + * - u_focusCenter (float): Reduces the spread in a circular zone at the centre; 0 keeps it full to the centre (0 to 1) + * - u_focusEdges (float): Reduces the spread toward the edges; 0 keeps it full to the edges, 1 restores the original image there (0 to 1) + * - u_swirl (float): Rotates the color layers around the centre by an angle growing along the spread; 0 is off (-1 to 1) + * - u_noise (float): Scatters the spread direction with noise; 0 is off (0 to 1) + * - u_noiseFrequency (float): Frequency of the noise, 0 to 1 mapped internally to 0 to 18; higher is finer (no effect with noise = 0) + * - u_noiseOffset (float): Offsets the noise pattern for a different seed, 0 to 1 mapped internally to 0 to 30 (no effect with noise = 0) + * - u_lensBulge (float): Radial lens warp of the image geometry; positive bulges out like a fisheye/barrel, negative pinches in like a pincushion; strong positive values fade out the corners (-1 to 1) + * - u_lensCircle (float): Squeezes pixels outside the inscribed circle inward so the outline becomes a circle; also turns the spread radial and damps it near the rim; 0 is off, 1 is full (0 to 1) + * - u_grainMixer (float): Scatters the spread with grain noise, breaking up the edges of the color layers (0 to 1) + * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1) + * - u_imageX (float): Pans the image horizontally behind the effect; 0 is centred (-1 to 1) + * - u_imageY (float): Pans the image vertically behind the effect; 0 is centred (-1 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * + */ +export declare const lensDistortionFragmentShader: string; +export interface LensDistortionUniforms extends ShaderSizingUniforms { + u_image: HTMLImageElement | string | undefined; + u_spread: number; + u_bias: number; + u_angle: number; + u_perspective: number; + u_count: number; + u_dispersion: number; + u_dispersionShift: number; + u_dispersionColor: number; + u_focusCenter: number; + u_focusEdges: number; + u_swirl: number; + u_noise: number; + u_noiseFrequency: number; + u_noiseOffset: number; + u_lensBulge: number; + u_lensCircle: number; + u_grainMixer: number; + u_grainOverlay: number; + u_imageX: number; + u_imageY: number; +} +export interface LensDistortionParams extends ShaderSizingParams, ShaderMotionParams { + image?: HTMLImageElement | string; + spread?: number; + bias?: number; + angle?: number; + perspective?: number; + count?: number; + dispersion?: number; + dispersionShift?: number; + dispersionColor?: number; + focusCenter?: number; + focusEdges?: number; + swirl?: number; + noise?: number; + noiseFrequency?: number; + noiseOffset?: number; + lensBulge?: number; + lensCircle?: number; + grainMixer?: number; + grainOverlay?: number; + imageX?: number; + imageY?: number; +} diff --git a/public/vendor/paper-shaders/shaders/lens-distortion.js b/public/vendor/paper-shaders/shaders/lens-distortion.js new file mode 100644 index 0000000..98a6cc4 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/lens-distortion.js @@ -0,0 +1,276 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, proceduralHash21, rotation2 } from "../shader-utils.js"; +const lensDistortionMeta = { + maxSamples: 50 +}; +const lensDistortionFragmentShader = `#version 300 es +precision mediump float; + +uniform sampler2D u_image; +uniform float u_imageAspectRatio; +uniform float u_spread; +uniform float u_bias; +uniform float u_angle; +uniform float u_perspective; +uniform float u_count; +uniform float u_dispersion; +uniform float u_dispersionShift; +uniform float u_dispersionColor; +uniform float u_focusCenter; +uniform float u_focusEdges; +uniform float u_swirl; +uniform float u_noise; +uniform float u_noiseFrequency; +uniform float u_noiseOffset; +uniform float u_lensBulge; +uniform float u_lensCircle; +uniform float u_grainMixer; +uniform float u_grainOverlay; +uniform float u_imageX; +uniform float u_imageY; + +in vec2 v_imageUV; + +out vec4 fragColor; + +${declarePI} +${proceduralHash21} +${rotation2} + +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = hash21(i); + float b = hash21(i + vec2(1., 0.)); + float c = hash21(i + vec2(0., 1.)); + float d = hash21(i + vec2(1., 1.)); + vec2 u = f * f * (3. - 2. * f); + return mix(mix(a, b, u.x), mix(c, d, u.x), u.y); +} + +float getUvFrame(vec2 uv) { + vec2 invAA = 1. / clamp(fwidth(uv), 1e-5, .02); + vec2 lo = clamp(uv * invAA + .5, 0., 1.); + vec2 hi = clamp((1. - uv) * invAA + .5, 0., 1.); + return lo.x * hi.x * lo.y * hi.y; +} + +vec4 sampleOverWhite(vec2 uv) { + vec4 img = texture(u_image, uv); + float cover = img.a * getUvFrame(uv); + vec3 colorOverWhite = mix(vec3(1.), img.rgb, cover); + return vec4(colorOverWhite, cover); +} + +vec3 hueColor(float hue) { + vec3 rgb = clamp(abs(mod(hue * 6. + vec3(0., 4., 2.), 6.) - 3.) - 1., 0., 1.); + rgb = rgb * rgb * (3. - 2. * rgb); + return rgb; +} + +float boxInradius() { + return .5 * min(u_imageAspectRatio, 1.); +} + +float boxOutradius() { + return .5 * length(vec2(u_imageAspectRatio, 1.)); +} + +float spreadReach() { + return .7 * pow(u_spread, 1.3 + 2.7 * u_spread); +} + +float innerCircleMask(float radius, float inradius) { + return 1. - smoothstep(.5 * inradius, 1.1 * inradius, radius); +} + +float dispersionCurve(float t, float biasPow) { + float mirroredT = u_bias < 0. ? 1. - t : t; + float curved = pow(mirroredT, biasPow); + return u_bias < 0. ? 1. - curved : curved; +} + +vec2 getSpread(vec2 fromCenter, float radius, vec2 warpedUV, float edgeAA, float inradius, float outradius, float reach, out float outStrength) { + float angleRad = radians(u_angle); + vec2 uniformDir = vec2(cos(angleRad), sin(angleRad)); + + float invInradius = 1. / inradius; + vec2 radialDir = fromCenter * invInradius; + float maxLen = (outradius + reach) * invInradius; + float radialLen = radius * invInradius; + if (radialLen > maxLen) radialDir *= maxLen / radialLen; + vec2 spreadDir = mix(uniformDir, radialDir, u_perspective); + + float bandProximity = smoothstep(inradius * .8, inradius, radius); + float lensCircleMaxing = bandProximity * u_lensCircle * u_lensCircle * u_lensCircle; + spreadDir = mix(spreadDir, radialDir, lensCircleMaxing); + + vec2 warpedAbs = abs(warpedUV - .5); + float inner = mix(1., smoothstep(0., mix(inradius, outradius, u_focusCenter), radius), u_focusCenter); + float boxDist = max(warpedAbs.x, warpedAbs.y) * 2.; + float outer = mix(1., 1. - min(boxDist, 1.), u_focusEdges); + float strength = inner * outer; + + strength *= mix(1., mix(.15, .03, max(-u_lensBulge, 0.)), lensCircleMaxing); + + vec2 outside = max(warpedAbs - .5, 0.); + float margin = max(reach * length(spreadDir) * (1. - u_lensCircle), edgeAA); + strength *= 1. - smoothstep(0., margin, length(outside)); + + outStrength = strength; + vec2 axis = spreadDir * (reach * strength); + + if (u_noise > 0.) { + float turn = (valueNoise(fromCenter * u_noiseFrequency * 18. + u_noiseOffset * 30.) - .5) * 2. * u_noise; + float cs = cos(turn), sn = sin(turn); + axis = mat2(cs, -sn, sn, cs) * axis; + } + + axis.x /= u_imageAspectRatio; + return axis; +} + +vec2 lensWarp(vec2 fromCenter, float radius, float inradius, out float bulgeFade) { + bulgeFade = 1.; + if (u_lensBulge == 0. && u_lensCircle <= 0.) return fromCenter; + if (radius < 1e-5) return fromCenter; + + float r = radius; + + if (u_lensBulge != 0.) { + float rn = radius / inradius; + float bulge = abs(u_lensBulge) * (u_lensBulge > 0. ? 1.4 : 1.2); + if (u_lensBulge > 0.) bulgeFade = 1. - smoothstep(1.45, 1.53, rn * bulge); + float map = u_lensBulge > 0. + ? tan(min(rn * bulge, 1.53)) / tan(bulge) + : atan(rn * tan(bulge)) / bulge; + float bulgeScale = map / rn; + fromCenter *= bulgeScale; + r *= bulgeScale; + } + + if (u_lensCircle > 0.) { + vec2 dir = fromCenter / max(r, 1e-5); + vec2 halfBox = vec2(u_imageAspectRatio, 1.) * .5; + float rBox = min(halfBox.x / max(abs(dir.x), 1e-4), halfBox.y / max(abs(dir.y), 1e-4)); + float band = inradius * mix(.03, .2, .5 * (u_lensBulge + 1.)); + float innerEdge = inradius - band; + float over = smoothstep(0., 1., (r - innerEdge) / band); + float g = r + (rBox - inradius) * pow(over, 14.); + fromCenter = dir * mix(r, g, u_lensCircle); + } + + return fromCenter; +} + +void main() { + vec2 uv = v_imageUV; + + float invAspect = 1. / u_imageAspectRatio; + float inradius = boxInradius(); + float outradius = boxOutradius(); + float reach = spreadReach(); + + vec2 fromCenter = uv - .5; + fromCenter.x *= u_imageAspectRatio; + float radius = length(fromCenter); + + float bulgeFade; + vec2 warpedFromCenter = lensWarp(fromCenter, radius, inradius, bulgeFade); + vec2 baseUV = vec2(warpedFromCenter.x * invAspect, warpedFromCenter.y) + .5; + + vec2 baseDerivative = fwidth(baseUV); + float edgeAA = clamp(2. * max(baseDerivative.x, baseDerivative.y), .001, .02); + + float spreadStrength; + vec2 spreadAxis = getSpread(fromCenter, radius, baseUV, edgeAA, inradius, outradius, reach, spreadStrength); + + vec2 grainUV = vec2(0.); + if (u_grainMixer > 0. || u_grainOverlay > 0.) { + vec2 dudx = dFdx(v_imageUV); + vec2 dudy = dFdy(v_imageUV); + grainUV = (v_imageUV - .5) * (.8 / vec2(length(dudx), length(dudy))) + .5; + } + + if (u_grainMixer > 0.) { + float grainSeed = valueNoise(grainUV); + vec2 jit = fract(grainSeed * vec2(157.31, 113.57)) * 2. - 1.; + spreadAxis += jit * .3 * u_grainMixer * length(spreadAxis); + } + + float count = floor(u_count); + int countInteger = int(count); + + float invCount = 1. / count; + float invSpan = 1. / max(count - 1., 1.); + float hueBase = u_dispersionColor + .5; + float biasPower = 1. + 2. * abs(u_bias); + vec2 tapBias = .5 - vec2(u_imageX, u_imageY); + + float centerAmount = clamp(1. - u_dispersionShift, 0., 1.); + float edgesAmount = clamp(1. + u_dispersionShift, 0., 1.); + float dispersion = u_dispersion * mix(edgesAmount, centerAmount, innerCircleMask(radius, inradius)); + float dispersionPower = pow(dispersion, .8); + + float warpedRadius = length(warpedFromCenter); + float swirlAngle = u_swirl * .4 * PI * spreadStrength * u_spread * min(1., inradius / max(warpedRadius, 1e-4)); + + vec3 colorSum = vec3(0.); + vec3 weightSum = vec3(0.); + float coverSum = 0.; + + for (int i = 0; i < ${lensDistortionMeta.maxSamples}; i++) { + if (i >= countInteger) break; + + float layer = float(i); + float hue = hueBase + layer * invCount; + + float spread = layer * invSpan; + if (u_bias != 0.) spread = dispersionCurve(spread, biasPower); + + // mix(axis, -axis, spread) is axis scaled by the signed position along the fan + float fanPos = 1. - 2. * spread; + vec2 tapUV = baseUV + spreadAxis * fanPos - .5; + + if (u_swirl != 0.) { + tapUV.x *= u_imageAspectRatio; + tapUV = rotate(tapUV, swirlAngle * fanPos); + tapUV.x *= invAspect; + } + + vec4 tap = sampleOverWhite(tapUV + tapBias); + vec3 weight = 1. - dispersionPower * hueColor(hue); + + colorSum += tap.rgb * weight; + weightSum += weight; + coverSum += tap.a; + } + + vec3 color = colorSum / max(weightSum, 1e-4); + float coverAvg = coverSum * invCount; + + float ground = min(color.r, min(color.g, color.b)); + float alpha = max(coverAvg, 1. - ground); + vec3 premult = max(color - (1. - alpha), 0.); + fragColor = vec4(premult, alpha) * bulgeFade; + + if (u_grainOverlay > 0.) { + float grain = valueNoise(rotate(grainUV, 1.) + vec2(3.)); + grain = mix(grain, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5); + grain = pow(grain, 1.3); + float grainV = grain * 2. - 1.; + float grainStrength = pow(u_grainOverlay * abs(grainV), .8) * fragColor.a; + fragColor.rgb = mix(fragColor.rgb, vec3(step(0., grainV)) * fragColor.a, .35 * grainStrength); + } +} +`; +export { + lensDistortionFragmentShader, + lensDistortionMeta +}; +//# sourceMappingURL=lens-distortion.js.map diff --git a/public/vendor/paper-shaders/shaders/lens-distortion.js.map b/public/vendor/paper-shaders/shaders/lens-distortion.js.map new file mode 100644 index 0000000..e5e6c2c --- /dev/null +++ b/public/vendor/paper-shaders/shaders/lens-distortion.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/lens-distortion.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, proceduralHash21, rotation2 } from '../shader-utils.js';\n\nexport const lensDistortionMeta = {\n maxSamples: 50,\n} as const;\n\n/**\n *\n * Lens Distortion image filter separates an image into shifting color layers\n * (recreating the chromatic aberration of a lens) and warps the image geometry,\n * curving it outward or inward like barrel and pincushion distortion.\n *\n * Fragment shader uniforms:\n * - u_image (sampler2D): Source image texture\n * - u_spread (float): Strength of the color split; how far the color layers are pushed apart; 0 is off (0 to 1)\n * - u_bias (float): Shifts the colors toward one end of the spread; 0 spaces them evenly (-1 to 1)\n * - u_angle (float): Direction of the spread in degrees (0 to 360)\n * - u_perspective (float): Shapes the spread direction from a straight line (0) to a radial burst out from the centre (1) (0 to 1)\n * - u_count (float): Number of sampled color layers along the spread; higher is smoother and costlier (2 to 50)\n * - u_dispersion (float): Overall amount of color dispersion; 1 gives each layer its own color from the spectrum, 0 keeps the original image color (0 to 1)\n * - u_dispersionShift (float): Balance of the dispersion between a soft circular zone at the centre and the rest of the image; 0 applies it evenly, -1 keeps it in the centre only, 1 keeps it at the edges only (-1 to 1)\n * - u_dispersionColor (float): Rotates the colors around the hue wheel, 0 to 1 for a full turn\n * - u_focusCenter (float): Reduces the spread in a circular zone at the centre; 0 keeps it full to the centre (0 to 1)\n * - u_focusEdges (float): Reduces the spread toward the edges; 0 keeps it full to the edges, 1 restores the original image there (0 to 1)\n * - u_swirl (float): Rotates the color layers around the centre by an angle growing along the spread; 0 is off (-1 to 1)\n * - u_noise (float): Scatters the spread direction with noise; 0 is off (0 to 1)\n * - u_noiseFrequency (float): Frequency of the noise, 0 to 1 mapped internally to 0 to 18; higher is finer (no effect with noise = 0)\n * - u_noiseOffset (float): Offsets the noise pattern for a different seed, 0 to 1 mapped internally to 0 to 30 (no effect with noise = 0)\n * - u_lensBulge (float): Radial lens warp of the image geometry; positive bulges out like a fisheye/barrel, negative pinches in like a pincushion; strong positive values fade out the corners (-1 to 1)\n * - u_lensCircle (float): Squeezes pixels outside the inscribed circle inward so the outline becomes a circle; also turns the spread radial and damps it near the rim; 0 is off, 1 is full (0 to 1)\n * - u_grainMixer (float): Scatters the spread with grain noise, breaking up the edges of the color layers (0 to 1)\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\n * - u_imageX (float): Pans the image horizontally behind the effect; 0 is centred (-1 to 1)\n * - u_imageY (float): Pans the image vertically behind the effect; 0 is centred (-1 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): Image UV coordinates with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const lensDistortionFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\nuniform float u_spread;\nuniform float u_bias;\nuniform float u_angle;\nuniform float u_perspective;\nuniform float u_count;\nuniform float u_dispersion;\nuniform float u_dispersionShift;\nuniform float u_dispersionColor;\nuniform float u_focusCenter;\nuniform float u_focusEdges;\nuniform float u_swirl;\nuniform float u_noise;\nuniform float u_noiseFrequency;\nuniform float u_noiseOffset;\nuniform float u_lensBulge;\nuniform float u_lensCircle;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\nuniform float u_imageX;\nuniform float u_imageY;\n\nin vec2 v_imageUV;\n\nout vec4 fragColor;\n\n${declarePI}\n${proceduralHash21}\n${rotation2}\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1., 0.));\n float c = hash21(i + vec2(0., 1.));\n float d = hash21(i + vec2(1., 1.));\n vec2 u = f * f * (3. - 2. * f);\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat getUvFrame(vec2 uv) {\n vec2 invAA = 1. / clamp(fwidth(uv), 1e-5, .02);\n vec2 lo = clamp(uv * invAA + .5, 0., 1.);\n vec2 hi = clamp((1. - uv) * invAA + .5, 0., 1.);\n return lo.x * hi.x * lo.y * hi.y;\n}\n\nvec4 sampleOverWhite(vec2 uv) {\n vec4 img = texture(u_image, uv);\n float cover = img.a * getUvFrame(uv);\n vec3 colorOverWhite = mix(vec3(1.), img.rgb, cover);\n return vec4(colorOverWhite, cover);\n}\n\nvec3 hueColor(float hue) {\n vec3 rgb = clamp(abs(mod(hue * 6. + vec3(0., 4., 2.), 6.) - 3.) - 1., 0., 1.);\n rgb = rgb * rgb * (3. - 2. * rgb);\n return rgb;\n}\n \nfloat boxInradius() {\n return .5 * min(u_imageAspectRatio, 1.);\n}\n\nfloat boxOutradius() {\n return .5 * length(vec2(u_imageAspectRatio, 1.));\n}\n\nfloat spreadReach() {\n return .7 * pow(u_spread, 1.3 + 2.7 * u_spread);\n}\n\nfloat innerCircleMask(float radius, float inradius) {\n return 1. - smoothstep(.5 * inradius, 1.1 * inradius, radius);\n}\n\nfloat dispersionCurve(float t, float biasPow) {\n float mirroredT = u_bias < 0. ? 1. - t : t;\n float curved = pow(mirroredT, biasPow);\n return u_bias < 0. ? 1. - curved : curved;\n}\n\nvec2 getSpread(vec2 fromCenter, float radius, vec2 warpedUV, float edgeAA, float inradius, float outradius, float reach, out float outStrength) {\n float angleRad = radians(u_angle);\n vec2 uniformDir = vec2(cos(angleRad), sin(angleRad));\n\n float invInradius = 1. / inradius;\n vec2 radialDir = fromCenter * invInradius;\n float maxLen = (outradius + reach) * invInradius;\n float radialLen = radius * invInradius;\n if (radialLen > maxLen) radialDir *= maxLen / radialLen;\n vec2 spreadDir = mix(uniformDir, radialDir, u_perspective);\n\n float bandProximity = smoothstep(inradius * .8, inradius, radius);\n float lensCircleMaxing = bandProximity * u_lensCircle * u_lensCircle * u_lensCircle;\n spreadDir = mix(spreadDir, radialDir, lensCircleMaxing);\n\n vec2 warpedAbs = abs(warpedUV - .5);\n float inner = mix(1., smoothstep(0., mix(inradius, outradius, u_focusCenter), radius), u_focusCenter);\n float boxDist = max(warpedAbs.x, warpedAbs.y) * 2.;\n float outer = mix(1., 1. - min(boxDist, 1.), u_focusEdges);\n float strength = inner * outer;\n\n strength *= mix(1., mix(.15, .03, max(-u_lensBulge, 0.)), lensCircleMaxing);\n\n vec2 outside = max(warpedAbs - .5, 0.);\n float margin = max(reach * length(spreadDir) * (1. - u_lensCircle), edgeAA);\n strength *= 1. - smoothstep(0., margin, length(outside));\n\n outStrength = strength;\n vec2 axis = spreadDir * (reach * strength);\n\n if (u_noise > 0.) {\n float turn = (valueNoise(fromCenter * u_noiseFrequency * 18. + u_noiseOffset * 30.) - .5) * 2. * u_noise;\n float cs = cos(turn), sn = sin(turn);\n axis = mat2(cs, -sn, sn, cs) * axis;\n }\n\n axis.x /= u_imageAspectRatio;\n return axis;\n}\n\nvec2 lensWarp(vec2 fromCenter, float radius, float inradius, out float bulgeFade) {\n bulgeFade = 1.;\n if (u_lensBulge == 0. && u_lensCircle <= 0.) return fromCenter;\n if (radius < 1e-5) return fromCenter;\n \n float r = radius;\n\n if (u_lensBulge != 0.) {\n float rn = radius / inradius;\n float bulge = abs(u_lensBulge) * (u_lensBulge > 0. ? 1.4 : 1.2);\n if (u_lensBulge > 0.) bulgeFade = 1. - smoothstep(1.45, 1.53, rn * bulge);\n float map = u_lensBulge > 0.\n ? tan(min(rn * bulge, 1.53)) / tan(bulge)\n : atan(rn * tan(bulge)) / bulge;\n float bulgeScale = map / rn;\n fromCenter *= bulgeScale;\n r *= bulgeScale;\n }\n\n if (u_lensCircle > 0.) {\n vec2 dir = fromCenter / max(r, 1e-5);\n vec2 halfBox = vec2(u_imageAspectRatio, 1.) * .5;\n float rBox = min(halfBox.x / max(abs(dir.x), 1e-4), halfBox.y / max(abs(dir.y), 1e-4));\n float band = inradius * mix(.03, .2, .5 * (u_lensBulge + 1.));\n float innerEdge = inradius - band;\n float over = smoothstep(0., 1., (r - innerEdge) / band);\n float g = r + (rBox - inradius) * pow(over, 14.);\n fromCenter = dir * mix(r, g, u_lensCircle);\n }\n\n return fromCenter;\n}\n\nvoid main() {\n vec2 uv = v_imageUV;\n\n float invAspect = 1. / u_imageAspectRatio;\n float inradius = boxInradius();\n float outradius = boxOutradius();\n float reach = spreadReach();\n\n vec2 fromCenter = uv - .5;\n fromCenter.x *= u_imageAspectRatio;\n float radius = length(fromCenter);\n\n float bulgeFade;\n vec2 warpedFromCenter = lensWarp(fromCenter, radius, inradius, bulgeFade);\n vec2 baseUV = vec2(warpedFromCenter.x * invAspect, warpedFromCenter.y) + .5;\n\n vec2 baseDerivative = fwidth(baseUV);\n float edgeAA = clamp(2. * max(baseDerivative.x, baseDerivative.y), .001, .02);\n\n float spreadStrength;\n vec2 spreadAxis = getSpread(fromCenter, radius, baseUV, edgeAA, inradius, outradius, reach, spreadStrength);\n\n vec2 grainUV = vec2(0.);\n if (u_grainMixer > 0. || u_grainOverlay > 0.) {\n vec2 dudx = dFdx(v_imageUV);\n vec2 dudy = dFdy(v_imageUV);\n grainUV = (v_imageUV - .5) * (.8 / vec2(length(dudx), length(dudy))) + .5;\n }\n\n if (u_grainMixer > 0.) {\n float grainSeed = valueNoise(grainUV);\n vec2 jit = fract(grainSeed * vec2(157.31, 113.57)) * 2. - 1.;\n spreadAxis += jit * .3 * u_grainMixer * length(spreadAxis);\n }\n\n float count = floor(u_count);\n int countInteger = int(count);\n\n float invCount = 1. / count;\n float invSpan = 1. / max(count - 1., 1.);\n float hueBase = u_dispersionColor + .5;\n float biasPower = 1. + 2. * abs(u_bias);\n vec2 tapBias = .5 - vec2(u_imageX, u_imageY);\n\n float centerAmount = clamp(1. - u_dispersionShift, 0., 1.);\n float edgesAmount = clamp(1. + u_dispersionShift, 0., 1.);\n float dispersion = u_dispersion * mix(edgesAmount, centerAmount, innerCircleMask(radius, inradius));\n float dispersionPower = pow(dispersion, .8);\n\n float warpedRadius = length(warpedFromCenter);\n float swirlAngle = u_swirl * .4 * PI * spreadStrength * u_spread * min(1., inradius / max(warpedRadius, 1e-4));\n\n vec3 colorSum = vec3(0.);\n vec3 weightSum = vec3(0.);\n float coverSum = 0.;\n\n for (int i = 0; i < ${lensDistortionMeta.maxSamples}; i++) {\n if (i >= countInteger) break;\n\n float layer = float(i);\n float hue = hueBase + layer * invCount;\n\n float spread = layer * invSpan;\n if (u_bias != 0.) spread = dispersionCurve(spread, biasPower);\n\n // mix(axis, -axis, spread) is axis scaled by the signed position along the fan\n float fanPos = 1. - 2. * spread;\n vec2 tapUV = baseUV + spreadAxis * fanPos - .5;\n\n if (u_swirl != 0.) {\n tapUV.x *= u_imageAspectRatio;\n tapUV = rotate(tapUV, swirlAngle * fanPos);\n tapUV.x *= invAspect;\n }\n\n vec4 tap = sampleOverWhite(tapUV + tapBias);\n vec3 weight = 1. - dispersionPower * hueColor(hue);\n\n colorSum += tap.rgb * weight;\n weightSum += weight;\n coverSum += tap.a;\n }\n\n vec3 color = colorSum / max(weightSum, 1e-4);\n float coverAvg = coverSum * invCount;\n\n float ground = min(color.r, min(color.g, color.b));\n float alpha = max(coverAvg, 1. - ground);\n vec3 premult = max(color - (1. - alpha), 0.);\n fragColor = vec4(premult, alpha) * bulgeFade;\n\n if (u_grainOverlay > 0.) {\n float grain = valueNoise(rotate(grainUV, 1.) + vec2(3.));\n grain = mix(grain, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\n grain = pow(grain, 1.3);\n float grainV = grain * 2. - 1.;\n float grainStrength = pow(u_grainOverlay * abs(grainV), .8) * fragColor.a;\n fragColor.rgb = mix(fragColor.rgb, vec3(step(0., grainV)) * fragColor.a, .35 * grainStrength);\n }\n}\n`;\n\nexport interface LensDistortionUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_spread: number;\n u_bias: number;\n u_angle: number;\n u_perspective: number;\n u_count: number;\n u_dispersion: number;\n u_dispersionShift: number;\n u_dispersionColor: number;\n u_focusCenter: number;\n u_focusEdges: number;\n u_swirl: number;\n u_noise: number;\n u_noiseFrequency: number;\n u_noiseOffset: number;\n u_lensBulge: number;\n u_lensCircle: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n u_imageX: number;\n u_imageY: number;\n}\n\nexport interface LensDistortionParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n spread?: number;\n bias?: number;\n angle?: number;\n perspective?: number;\n count?: number;\n dispersion?: number;\n dispersionShift?: number;\n dispersionColor?: number;\n focusCenter?: number;\n focusEdges?: number;\n swirl?: number;\n noise?: number;\n noiseFrequency?: number;\n noiseOffset?: number;\n lensBulge?: number;\n lensCircle?: number;\n grainMixer?: number;\n grainOverlay?: number;\n imageX?: number;\n imageY?: number;\n}\n"], + "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,WAAW,kBAAkB,iBAAiB;AAEhD,MAAM,qBAAqB;AAAA,EAChC,YAAY;AACd;AAiDO,MAAM,+BAAuC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BlD,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAwLa,mBAAmB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/liquid-metal.d.ts b/public/vendor/paper-shaders/shaders/liquid-metal.d.ts new file mode 100644 index 0000000..fd09ed5 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/liquid-metal.d.ts @@ -0,0 +1,90 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * Futuristic liquid metal material applied to uploaded logo or abstract shape. + * Fluid motion imitation applied over user image with animated stripe pattern + * getting distorted along shape edges. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_resolution (vec2): Canvas resolution in pixels + * - u_image (sampler2D): Pre-processed source image texture (R = edge gradient, G = opacity) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * - u_colorBack (vec4): Background color in RGBA + * - u_colorTint (vec4): Overlay color in RGBA (color burn blending used) + * - u_repetition (float): Density of pattern stripes (1 to 10) + * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1) + * - u_shiftRed (float): R-channel dispersion (-1 to 1) + * - u_shiftBlue (float): B-channel dispersion (-1 to 1) + * - u_distortion (float): Noise distortion over the stripes pattern (0 to 1) + * - u_contour (float): Strength of the distortion on the shape edges (0 to 1) + * - u_angle (float): Direction of pattern animation in degrees (0 to 360) + * - u_shape (float): Predefined shape when no image provided (0 = none, 1 = circle, 2 = daisy, 3 = diamond, 4 = metaballs) + * - u_isImage (bool): Whether an image is being used as the effect mask + * + * Vertex shader outputs (used in fragment shader): + * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied (used when no image) + * - v_responsiveUV (vec2): Responsive UV coordinates that adapt to canvas aspect ratio (used for canvas-fill mode) + * - v_responsiveBoxGivenSize (vec2): TBD + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * + */ +export declare const liquidMetalFragmentShader: string; +export declare const POISSON_CONFIG_OPTIMIZED: { + measurePerformance: boolean; + workingSize: number; + iterations: number; +}; +export declare function toProcessedLiquidMetal(file: File | string): Promise<{ + imageData: ImageData; + pngBlob: Blob; +}>; +export interface LiquidMetalUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colorTint: [number, number, number, number]; + u_image: HTMLImageElement | string | undefined; + u_repetition: number; + u_shiftRed: number; + u_shiftBlue: number; + u_contour: number; + u_softness: number; + u_distortion: number; + u_angle: number; + u_shape: (typeof LiquidMetalShapes)[LiquidMetalShape]; + u_isImage: boolean; +} +export interface LiquidMetalParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colorTint?: string; + image?: HTMLImageElement | string | undefined; + repetition?: number; + shiftRed?: number; + shiftBlue?: number; + contour?: number; + softness?: number; + distortion?: number; + angle?: number; + shape?: LiquidMetalShape; +} +export declare const LiquidMetalShapes: { + readonly none: 0; + readonly circle: 1; + readonly daisy: 2; + readonly diamond: 3; + readonly metaballs: 4; +}; +export type LiquidMetalShape = keyof typeof LiquidMetalShapes; diff --git a/public/vendor/paper-shaders/shaders/liquid-metal.js b/public/vendor/paper-shaders/shaders/liquid-metal.js new file mode 100644 index 0000000..31fb3ea --- /dev/null +++ b/public/vendor/paper-shaders/shaders/liquid-metal.js @@ -0,0 +1,630 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, rotation2, simplexNoise, colorBandingFix } from "../shader-utils.js"; +const liquidMetalFragmentShader = `#version 300 es +precision mediump float; + +uniform sampler2D u_image; +uniform float u_imageAspectRatio; + +uniform vec2 u_resolution; +uniform float u_time; + +uniform vec4 u_colorBack; +uniform vec4 u_colorTint; + +uniform float u_softness; +uniform float u_repetition; +uniform float u_shiftRed; +uniform float u_shiftBlue; +uniform float u_distortion; +uniform float u_contour; +uniform float u_angle; + +uniform float u_shape; +uniform bool u_isImage; + +in vec2 v_objectUV; +in vec2 v_responsiveUV; +in vec2 v_responsiveBoxGivenSize; +in vec2 v_imageUV; + +out vec4 fragColor; + +${declarePI} +${rotation2} +${simplexNoise} + +float getColorChanges(float c1, float c2, float stripe_p, vec3 w, float blur, float bump, float tint) { + + float ch = mix(c2, c1, smoothstep(.0, 2. * blur, stripe_p)); + + float border = w[0]; + ch = mix(ch, c2, smoothstep(border, border + 2. * blur, stripe_p)); + + if (u_isImage == true) { + bump = smoothstep(.2, .8, bump); + } + border = w[0] + .4 * (1. - bump) * w[1]; + ch = mix(ch, c1, smoothstep(border, border + 2. * blur, stripe_p)); + + border = w[0] + .5 * (1. - bump) * w[1]; + ch = mix(ch, c2, smoothstep(border, border + 2. * blur, stripe_p)); + + border = w[0] + w[1]; + ch = mix(ch, c1, smoothstep(border, border + 2. * blur, stripe_p)); + + float gradient_t = (stripe_p - w[0] - w[1]) / w[2]; + float gradient = mix(c1, c2, smoothstep(0., 1., gradient_t)); + ch = mix(ch, gradient, smoothstep(border, border + .5 * blur, stripe_p)); + + // Tint color is applied with color burn blending + ch = mix(ch, 1. - min(1., (1. - ch) / max(tint, 0.0001)), u_colorTint.a); + return ch; +} + +float getImgFrame(vec2 uv, float th) { + float frame = 1.; + frame *= smoothstep(0., th, uv.y); + frame *= 1.0 - smoothstep(1. - th, 1., uv.y); + frame *= smoothstep(0., th, uv.x); + frame *= 1.0 - smoothstep(1. - th, 1., uv.x); + return frame; +} + +float blurEdge3x3(sampler2D tex, vec2 uv, vec2 dudx, vec2 dudy, float radius, float centerSample) { + vec2 texel = 1.0 / vec2(textureSize(tex, 0)); + vec2 r = radius * texel; + + float w1 = 1.0, w2 = 2.0, w4 = 4.0; + float norm = 16.0; + float sum = w4 * centerSample; + + sum += w2 * textureGrad(tex, uv + vec2(0.0, -r.y), dudx, dudy).r; + sum += w2 * textureGrad(tex, uv + vec2(0.0, r.y), dudx, dudy).r; + sum += w2 * textureGrad(tex, uv + vec2(-r.x, 0.0), dudx, dudy).r; + sum += w2 * textureGrad(tex, uv + vec2(r.x, 0.0), dudx, dudy).r; + + sum += w1 * textureGrad(tex, uv + vec2(-r.x, -r.y), dudx, dudy).r; + sum += w1 * textureGrad(tex, uv + vec2(r.x, -r.y), dudx, dudy).r; + sum += w1 * textureGrad(tex, uv + vec2(-r.x, r.y), dudx, dudy).r; + sum += w1 * textureGrad(tex, uv + vec2(r.x, r.y), dudx, dudy).r; + + return sum / norm; +} + +float lst(float edge0, float edge1, float x) { + return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); +} + +void main() { + + const float firstFrameOffset = 2.8; + float t = .3 * (u_time + firstFrameOffset); + + vec2 uv = v_imageUV; + vec2 dudx = dFdx(v_imageUV); + vec2 dudy = dFdy(v_imageUV); + vec4 img = textureGrad(u_image, uv, dudx, dudy); + + if (u_isImage == false) { + uv = v_objectUV + .5; + uv.y = 1. - uv.y; + } + + float cycleWidth = u_repetition; + float edge = 0.; + float contOffset = 1.; + + vec2 rotatedUV = uv - vec2(.5); + float angle = (-u_angle + 70.) * PI / 180.; + float cosA = cos(angle); + float sinA = sin(angle); + rotatedUV = vec2( + rotatedUV.x * cosA - rotatedUV.y * sinA, + rotatedUV.x * sinA + rotatedUV.y * cosA + ) + vec2(.5); + + if (u_isImage == true) { + float edgeRaw = img.r; + edge = blurEdge3x3(u_image, uv, dudx, dudy, 6., edgeRaw); + edge = pow(edge, 1.6); + edge *= mix(0.0, 1.0, smoothstep(0.0, 0.4, u_contour)); + } else { + if (u_shape < 1.) { + // full-fill on canvas + vec2 borderUV = v_responsiveUV + .5; + float ratio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y; + vec2 mask = min(borderUV, 1. - borderUV); + vec2 pixel_thickness = min(250. / v_responsiveBoxGivenSize, vec2(.5)); + float maskX = smoothstep(0.0, pixel_thickness.x, mask.x); + float maskY = smoothstep(0.0, pixel_thickness.y, mask.y); + maskX = pow(maskX, .25); + maskY = pow(maskY, .25); + edge = clamp(1. - maskX * maskY, 0., 1.); + + uv = v_responsiveUV; + if (ratio > 1.) { + uv.y /= ratio; + } else { + uv.x *= ratio; + } + uv += .5; + uv.y = 1. - uv.y; + + cycleWidth *= 2.; + contOffset = 1.5; + + } else if (u_shape < 2.) { + // circle + vec2 shapeUV = uv - .5; + shapeUV *= .67; + edge = pow(clamp(3. * length(shapeUV), 0., 1.), 18.); + } else if (u_shape < 3.) { + // daisy + vec2 shapeUV = uv - .5; + shapeUV *= 1.68; + + float r = length(shapeUV) * 2.; + float a = atan(shapeUV.y, shapeUV.x) + .2; + r *= (1. + .05 * sin(3. * a + 2. * t)); + float f = abs(cos(a * 3.)); + edge = smoothstep(f, f + .7, r); + edge *= edge; + + uv *= .8; + cycleWidth *= 1.6; + + } else if (u_shape < 4.) { + // diamond + vec2 shapeUV = uv - .5; + shapeUV = rotate(shapeUV, .25 * PI); + shapeUV *= 1.42; + shapeUV += .5; + vec2 mask = min(shapeUV, 1. - shapeUV); + vec2 pixel_thickness = vec2(.15); + float maskX = smoothstep(0.0, pixel_thickness.x, mask.x); + float maskY = smoothstep(0.0, pixel_thickness.y, mask.y); + maskX = pow(maskX, .25); + maskY = pow(maskY, .25); + edge = clamp(1. - maskX * maskY, 0., 1.); + } else if (u_shape < 5.) { + // metaballs + vec2 shapeUV = uv - .5; + shapeUV *= 1.3; + edge = 0.; + for (int i = 0; i < 5; i++) { + float fi = float(i); + float speed = 1.5 + 2./3. * sin(fi * 12.345); + float angle = -fi * 1.5; + vec2 dir1 = vec2(cos(angle), sin(angle)); + vec2 dir2 = vec2(cos(angle + 1.57), sin(angle + 1.)); + vec2 traj = .4 * (dir1 * sin(t * speed + fi * 1.23) + dir2 * cos(t * (speed * 0.7) + fi * 2.17)); + float d = length(shapeUV + traj); + edge += pow(1.0 - clamp(d, 0.0, 1.0), 4.0); + } + edge = 1. - smoothstep(.65, .9, edge); + edge = pow(edge, 4.); + } + + edge = mix(smoothstep(.9 - 2. * fwidth(edge), .9, edge), edge, smoothstep(0.0, 0.4, u_contour)); + + } + + float opacity = 0.; + if (u_isImage == true) { + opacity = img.g; + float frame = getImgFrame(v_imageUV, 0.); + opacity *= frame; + } else { + opacity = 1. - smoothstep(.9 - 2. * fwidth(edge), .9, edge); + if (u_shape < 2.) { + edge = 1.2 * edge; + } else if (u_shape < 5.) { + edge = 1.8 * pow(edge, 1.5); + } + } + + float diagBLtoTR = rotatedUV.x - rotatedUV.y; + float diagTLtoBR = rotatedUV.x + rotatedUV.y; + + vec3 color = vec3(0.); + vec3 color1 = vec3(.98, 0.98, 1.); + vec3 color2 = vec3(.1, .1, .1 + .1 * smoothstep(.7, 1.3, diagTLtoBR)); + + vec2 grad_uv = uv - .5; + + float dist = length(grad_uv + vec2(0., .2 * diagBLtoTR)); + grad_uv = rotate(grad_uv, (.25 - .2 * diagBLtoTR) * PI); + float direction = grad_uv.x; + + float bump = pow(1.8 * dist, 1.2); + bump = 1. - bump; + bump *= pow(uv.y, .3); + + + float thin_strip_1_ratio = .12 / cycleWidth * (1. - .4 * bump); + float thin_strip_2_ratio = .07 / cycleWidth * (1. + .4 * bump); + float wide_strip_ratio = (1. - thin_strip_1_ratio - thin_strip_2_ratio); + + float thin_strip_1_width = cycleWidth * thin_strip_1_ratio; + float thin_strip_2_width = cycleWidth * thin_strip_2_ratio; + + float noise = snoise(uv - t); + + edge += (1. - edge) * u_distortion * noise; + + direction += diagBLtoTR; + float contour = 0.; + direction -= 2. * noise * diagBLtoTR * (smoothstep(0., 1., edge) * (1.0 - smoothstep(0., 1., edge))); + direction *= mix(1., 1. - edge, smoothstep(.5, 1., u_contour)); + direction -= 1.7 * edge * smoothstep(.5, 1., u_contour); + direction += .2 * pow(u_contour, 4.) * (1.0 - smoothstep(0., 1., edge)); + + bump *= clamp(pow(uv.y, .1), .3, 1.); + direction *= (.1 + (1.1 - edge) * bump); + + direction *= (.4 + .6 * (1.0 - smoothstep(.5, 1., edge))); + direction += .18 * (smoothstep(.1, .2, uv.y) * (1.0 - smoothstep(.2, .4, uv.y))); + direction += .03 * (smoothstep(.1, .2, 1. - uv.y) * (1.0 - smoothstep(.2, .4, 1. - uv.y))); + + direction *= (.5 + .5 * pow(uv.y, 2.)); + direction *= cycleWidth; + direction -= t; + + + float colorDispersion = (1. - bump); + colorDispersion = clamp(colorDispersion, 0., 1.); + float dispersionRed = colorDispersion; + dispersionRed += .03 * bump * noise; + dispersionRed += 5. * (smoothstep(-.1, .2, uv.y) * (1.0 - smoothstep(.1, .5, uv.y))) * (smoothstep(.4, .6, bump) * (1.0 - smoothstep(.4, 1., bump))); + dispersionRed -= diagBLtoTR; + + float dispersionBlue = colorDispersion; + dispersionBlue *= 1.3; + dispersionBlue += (smoothstep(0., .4, uv.y) * (1.0 - smoothstep(.1, .8, uv.y))) * (smoothstep(.4, .6, bump) * (1.0 - smoothstep(.4, .8, bump))); + dispersionBlue -= .2 * edge; + + dispersionRed *= (u_shiftRed / 20.); + dispersionBlue *= (u_shiftBlue / 20.); + + float blur = 0.; + float rExtraBlur = 0.; + float gExtraBlur = 0.; + if (u_isImage == true) { + float softness = 0.05 * u_softness; + blur = softness + .5 * smoothstep(1., 10., u_repetition) * smoothstep(.0, 1., edge); + float smallCanvasT = 1.0 - smoothstep(100., 500., min(u_resolution.x, u_resolution.y)); + blur += smallCanvasT * smoothstep(.0, 1., edge); + rExtraBlur = softness * (0.05 + .1 * (u_shiftRed / 20.) * bump); + gExtraBlur = softness * 0.05 / max(0.001, abs(1. - diagBLtoTR)); + } else { + blur = u_softness / 15. + .3 * contour; + } + + vec3 w = vec3(thin_strip_1_width, thin_strip_2_width, wide_strip_ratio); + w[1] -= .02 * smoothstep(.0, 1., edge + bump); + float stripe_r = fract(direction + dispersionRed); + float r = getColorChanges(color1.r, color2.r, stripe_r, w, blur + fwidth(stripe_r) + rExtraBlur, bump, u_colorTint.r); + float stripe_g = fract(direction); + float g = getColorChanges(color1.g, color2.g, stripe_g, w, blur + fwidth(stripe_g) + gExtraBlur, bump, u_colorTint.g); + float stripe_b = fract(direction - dispersionBlue); + float b = getColorChanges(color1.b, color2.b, stripe_b, w, blur + fwidth(stripe_b), bump, u_colorTint.b); + + color = vec3(r, g, b); + color *= opacity; + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1. - opacity); + opacity = opacity + u_colorBack.a * (1. - opacity); + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +const POISSON_CONFIG_OPTIMIZED = { + measurePerformance: false, + // Set to true to see performance metrics + workingSize: 512, + // Size to solve Poisson at (will upscale to original size) + iterations: 40 + // SOR converges ~2-20x faster than standard Gauss-Seidel +}; +function toProcessedLiquidMetal(file) { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + const isBlob = typeof file === "string" && file.startsWith("blob:"); + return new Promise((resolve, reject) => { + if (!file || !ctx) { + reject(new Error("Invalid file or canvas context")); + return; + } + const blobContentTypePromise = isBlob && fetch(file).then((res) => res.headers.get("Content-Type")); + const img = new Image(); + img.crossOrigin = "anonymous"; + const totalStartTime = performance.now(); + img.onload = async () => { + let isSVG; + const blobContentType = await blobContentTypePromise; + if (blobContentType) { + isSVG = blobContentType === "image/svg+xml"; + } else if (typeof file === "string") { + isSVG = file.endsWith(".svg") || file.startsWith("data:image/svg+xml"); + } else { + isSVG = file.type === "image/svg+xml"; + } + let originalWidth = img.width || img.naturalWidth; + let originalHeight = img.height || img.naturalHeight; + if (isSVG) { + const svgMaxSize = 4096; + const aspectRatio = originalWidth / originalHeight; + if (originalWidth > originalHeight) { + originalWidth = svgMaxSize; + originalHeight = svgMaxSize / aspectRatio; + } else { + originalHeight = svgMaxSize; + originalWidth = svgMaxSize * aspectRatio; + } + img.width = originalWidth; + img.height = originalHeight; + } + const minDimension = Math.min(originalWidth, originalHeight); + const targetSize = POISSON_CONFIG_OPTIMIZED.workingSize; + const scaleFactor = targetSize / minDimension; + const width = Math.round(originalWidth * scaleFactor); + const height = Math.round(originalHeight * scaleFactor); + if (POISSON_CONFIG_OPTIMIZED.measurePerformance) { + console.log(`[Processing Mode]`); + console.log(` Original: ${originalWidth}\xD7${originalHeight}`); + console.log(` Working: ${width}\xD7${height} (${(scaleFactor * 100).toFixed(1)}% scale)`); + if (scaleFactor < 1) { + console.log(` Speedup: ~${Math.round(1 / (scaleFactor * scaleFactor))}\xD7`); + } + } + canvas.width = originalWidth; + canvas.height = originalHeight; + const shapeCanvas = document.createElement("canvas"); + shapeCanvas.width = width; + shapeCanvas.height = height; + const shapeCtx = shapeCanvas.getContext("2d"); + shapeCtx.drawImage(img, 0, 0, width, height); + const startMask = performance.now(); + const shapeImageData = shapeCtx.getImageData(0, 0, width, height); + const data = shapeImageData.data; + const shapeMask = new Uint8Array(width * height); + const boundaryMask = new Uint8Array(width * height); + let shapePixelCount = 0; + for (let i = 0, idx = 0; i < data.length; i += 4, idx++) { + const a = data[i + 3]; + const isShape = a === 0 ? 0 : 1; + shapeMask[idx] = isShape; + shapePixelCount += isShape; + } + const boundaryIndices = []; + const interiorIndices = []; + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const idx = y * width + x; + if (!shapeMask[idx]) continue; + let isBoundary = false; + if (x === 0 || x === width - 1 || y === 0 || y === height - 1) { + isBoundary = true; + } else { + isBoundary = !shapeMask[idx - 1] || // left + !shapeMask[idx + 1] || // right + !shapeMask[idx - width] || // top + !shapeMask[idx + width] || // bottom + !shapeMask[idx - width - 1] || // top-left + !shapeMask[idx - width + 1] || // top-right + !shapeMask[idx + width - 1] || // bottom-left + !shapeMask[idx + width + 1]; + } + if (isBoundary) { + boundaryMask[idx] = 1; + boundaryIndices.push(idx); + } else { + interiorIndices.push(idx); + } + } + } + if (POISSON_CONFIG_OPTIMIZED.measurePerformance) { + console.log(`[Mask Building] Time: ${(performance.now() - startMask).toFixed(2)}ms`); + console.log( + ` Shape pixels: ${shapePixelCount} / ${width * height} (${(shapePixelCount / (width * height) * 100).toFixed(1)}%)` + ); + console.log(` Interior pixels: ${interiorIndices.length}`); + console.log(` Boundary pixels: ${boundaryIndices.length}`); + } + const sparseData = buildSparseData( + shapeMask, + boundaryMask, + new Uint32Array(interiorIndices), + new Uint32Array(boundaryIndices), + width, + height + ); + const startSolve = performance.now(); + const u = solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height); + if (POISSON_CONFIG_OPTIMIZED.measurePerformance) { + console.log(`[Poisson Solve] Time: ${(performance.now() - startSolve).toFixed(2)}ms`); + } + let maxVal = 0; + let finalImageData; + for (let i = 0; i < interiorIndices.length; i++) { + const idx = interiorIndices[i]; + if (u[idx] > maxVal) maxVal = u[idx]; + } + const tempCanvas = document.createElement("canvas"); + tempCanvas.width = width; + tempCanvas.height = height; + const tempCtx = tempCanvas.getContext("2d"); + const tempImg = tempCtx.createImageData(width, height); + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const idx = y * width + x; + const px = idx * 4; + if (!shapeMask[idx]) { + tempImg.data[px] = 255; + tempImg.data[px + 1] = 255; + tempImg.data[px + 2] = 255; + tempImg.data[px + 3] = 0; + } else { + const poissonRatio = u[idx] / maxVal; + const gray = 255 * (1 - poissonRatio); + tempImg.data[px] = gray; + tempImg.data[px + 1] = gray; + tempImg.data[px + 2] = gray; + tempImg.data[px + 3] = 255; + } + } + } + tempCtx.putImageData(tempImg, 0, 0); + ctx.imageSmoothingEnabled = true; + ctx.imageSmoothingQuality = "high"; + ctx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, originalWidth, originalHeight); + const outImg = ctx.getImageData(0, 0, originalWidth, originalHeight); + const originalCanvas = document.createElement("canvas"); + originalCanvas.width = originalWidth; + originalCanvas.height = originalHeight; + const originalCtx = originalCanvas.getContext("2d"); + originalCtx.drawImage(img, 0, 0, originalWidth, originalHeight); + const originalData = originalCtx.getImageData(0, 0, originalWidth, originalHeight); + for (let i = 0; i < outImg.data.length; i += 4) { + const a = originalData.data[i + 3]; + const upscaledAlpha = outImg.data[i + 3]; + if (a === 0) { + outImg.data[i] = 255; + outImg.data[i + 1] = 0; + } else { + outImg.data[i] = upscaledAlpha === 0 ? 0 : outImg.data[i]; + outImg.data[i + 1] = a; + } + outImg.data[i + 2] = 255; + outImg.data[i + 3] = 255; + } + ctx.putImageData(outImg, 0, 0); + finalImageData = outImg; + canvas.toBlob((blob) => { + if (!blob) { + reject(new Error("Failed to create PNG blob")); + return; + } + if (POISSON_CONFIG_OPTIMIZED.measurePerformance) { + const totalTime = performance.now() - totalStartTime; + console.log(`[Total Processing Time] ${totalTime.toFixed(2)}ms`); + if (scaleFactor < 1) { + const estimatedFullResTime = totalTime * Math.pow(originalWidth * originalHeight / (width * height), 1.5); + console.log(`[Estimated time at full resolution] ~${estimatedFullResTime.toFixed(0)}ms`); + console.log( + `[Time saved] ~${(estimatedFullResTime - totalTime).toFixed(0)}ms (${Math.round(estimatedFullResTime / totalTime)}\xD7 faster)` + ); + } + } + resolve({ + imageData: finalImageData, + pngBlob: blob + }); + }, "image/png"); + }; + img.onerror = () => reject(new Error("Failed to load image")); + img.src = typeof file === "string" ? file : URL.createObjectURL(file); + }); +} +function buildSparseData(shapeMask, boundaryMask, interiorPixels, boundaryPixels, width, height) { + const pixelCount = interiorPixels.length; + const neighborIndices = new Int32Array(pixelCount * 4); + for (let i = 0; i < pixelCount; i++) { + const idx = interiorPixels[i]; + const x = idx % width; + const y = Math.floor(idx / width); + neighborIndices[i * 4 + 0] = x < width - 1 && shapeMask[idx + 1] ? idx + 1 : -1; + neighborIndices[i * 4 + 1] = x > 0 && shapeMask[idx - 1] ? idx - 1 : -1; + neighborIndices[i * 4 + 2] = y > 0 && shapeMask[idx - width] ? idx - width : -1; + neighborIndices[i * 4 + 3] = y < height - 1 && shapeMask[idx + width] ? idx + width : -1; + } + return { + interiorPixels, + boundaryPixels, + pixelCount, + neighborIndices + }; +} +function solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height) { + const ITERATIONS = POISSON_CONFIG_OPTIMIZED.iterations; + const C = 0.01; + const u = new Float32Array(width * height); + const { interiorPixels, neighborIndices, pixelCount } = sparseData; + const startTime = performance.now(); + const omega = 1.9; + const redPixels = []; + const blackPixels = []; + for (let i = 0; i < pixelCount; i++) { + const idx = interiorPixels[i]; + const x = idx % width; + const y = Math.floor(idx / width); + if ((x + y) % 2 === 0) { + redPixels.push(i); + } else { + blackPixels.push(i); + } + } + for (let iter = 0; iter < ITERATIONS; iter++) { + for (const i of redPixels) { + const idx = interiorPixels[i]; + const eastIdx = neighborIndices[i * 4 + 0]; + const westIdx = neighborIndices[i * 4 + 1]; + const northIdx = neighborIndices[i * 4 + 2]; + const southIdx = neighborIndices[i * 4 + 3]; + let sumN = 0; + if (eastIdx >= 0) sumN += u[eastIdx]; + if (westIdx >= 0) sumN += u[westIdx]; + if (northIdx >= 0) sumN += u[northIdx]; + if (southIdx >= 0) sumN += u[southIdx]; + const newValue = (C + sumN) / 4; + u[idx] = omega * newValue + (1 - omega) * u[idx]; + } + for (const i of blackPixels) { + const idx = interiorPixels[i]; + const eastIdx = neighborIndices[i * 4 + 0]; + const westIdx = neighborIndices[i * 4 + 1]; + const northIdx = neighborIndices[i * 4 + 2]; + const southIdx = neighborIndices[i * 4 + 3]; + let sumN = 0; + if (eastIdx >= 0) sumN += u[eastIdx]; + if (westIdx >= 0) sumN += u[westIdx]; + if (northIdx >= 0) sumN += u[northIdx]; + if (southIdx >= 0) sumN += u[southIdx]; + const newValue = (C + sumN) / 4; + u[idx] = omega * newValue + (1 - omega) * u[idx]; + } + } + if (POISSON_CONFIG_OPTIMIZED.measurePerformance) { + const elapsed = performance.now() - startTime; + console.log(`[Optimized Poisson Solver (SOR \u03C9=${omega})]`); + console.log(` Working size: ${width}\xD7${height}`); + console.log(` Iterations: ${ITERATIONS}`); + console.log(` Time: ${elapsed.toFixed(2)}ms`); + console.log(` Interior pixels processed: ${pixelCount}`); + console.log(` Speed: ${(ITERATIONS * pixelCount / (elapsed * 1e3)).toFixed(2)} Mpixels/sec`); + } + return u; +} +const LiquidMetalShapes = { + none: 0, + circle: 1, + daisy: 2, + diamond: 3, + metaballs: 4 +}; +export { + LiquidMetalShapes, + POISSON_CONFIG_OPTIMIZED, + liquidMetalFragmentShader, + toProcessedLiquidMetal +}; +//# sourceMappingURL=liquid-metal.js.map diff --git a/public/vendor/paper-shaders/shaders/liquid-metal.js.map b/public/vendor/paper-shaders/shaders/liquid-metal.js.map new file mode 100644 index 0000000..1cdd58b --- /dev/null +++ b/public/vendor/paper-shaders/shaders/liquid-metal.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/liquid-metal.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, simplexNoise, colorBandingFix } from '../shader-utils.js';\n\n/**\n * Futuristic liquid metal material applied to uploaded logo or abstract shape.\n * Fluid motion imitation applied over user image with animated stripe pattern\n * getting distorted along shape edges.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_image (sampler2D): Pre-processed source image texture (R = edge gradient, G = opacity)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colorTint (vec4): Overlay color in RGBA (color burn blending used)\n * - u_repetition (float): Density of pattern stripes (1 to 10)\n * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)\n * - u_shiftRed (float): R-channel dispersion (-1 to 1)\n * - u_shiftBlue (float): B-channel dispersion (-1 to 1)\n * - u_distortion (float): Noise distortion over the stripes pattern (0 to 1)\n * - u_contour (float): Strength of the distortion on the shape edges (0 to 1)\n * - u_angle (float): Direction of pattern animation in degrees (0 to 360)\n * - u_shape (float): Predefined shape when no image provided (0 = none, 1 = circle, 2 = daisy, 3 = diamond, 4 = metaballs)\n * - u_isImage (bool): Whether an image is being used as the effect mask\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied (used when no image)\n * - v_responsiveUV (vec2): Responsive UV coordinates that adapt to canvas aspect ratio (used for canvas-fill mode)\n * - v_responsiveBoxGivenSize (vec2): TBD\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const liquidMetalFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform vec2 u_resolution;\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorTint;\n\nuniform float u_softness;\nuniform float u_repetition;\nuniform float u_shiftRed;\nuniform float u_shiftBlue;\nuniform float u_distortion;\nuniform float u_contour;\nuniform float u_angle;\n\nuniform float u_shape;\nuniform bool u_isImage;\n\nin vec2 v_objectUV;\nin vec2 v_responsiveUV;\nin vec2 v_responsiveBoxGivenSize;\nin vec2 v_imageUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\n${ simplexNoise }\n\nfloat getColorChanges(float c1, float c2, float stripe_p, vec3 w, float blur, float bump, float tint) {\n\n float ch = mix(c2, c1, smoothstep(.0, 2. * blur, stripe_p));\n\n float border = w[0];\n ch = mix(ch, c2, smoothstep(border, border + 2. * blur, stripe_p));\n\n if (u_isImage == true) {\n bump = smoothstep(.2, .8, bump);\n }\n border = w[0] + .4 * (1. - bump) * w[1];\n ch = mix(ch, c1, smoothstep(border, border + 2. * blur, stripe_p));\n\n border = w[0] + .5 * (1. - bump) * w[1];\n ch = mix(ch, c2, smoothstep(border, border + 2. * blur, stripe_p));\n\n border = w[0] + w[1];\n ch = mix(ch, c1, smoothstep(border, border + 2. * blur, stripe_p));\n\n float gradient_t = (stripe_p - w[0] - w[1]) / w[2];\n float gradient = mix(c1, c2, smoothstep(0., 1., gradient_t));\n ch = mix(ch, gradient, smoothstep(border, border + .5 * blur, stripe_p));\n\n // Tint color is applied with color burn blending\n ch = mix(ch, 1. - min(1., (1. - ch) / max(tint, 0.0001)), u_colorTint.a);\n return ch;\n}\n\nfloat getImgFrame(vec2 uv, float th) {\n float frame = 1.;\n frame *= smoothstep(0., th, uv.y);\n frame *= 1.0 - smoothstep(1. - th, 1., uv.y);\n frame *= smoothstep(0., th, uv.x);\n frame *= 1.0 - smoothstep(1. - th, 1., uv.x);\n return frame;\n}\n\nfloat blurEdge3x3(sampler2D tex, vec2 uv, vec2 dudx, vec2 dudy, float radius, float centerSample) {\n vec2 texel = 1.0 / vec2(textureSize(tex, 0));\n vec2 r = radius * texel;\n\n float w1 = 1.0, w2 = 2.0, w4 = 4.0;\n float norm = 16.0;\n float sum = w4 * centerSample;\n\n sum += w2 * textureGrad(tex, uv + vec2(0.0, -r.y), dudx, dudy).r;\n sum += w2 * textureGrad(tex, uv + vec2(0.0, r.y), dudx, dudy).r;\n sum += w2 * textureGrad(tex, uv + vec2(-r.x, 0.0), dudx, dudy).r;\n sum += w2 * textureGrad(tex, uv + vec2(r.x, 0.0), dudx, dudy).r;\n\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, -r.y), dudx, dudy).r;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, -r.y), dudx, dudy).r;\n sum += w1 * textureGrad(tex, uv + vec2(-r.x, r.y), dudx, dudy).r;\n sum += w1 * textureGrad(tex, uv + vec2(r.x, r.y), dudx, dudy).r;\n\n return sum / norm;\n}\n\nfloat lst(float edge0, float edge1, float x) {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\nvoid main() {\n\n const float firstFrameOffset = 2.8;\n float t = .3 * (u_time + firstFrameOffset);\n\n vec2 uv = v_imageUV;\n vec2 dudx = dFdx(v_imageUV);\n vec2 dudy = dFdy(v_imageUV);\n vec4 img = textureGrad(u_image, uv, dudx, dudy);\n\n if (u_isImage == false) {\n uv = v_objectUV + .5;\n uv.y = 1. - uv.y;\n }\n\n float cycleWidth = u_repetition;\n float edge = 0.;\n float contOffset = 1.;\n\n vec2 rotatedUV = uv - vec2(.5);\n float angle = (-u_angle + 70.) * PI / 180.;\n float cosA = cos(angle);\n float sinA = sin(angle);\n rotatedUV = vec2(\n rotatedUV.x * cosA - rotatedUV.y * sinA,\n rotatedUV.x * sinA + rotatedUV.y * cosA\n ) + vec2(.5);\n\n if (u_isImage == true) {\n float edgeRaw = img.r;\n edge = blurEdge3x3(u_image, uv, dudx, dudy, 6., edgeRaw);\n edge = pow(edge, 1.6);\n edge *= mix(0.0, 1.0, smoothstep(0.0, 0.4, u_contour));\n } else {\n if (u_shape < 1.) {\n // full-fill on canvas\n vec2 borderUV = v_responsiveUV + .5;\n float ratio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n vec2 mask = min(borderUV, 1. - borderUV);\n vec2 pixel_thickness = min(250. / v_responsiveBoxGivenSize, vec2(.5));\n float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);\n float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);\n maskX = pow(maskX, .25);\n maskY = pow(maskY, .25);\n edge = clamp(1. - maskX * maskY, 0., 1.);\n\n uv = v_responsiveUV;\n if (ratio > 1.) {\n uv.y /= ratio;\n } else {\n uv.x *= ratio;\n }\n uv += .5;\n uv.y = 1. - uv.y;\n\n cycleWidth *= 2.;\n contOffset = 1.5;\n\n } else if (u_shape < 2.) {\n // circle\n vec2 shapeUV = uv - .5;\n shapeUV *= .67;\n edge = pow(clamp(3. * length(shapeUV), 0., 1.), 18.);\n } else if (u_shape < 3.) {\n // daisy\n vec2 shapeUV = uv - .5;\n shapeUV *= 1.68;\n\n float r = length(shapeUV) * 2.;\n float a = atan(shapeUV.y, shapeUV.x) + .2;\n r *= (1. + .05 * sin(3. * a + 2. * t));\n float f = abs(cos(a * 3.));\n edge = smoothstep(f, f + .7, r);\n edge *= edge;\n\n uv *= .8;\n cycleWidth *= 1.6;\n\n } else if (u_shape < 4.) {\n // diamond\n vec2 shapeUV = uv - .5;\n shapeUV = rotate(shapeUV, .25 * PI);\n shapeUV *= 1.42;\n shapeUV += .5;\n vec2 mask = min(shapeUV, 1. - shapeUV);\n vec2 pixel_thickness = vec2(.15);\n float maskX = smoothstep(0.0, pixel_thickness.x, mask.x);\n float maskY = smoothstep(0.0, pixel_thickness.y, mask.y);\n maskX = pow(maskX, .25);\n maskY = pow(maskY, .25);\n edge = clamp(1. - maskX * maskY, 0., 1.);\n } else if (u_shape < 5.) {\n // metaballs\n vec2 shapeUV = uv - .5;\n shapeUV *= 1.3;\n edge = 0.;\n for (int i = 0; i < 5; i++) {\n float fi = float(i);\n float speed = 1.5 + 2./3. * sin(fi * 12.345);\n float angle = -fi * 1.5;\n vec2 dir1 = vec2(cos(angle), sin(angle));\n vec2 dir2 = vec2(cos(angle + 1.57), sin(angle + 1.));\n vec2 traj = .4 * (dir1 * sin(t * speed + fi * 1.23) + dir2 * cos(t * (speed * 0.7) + fi * 2.17));\n float d = length(shapeUV + traj);\n edge += pow(1.0 - clamp(d, 0.0, 1.0), 4.0);\n }\n edge = 1. - smoothstep(.65, .9, edge);\n edge = pow(edge, 4.);\n }\n\n edge = mix(smoothstep(.9 - 2. * fwidth(edge), .9, edge), edge, smoothstep(0.0, 0.4, u_contour));\n\n }\n\n float opacity = 0.;\n if (u_isImage == true) {\n opacity = img.g;\n float frame = getImgFrame(v_imageUV, 0.);\n opacity *= frame;\n } else {\n opacity = 1. - smoothstep(.9 - 2. * fwidth(edge), .9, edge);\n if (u_shape < 2.) {\n edge = 1.2 * edge;\n } else if (u_shape < 5.) {\n edge = 1.8 * pow(edge, 1.5);\n }\n }\n\n float diagBLtoTR = rotatedUV.x - rotatedUV.y;\n float diagTLtoBR = rotatedUV.x + rotatedUV.y;\n\n vec3 color = vec3(0.);\n vec3 color1 = vec3(.98, 0.98, 1.);\n vec3 color2 = vec3(.1, .1, .1 + .1 * smoothstep(.7, 1.3, diagTLtoBR));\n\n vec2 grad_uv = uv - .5;\n\n float dist = length(grad_uv + vec2(0., .2 * diagBLtoTR));\n grad_uv = rotate(grad_uv, (.25 - .2 * diagBLtoTR) * PI);\n float direction = grad_uv.x;\n\n float bump = pow(1.8 * dist, 1.2);\n bump = 1. - bump;\n bump *= pow(uv.y, .3);\n\n\n float thin_strip_1_ratio = .12 / cycleWidth * (1. - .4 * bump);\n float thin_strip_2_ratio = .07 / cycleWidth * (1. + .4 * bump);\n float wide_strip_ratio = (1. - thin_strip_1_ratio - thin_strip_2_ratio);\n\n float thin_strip_1_width = cycleWidth * thin_strip_1_ratio;\n float thin_strip_2_width = cycleWidth * thin_strip_2_ratio;\n\n float noise = snoise(uv - t);\n\n edge += (1. - edge) * u_distortion * noise;\n\n direction += diagBLtoTR;\n float contour = 0.;\n direction -= 2. * noise * diagBLtoTR * (smoothstep(0., 1., edge) * (1.0 - smoothstep(0., 1., edge)));\n direction *= mix(1., 1. - edge, smoothstep(.5, 1., u_contour));\n direction -= 1.7 * edge * smoothstep(.5, 1., u_contour);\n direction += .2 * pow(u_contour, 4.) * (1.0 - smoothstep(0., 1., edge));\n\n bump *= clamp(pow(uv.y, .1), .3, 1.);\n direction *= (.1 + (1.1 - edge) * bump);\n\n direction *= (.4 + .6 * (1.0 - smoothstep(.5, 1., edge)));\n direction += .18 * (smoothstep(.1, .2, uv.y) * (1.0 - smoothstep(.2, .4, uv.y)));\n direction += .03 * (smoothstep(.1, .2, 1. - uv.y) * (1.0 - smoothstep(.2, .4, 1. - uv.y)));\n\n direction *= (.5 + .5 * pow(uv.y, 2.));\n direction *= cycleWidth;\n direction -= t;\n\n\n float colorDispersion = (1. - bump);\n colorDispersion = clamp(colorDispersion, 0., 1.);\n float dispersionRed = colorDispersion;\n dispersionRed += .03 * bump * noise;\n dispersionRed += 5. * (smoothstep(-.1, .2, uv.y) * (1.0 - smoothstep(.1, .5, uv.y))) * (smoothstep(.4, .6, bump) * (1.0 - smoothstep(.4, 1., bump)));\n dispersionRed -= diagBLtoTR;\n\n float dispersionBlue = colorDispersion;\n dispersionBlue *= 1.3;\n dispersionBlue += (smoothstep(0., .4, uv.y) * (1.0 - smoothstep(.1, .8, uv.y))) * (smoothstep(.4, .6, bump) * (1.0 - smoothstep(.4, .8, bump)));\n dispersionBlue -= .2 * edge;\n\n dispersionRed *= (u_shiftRed / 20.);\n dispersionBlue *= (u_shiftBlue / 20.);\n\n float blur = 0.;\n float rExtraBlur = 0.;\n float gExtraBlur = 0.;\n if (u_isImage == true) {\n float softness = 0.05 * u_softness;\n blur = softness + .5 * smoothstep(1., 10., u_repetition) * smoothstep(.0, 1., edge);\n float smallCanvasT = 1.0 - smoothstep(100., 500., min(u_resolution.x, u_resolution.y));\n blur += smallCanvasT * smoothstep(.0, 1., edge);\n rExtraBlur = softness * (0.05 + .1 * (u_shiftRed / 20.) * bump);\n gExtraBlur = softness * 0.05 / max(0.001, abs(1. - diagBLtoTR));\n } else {\n blur = u_softness / 15. + .3 * contour;\n }\n\n vec3 w = vec3(thin_strip_1_width, thin_strip_2_width, wide_strip_ratio);\n w[1] -= .02 * smoothstep(.0, 1., edge + bump);\n float stripe_r = fract(direction + dispersionRed);\n float r = getColorChanges(color1.r, color2.r, stripe_r, w, blur + fwidth(stripe_r) + rExtraBlur, bump, u_colorTint.r);\n float stripe_g = fract(direction);\n float g = getColorChanges(color1.g, color2.g, stripe_g, w, blur + fwidth(stripe_g) + gExtraBlur, bump, u_colorTint.g);\n float stripe_b = fract(direction - dispersionBlue);\n float b = getColorChanges(color1.b, color2.b, stripe_b, w, blur + fwidth(stripe_b), bump, u_colorTint.b);\n\n color = vec3(r, g, b);\n color *= opacity;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\n// Configuration for Poisson solver\nexport const POISSON_CONFIG_OPTIMIZED = {\n measurePerformance: false, // Set to true to see performance metrics\n workingSize: 512, // Size to solve Poisson at (will upscale to original size)\n iterations: 40, // SOR converges ~2-20x faster than standard Gauss-Seidel\n};\n\n// Precomputed pixel data for sparse processing\ninterface SparsePixelData {\n interiorPixels: Uint32Array; // Indices of interior pixels\n boundaryPixels: Uint32Array; // Indices of boundary pixels\n pixelCount: number;\n // Neighbor indices for each interior pixel (4 neighbors per pixel)\n // Layout: [east, west, north, south] for each pixel\n neighborIndices: Int32Array;\n}\n\nexport function toProcessedLiquidMetal(file: File | string): Promise<{ imageData: ImageData; pngBlob: Blob }> {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n const isBlob = typeof file === 'string' && file.startsWith('blob:');\n\n return new Promise((resolve, reject) => {\n if (!file || !ctx) {\n reject(new Error('Invalid file or canvas context'));\n return;\n }\n\n const blobContentTypePromise = isBlob && fetch(file).then((res) => res.headers.get('Content-Type'));\n const img = new Image();\n img.crossOrigin = 'anonymous';\n const totalStartTime = performance.now();\n\n img.onload = async () => {\n // Force SVG to load at a high fidelity size if it's an SVG\n let isSVG;\n\n const blobContentType = await blobContentTypePromise;\n\n if (blobContentType) {\n isSVG = blobContentType === 'image/svg+xml';\n } else if (typeof file === 'string') {\n isSVG = file.endsWith('.svg') || file.startsWith('data:image/svg+xml');\n } else {\n isSVG = file.type === 'image/svg+xml';\n }\n\n let originalWidth = img.width || img.naturalWidth;\n let originalHeight = img.height || img.naturalHeight;\n\n if (isSVG) {\n // Scale SVG to max dimension while preserving aspect ratio\n const svgMaxSize = 4096;\n const aspectRatio = originalWidth / originalHeight;\n\n if (originalWidth > originalHeight) {\n originalWidth = svgMaxSize;\n originalHeight = svgMaxSize / aspectRatio;\n } else {\n originalHeight = svgMaxSize;\n originalWidth = svgMaxSize * aspectRatio;\n }\n\n img.width = originalWidth;\n img.height = originalHeight;\n }\n\n // Always scale to working resolution for consistency\n const minDimension = Math.min(originalWidth, originalHeight);\n const targetSize = POISSON_CONFIG_OPTIMIZED.workingSize;\n\n // Calculate scale to fit within workingSize\n const scaleFactor = targetSize / minDimension;\n const width = Math.round(originalWidth * scaleFactor);\n const height = Math.round(originalHeight * scaleFactor);\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Processing Mode]`);\n console.log(` Original: ${originalWidth}\u00D7${originalHeight}`);\n console.log(` Working: ${width}\u00D7${height} (${(scaleFactor * 100).toFixed(1)}% scale)`);\n if (scaleFactor < 1) {\n console.log(` Speedup: ~${Math.round(1 / (scaleFactor * scaleFactor))}\u00D7`);\n }\n }\n\n canvas.width = originalWidth;\n canvas.height = originalHeight;\n\n // Use a smaller canvas for shape detection and Poisson solving\n const shapeCanvas = document.createElement('canvas');\n shapeCanvas.width = width;\n shapeCanvas.height = height;\n\n const shapeCtx = shapeCanvas.getContext('2d')!;\n shapeCtx.drawImage(img, 0, 0, width, height);\n\n // 1) Build optimized masks using TypedArrays\n const startMask = performance.now();\n\n const shapeImageData = shapeCtx.getImageData(0, 0, width, height);\n const data = shapeImageData.data;\n\n // Use Uint8Array for masks (1 byte per pixel vs 8+ bytes for boolean array)\n const shapeMask = new Uint8Array(width * height);\n const boundaryMask = new Uint8Array(width * height);\n\n // First pass: identify shape pixels\n let shapePixelCount = 0;\n for (let i = 0, idx = 0; i < data.length; i += 4, idx++) {\n const a = data[i + 3];\n const isShape = a === 0 ? 0 : 1;\n shapeMask[idx] = isShape;\n shapePixelCount += isShape;\n }\n\n // 2) Optimized boundary detection using sparse approach\n // Only check shape pixels, not all pixels\n const boundaryIndices: number[] = [];\n const interiorIndices: number[] = [];\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n if (!shapeMask[idx]) continue;\n\n // Check if pixel is on boundary (optimized: early exit)\n let isBoundary = false;\n\n // Check 4-connected neighbors first (most common case)\n if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {\n isBoundary = true;\n } else {\n // Check all 8 neighbors (including diagonals) for comprehensive boundary detection\n isBoundary =\n !shapeMask[idx - 1] || // left\n !shapeMask[idx + 1] || // right\n !shapeMask[idx - width] || // top\n !shapeMask[idx + width] || // bottom\n !shapeMask[idx - width - 1] || // top-left\n !shapeMask[idx - width + 1] || // top-right\n !shapeMask[idx + width - 1] || // bottom-left\n !shapeMask[idx + width + 1]; // bottom-right\n }\n\n if (isBoundary) {\n boundaryMask[idx] = 1;\n boundaryIndices.push(idx);\n } else {\n interiorIndices.push(idx);\n }\n }\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Mask Building] Time: ${(performance.now() - startMask).toFixed(2)}ms`);\n console.log(\n ` Shape pixels: ${shapePixelCount} / ${width * height} (${((shapePixelCount / (width * height)) * 100).toFixed(1)}%)`\n );\n console.log(` Interior pixels: ${interiorIndices.length}`);\n console.log(` Boundary pixels: ${boundaryIndices.length}`);\n }\n\n // 3) Precompute sparse data structure for solver\n const sparseData = buildSparseData(\n shapeMask,\n boundaryMask,\n new Uint32Array(interiorIndices),\n new Uint32Array(boundaryIndices),\n width,\n height\n );\n\n // 4) Solve Poisson equation with optimized sparse solver\n const startSolve = performance.now();\n const u = solvePoissonSparse(sparseData, shapeMask, boundaryMask, width, height);\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n console.log(`[Poisson Solve] Time: ${(performance.now() - startSolve).toFixed(2)}ms`);\n }\n\n // 5) Generate output image\n let maxVal = 0;\n let finalImageData: ImageData;\n\n // Only check shape pixels for max value\n for (let i = 0; i < interiorIndices.length; i++) {\n const idx = interiorIndices[i]!;\n if (u[idx]! > maxVal) maxVal = u[idx]!;\n }\n\n // Create gradient image at working resolution\n const tempCanvas = document.createElement('canvas');\n tempCanvas.width = width;\n tempCanvas.height = height;\n const tempCtx = tempCanvas.getContext('2d')!;\n\n const tempImg = tempCtx.createImageData(width, height);\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = y * width + x;\n const px = idx * 4;\n\n if (!shapeMask[idx]) {\n tempImg.data[px] = 255;\n tempImg.data[px + 1] = 255;\n tempImg.data[px + 2] = 255;\n tempImg.data[px + 3] = 0; // Alpha = 0 for background\n } else {\n const poissonRatio = u[idx]! / maxVal;\n const gray = 255 * (1 - poissonRatio);\n tempImg.data[px] = gray;\n tempImg.data[px + 1] = gray;\n tempImg.data[px + 2] = gray;\n tempImg.data[px + 3] = 255; // Alpha = 255 for shape\n }\n }\n }\n tempCtx.putImageData(tempImg, 0, 0);\n\n // Upscale to original resolution with smooth interpolation\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = 'high';\n ctx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, originalWidth, originalHeight);\n\n // Now get the upscaled image data for final output\n const outImg = ctx.getImageData(0, 0, originalWidth, originalHeight);\n\n // Re-apply edges from original resolution with anti-aliasing\n // This ensures edges are pixel-perfect while gradient is smooth\n const originalCanvas = document.createElement('canvas');\n originalCanvas.width = originalWidth;\n originalCanvas.height = originalHeight;\n const originalCtx = originalCanvas.getContext('2d')!;\n // originalCtx.fillStyle = \"white\";\n // originalCtx.fillRect(0, 0, originalWidth, originalHeight);\n originalCtx.drawImage(img, 0, 0, originalWidth, originalHeight);\n const originalData = originalCtx.getImageData(0, 0, originalWidth, originalHeight);\n\n // Process each pixel: Red channel = gradient, Alpha channel = original alpha\n for (let i = 0; i < outImg.data.length; i += 4) {\n const a = originalData.data[i + 3]!;\n // Use only alpha to determine background vs shape\n const upscaledAlpha = outImg.data[i + 3]!;\n if (a === 0) {\n // Background pixel\n outImg.data[i] = 255;\n outImg.data[i + 1] = 0;\n } else {\n // Red channel carries the gradient\n // Check if upscale missed this pixel by looking at alpha channel\n // If upscaled alpha is 0, the low-res version thought this was background\n outImg.data[i] = upscaledAlpha === 0 ? 0 : outImg.data[i]!; // gradient or 0\n outImg.data[i + 1] = a; // original alpha\n }\n\n // Unused channels fixed\n outImg.data[i + 2] = 255;\n outImg.data[i + 3] = 255;\n }\n\n ctx.putImageData(outImg, 0, 0);\n finalImageData = outImg;\n canvas.toBlob((blob) => {\n if (!blob) {\n reject(new Error('Failed to create PNG blob'));\n return;\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n const totalTime = performance.now() - totalStartTime;\n console.log(`[Total Processing Time] ${totalTime.toFixed(2)}ms`);\n if (scaleFactor < 1) {\n const estimatedFullResTime = totalTime * Math.pow((originalWidth * originalHeight) / (width * height), 1.5);\n console.log(`[Estimated time at full resolution] ~${estimatedFullResTime.toFixed(0)}ms`);\n console.log(\n `[Time saved] ~${(estimatedFullResTime - totalTime).toFixed(0)}ms (${Math.round(estimatedFullResTime / totalTime)}\u00D7 faster)`\n );\n }\n }\n\n resolve({\n imageData: finalImageData,\n pngBlob: blob,\n });\n }, 'image/png');\n };\n\n img.onerror = () => reject(new Error('Failed to load image'));\n img.src = typeof file === 'string' ? file : URL.createObjectURL(file);\n });\n}\n\nfunction buildSparseData(\n shapeMask: Uint8Array,\n boundaryMask: Uint8Array,\n interiorPixels: Uint32Array,\n boundaryPixels: Uint32Array,\n width: number,\n height: number\n): SparsePixelData {\n const pixelCount = interiorPixels.length;\n\n // Build neighbor indices for sparse processing\n // For each interior pixel, store indices of its 4 neighbors\n // Use -1 for out-of-bounds or non-shape neighbors\n const neighborIndices = new Int32Array(pixelCount * 4);\n\n for (let i = 0; i < pixelCount; i++) {\n const idx = interiorPixels[i]!;\n const x = idx % width;\n const y = Math.floor(idx / width);\n\n // East neighbor\n neighborIndices[i * 4 + 0] = x < width - 1 && shapeMask[idx + 1] ? idx + 1 : -1;\n // West neighbor\n neighborIndices[i * 4 + 1] = x > 0 && shapeMask[idx - 1] ? idx - 1 : -1;\n // North neighbor\n neighborIndices[i * 4 + 2] = y > 0 && shapeMask[idx - width] ? idx - width : -1;\n // South neighbor\n neighborIndices[i * 4 + 3] = y < height - 1 && shapeMask[idx + width] ? idx + width : -1;\n }\n\n return {\n interiorPixels,\n boundaryPixels,\n pixelCount,\n neighborIndices,\n };\n}\n\nfunction solvePoissonSparse(\n sparseData: SparsePixelData,\n shapeMask: Uint8Array,\n boundaryMask: Uint8Array,\n width: number,\n height: number\n): Float32Array {\n // This controls how smooth the falloff gradient will be and extend into the shape\n const ITERATIONS = POISSON_CONFIG_OPTIMIZED.iterations;\n\n // Keep C constant - only iterations control gradient spread\n const C = 0.01;\n\n const u = new Float32Array(width * height);\n const { interiorPixels, neighborIndices, pixelCount } = sparseData;\n\n // Performance tracking\n const startTime = performance.now();\n\n // Red-Black SOR for better symmetry with fewer iterations\n // omega between 1.8-1.95 typically gives best convergence for Poisson\n const omega = 1.9;\n\n // Pre-classify pixels as red or black for efficient processing\n const redPixels: number[] = [];\n const blackPixels: number[] = [];\n\n for (let i = 0; i < pixelCount; i++) {\n const idx = interiorPixels[i]!;\n const x = idx % width;\n const y = Math.floor(idx / width);\n\n if ((x + y) % 2 === 0) {\n redPixels.push(i);\n } else {\n blackPixels.push(i);\n }\n }\n\n for (let iter = 0; iter < ITERATIONS; iter++) {\n // Red pass: update red pixels\n for (const i of redPixels) {\n const idx = interiorPixels[i]!;\n\n // Get precomputed neighbor indices\n const eastIdx = neighborIndices[i * 4 + 0]!;\n const westIdx = neighborIndices[i * 4 + 1]!;\n const northIdx = neighborIndices[i * 4 + 2]!;\n const southIdx = neighborIndices[i * 4 + 3]!;\n\n // Sum neighbors (use 0 for out-of-bounds)\n let sumN = 0;\n if (eastIdx >= 0) sumN += u[eastIdx]!;\n if (westIdx >= 0) sumN += u[westIdx]!;\n if (northIdx >= 0) sumN += u[northIdx]!;\n if (southIdx >= 0) sumN += u[southIdx]!;\n\n // SOR update: blend new value with old value\n const newValue = (C + sumN) / 4;\n u[idx] = omega * newValue + (1 - omega) * u[idx]!;\n }\n\n // Black pass: update black pixels\n for (const i of blackPixels) {\n const idx = interiorPixels[i]!;\n\n // Get precomputed neighbor indices\n const eastIdx = neighborIndices[i * 4 + 0]!;\n const westIdx = neighborIndices[i * 4 + 1]!;\n const northIdx = neighborIndices[i * 4 + 2]!;\n const southIdx = neighborIndices[i * 4 + 3]!;\n\n // Sum neighbors (use 0 for out-of-bounds)\n let sumN = 0;\n if (eastIdx >= 0) sumN += u[eastIdx]!;\n if (westIdx >= 0) sumN += u[westIdx]!;\n if (northIdx >= 0) sumN += u[northIdx]!;\n if (southIdx >= 0) sumN += u[southIdx]!;\n\n // SOR update: blend new value with old value\n const newValue = (C + sumN) / 4;\n u[idx] = omega * newValue + (1 - omega) * u[idx]!;\n }\n }\n\n if (POISSON_CONFIG_OPTIMIZED.measurePerformance) {\n const elapsed = performance.now() - startTime;\n\n console.log(`[Optimized Poisson Solver (SOR \u03C9=${omega})]`);\n console.log(` Working size: ${width}\u00D7${height}`);\n console.log(` Iterations: ${ITERATIONS}`);\n console.log(` Time: ${elapsed.toFixed(2)}ms`);\n console.log(` Interior pixels processed: ${pixelCount}`);\n console.log(` Speed: ${((ITERATIONS * pixelCount) / (elapsed * 1000)).toFixed(2)} Mpixels/sec`);\n }\n\n return u;\n}\n\nexport interface LiquidMetalUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colorTint: [number, number, number, number];\n u_image: HTMLImageElement | string | undefined;\n u_repetition: number;\n u_shiftRed: number;\n u_shiftBlue: number;\n u_contour: number;\n u_softness: number;\n u_distortion: number;\n u_angle: number;\n u_shape: (typeof LiquidMetalShapes)[LiquidMetalShape];\n u_isImage: boolean;\n}\n\nexport interface LiquidMetalParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colorTint?: string;\n image?: HTMLImageElement | string | undefined;\n repetition?: number;\n shiftRed?: number;\n shiftBlue?: number;\n contour?: number;\n softness?: number;\n distortion?: number;\n angle?: number;\n shape?: LiquidMetalShape;\n}\n\nexport const LiquidMetalShapes = {\n none: 0,\n circle: 1,\n daisy: 2,\n diamond: 3,\n metaballs: 4,\n} as const;\n\nexport type LiquidMetalShape = keyof typeof LiquidMetalShapes;\n"], + "mappings": "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", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/mesh-gradient.d.ts b/public/vendor/paper-shaders/shaders/mesh-gradient.d.ts new file mode 100644 index 0000000..bb51942 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/mesh-gradient.d.ts @@ -0,0 +1,52 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const meshGradientMeta: { + readonly maxColorCount: 10; +}; +/** + * A flowing composition of color spots, moving along distinct trajectories + * and transformed by organic distortion. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_colors (vec4[]): Up to 10 color spots in RGBA + * - u_colorsCount (float): Number of active colors + * - u_distortion (float): Power of organic noise distortion (0 to 1) + * - u_swirl (float): Power of vortex distortion (0 to 1) + * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1) + * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const meshGradientFragmentShader: string; +export interface MeshGradientUniforms extends ShaderSizingUniforms { + u_colors: vec4[]; + u_colorsCount: number; + u_distortion: number; + u_swirl: number; + u_grainMixer: number; + u_grainOverlay: number; +} +export interface MeshGradientParams extends ShaderSizingParams, ShaderMotionParams { + colors?: string[]; + distortion?: number; + swirl?: number; + grainMixer?: number; + grainOverlay?: number; +} diff --git a/public/vendor/paper-shaders/shaders/mesh-gradient.js b/public/vendor/paper-shaders/shaders/mesh-gradient.js new file mode 100644 index 0000000..49b4f2b --- /dev/null +++ b/public/vendor/paper-shaders/shaders/mesh-gradient.js @@ -0,0 +1,127 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import { +} from "../shader-sizing.js"; +import { declarePI, rotation2, proceduralHash21 } from "../shader-utils.js"; +const meshGradientMeta = { + maxColorCount: 10 +}; +const meshGradientFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform vec4 u_colors[${meshGradientMeta.maxColorCount}]; +uniform float u_colorsCount; + +uniform float u_distortion; +uniform float u_swirl; +uniform float u_grainMixer; +uniform float u_grainOverlay; + +in vec2 v_objectUV; +out vec4 fragColor; + +${declarePI} +${rotation2} +${proceduralHash21} + +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = hash21(i); + float b = hash21(i + vec2(1.0, 0.0)); + float c = hash21(i + vec2(0.0, 1.0)); + float d = hash21(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} + +float noise(vec2 n, vec2 seedOffset) { + return valueNoise(n + seedOffset); +} + +vec2 getPosition(int i, float t) { + float a = float(i) * .37; + float b = .6 + fract(float(i) / 3.) * .9; + float c = .8 + fract(float(i + 1) / 4.); + + float x = sin(t * b + a); + float y = cos(t * c + a * 1.5); + + return .5 + .5 * vec2(x, y); +} + +void main() { + vec2 uv = v_objectUV; + uv += .5; + vec2 grainUV = uv * 1000.; + + float grain = noise(grainUV, vec2(0.)); + float mixerGrain = .4 * u_grainMixer * (grain - .5); + + const float firstFrameOffset = 41.5; + float t = .5 * (u_time + firstFrameOffset); + + float radius = smoothstep(0., 1., length(uv - .5)); + float center = 1. - radius; + for (float i = 1.; i <= 2.; i++) { + uv.x += u_distortion * center / i * sin(t + i * .4 * smoothstep(.0, 1., uv.y)) * cos(.2 * t + i * 2.4 * smoothstep(.0, 1., uv.y)); + uv.y += u_distortion * center / i * cos(t + i * 2. * smoothstep(.0, 1., uv.x)); + } + + vec2 uvRotated = uv; + uvRotated -= vec2(.5); + float angle = 3. * u_swirl * radius; + uvRotated = rotate(uvRotated, -angle); + uvRotated += vec2(.5); + + vec3 color = vec3(0.); + float opacity = 0.; + float totalWeight = 0.; + + for (int i = 0; i < ${meshGradientMeta.maxColorCount}; i++) { + if (i >= int(u_colorsCount)) break; + + vec2 pos = getPosition(i, t) + mixerGrain; + vec3 colorFraction = u_colors[i].rgb * u_colors[i].a; + float opacityFraction = u_colors[i].a; + + float dist = length(uvRotated - pos); + + dist = pow(dist, 3.5); + float weight = 1. / (dist + 1e-3); + color += colorFraction * weight; + opacity += opacityFraction * weight; + totalWeight += weight; + } + + color /= max(1e-4, totalWeight); + opacity /= max(1e-4, totalWeight); + + float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.)); + grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5); + grainOverlay = pow(grainOverlay, 1.3); + + float grainOverlayV = grainOverlay * 2. - 1.; + vec3 grainOverlayColor = vec3(step(0., grainOverlayV)); + float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV); + grainOverlayStrength = pow(grainOverlayStrength, .8); + color = mix(color, grainOverlayColor, .35 * grainOverlayStrength); + + opacity += .5 * grainOverlayStrength; + opacity = clamp(opacity, 0., 1.); + + fragColor = vec4(color, opacity); +} +`; +export { + meshGradientFragmentShader, + meshGradientMeta +}; +//# sourceMappingURL=mesh-gradient.js.map diff --git a/public/vendor/paper-shaders/shaders/mesh-gradient.js.map b/public/vendor/paper-shaders/shaders/mesh-gradient.js.map new file mode 100644 index 0000000..baf1522 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/mesh-gradient.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/mesh-gradient.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\nexport const meshGradientMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * A flowing composition of color spots, moving along distinct trajectories\n * and transformed by organic distortion.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_colors (vec4[]): Up to 10 color spots in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_distortion (float): Power of organic noise distortion (0 to 1)\n * - u_swirl (float): Power of vortex distortion (0 to 1)\n * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1)\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const meshGradientFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colors[${meshGradientMeta.maxColorCount}];\nuniform float u_colorsCount;\n\nuniform float u_distortion;\nuniform float u_swirl;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\n\nin vec2 v_objectUV;\nout vec4 fragColor;\n\n${declarePI}\n${rotation2}\n${proceduralHash21}\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat noise(vec2 n, vec2 seedOffset) {\n return valueNoise(n + seedOffset);\n}\n\nvec2 getPosition(int i, float t) {\n float a = float(i) * .37;\n float b = .6 + fract(float(i) / 3.) * .9;\n float c = .8 + fract(float(i + 1) / 4.);\n\n float x = sin(t * b + a);\n float y = cos(t * c + a * 1.5);\n\n return .5 + .5 * vec2(x, y);\n}\n\nvoid main() {\n vec2 uv = v_objectUV;\n uv += .5;\n vec2 grainUV = uv * 1000.;\n\n float grain = noise(grainUV, vec2(0.));\n float mixerGrain = .4 * u_grainMixer * (grain - .5);\n\n const float firstFrameOffset = 41.5;\n float t = .5 * (u_time + firstFrameOffset);\n\n float radius = smoothstep(0., 1., length(uv - .5));\n float center = 1. - radius;\n for (float i = 1.; i <= 2.; i++) {\n uv.x += u_distortion * center / i * sin(t + i * .4 * smoothstep(.0, 1., uv.y)) * cos(.2 * t + i * 2.4 * smoothstep(.0, 1., uv.y));\n uv.y += u_distortion * center / i * cos(t + i * 2. * smoothstep(.0, 1., uv.x));\n }\n\n vec2 uvRotated = uv;\n uvRotated -= vec2(.5);\n float angle = 3. * u_swirl * radius;\n uvRotated = rotate(uvRotated, -angle);\n uvRotated += vec2(.5);\n\n vec3 color = vec3(0.);\n float opacity = 0.;\n float totalWeight = 0.;\n\n for (int i = 0; i < ${meshGradientMeta.maxColorCount}; i++) {\n if (i >= int(u_colorsCount)) break;\n\n vec2 pos = getPosition(i, t) + mixerGrain;\n vec3 colorFraction = u_colors[i].rgb * u_colors[i].a;\n float opacityFraction = u_colors[i].a;\n\n float dist = length(uvRotated - pos);\n\n dist = pow(dist, 3.5);\n float weight = 1. / (dist + 1e-3);\n color += colorFraction * weight;\n opacity += opacityFraction * weight;\n totalWeight += weight;\n }\n\n color /= max(1e-4, totalWeight);\n opacity /= max(1e-4, totalWeight);\n\n float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.));\n grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\n grainOverlay = pow(grainOverlay, 1.3);\n\n float grainOverlayV = grainOverlay * 2. - 1.;\n vec3 grainOverlayColor = vec3(step(0., grainOverlayV));\n float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV);\n grainOverlayStrength = pow(grainOverlayStrength, .8);\n color = mix(color, grainOverlayColor, .35 * grainOverlayStrength);\n\n opacity += .5 * grainOverlayStrength;\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface MeshGradientUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_distortion: number;\n u_swirl: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n}\n\nexport interface MeshGradientParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n distortion?: number;\n swirl?: number;\n grainMixer?: number;\n grainOverlay?: number;\n}\n"], + "mappings": ";;;;;AAEA;AAAA,OAGO;AACP,SAAS,WAAW,WAAW,wBAAwB;AAEhD,MAAM,mBAAmB;AAAA,EAC9B,eAAe;AACjB;AAkCO,MAAM,6BAAqC;AAAA;AAAA;AAAA;AAAA;AAAA,wBAK1B,iBAAiB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWpD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBA0DM,iBAAiB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/metaballs.d.ts b/public/vendor/paper-shaders/shaders/metaballs.d.ts new file mode 100644 index 0000000..bf6cae2 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/metaballs.d.ts @@ -0,0 +1,51 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const metaballsMeta: { + readonly maxColorCount: 8; + readonly maxBallsCount: 20; +}; +/** + * Up to 20 colored gooey balls moving around the center and merging into smooth organic shapes. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_colorBack (vec4): Background color in RGBA + * - u_colors (vec4[]): Up to 8 base colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_count (float): Number of balls (1 to 20) + * - u_size (float): Size of the balls (0 to 1) + * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture + * + * Vertex shader outputs (used in fragment shader): + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const metaballsFragmentShader: string; +export interface MetaballsUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colors: vec4[]; + u_colorsCount: number; + u_count: number; + u_size: number; + u_noiseTexture?: HTMLImageElement; +} +export interface MetaballsParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colors?: string[]; + count?: number; + size?: number; +} diff --git a/public/vendor/paper-shaders/shaders/metaballs.js b/public/vendor/paper-shaders/shaders/metaballs.js new file mode 100644 index 0000000..36fb85a --- /dev/null +++ b/public/vendor/paper-shaders/shaders/metaballs.js @@ -0,0 +1,112 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, textureRandomizerR, colorBandingFix } from "../shader-utils.js"; +const metaballsMeta = { + maxColorCount: 8, + maxBallsCount: 20 +}; +const metaballsFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform sampler2D u_noiseTexture; + +uniform vec4 u_colorBack; +uniform vec4 u_colors[${metaballsMeta.maxColorCount}]; +uniform float u_colorsCount; +uniform float u_size; +uniform float u_sizeRange; +uniform float u_count; + +in vec2 v_objectUV; + +out vec4 fragColor; + +${declarePI} +${textureRandomizerR} +float noise(float x) { + float i = floor(x); + float f = fract(x); + float u = f * f * (3.0 - 2.0 * f); + vec2 p0 = vec2(i, 0.0); + vec2 p1 = vec2(i + 1.0, 0.0); + return mix(randomR(p0), randomR(p1), u); +} + +float getBallShape(vec2 uv, vec2 c, float p) { + float s = .5 * length(uv - c); + s = 1. - clamp(s, 0., 1.); + s = pow(s, p); + return s; +} + +void main() { + vec2 shape_uv = v_objectUV; + + shape_uv += .5; + + const float firstFrameOffset = 2503.4; + float t = .2 * (u_time + firstFrameOffset); + + vec3 totalColor = vec3(0.); + float totalShape = 0.; + float totalOpacity = 0.; + + for (int i = 0; i < ${metaballsMeta.maxBallsCount}; i++) { + if (i >= int(ceil(u_count))) break; + + float idxFract = float(i) / float(${metaballsMeta.maxBallsCount}); + float angle = TWO_PI * idxFract; + + float speed = 1. - .2 * idxFract; + float noiseX = noise(angle * 10. + float(i) + t * speed); + float noiseY = noise(angle * 20. + float(i) - t * speed); + + vec2 pos = vec2(.5) + 1e-4 + .9 * (vec2(noiseX, noiseY) - .5); + + int safeIndex = i % int(u_colorsCount + 0.5); + vec4 ballColor = u_colors[safeIndex]; + ballColor.rgb *= ballColor.a; + + float sizeFrac = 1.; + if (float(i) > floor(u_count - 1.)) { + sizeFrac *= fract(u_count); + } + + float shape = getBallShape(shape_uv, pos, 45. - 30. * u_size * sizeFrac); + shape *= pow(u_size, .2); + shape = smoothstep(0., 1., shape); + + totalColor += ballColor.rgb * shape; + totalShape += shape; + totalOpacity += ballColor.a * shape; + } + + totalColor /= max(totalShape, 1e-4); + totalOpacity /= max(totalShape, 1e-4); + + float edge_width = fwidth(totalShape); + float finalShape = smoothstep(.4, .4 + edge_width, totalShape); + + vec3 color = totalColor * finalShape; + float opacity = totalOpacity * finalShape; + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1. - opacity); + opacity = opacity + u_colorBack.a * (1. - opacity); + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +export { + metaballsFragmentShader, + metaballsMeta +}; +//# sourceMappingURL=metaballs.js.map diff --git a/public/vendor/paper-shaders/shaders/metaballs.js.map b/public/vendor/paper-shaders/shaders/metaballs.js.map new file mode 100644 index 0000000..82beb34 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/metaballs.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/metaballs.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, textureRandomizerR, colorBandingFix } from '../shader-utils.js';\n\nexport const metaballsMeta = {\n maxColorCount: 8,\n maxBallsCount: 20,\n} as const;\n\n/**\n * Up to 20 colored gooey balls moving around the center and merging into smooth organic shapes.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colors (vec4[]): Up to 8 base colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_count (float): Number of balls (1 to 20)\n * - u_size (float): Size of the balls (0 to 1)\n * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const metaballsFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${ metaballsMeta.maxColorCount }];\nuniform float u_colorsCount;\nuniform float u_size;\nuniform float u_sizeRange;\nuniform float u_count;\n\nin vec2 v_objectUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ textureRandomizerR }\nfloat noise(float x) {\n float i = floor(x);\n float f = fract(x);\n float u = f * f * (3.0 - 2.0 * f);\n vec2 p0 = vec2(i, 0.0);\n vec2 p1 = vec2(i + 1.0, 0.0);\n return mix(randomR(p0), randomR(p1), u);\n}\n\nfloat getBallShape(vec2 uv, vec2 c, float p) {\n float s = .5 * length(uv - c);\n s = 1. - clamp(s, 0., 1.);\n s = pow(s, p);\n return s;\n}\n\nvoid main() {\n vec2 shape_uv = v_objectUV;\n\n shape_uv += .5;\n\n const float firstFrameOffset = 2503.4;\n float t = .2 * (u_time + firstFrameOffset);\n\n vec3 totalColor = vec3(0.);\n float totalShape = 0.;\n float totalOpacity = 0.;\n\n for (int i = 0; i < ${ metaballsMeta.maxBallsCount }; i++) {\n if (i >= int(ceil(u_count))) break;\n\n float idxFract = float(i) / float(${ metaballsMeta.maxBallsCount });\n float angle = TWO_PI * idxFract;\n\n float speed = 1. - .2 * idxFract;\n float noiseX = noise(angle * 10. + float(i) + t * speed);\n float noiseY = noise(angle * 20. + float(i) - t * speed);\n\n vec2 pos = vec2(.5) + 1e-4 + .9 * (vec2(noiseX, noiseY) - .5);\n\n int safeIndex = i % int(u_colorsCount + 0.5);\n vec4 ballColor = u_colors[safeIndex];\n ballColor.rgb *= ballColor.a;\n\n float sizeFrac = 1.;\n if (float(i) > floor(u_count - 1.)) {\n sizeFrac *= fract(u_count);\n }\n\n float shape = getBallShape(shape_uv, pos, 45. - 30. * u_size * sizeFrac);\n shape *= pow(u_size, .2);\n shape = smoothstep(0., 1., shape);\n\n totalColor += ballColor.rgb * shape;\n totalShape += shape;\n totalOpacity += ballColor.a * shape;\n }\n\n totalColor /= max(totalShape, 1e-4);\n totalOpacity /= max(totalShape, 1e-4);\n\n float edge_width = fwidth(totalShape);\n float finalShape = smoothstep(.4, .4 + edge_width, totalShape);\n\n vec3 color = totalColor * finalShape;\n float opacity = totalOpacity * finalShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface MetaballsUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_count: number;\n u_size: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface MetaballsParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n count?: number;\n size?: number;\n}\n"], + "mappings": ";;;;;AAEA,eAAmE;AACnE,SAAS,WAAW,oBAAoB,uBAAuB;AAExD,MAAM,gBAAgB;AAAA,EAC3B,eAAe;AAAA,EACf,eAAe;AACjB;AAiCO,MAAM,0BAAkC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAQtB,cAAc,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUlD,SAAU;AAAA,EACV,kBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBA6BG,cAAc,aAAc;AAAA;AAAA;AAAA,wCAGZ,cAAc,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAwChE,eAAgB;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/neuro-noise.d.ts b/public/vendor/paper-shaders/shaders/neuro-noise.d.ts new file mode 100644 index 0000000..7ee1883 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/neuro-noise.d.ts @@ -0,0 +1,49 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * A glowing, web-like structure of fluid lines and soft intersections. + * Great for creating atmospheric, organic-yet-futuristic visuals. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_colorFront (vec4): Graphics highlight color in RGBA + * - u_colorMid (vec4): Graphics main color in RGBA + * - u_colorBack (vec4): Background color in RGBA + * - u_brightness (float): Luminosity of the crossing points (0 to 1) + * - u_contrast (float): Sharpness of the bright-dark transition (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + * Original algorithm: https://x.com/zozuar/status/1625182758745128981/ + */ +export declare const neuroNoiseFragmentShader: string; +export interface NeuroNoiseUniforms extends ShaderSizingUniforms { + u_colorFront: [number, number, number, number]; + u_colorMid: [number, number, number, number]; + u_colorBack: [number, number, number, number]; + u_brightness: number; + u_contrast: number; +} +export interface NeuroNoiseParams extends ShaderSizingParams, ShaderMotionParams { + colorFront?: string; + colorMid?: string; + colorBack?: string; + brightness?: number; + contrast?: number; +} diff --git a/public/vendor/paper-shaders/shaders/neuro-noise.js b/public/vendor/paper-shaders/shaders/neuro-noise.js new file mode 100644 index 0000000..29a5533 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/neuro-noise.js @@ -0,0 +1,79 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { rotation2, colorBandingFix } from "../shader-utils.js"; +const neuroNoiseFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; +uniform vec2 u_resolution; +uniform float u_pixelRatio; + +uniform vec4 u_colorFront; +uniform vec4 u_colorMid; +uniform vec4 u_colorBack; +uniform float u_brightness; +uniform float u_contrast; + +in vec2 v_patternUV; + +out vec4 fragColor; + +${rotation2} + +float neuroShape(vec2 uv, float t) { + vec2 sine_acc = vec2(0.); + vec2 res = vec2(0.); + float scale = 8.; + + for (int j = 0; j < 15; j++) { + uv = rotate(uv, 1.); + sine_acc = rotate(sine_acc, 1.); + vec2 layer = uv * scale + float(j) + sine_acc - t; + sine_acc += sin(layer); + res += (.5 + .5 * cos(layer)) / scale; + scale *= (1.2); + } + return res.x + res.y; +} + +void main() { + vec2 shape_uv = v_patternUV; + shape_uv *= .13; + + float t = .5 * u_time; + + float noise = neuroShape(shape_uv, t); + + noise = (1. + u_brightness) * noise * noise; + noise = pow(noise, .7 + 6. * u_contrast); + noise = min(1.4, noise); + + float blend = smoothstep(0.7, 1.4, noise); + + vec4 frontC = u_colorFront; + frontC.rgb *= frontC.a; + vec4 midC = u_colorMid; + midC.rgb *= midC.a; + vec4 blendFront = mix(midC, frontC, blend); + + float safeNoise = max(noise, 0.0); + vec3 color = blendFront.rgb * safeNoise; + float opacity = clamp(blendFront.a * safeNoise, 0., 1.); + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1. - opacity); + opacity = opacity + u_colorBack.a * (1. - opacity); + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +export { + neuroNoiseFragmentShader +}; +//# sourceMappingURL=neuro-noise.js.map diff --git a/public/vendor/paper-shaders/shaders/neuro-noise.js.map b/public/vendor/paper-shaders/shaders/neuro-noise.js.map new file mode 100644 index 0000000..9280f31 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/neuro-noise.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/neuro-noise.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { rotation2, colorBandingFix } from '../shader-utils.js';\n\n/**\n * A glowing, web-like structure of fluid lines and soft intersections.\n * Great for creating atmospheric, organic-yet-futuristic visuals.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_colorFront (vec4): Graphics highlight color in RGBA\n * - u_colorMid (vec4): Graphics main color in RGBA\n * - u_colorBack (vec4): Background color in RGBA\n * - u_brightness (float): Luminosity of the crossing points (0 to 1)\n * - u_contrast (float): Sharpness of the bright-dark transition (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n * Original algorithm: https://x.com/zozuar/status/1625182758745128981/\n */\n\n// language=GLSL\nexport const neuroNoiseFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorMid;\nuniform vec4 u_colorBack;\nuniform float u_brightness;\nuniform float u_contrast;\n\nin vec2 v_patternUV;\n\nout vec4 fragColor;\n\n${ rotation2 }\n\nfloat neuroShape(vec2 uv, float t) {\n vec2 sine_acc = vec2(0.);\n vec2 res = vec2(0.);\n float scale = 8.;\n\n for (int j = 0; j < 15; j++) {\n uv = rotate(uv, 1.);\n sine_acc = rotate(sine_acc, 1.);\n vec2 layer = uv * scale + float(j) + sine_acc - t;\n sine_acc += sin(layer);\n res += (.5 + .5 * cos(layer)) / scale;\n scale *= (1.2);\n }\n return res.x + res.y;\n}\n\nvoid main() {\n vec2 shape_uv = v_patternUV;\n shape_uv *= .13;\n\n float t = .5 * u_time;\n\n float noise = neuroShape(shape_uv, t);\n\n noise = (1. + u_brightness) * noise * noise;\n noise = pow(noise, .7 + 6. * u_contrast);\n noise = min(1.4, noise);\n\n float blend = smoothstep(0.7, 1.4, noise);\n\n vec4 frontC = u_colorFront;\n frontC.rgb *= frontC.a;\n vec4 midC = u_colorMid;\n midC.rgb *= midC.a;\n vec4 blendFront = mix(midC, frontC, blend);\n\n float safeNoise = max(noise, 0.0);\n vec3 color = blendFront.rgb * safeNoise;\n float opacity = clamp(blendFront.a * safeNoise, 0., 1.);\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface NeuroNoiseUniforms extends ShaderSizingUniforms {\n u_colorFront: [number, number, number, number];\n u_colorMid: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_brightness: number;\n u_contrast: number;\n}\n\nexport interface NeuroNoiseParams extends ShaderSizingParams, ShaderMotionParams {\n colorFront?: string;\n colorMid?: string;\n colorBack?: string;\n brightness?: number;\n contrast?: number;\n}\n"], + "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,WAAW,uBAAuB;AAoCpC,MAAM,2BAAmC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiB7C,SAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA8CR,eAAgB;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/paper-texture.d.ts b/public/vendor/paper-shaders/shaders/paper-texture.d.ts new file mode 100644 index 0000000..9e8b324 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/paper-texture.d.ts @@ -0,0 +1,78 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * A static texture built from multiple noise layers, usable for realistic paper and cardboard surfaces. + * Can be used as an image filter or as a standalone texture. + * + * Fragment shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_image (sampler2D): Optional source image texture + * - u_imageAspectRatio (float): Aspect ratio of the source image + * - u_colorFront (vec4): Foreground color in RGBA + * - u_colorBack (vec4): Background color in RGBA + * - u_contrast (float): Blending behavior, sharper vs smoother color transitions (0 to 1) + * - u_roughness (float): Pixel noise, related to canvas and not scalable (0 to 1) + * - u_fiber (float): Curly-shaped noise intensity (0 to 1) + * - u_fiberSize (float): Curly-shaped noise scale (0 to 1) + * - u_crumples (float): Cell-based crumple pattern intensity (0 to 1) + * - u_crumpleSize (float): Cell-based crumple pattern scale (0 to 1) + * - u_folds (float): Depth of the folds (0 to 1) + * - u_foldCount (float): Number of folds (1 to 15) + * - u_fade (float): Big-scale noise mask applied to the pattern (0 to 1) + * - u_drops (float): Visibility of speckle pattern (0 to 1) + * - u_seed (float): Seed applied to folds, crumples and dots (0 to 1000) + * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture + * + * Vertex shader outputs (used in fragment shader): + * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * + */ +export declare const paperTextureFragmentShader: string; +export interface PaperTextureUniforms extends ShaderSizingUniforms { + u_image: HTMLImageElement | string | undefined; + u_noiseTexture?: HTMLImageElement; + u_colorFront: [number, number, number, number]; + u_colorBack: [number, number, number, number]; + u_contrast: number; + u_roughness: number; + u_fiber: number; + u_fiberSize: number; + u_crumples: number; + u_foldCount: number; + u_folds: number; + u_fade: number; + u_crumpleSize: number; + u_drops: number; + u_seed: number; +} +export interface PaperTextureParams extends ShaderSizingParams, ShaderMotionParams { + image?: HTMLImageElement | string; + colorFront?: string; + colorBack?: string; + contrast?: number; + roughness?: number; + fiber?: number; + fiberSize?: number; + crumples?: number; + foldCount?: number; + folds?: number; + fade?: number; + crumpleSize?: number; + drops?: number; + seed?: number; +} diff --git a/public/vendor/paper-shaders/shaders/paper-texture.js b/public/vendor/paper-shaders/shaders/paper-texture.js new file mode 100644 index 0000000..f926128 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/paper-texture.js @@ -0,0 +1,248 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { rotation2, declarePI, fiberNoise, textureRandomizerR } from "../shader-utils.js"; +const paperTextureFragmentShader = `#version 300 es +precision mediump float; + +uniform vec2 u_resolution; +uniform float u_pixelRatio; + +uniform vec4 u_colorFront; +uniform vec4 u_colorBack; + +uniform sampler2D u_image; +uniform float u_imageAspectRatio; + +uniform float u_contrast; +uniform float u_roughness; +uniform float u_fiber; +uniform float u_fiberSize; +uniform float u_crumples; +uniform float u_crumpleSize; +uniform float u_folds; +uniform float u_foldCount; +uniform float u_drops; +uniform float u_seed; +uniform float u_fade; + +uniform sampler2D u_noiseTexture; + +in vec2 v_imageUV; + +out vec4 fragColor; + +float getUvFrame(vec2 uv) { + float aax = 2. * fwidth(uv.x); + float aay = 2. * fwidth(uv.y); + + float left = smoothstep(0., aax, uv.x); + float right = 1. - smoothstep(1. - aax, 1., uv.x); + float bottom = smoothstep(0., aay, uv.y); + float top = 1. - smoothstep(1. - aay, 1., uv.y); + + return left * right * bottom * top; +} + +${declarePI} +${rotation2} +${textureRandomizerR} +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = randomR(i); + float b = randomR(i + vec2(1.0, 0.0)); + float c = randomR(i + vec2(0.0, 1.0)); + float d = randomR(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} +float fbm(vec2 n) { + float total = 0.0, amplitude = .4; + for (int i = 0; i < 3; i++) { + total += valueNoise(n) * amplitude; + n *= 1.99; + amplitude *= 0.65; + } + return total; +} + + +float randomG(vec2 p) { + vec2 uv = floor(p) / 50. + .5; + return texture(u_noiseTexture, fract(uv)).g; +} +float roughness(vec2 p) { + p *= .1; + float o = 0.; + for (float i = 0.; ++i < 4.; p *= 2.1) { + vec4 w = vec4(floor(p), ceil(p)); + vec2 f = fract(p); + o += mix( + mix(randomG(w.xy), randomG(w.xw), f.y), + mix(randomG(w.zy), randomG(w.zw), f.y), + f.x); + o += .2 / exp(2. * abs(sin(.2 * p.x + .5 * p.y))); + } + return o / 3.; +} + +${fiberNoise} + +vec2 randomGB(vec2 p) { + vec2 uv = floor(p) / 50. + .5; + return texture(u_noiseTexture, fract(uv)).gb; +} +float crumpledNoise(vec2 t, float pw) { + vec2 p = floor(t); + float wsum = 0.; + float cl = 0.; + for (int y = -1; y < 2; y += 1) { + for (int x = -1; x < 2; x += 1) { + vec2 b = vec2(float(x), float(y)); + vec2 q = b + p; + vec2 q2 = q - floor(q / 8.) * 8.; + vec2 c = q + randomGB(q2); + vec2 r = c - t; + float w = pow(smoothstep(0., 1., 1. - abs(r.x)), pw) * pow(smoothstep(0., 1., 1. - abs(r.y)), pw); + cl += (.5 + .5 * sin((q2.x + q2.y * 5.) * 8.)) * w; + wsum += w; + } + } + return pow(wsum != 0.0 ? cl / wsum : 0.0, .5) * 2.; +} +float crumplesShape(vec2 uv) { + return crumpledNoise(uv * .25, 16.) * crumpledNoise(uv * .5, 2.); +} + + +vec2 folds(vec2 uv) { + vec3 pp = vec3(0.); + float l = 9.; + for (float i = 0.; i < 15.; i++) { + if (i >= u_foldCount) break; + vec2 rand = randomGB(vec2(i, i * u_seed)); + float an = rand.x * TWO_PI; + vec2 p = vec2(cos(an), sin(an)) * rand.y; + float dist = distance(uv, p); + l = min(l, dist); + + if (l == dist) { + pp.xy = (uv - p.xy); + pp.z = dist; + } + } + return mix(pp.xy, vec2(0.), pow(pp.z, .25)); +} + +float drops(vec2 uv) { + vec2 iDropsUV = floor(uv); + vec2 fDropsUV = fract(uv); + float dropsMinDist = 1.; + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + vec2 neighbor = vec2(float(i), float(j)); + vec2 offset = randomGB(iDropsUV + neighbor); + offset = .5 + .5 * sin(10. * u_seed + TWO_PI * offset); + vec2 pos = neighbor + offset - fDropsUV; + float dist = length(pos); + dropsMinDist = min(dropsMinDist, dropsMinDist*dist); + } + } + return 1. - smoothstep(.05, .09, pow(dropsMinDist, .5)); +} + +float lst(float edge0, float edge1, float x) { + return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); +} + +void main() { + + vec2 imageUV = v_imageUV; + vec2 patternUV = v_imageUV - .5; + patternUV = 5. * (patternUV * vec2(u_imageAspectRatio, 1.)); + + vec2 roughnessUv = 1.5 * (gl_FragCoord.xy - .5 * u_resolution) / u_pixelRatio; + float roughness = roughness(roughnessUv + vec2(1., 0.)) - roughness(roughnessUv - vec2(1., 0.)); + + vec2 crumplesUV = fract(patternUV * .02 / u_crumpleSize - u_seed) * 32.; + float crumples = u_crumples * (crumplesShape(crumplesUV + vec2(.05, 0.)) - crumplesShape(crumplesUV)); + + vec2 fiberUV = 2. / u_fiberSize * patternUV; + float fiber = fiberNoise(fiberUV, vec2(0.)); + fiber = .5 * u_fiber * (fiber - 1.); + + vec2 normal = vec2(0.); + vec2 normalImage = vec2(0.); + + vec2 foldsUV = patternUV * .12; + foldsUV = rotate(foldsUV, 4. * u_seed); + vec2 w = folds(foldsUV); + foldsUV = rotate(foldsUV + .007 * cos(u_seed), .01 * sin(u_seed)); + vec2 w2 = folds(foldsUV); + + float drops = u_drops * drops(patternUV * 2.); + + float fade = u_fade * fbm(.17 * patternUV + 10. * u_seed); + fade = clamp(8. * fade * fade * fade, 0., 1.); + + w = mix(w, vec2(0.), fade); + w2 = mix(w2, vec2(0.), fade); + crumples = mix(crumples, 0., fade); + drops = mix(drops, 0., fade); + fiber *= mix(1., .5, fade); + roughness *= mix(1., .5, fade); + + normal.xy += u_folds * min(5. * u_contrast, 1.) * 4. * max(vec2(0.), w + w2); + normalImage.xy += u_folds * 2. * w; + + normal.xy += crumples; + normalImage.xy += 1.5 * crumples; + + normal.xy += 3. * drops; + normalImage.xy += .2 * drops; + + normal.xy += u_roughness * 1.5 * roughness; + normal.xy += fiber; + + normalImage += u_roughness * .75 * roughness; + normalImage += .2 * fiber; + + vec3 lightPos = vec3(1., 2., 1.); + float res = dot(normalize(vec3(normal, 9.5 - 9. * pow(u_contrast, .1))), normalize(lightPos)); + + vec3 fgColor = u_colorFront.rgb * u_colorFront.a; + float fgOpacity = u_colorFront.a; + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + float bgOpacity = u_colorBack.a; + + imageUV += .02 * normalImage; + float frame = getUvFrame(imageUV); + vec4 image = texture(u_image, imageUV); + image.rgb += .6 * pow(u_contrast, .4) * (res - .7); + + frame *= image.a; + + vec3 color = fgColor * res; + float opacity = fgOpacity * res; + + color += bgColor * (1. - opacity); + opacity += bgOpacity * (1. - opacity); + opacity = mix(opacity, 1., frame); + + color -= .007 * drops; + + color.rgb = mix(color, image.rgb, frame); + + fragColor = vec4(color, opacity); +} +`; +export { + paperTextureFragmentShader +}; +//# sourceMappingURL=paper-texture.js.map diff --git a/public/vendor/paper-shaders/shaders/paper-texture.js.map b/public/vendor/paper-shaders/shaders/paper-texture.js.map new file mode 100644 index 0000000..d189405 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/paper-texture.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/paper-texture.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { rotation2, declarePI, fiberNoise, textureRandomizerR } from '../shader-utils.js';\n\n/**\n * A static texture built from multiple noise layers, usable for realistic paper and cardboard surfaces.\n * Can be used as an image filter or as a standalone texture.\n *\n * Fragment shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_image (sampler2D): Optional source image texture\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorFront (vec4): Foreground color in RGBA\n * - u_colorBack (vec4): Background color in RGBA\n * - u_contrast (float): Blending behavior, sharper vs smoother color transitions (0 to 1)\n * - u_roughness (float): Pixel noise, related to canvas and not scalable (0 to 1)\n * - u_fiber (float): Curly-shaped noise intensity (0 to 1)\n * - u_fiberSize (float): Curly-shaped noise scale (0 to 1)\n * - u_crumples (float): Cell-based crumple pattern intensity (0 to 1)\n * - u_crumpleSize (float): Cell-based crumple pattern scale (0 to 1)\n * - u_folds (float): Depth of the folds (0 to 1)\n * - u_foldCount (float): Number of folds (1 to 15)\n * - u_fade (float): Big-scale noise mask applied to the pattern (0 to 1)\n * - u_drops (float): Visibility of speckle pattern (0 to 1)\n * - u_seed (float): Seed applied to folds, crumples and dots (0 to 1000)\n * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const paperTextureFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_contrast;\nuniform float u_roughness;\nuniform float u_fiber;\nuniform float u_fiberSize;\nuniform float u_crumples;\nuniform float u_crumpleSize;\nuniform float u_folds;\nuniform float u_foldCount;\nuniform float u_drops;\nuniform float u_seed;\nuniform float u_fade;\n\nuniform sampler2D u_noiseTexture;\n\nin vec2 v_imageUV;\n\nout vec4 fragColor;\n\nfloat getUvFrame(vec2 uv) {\n float aax = 2. * fwidth(uv.x);\n float aay = 2. * fwidth(uv.y);\n\n float left = smoothstep(0., aax, uv.x);\n float right = 1. - smoothstep(1. - aax, 1., uv.x);\n float bottom = smoothstep(0., aay, uv.y);\n float top = 1. - smoothstep(1. - aay, 1., uv.y);\n\n return left * right * bottom * top;\n}\n\n${ declarePI }\n${ rotation2 }\n${ textureRandomizerR }\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomR(i);\n float b = randomR(i + vec2(1.0, 0.0));\n float c = randomR(i + vec2(0.0, 1.0));\n float d = randomR(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\nfloat fbm(vec2 n) {\n float total = 0.0, amplitude = .4;\n for (int i = 0; i < 3; i++) {\n total += valueNoise(n) * amplitude;\n n *= 1.99;\n amplitude *= 0.65;\n }\n return total;\n}\n\n\nfloat randomG(vec2 p) {\n vec2 uv = floor(p) / 50. + .5;\n return texture(u_noiseTexture, fract(uv)).g;\n}\nfloat roughness(vec2 p) {\n p *= .1;\n float o = 0.;\n for (float i = 0.; ++i < 4.; p *= 2.1) {\n vec4 w = vec4(floor(p), ceil(p));\n vec2 f = fract(p);\n o += mix(\n mix(randomG(w.xy), randomG(w.xw), f.y),\n mix(randomG(w.zy), randomG(w.zw), f.y),\n f.x);\n o += .2 / exp(2. * abs(sin(.2 * p.x + .5 * p.y)));\n }\n return o / 3.;\n}\n\n${ fiberNoise }\n\nvec2 randomGB(vec2 p) {\n vec2 uv = floor(p) / 50. + .5;\n return texture(u_noiseTexture, fract(uv)).gb;\n}\nfloat crumpledNoise(vec2 t, float pw) {\n vec2 p = floor(t);\n float wsum = 0.;\n float cl = 0.;\n for (int y = -1; y < 2; y += 1) {\n for (int x = -1; x < 2; x += 1) {\n vec2 b = vec2(float(x), float(y));\n vec2 q = b + p;\n vec2 q2 = q - floor(q / 8.) * 8.;\n vec2 c = q + randomGB(q2);\n vec2 r = c - t;\n float w = pow(smoothstep(0., 1., 1. - abs(r.x)), pw) * pow(smoothstep(0., 1., 1. - abs(r.y)), pw);\n cl += (.5 + .5 * sin((q2.x + q2.y * 5.) * 8.)) * w;\n wsum += w;\n }\n }\n return pow(wsum != 0.0 ? cl / wsum : 0.0, .5) * 2.;\n}\nfloat crumplesShape(vec2 uv) {\n return crumpledNoise(uv * .25, 16.) * crumpledNoise(uv * .5, 2.);\n}\n\n\nvec2 folds(vec2 uv) {\n vec3 pp = vec3(0.);\n float l = 9.;\n for (float i = 0.; i < 15.; i++) {\n if (i >= u_foldCount) break;\n vec2 rand = randomGB(vec2(i, i * u_seed));\n float an = rand.x * TWO_PI;\n vec2 p = vec2(cos(an), sin(an)) * rand.y;\n float dist = distance(uv, p);\n l = min(l, dist);\n\n if (l == dist) {\n pp.xy = (uv - p.xy);\n pp.z = dist;\n }\n }\n return mix(pp.xy, vec2(0.), pow(pp.z, .25));\n}\n\nfloat drops(vec2 uv) {\n vec2 iDropsUV = floor(uv);\n vec2 fDropsUV = fract(uv);\n float dropsMinDist = 1.;\n for (int j = -1; j <= 1; j++) {\n for (int i = -1; i <= 1; i++) {\n vec2 neighbor = vec2(float(i), float(j));\n vec2 offset = randomGB(iDropsUV + neighbor);\n offset = .5 + .5 * sin(10. * u_seed + TWO_PI * offset);\n vec2 pos = neighbor + offset - fDropsUV;\n float dist = length(pos);\n dropsMinDist = min(dropsMinDist, dropsMinDist*dist);\n }\n }\n return 1. - smoothstep(.05, .09, pow(dropsMinDist, .5));\n}\n\nfloat lst(float edge0, float edge1, float x) {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\nvoid main() {\n\n vec2 imageUV = v_imageUV;\n vec2 patternUV = v_imageUV - .5;\n patternUV = 5. * (patternUV * vec2(u_imageAspectRatio, 1.));\n\n vec2 roughnessUv = 1.5 * (gl_FragCoord.xy - .5 * u_resolution) / u_pixelRatio;\n float roughness = roughness(roughnessUv + vec2(1., 0.)) - roughness(roughnessUv - vec2(1., 0.));\n\n vec2 crumplesUV = fract(patternUV * .02 / u_crumpleSize - u_seed) * 32.;\n float crumples = u_crumples * (crumplesShape(crumplesUV + vec2(.05, 0.)) - crumplesShape(crumplesUV));\n\n vec2 fiberUV = 2. / u_fiberSize * patternUV;\n float fiber = fiberNoise(fiberUV, vec2(0.));\n fiber = .5 * u_fiber * (fiber - 1.);\n\n vec2 normal = vec2(0.);\n vec2 normalImage = vec2(0.);\n\n vec2 foldsUV = patternUV * .12;\n foldsUV = rotate(foldsUV, 4. * u_seed);\n vec2 w = folds(foldsUV);\n foldsUV = rotate(foldsUV + .007 * cos(u_seed), .01 * sin(u_seed));\n vec2 w2 = folds(foldsUV);\n\n float drops = u_drops * drops(patternUV * 2.);\n\n float fade = u_fade * fbm(.17 * patternUV + 10. * u_seed);\n fade = clamp(8. * fade * fade * fade, 0., 1.);\n\n w = mix(w, vec2(0.), fade);\n w2 = mix(w2, vec2(0.), fade);\n crumples = mix(crumples, 0., fade);\n drops = mix(drops, 0., fade);\n fiber *= mix(1., .5, fade);\n roughness *= mix(1., .5, fade);\n\n normal.xy += u_folds * min(5. * u_contrast, 1.) * 4. * max(vec2(0.), w + w2);\n normalImage.xy += u_folds * 2. * w;\n\n normal.xy += crumples;\n normalImage.xy += 1.5 * crumples;\n\n normal.xy += 3. * drops;\n normalImage.xy += .2 * drops;\n\n normal.xy += u_roughness * 1.5 * roughness;\n normal.xy += fiber;\n\n normalImage += u_roughness * .75 * roughness;\n normalImage += .2 * fiber;\n\n vec3 lightPos = vec3(1., 2., 1.);\n float res = dot(normalize(vec3(normal, 9.5 - 9. * pow(u_contrast, .1))), normalize(lightPos));\n\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n imageUV += .02 * normalImage;\n float frame = getUvFrame(imageUV);\n vec4 image = texture(u_image, imageUV);\n image.rgb += .6 * pow(u_contrast, .4) * (res - .7);\n\n frame *= image.a;\n\n vec3 color = fgColor * res;\n float opacity = fgOpacity * res;\n\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n opacity = mix(opacity, 1., frame);\n\n color -= .007 * drops;\n\n color.rgb = mix(color, image.rgb, frame);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface PaperTextureUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_noiseTexture?: HTMLImageElement;\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_contrast: number;\n u_roughness: number;\n u_fiber: number;\n u_fiberSize: number;\n u_crumples: number;\n u_foldCount: number;\n u_folds: number;\n u_fade: number;\n u_crumpleSize: number;\n u_drops: number;\n u_seed: number;\n}\n\nexport interface PaperTextureParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n colorFront?: string;\n colorBack?: string;\n contrast?: number;\n roughness?: number;\n fiber?: number;\n fiberSize?: number;\n crumples?: number;\n foldCount?: number;\n folds?: number;\n fade?: number;\n crumpleSize?: number;\n drops?: number;\n seed?: number;\n}\n"], + "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,WAAW,WAAW,YAAY,0BAA0B;AA8C9D,MAAM,6BAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0C/C,SAAU;AAAA,EACV,SAAU;AAAA,EACV,kBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2CnB,UAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/perlin-noise.d.ts b/public/vendor/paper-shaders/shaders/perlin-noise.d.ts new file mode 100644 index 0000000..b7cf4aa --- /dev/null +++ b/public/vendor/paper-shaders/shaders/perlin-noise.d.ts @@ -0,0 +1,52 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * Classic animated 3D Perlin noise with exposed controls. + * Original algorithm: https://www.shadertoy.com/view/NlSGDz + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_colorFront (vec4): Foreground color in RGBA + * - u_colorBack (vec4): Background color in RGBA + * - u_proportion (float): Blend point between 2 colors, 0.5 = equal distribution (0 to 1) + * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1) + * - u_octaveCount (float): Perlin noise octaves number, more octaves for more detailed patterns (1 to 8) + * - u_persistence (float): Roughness, falloff between octaves (0.3 to 1) + * - u_lacunarity (float): Frequency step, defines how compressed the pattern is (1.5 to 10) + * + * Vertex shader outputs (used in fragment shader): + * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const perlinNoiseFragmentShader: string; +export interface PerlinNoiseUniforms extends ShaderSizingUniforms { + u_colorFront: [number, number, number, number]; + u_colorBack: [number, number, number, number]; + u_proportion: number; + u_softness: number; + u_octaveCount: number; + u_persistence: number; + u_lacunarity: number; +} +export interface PerlinNoiseParams extends ShaderSizingParams, ShaderMotionParams { + colorFront?: string; + colorBack?: string; + proportion?: number; + softness?: number; + octaveCount?: number; + persistence?: number; + lacunarity?: number; +} diff --git a/public/vendor/paper-shaders/shaders/perlin-noise.js b/public/vendor/paper-shaders/shaders/perlin-noise.js new file mode 100644 index 0000000..941bff1 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/perlin-noise.js @@ -0,0 +1,173 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, colorBandingFix, proceduralHash11, proceduralHash21 } from "../shader-utils.js"; +const perlinNoiseFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform vec4 u_colorFront; +uniform vec4 u_colorBack; +uniform float u_proportion; +uniform float u_softness; +uniform float u_octaveCount; +uniform float u_persistence; +uniform float u_lacunarity; + +in vec2 v_patternUV; + +out vec4 fragColor; + +${declarePI} +${proceduralHash11} +${proceduralHash21} + +float hash31(vec3 p) { + p = fract(p * 0.3183099) + 0.1; + p += dot(p, p.yzx + 19.19); + return fract(p.x * (p.y + p.z)); +} + +vec3 gradientPredefined(float hash) { + int idx = int(hash * 12.0) % 12; + + if (idx == 0) return vec3(1, 1, 0); + if (idx == 1) return vec3(-1, 1, 0); + if (idx == 2) return vec3(1, -1, 0); + if (idx == 3) return vec3(-1, -1, 0); + if (idx == 4) return vec3(1, 0, 1); + if (idx == 5) return vec3(-1, 0, 1); + if (idx == 6) return vec3(1, 0, -1); + if (idx == 7) return vec3(-1, 0, -1); + if (idx == 8) return vec3(0, 1, 1); + if (idx == 9) return vec3(0, -1, 1); + if (idx == 10) return vec3(0, 1, -1); + return vec3(0, -1, -1);// idx == 11 +} + +float interpolateSafe(float v000, float v001, float v010, float v011, +float v100, float v101, float v110, float v111, vec3 t) { + t = clamp(t, 0.0, 1.0); + + float v00 = mix(v000, v100, t.x); + float v01 = mix(v001, v101, t.x); + float v10 = mix(v010, v110, t.x); + float v11 = mix(v011, v111, t.x); + + float v0 = mix(v00, v10, t.y); + float v1 = mix(v01, v11, t.y); + + return mix(v0, v1, t.z); +} + +vec3 fade(vec3 t) { + return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); +} + +float perlinNoise(vec3 position, float seed) { + position += vec3(seed * 127.1, seed * 311.7, seed * 74.7); + + vec3 i = floor(position); + vec3 f = fract(position); + float h000 = hash31(i); + float h001 = hash31(i + vec3(0, 0, 1)); + float h010 = hash31(i + vec3(0, 1, 0)); + float h011 = hash31(i + vec3(0, 1, 1)); + float h100 = hash31(i + vec3(1, 0, 0)); + float h101 = hash31(i + vec3(1, 0, 1)); + float h110 = hash31(i + vec3(1, 1, 0)); + float h111 = hash31(i + vec3(1, 1, 1)); + vec3 g000 = gradientPredefined(h000); + vec3 g001 = gradientPredefined(h001); + vec3 g010 = gradientPredefined(h010); + vec3 g011 = gradientPredefined(h011); + vec3 g100 = gradientPredefined(h100); + vec3 g101 = gradientPredefined(h101); + vec3 g110 = gradientPredefined(h110); + vec3 g111 = gradientPredefined(h111); + float v000 = dot(g000, f - vec3(0, 0, 0)); + float v001 = dot(g001, f - vec3(0, 0, 1)); + float v010 = dot(g010, f - vec3(0, 1, 0)); + float v011 = dot(g011, f - vec3(0, 1, 1)); + float v100 = dot(g100, f - vec3(1, 0, 0)); + float v101 = dot(g101, f - vec3(1, 0, 1)); + float v110 = dot(g110, f - vec3(1, 1, 0)); + float v111 = dot(g111, f - vec3(1, 1, 1)); + + vec3 u = fade(f); + return interpolateSafe(v000, v001, v010, v011, v100, v101, v110, v111, u); +} + +float p_noise(vec3 position, int octaveCount, float persistence, float lacunarity) { + float value = 0.0; + float amplitude = 1.0; + float frequency = 10.0; + float maxValue = 0.0; + octaveCount = clamp(octaveCount, 1, 8); + + for (int i = 0; i < octaveCount; i++) { + float seed = float(i) * 0.7319; + value += perlinNoise(position * frequency, seed) * amplitude; + maxValue += amplitude; + amplitude *= persistence; + frequency *= lacunarity; + } + return value; +} + +float get_max_amp(float persistence, float octaveCount) { + persistence = clamp(persistence * 0.999, 0.0, 0.999); + octaveCount = clamp(octaveCount, 1.0, 8.0); + + if (abs(persistence - 1.0) < 0.001) { + return octaveCount; + } + + return (1.0 - pow(persistence, octaveCount)) / max(1e-4, (1.0 - persistence)); +} + +void main() { + vec2 uv = v_patternUV; + uv *= .5; + + float t = .2 * u_time; + + vec3 p = vec3(uv, t); + + float octCount = floor(u_octaveCount); + float noise = p_noise(p, int(octCount), u_persistence, u_lacunarity); + + float max_amp = get_max_amp(u_persistence, octCount); + float noise_normalized = clamp((noise + max_amp) / max(1e-4, (2. * max_amp)) + (u_proportion - .5), 0.0, 1.0); + float sharpness = clamp(u_softness, 0., 1.); + float smooth_w = 0.5 * max(fwidth(noise_normalized), 0.001); + float res = smoothstep( + .5 - .5 * sharpness - smooth_w, + .5 + .5 * sharpness + smooth_w, + noise_normalized + ); + + vec3 fgColor = u_colorFront.rgb * u_colorFront.a; + float fgOpacity = u_colorFront.a; + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + float bgOpacity = u_colorBack.a; + + vec3 color = fgColor * res; + float opacity = fgOpacity * res; + + color += bgColor * (1. - opacity); + opacity += bgOpacity * (1. - opacity); + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +export { + perlinNoiseFragmentShader +}; +//# sourceMappingURL=perlin-noise.js.map diff --git a/public/vendor/paper-shaders/shaders/perlin-noise.js.map b/public/vendor/paper-shaders/shaders/perlin-noise.js.map new file mode 100644 index 0000000..500a8d6 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/perlin-noise.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/perlin-noise.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, colorBandingFix, proceduralHash11, proceduralHash21 } from '../shader-utils.js';\n\n/**\n * Classic animated 3D Perlin noise with exposed controls.\n * Original algorithm: https://www.shadertoy.com/view/NlSGDz\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_colorFront (vec4): Foreground color in RGBA\n * - u_colorBack (vec4): Background color in RGBA\n * - u_proportion (float): Blend point between 2 colors, 0.5 = equal distribution (0 to 1)\n * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)\n * - u_octaveCount (float): Perlin noise octaves number, more octaves for more detailed patterns (1 to 8)\n * - u_persistence (float): Roughness, falloff between octaves (0.3 to 1)\n * - u_lacunarity (float): Frequency step, defines how compressed the pattern is (1.5 to 10)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const perlinNoiseFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_octaveCount;\nuniform float u_persistence;\nuniform float u_lacunarity;\n\nin vec2 v_patternUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ proceduralHash11 }\n${ proceduralHash21 }\n\nfloat hash31(vec3 p) {\n p = fract(p * 0.3183099) + 0.1;\n p += dot(p, p.yzx + 19.19);\n return fract(p.x * (p.y + p.z));\n}\n\nvec3 gradientPredefined(float hash) {\n int idx = int(hash * 12.0) % 12;\n\n if (idx == 0) return vec3(1, 1, 0);\n if (idx == 1) return vec3(-1, 1, 0);\n if (idx == 2) return vec3(1, -1, 0);\n if (idx == 3) return vec3(-1, -1, 0);\n if (idx == 4) return vec3(1, 0, 1);\n if (idx == 5) return vec3(-1, 0, 1);\n if (idx == 6) return vec3(1, 0, -1);\n if (idx == 7) return vec3(-1, 0, -1);\n if (idx == 8) return vec3(0, 1, 1);\n if (idx == 9) return vec3(0, -1, 1);\n if (idx == 10) return vec3(0, 1, -1);\n return vec3(0, -1, -1);// idx == 11\n}\n\nfloat interpolateSafe(float v000, float v001, float v010, float v011,\nfloat v100, float v101, float v110, float v111, vec3 t) {\n t = clamp(t, 0.0, 1.0);\n\n float v00 = mix(v000, v100, t.x);\n float v01 = mix(v001, v101, t.x);\n float v10 = mix(v010, v110, t.x);\n float v11 = mix(v011, v111, t.x);\n\n float v0 = mix(v00, v10, t.y);\n float v1 = mix(v01, v11, t.y);\n\n return mix(v0, v1, t.z);\n}\n\nvec3 fade(vec3 t) {\n return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);\n}\n\nfloat perlinNoise(vec3 position, float seed) {\n position += vec3(seed * 127.1, seed * 311.7, seed * 74.7);\n\n vec3 i = floor(position);\n vec3 f = fract(position);\n float h000 = hash31(i);\n float h001 = hash31(i + vec3(0, 0, 1));\n float h010 = hash31(i + vec3(0, 1, 0));\n float h011 = hash31(i + vec3(0, 1, 1));\n float h100 = hash31(i + vec3(1, 0, 0));\n float h101 = hash31(i + vec3(1, 0, 1));\n float h110 = hash31(i + vec3(1, 1, 0));\n float h111 = hash31(i + vec3(1, 1, 1));\n vec3 g000 = gradientPredefined(h000);\n vec3 g001 = gradientPredefined(h001);\n vec3 g010 = gradientPredefined(h010);\n vec3 g011 = gradientPredefined(h011);\n vec3 g100 = gradientPredefined(h100);\n vec3 g101 = gradientPredefined(h101);\n vec3 g110 = gradientPredefined(h110);\n vec3 g111 = gradientPredefined(h111);\n float v000 = dot(g000, f - vec3(0, 0, 0));\n float v001 = dot(g001, f - vec3(0, 0, 1));\n float v010 = dot(g010, f - vec3(0, 1, 0));\n float v011 = dot(g011, f - vec3(0, 1, 1));\n float v100 = dot(g100, f - vec3(1, 0, 0));\n float v101 = dot(g101, f - vec3(1, 0, 1));\n float v110 = dot(g110, f - vec3(1, 1, 0));\n float v111 = dot(g111, f - vec3(1, 1, 1));\n\n vec3 u = fade(f);\n return interpolateSafe(v000, v001, v010, v011, v100, v101, v110, v111, u);\n}\n\nfloat p_noise(vec3 position, int octaveCount, float persistence, float lacunarity) {\n float value = 0.0;\n float amplitude = 1.0;\n float frequency = 10.0;\n float maxValue = 0.0;\n octaveCount = clamp(octaveCount, 1, 8);\n\n for (int i = 0; i < octaveCount; i++) {\n float seed = float(i) * 0.7319;\n value += perlinNoise(position * frequency, seed) * amplitude;\n maxValue += amplitude;\n amplitude *= persistence;\n frequency *= lacunarity;\n }\n return value;\n}\n\nfloat get_max_amp(float persistence, float octaveCount) {\n persistence = clamp(persistence * 0.999, 0.0, 0.999);\n octaveCount = clamp(octaveCount, 1.0, 8.0);\n\n if (abs(persistence - 1.0) < 0.001) {\n return octaveCount;\n }\n\n return (1.0 - pow(persistence, octaveCount)) / max(1e-4, (1.0 - persistence));\n}\n\nvoid main() {\n vec2 uv = v_patternUV;\n uv *= .5;\n\n float t = .2 * u_time;\n\n vec3 p = vec3(uv, t);\n\n float octCount = floor(u_octaveCount);\n float noise = p_noise(p, int(octCount), u_persistence, u_lacunarity);\n\n float max_amp = get_max_amp(u_persistence, octCount);\n float noise_normalized = clamp((noise + max_amp) / max(1e-4, (2. * max_amp)) + (u_proportion - .5), 0.0, 1.0);\n float sharpness = clamp(u_softness, 0., 1.);\n float smooth_w = 0.5 * max(fwidth(noise_normalized), 0.001);\n float res = smoothstep(\n .5 - .5 * sharpness - smooth_w,\n .5 + .5 * sharpness + smooth_w,\n noise_normalized\n );\n\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n vec3 color = fgColor * res;\n float opacity = fgOpacity * res;\n\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface PerlinNoiseUniforms extends ShaderSizingUniforms {\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_proportion: number;\n u_softness: number;\n u_octaveCount: number;\n u_persistence: number;\n u_lacunarity: number;\n}\n\nexport interface PerlinNoiseParams extends ShaderSizingParams, ShaderMotionParams {\n colorFront?: string;\n colorBack?: string;\n proportion?: number;\n softness?: number;\n octaveCount?: number;\n persistence?: number;\n lacunarity?: number;\n}\n"], + "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,WAAW,iBAAiB,kBAAkB,wBAAwB;AAmCxE,MAAM,4BAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiB9C,SAAU;AAAA,EACV,gBAAiB;AAAA,EACjB,gBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA0If,eAAgB;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/pulsing-border.d.ts b/public/vendor/paper-shaders/shaders/pulsing-border.d.ts new file mode 100644 index 0000000..06dace6 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/pulsing-border.d.ts @@ -0,0 +1,98 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const pulsingBorderMeta: { + readonly maxColorCount: 5; + readonly maxSpots: 4; +}; +/** + * Luminous trails of color merging into a glowing gradient contour. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_colorBack (vec4): Background color in RGBA + * - u_colors (vec4[]): Up to 5 spot colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_roundness (float): Border radius (0 to 1) + * - u_thickness (float): Border base width (0 to 1) + * - u_softness (float): Border edge sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1) + * - u_marginLeft (float): Distance from the left edge to the effect (0 to 1) + * - u_marginRight (float): Distance from the right edge to the effect (0 to 1) + * - u_marginTop (float): Distance from the top edge to the effect (0 to 1) + * - u_marginBottom (float): Distance from the bottom edge to the effect (0 to 1) + * - u_aspectRatio (float): Aspect ratio mode (0 = auto, 1 = square) + * - u_intensity (float): Thickness of individual color spots (0 to 1) + * - u_bloom (float): Power of glow, 0 = normal blending, 1 = additive blending (0 to 1) + * - u_spots (float): Number of spots added for each color (1 to 20) + * - u_spotSize (float): Angular size of spots (0 to 1) + * - u_pulse (float): Optional pulsing animation intensity (0 to 1) + * - u_smoke (float): Optional noisy shape extending the border (0 to 1) + * - u_smokeSize (float): Size of the smoke effect (0 to 1) + * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture + * + * Vertex shader outputs (used in fragment shader): + * - v_responsiveUV (vec2): Responsive UV coordinates that adapt to canvas aspect ratio + * - v_responsiveBoxGivenSize (vec2): Given size of the responsive bounding box + * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied (used for smoke calculation) + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const pulsingBorderFragmentShader: string; +export interface PulsingBorderUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colors: vec4[]; + u_colorsCount: number; + u_roundness: number; + u_thickness: number; + u_marginLeft: number; + u_marginRight: number; + u_marginTop: number; + u_marginBottom: number; + u_aspectRatio: (typeof PulsingBorderAspectRatios)[PulsingBorderAspectRatio]; + u_softness: number; + u_intensity: number; + u_bloom: number; + u_spots: number; + u_spotSize: number; + u_pulse: number; + u_smoke: number; + u_smokeSize: number; + u_noiseTexture?: HTMLImageElement; +} +export interface PulsingBorderParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colors?: string[]; + roundness?: number; + thickness?: number; + margin?: number; + marginLeft?: number; + marginRight?: number; + marginTop?: number; + marginBottom?: number; + aspectRatio?: PulsingBorderAspectRatio; + softness?: number; + intensity?: number; + bloom?: number; + spots?: number; + spotSize?: number; + pulse?: number; + smoke?: number; + smokeSize?: number; +} +export declare const PulsingBorderAspectRatios: { + readonly auto: 0; + readonly square: 1; +}; +export type PulsingBorderAspectRatio = keyof typeof PulsingBorderAspectRatios; diff --git a/public/vendor/paper-shaders/shaders/pulsing-border.js b/public/vendor/paper-shaders/shaders/pulsing-border.js new file mode 100644 index 0000000..464e5f7 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/pulsing-border.js @@ -0,0 +1,239 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, textureRandomizerGB, colorBandingFix } from "../shader-utils.js"; +const pulsingBorderMeta = { + maxColorCount: 5, + maxSpots: 4 +}; +const pulsingBorderFragmentShader = `#version 300 es +precision lowp float; + +uniform float u_time; + +uniform vec4 u_colorBack; +uniform vec4 u_colors[${pulsingBorderMeta.maxColorCount}]; +uniform float u_colorsCount; +uniform float u_roundness; +uniform float u_thickness; +uniform float u_marginLeft; +uniform float u_marginRight; +uniform float u_marginTop; +uniform float u_marginBottom; +uniform float u_aspectRatio; +uniform float u_softness; +uniform float u_intensity; +uniform float u_bloom; +uniform float u_spotSize; +uniform float u_spots; +uniform float u_pulse; +uniform float u_smoke; +uniform float u_smokeSize; + +uniform sampler2D u_noiseTexture; + +in vec2 v_responsiveUV; +in vec2 v_responsiveBoxGivenSize; +in vec2 v_patternUV; + +out vec4 fragColor; + +${declarePI} + +float beat(float time) { + float first = pow(abs(sin(time * TWO_PI)), 10.); + float second = pow(abs(sin((time - .15) * TWO_PI)), 10.); + + return clamp(first + 0.6 * second, 0.0, 1.0); +} + +float sst(float edge0, float edge1, float x) { + return smoothstep(edge0, edge1, x); +} + +float roundedBox(vec2 uv, vec2 halfSize, float distance, float cornerDistance, float thickness, float softness) { + float borderDistance = abs(distance); + float aa = 2. * fwidth(distance); + float border = 1. - sst(min(mix(thickness, -thickness, softness), thickness + aa), max(mix(thickness, -thickness, softness), thickness + aa), borderDistance); + float cornerFadeCircles = 0.; + cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv + halfSize) / thickness))); + cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - vec2(-halfSize.x, halfSize.y)) / thickness))); + cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - vec2(halfSize.x, -halfSize.y)) / thickness))); + cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - halfSize) / thickness))); + aa = fwidth(cornerDistance); + float cornerFade = sst(0., mix(aa, thickness, softness), cornerDistance); + cornerFade *= cornerFadeCircles; + border += cornerFade; + return border; +} + +${textureRandomizerGB} + +float randomG(vec2 p) { + vec2 uv = floor(p) / 100. + .5; + return texture(u_noiseTexture, fract(uv)).g; +} +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = randomG(i); + float b = randomG(i + vec2(1.0, 0.0)); + float c = randomG(i + vec2(0.0, 1.0)); + float d = randomG(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} + +void main() { + const float firstFrameOffset = 109.; + float t = 1.2 * (u_time + firstFrameOffset); + + vec2 borderUV = v_responsiveUV; + float pulse = u_pulse * beat(.18 * u_time); + + float canvasRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y; + vec2 halfSize = vec2(.5); + borderUV.x *= max(canvasRatio, 1.); + borderUV.y /= min(canvasRatio, 1.); + halfSize.x *= max(canvasRatio, 1.); + halfSize.y /= min(canvasRatio, 1.); + + float mL = u_marginLeft; + float mR = u_marginRight; + float mT = u_marginTop; + float mB = u_marginBottom; + float mX = mL + mR; + float mY = mT + mB; + + if (u_aspectRatio > 0.) { + float shapeRatio = canvasRatio * (1. - mX) / max(1. - mY, 1e-6); + float freeX = shapeRatio > 1. ? (1. - mX) * (1. - 1. / max(abs(shapeRatio), 1e-6)) : 0.; + float freeY = shapeRatio < 1. ? (1. - mY) * (1. - shapeRatio) : 0.; + mL += freeX * 0.5; + mR += freeX * 0.5; + mT += freeY * 0.5; + mB += freeY * 0.5; + mX = mL + mR; + mY = mT + mB; + } + + float thickness = .5 * u_thickness * min(halfSize.x, halfSize.y); + + halfSize.x *= (1. - mX); + halfSize.y *= (1. - mY); + + vec2 centerShift = vec2( + (mL - mR) * max(canvasRatio, 1.) * 0.5, + (mB - mT) / min(canvasRatio, 1.) * 0.5 + ); + + borderUV -= centerShift; + halfSize -= mix(thickness, 0., u_softness); + + float radius = mix(0., min(halfSize.x, halfSize.y), u_roundness); + vec2 d = abs(borderUV) - halfSize + radius; + float outsideDistance = length(max(d, .0001)) - radius; + float insideDistance = min(max(d.x, d.y), .0001); + float cornerDistance = abs(min(max(d.x, d.y) - .45 * radius, .0)); + float distance = outsideDistance + insideDistance; + + float borderThickness = mix(thickness, 3. * thickness, u_softness); + float border = roundedBox(borderUV, halfSize, distance, cornerDistance, borderThickness, u_softness); + border = pow(border, 1. + u_softness); + + vec2 smokeUV = .3 * u_smokeSize * v_patternUV; + float smoke = clamp(3. * valueNoise(2.7 * smokeUV + .5 * t), 0., 1.); + smoke -= valueNoise(3.4 * smokeUV - .5 * t); + float smokeThickness = thickness + .2; + smokeThickness = min(.4, max(smokeThickness, .1)); + smoke *= roundedBox(borderUV, halfSize, distance, cornerDistance, smokeThickness, 1.); + smoke = 30. * smoke * smoke; + smoke *= mix(0., .5, pow(u_smoke, 2.)); + smoke *= mix(1., pulse, u_pulse); + smoke = clamp(smoke, 0., 1.); + border += smoke; + + border = clamp(border, 0., 1.); + + vec3 blendColor = vec3(0.); + float blendAlpha = 0.; + vec3 addColor = vec3(0.); + float addAlpha = 0.; + + float bloom = 4. * u_bloom; + float intensity = 1. + (1. + 4. * u_softness) * u_intensity; + + float angle = atan(borderUV.y, borderUV.x) / TWO_PI; + + for (int colorIdx = 0; colorIdx < ${pulsingBorderMeta.maxColorCount}; colorIdx++) { + if (colorIdx >= int(u_colorsCount)) break; + float colorIdxF = float(colorIdx); + + vec3 c = u_colors[colorIdx].rgb * u_colors[colorIdx].a; + float a = u_colors[colorIdx].a; + + for (int spotIdx = 0; spotIdx < ${pulsingBorderMeta.maxSpots}; spotIdx++) { + if (spotIdx >= int(u_spots)) break; + float spotIdxF = float(spotIdx); + + vec2 randVal = randomGB(vec2(spotIdxF * 10. + 2., 40. + colorIdxF)); + + float time = (.1 + .15 * abs(sin(spotIdxF * (2. + colorIdxF)) * cos(spotIdxF * (2. + 2.5 * colorIdxF)))) * t + randVal.x * 3.; + time *= mix(1., -1., step(.5, randVal.y)); + + float mask = .5 + .5 * mix( + sin(t + spotIdxF * (5. - 1.5 * colorIdxF)), + cos(t + spotIdxF * (3. + 1.3 * colorIdxF)), + step(mod(colorIdxF, 2.), .5) + ); + + float p = clamp(2. * u_pulse - randVal.x, 0., 1.); + mask = mix(mask, pulse, p); + + float atg1 = fract(angle + time); + float spotSize = .05 + .6 * pow(u_spotSize, 2.) + .05 * randVal.x; + spotSize = mix(spotSize, .1, p); + float sector = sst(.5 - spotSize, .5, atg1) * (1. - sst(.5, .5 + spotSize, atg1)); + + sector *= mask; + sector *= border; + sector *= intensity; + sector = clamp(sector, 0., 1.); + + vec3 srcColor = c * sector; + float srcAlpha = a * sector; + + blendColor += ((1. - blendAlpha) * srcColor); + blendAlpha = blendAlpha + (1. - blendAlpha) * srcAlpha; + addColor += srcColor; + addAlpha += srcAlpha; + } + } + + vec3 accumColor = mix(blendColor, addColor, bloom); + float accumAlpha = mix(blendAlpha, addAlpha, bloom); + accumAlpha = clamp(accumAlpha, 0., 1.); + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + vec3 color = accumColor + (1. - accumAlpha) * bgColor; + float opacity = accumAlpha + (1. - accumAlpha) * u_colorBack.a; + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +}`; +const PulsingBorderAspectRatios = { + auto: 0, + square: 1 +}; +export { + PulsingBorderAspectRatios, + pulsingBorderFragmentShader, + pulsingBorderMeta +}; +//# sourceMappingURL=pulsing-border.js.map diff --git a/public/vendor/paper-shaders/shaders/pulsing-border.js.map b/public/vendor/paper-shaders/shaders/pulsing-border.js.map new file mode 100644 index 0000000..7bef2aa --- /dev/null +++ b/public/vendor/paper-shaders/shaders/pulsing-border.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/pulsing-border.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, textureRandomizerGB, colorBandingFix } from '../shader-utils.js';\n\nexport const pulsingBorderMeta = {\n maxColorCount: 5,\n maxSpots: 4,\n} as const;\n\n/**\n * Luminous trails of color merging into a glowing gradient contour.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colors (vec4[]): Up to 5 spot colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_roundness (float): Border radius (0 to 1)\n * - u_thickness (float): Border base width (0 to 1)\n * - u_softness (float): Border edge sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)\n * - u_marginLeft (float): Distance from the left edge to the effect (0 to 1)\n * - u_marginRight (float): Distance from the right edge to the effect (0 to 1)\n * - u_marginTop (float): Distance from the top edge to the effect (0 to 1)\n * - u_marginBottom (float): Distance from the bottom edge to the effect (0 to 1)\n * - u_aspectRatio (float): Aspect ratio mode (0 = auto, 1 = square)\n * - u_intensity (float): Thickness of individual color spots (0 to 1)\n * - u_bloom (float): Power of glow, 0 = normal blending, 1 = additive blending (0 to 1)\n * - u_spots (float): Number of spots added for each color (1 to 20)\n * - u_spotSize (float): Angular size of spots (0 to 1)\n * - u_pulse (float): Optional pulsing animation intensity (0 to 1)\n * - u_smoke (float): Optional noisy shape extending the border (0 to 1)\n * - u_smokeSize (float): Size of the smoke effect (0 to 1)\n * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_responsiveUV (vec2): Responsive UV coordinates that adapt to canvas aspect ratio\n * - v_responsiveBoxGivenSize (vec2): Given size of the responsive bounding box\n * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied (used for smoke calculation)\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const pulsingBorderFragmentShader: string = `#version 300 es\nprecision lowp float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${ pulsingBorderMeta.maxColorCount }];\nuniform float u_colorsCount;\nuniform float u_roundness;\nuniform float u_thickness;\nuniform float u_marginLeft;\nuniform float u_marginRight;\nuniform float u_marginTop;\nuniform float u_marginBottom;\nuniform float u_aspectRatio;\nuniform float u_softness;\nuniform float u_intensity;\nuniform float u_bloom;\nuniform float u_spotSize;\nuniform float u_spots;\nuniform float u_pulse;\nuniform float u_smoke;\nuniform float u_smokeSize;\n\nuniform sampler2D u_noiseTexture;\n\nin vec2 v_responsiveUV;\nin vec2 v_responsiveBoxGivenSize;\nin vec2 v_patternUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n\nfloat beat(float time) {\n float first = pow(abs(sin(time * TWO_PI)), 10.);\n float second = pow(abs(sin((time - .15) * TWO_PI)), 10.);\n\n return clamp(first + 0.6 * second, 0.0, 1.0);\n}\n\nfloat sst(float edge0, float edge1, float x) {\n return smoothstep(edge0, edge1, x);\n}\n\nfloat roundedBox(vec2 uv, vec2 halfSize, float distance, float cornerDistance, float thickness, float softness) {\n float borderDistance = abs(distance);\n float aa = 2. * fwidth(distance);\n float border = 1. - sst(min(mix(thickness, -thickness, softness), thickness + aa), max(mix(thickness, -thickness, softness), thickness + aa), borderDistance);\n float cornerFadeCircles = 0.;\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv + halfSize) / thickness)));\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - vec2(-halfSize.x, halfSize.y)) / thickness)));\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - vec2(halfSize.x, -halfSize.y)) / thickness)));\n cornerFadeCircles = mix(1., cornerFadeCircles, sst(0., 1., length((uv - halfSize) / thickness)));\n aa = fwidth(cornerDistance);\n float cornerFade = sst(0., mix(aa, thickness, softness), cornerDistance);\n cornerFade *= cornerFadeCircles;\n border += cornerFade;\n return border;\n}\n\n${ textureRandomizerGB }\n\nfloat randomG(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).g;\n}\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomG(i);\n float b = randomG(i + vec2(1.0, 0.0));\n float c = randomG(i + vec2(0.0, 1.0));\n float d = randomG(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nvoid main() {\n const float firstFrameOffset = 109.;\n float t = 1.2 * (u_time + firstFrameOffset);\n\n vec2 borderUV = v_responsiveUV;\n float pulse = u_pulse * beat(.18 * u_time);\n\n float canvasRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n vec2 halfSize = vec2(.5);\n borderUV.x *= max(canvasRatio, 1.);\n borderUV.y /= min(canvasRatio, 1.);\n halfSize.x *= max(canvasRatio, 1.);\n halfSize.y /= min(canvasRatio, 1.);\n\n float mL = u_marginLeft;\n float mR = u_marginRight;\n float mT = u_marginTop;\n float mB = u_marginBottom;\n float mX = mL + mR;\n float mY = mT + mB;\n\n if (u_aspectRatio > 0.) {\n float shapeRatio = canvasRatio * (1. - mX) / max(1. - mY, 1e-6);\n float freeX = shapeRatio > 1. ? (1. - mX) * (1. - 1. / max(abs(shapeRatio), 1e-6)) : 0.;\n float freeY = shapeRatio < 1. ? (1. - mY) * (1. - shapeRatio) : 0.;\n mL += freeX * 0.5;\n mR += freeX * 0.5;\n mT += freeY * 0.5;\n mB += freeY * 0.5;\n mX = mL + mR;\n mY = mT + mB;\n }\n\n float thickness = .5 * u_thickness * min(halfSize.x, halfSize.y);\n\n halfSize.x *= (1. - mX);\n halfSize.y *= (1. - mY);\n\n vec2 centerShift = vec2(\n (mL - mR) * max(canvasRatio, 1.) * 0.5,\n (mB - mT) / min(canvasRatio, 1.) * 0.5\n );\n\n borderUV -= centerShift;\n halfSize -= mix(thickness, 0., u_softness);\n\n float radius = mix(0., min(halfSize.x, halfSize.y), u_roundness);\n vec2 d = abs(borderUV) - halfSize + radius;\n float outsideDistance = length(max(d, .0001)) - radius;\n float insideDistance = min(max(d.x, d.y), .0001);\n float cornerDistance = abs(min(max(d.x, d.y) - .45 * radius, .0));\n float distance = outsideDistance + insideDistance;\n\n float borderThickness = mix(thickness, 3. * thickness, u_softness);\n float border = roundedBox(borderUV, halfSize, distance, cornerDistance, borderThickness, u_softness);\n border = pow(border, 1. + u_softness);\n\n vec2 smokeUV = .3 * u_smokeSize * v_patternUV;\n float smoke = clamp(3. * valueNoise(2.7 * smokeUV + .5 * t), 0., 1.);\n smoke -= valueNoise(3.4 * smokeUV - .5 * t);\n float smokeThickness = thickness + .2;\n smokeThickness = min(.4, max(smokeThickness, .1));\n smoke *= roundedBox(borderUV, halfSize, distance, cornerDistance, smokeThickness, 1.);\n smoke = 30. * smoke * smoke;\n smoke *= mix(0., .5, pow(u_smoke, 2.));\n smoke *= mix(1., pulse, u_pulse);\n smoke = clamp(smoke, 0., 1.);\n border += smoke;\n\n border = clamp(border, 0., 1.);\n\n vec3 blendColor = vec3(0.);\n float blendAlpha = 0.;\n vec3 addColor = vec3(0.);\n float addAlpha = 0.;\n\n float bloom = 4. * u_bloom;\n float intensity = 1. + (1. + 4. * u_softness) * u_intensity;\n\n float angle = atan(borderUV.y, borderUV.x) / TWO_PI;\n\n for (int colorIdx = 0; colorIdx < ${ pulsingBorderMeta.maxColorCount }; colorIdx++) {\n if (colorIdx >= int(u_colorsCount)) break;\n float colorIdxF = float(colorIdx);\n\n vec3 c = u_colors[colorIdx].rgb * u_colors[colorIdx].a;\n float a = u_colors[colorIdx].a;\n\n for (int spotIdx = 0; spotIdx < ${ pulsingBorderMeta.maxSpots }; spotIdx++) {\n if (spotIdx >= int(u_spots)) break;\n float spotIdxF = float(spotIdx);\n\n vec2 randVal = randomGB(vec2(spotIdxF * 10. + 2., 40. + colorIdxF));\n\n float time = (.1 + .15 * abs(sin(spotIdxF * (2. + colorIdxF)) * cos(spotIdxF * (2. + 2.5 * colorIdxF)))) * t + randVal.x * 3.;\n time *= mix(1., -1., step(.5, randVal.y));\n\n float mask = .5 + .5 * mix(\n sin(t + spotIdxF * (5. - 1.5 * colorIdxF)),\n cos(t + spotIdxF * (3. + 1.3 * colorIdxF)),\n step(mod(colorIdxF, 2.), .5)\n );\n\n float p = clamp(2. * u_pulse - randVal.x, 0., 1.);\n mask = mix(mask, pulse, p);\n\n float atg1 = fract(angle + time);\n float spotSize = .05 + .6 * pow(u_spotSize, 2.) + .05 * randVal.x;\n spotSize = mix(spotSize, .1, p);\n float sector = sst(.5 - spotSize, .5, atg1) * (1. - sst(.5, .5 + spotSize, atg1));\n\n sector *= mask;\n sector *= border;\n sector *= intensity;\n sector = clamp(sector, 0., 1.);\n\n vec3 srcColor = c * sector;\n float srcAlpha = a * sector;\n\n blendColor += ((1. - blendAlpha) * srcColor);\n blendAlpha = blendAlpha + (1. - blendAlpha) * srcAlpha;\n addColor += srcColor;\n addAlpha += srcAlpha;\n }\n }\n\n vec3 accumColor = mix(blendColor, addColor, bloom);\n float accumAlpha = mix(blendAlpha, addAlpha, bloom);\n accumAlpha = clamp(accumAlpha, 0., 1.);\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n vec3 color = accumColor + (1. - accumAlpha) * bgColor;\n float opacity = accumAlpha + (1. - accumAlpha) * u_colorBack.a;\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}`;\n\nexport interface PulsingBorderUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_roundness: number;\n u_thickness: number;\n u_marginLeft: number;\n u_marginRight: number;\n u_marginTop: number;\n u_marginBottom: number;\n u_aspectRatio: (typeof PulsingBorderAspectRatios)[PulsingBorderAspectRatio];\n u_softness: number;\n u_intensity: number;\n u_bloom: number;\n u_spots: number;\n u_spotSize: number;\n u_pulse: number;\n u_smoke: number;\n u_smokeSize: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface PulsingBorderParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n roundness?: number;\n thickness?: number;\n margin?: number;\n marginLeft?: number;\n marginRight?: number;\n marginTop?: number;\n marginBottom?: number;\n aspectRatio?: PulsingBorderAspectRatio;\n softness?: number;\n intensity?: number;\n bloom?: number;\n spots?: number;\n spotSize?: number;\n pulse?: number;\n smoke?: number;\n smokeSize?: number;\n}\n\nexport const PulsingBorderAspectRatios = {\n auto: 0,\n square: 1,\n} as const;\n\nexport type PulsingBorderAspectRatio = keyof typeof PulsingBorderAspectRatios;\n"], + "mappings": ";;;;;AAEA,eAAmE;AACnE,SAAS,WAAW,qBAAqB,uBAAuB;AAEzD,MAAM,oBAAoB;AAAA,EAC/B,eAAe;AAAA,EACf,UAAU;AACZ;AAgDO,MAAM,8BAAsC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAM1B,kBAAkB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BtD,SAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BV,mBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAoGgB,kBAAkB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAOhC,kBAAkB,QAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA8C7D,eAAgB;AAAA;AAAA;AAAA;AAgDd,MAAM,4BAA4B;AAAA,EACvC,MAAM;AAAA,EACN,QAAQ;AACV;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/simplex-noise.d.ts b/public/vendor/paper-shaders/shaders/simplex-noise.d.ts new file mode 100644 index 0000000..3d409a4 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/simplex-noise.d.ts @@ -0,0 +1,46 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const simplexNoiseMeta: { + readonly maxColorCount: 10; +}; +/** + * A multi-color gradient mapped into smooth, animated curves built as a combination of 2 Simplex noises. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_scale (float): Overall zoom level, used for anti-aliasing calculations + * - u_colors (vec4[]): Up to 10 base colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_stepsPerColor (float): Number of extra colors between base colors, 1 = N colors, 2 = 2×N, etc. (1 to 10) + * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const simplexNoiseFragmentShader: string; +export interface SimplexNoiseUniforms extends ShaderSizingUniforms { + u_colors: vec4[]; + u_colorsCount: number; + u_stepsPerColor: number; + u_softness: number; +} +export interface SimplexNoiseParams extends ShaderSizingParams, ShaderMotionParams { + colors?: string[]; + stepsPerColor?: number; + softness?: number; +} diff --git a/public/vendor/paper-shaders/shaders/simplex-noise.js b/public/vendor/paper-shaders/shaders/simplex-noise.js new file mode 100644 index 0000000..5ececb0 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/simplex-noise.js @@ -0,0 +1,100 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { simplexNoise, colorBandingFix } from "../shader-utils.js"; +const simplexNoiseMeta = { + maxColorCount: 10 +}; +const simplexNoiseFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; +uniform float u_scale; + +uniform vec4 u_colors[${simplexNoiseMeta.maxColorCount}]; +uniform float u_colorsCount; +uniform float u_stepsPerColor; +uniform float u_softness; + +in vec2 v_patternUV; + +out vec4 fragColor; + +${simplexNoise} + +float getNoise(vec2 uv, float t) { + float noise = .5 * snoise(uv - vec2(0., .3 * t)); + noise += .5 * snoise(2. * uv + vec2(0., .32 * t)); + + return noise; +} + +float steppedSmooth(float m, float steps, float softness) { + float stepT = floor(m * steps) / steps; + float f = m * steps - floor(m * steps); + float fw = steps * fwidth(m); + float smoothed = smoothstep(.5 - softness, min(1., .5 + softness + fw), f); + return stepT + smoothed / steps; +} + +void main() { + vec2 shape_uv = v_patternUV; + shape_uv *= .1; + + float t = .2 * u_time; + + float shape = .5 + .5 * getNoise(shape_uv, t); + + bool u_extraSides = true; + + float mixer = shape * (u_colorsCount - 1.); + if (u_extraSides == true) { + mixer = (shape - .5 / u_colorsCount) * u_colorsCount; + } + + float steps = max(1., u_stepsPerColor); + + vec4 gradient = u_colors[0]; + gradient.rgb *= gradient.a; + for (int i = 1; i < ${simplexNoiseMeta.maxColorCount}; i++) { + if (i >= int(u_colorsCount)) break; + + float localM = clamp(mixer - float(i - 1), 0., 1.); + localM = steppedSmooth(localM, steps, .5 * u_softness); + + vec4 c = u_colors[i]; + c.rgb *= c.a; + gradient = mix(gradient, c, localM); + } + + if (u_extraSides == true) { + if ((mixer < 0.) || (mixer > (u_colorsCount - 1.))) { + float localM = mixer + 1.; + if (mixer > (u_colorsCount - 1.)) { + localM = mixer - (u_colorsCount - 1.); + } + localM = steppedSmooth(localM, steps, .5 * u_softness); + vec4 cFst = u_colors[0]; + cFst.rgb *= cFst.a; + vec4 cLast = u_colors[int(u_colorsCount - 1.)]; + cLast.rgb *= cLast.a; + gradient = mix(cLast, cFst, localM); + } + } + + vec3 color = gradient.rgb; + float opacity = gradient.a; + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +export { + simplexNoiseFragmentShader, + simplexNoiseMeta +}; +//# sourceMappingURL=simplex-noise.js.map diff --git a/public/vendor/paper-shaders/shaders/simplex-noise.js.map b/public/vendor/paper-shaders/shaders/simplex-noise.js.map new file mode 100644 index 0000000..b1febb3 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/simplex-noise.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/simplex-noise.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { simplexNoise, colorBandingFix } from '../shader-utils.js';\n\nexport const simplexNoiseMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * A multi-color gradient mapped into smooth, animated curves built as a combination of 2 Simplex noises.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_scale (float): Overall zoom level, used for anti-aliasing calculations\n * - u_colors (vec4[]): Up to 10 base colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_stepsPerColor (float): Number of extra colors between base colors, 1 = N colors, 2 = 2\u00D7N, etc. (1 to 10)\n * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const simplexNoiseFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\nuniform float u_scale;\n\nuniform vec4 u_colors[${ simplexNoiseMeta.maxColorCount }];\nuniform float u_colorsCount;\nuniform float u_stepsPerColor;\nuniform float u_softness;\n\nin vec2 v_patternUV;\n\nout vec4 fragColor;\n\n${ simplexNoise }\n\nfloat getNoise(vec2 uv, float t) {\n float noise = .5 * snoise(uv - vec2(0., .3 * t));\n noise += .5 * snoise(2. * uv + vec2(0., .32 * t));\n\n return noise;\n}\n\nfloat steppedSmooth(float m, float steps, float softness) {\n float stepT = floor(m * steps) / steps;\n float f = m * steps - floor(m * steps);\n float fw = steps * fwidth(m);\n float smoothed = smoothstep(.5 - softness, min(1., .5 + softness + fw), f);\n return stepT + smoothed / steps;\n}\n\nvoid main() {\n vec2 shape_uv = v_patternUV;\n shape_uv *= .1;\n\n float t = .2 * u_time;\n\n float shape = .5 + .5 * getNoise(shape_uv, t);\n\n bool u_extraSides = true;\n\n float mixer = shape * (u_colorsCount - 1.);\n if (u_extraSides == true) {\n mixer = (shape - .5 / u_colorsCount) * u_colorsCount;\n }\n\n float steps = max(1., u_stepsPerColor);\n\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n for (int i = 1; i < ${ simplexNoiseMeta.maxColorCount }; i++) {\n if (i >= int(u_colorsCount)) break;\n\n float localM = clamp(mixer - float(i - 1), 0., 1.);\n localM = steppedSmooth(localM, steps, .5 * u_softness);\n\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, localM);\n }\n\n if (u_extraSides == true) {\n if ((mixer < 0.) || (mixer > (u_colorsCount - 1.))) {\n float localM = mixer + 1.;\n if (mixer > (u_colorsCount - 1.)) {\n localM = mixer - (u_colorsCount - 1.);\n }\n localM = steppedSmooth(localM, steps, .5 * u_softness);\n vec4 cFst = u_colors[0];\n cFst.rgb *= cFst.a;\n vec4 cLast = u_colors[int(u_colorsCount - 1.)];\n cLast.rgb *= cLast.a;\n gradient = mix(cLast, cFst, localM);\n }\n }\n\n vec3 color = gradient.rgb;\n float opacity = gradient.a;\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SimplexNoiseUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_stepsPerColor: number;\n u_softness: number;\n}\n\nexport interface SimplexNoiseParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n stepsPerColor?: number;\n softness?: number;\n}\n"], + "mappings": ";;;;;AAEA,eAAmE;AACnE,SAAS,cAAc,uBAAuB;AAEvC,MAAM,mBAAmB;AAAA,EAC9B,eAAe;AACjB;AAgCO,MAAM,6BAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMzB,iBAAiB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASrD,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAoCS,iBAAiB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA6BnD,eAAgB;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/smoke-ring.d.ts b/public/vendor/paper-shaders/shaders/smoke-ring.d.ts new file mode 100644 index 0000000..07bd17e --- /dev/null +++ b/public/vendor/paper-shaders/shaders/smoke-ring.d.ts @@ -0,0 +1,61 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const smokeRingMeta: { + readonly maxColorCount: 10; + readonly maxNoiseIterations: 8; +}; +/** + * Radial multi-colored gradient shaped with layered noise for a natural, smoky aesthetic. + * + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_colorBack (vec4): Background color in RGBA + * - u_colors (vec4[]): Up to 10 gradient colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_thickness (float): Thickness of the ring shape (0.01 to 1) + * - u_radius (float): Radius of the ring shape (0 to 1) + * - u_innerShape (float): Ring inner fill amount (0 to 4) + * - u_noiseIterations (float): Number of noise layers, more layers gives more details (1 to 8) + * - u_noiseScale (float): Noise frequency (0.01 to 5) + * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture + * + * Vertex shader outputs (used in fragment shader): + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const smokeRingFragmentShader: string; +export interface SmokeRingUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colors: vec4[]; + u_colorsCount: number; + u_noiseScale: number; + u_thickness: number; + u_radius: number; + u_innerShape: number; + u_noiseIterations: number; + u_noiseTexture?: HTMLImageElement; +} +export interface SmokeRingParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colors?: string[]; + noiseScale?: number; + thickness?: number; + radius?: number; + innerShape?: number; + noiseIterations?: number; +} diff --git a/public/vendor/paper-shaders/shaders/smoke-ring.js b/public/vendor/paper-shaders/shaders/smoke-ring.js new file mode 100644 index 0000000..aa6f126 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/smoke-ring.js @@ -0,0 +1,133 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, textureRandomizerR, colorBandingFix } from "../shader-utils.js"; +const smokeRingMeta = { + maxColorCount: 10, + maxNoiseIterations: 8 +}; +const smokeRingFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform sampler2D u_noiseTexture; + +uniform vec4 u_colorBack; +uniform vec4 u_colors[${smokeRingMeta.maxColorCount}]; +uniform float u_colorsCount; + +uniform float u_thickness; +uniform float u_radius; +uniform float u_innerShape; +uniform float u_noiseScale; +uniform float u_noiseIterations; + +in vec2 v_objectUV; + +out vec4 fragColor; + +${declarePI} +${textureRandomizerR} +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = randomR(i); + float b = randomR(i + vec2(1.0, 0.0)); + float c = randomR(i + vec2(0.0, 1.0)); + float d = randomR(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} +vec2 fbm(vec2 n0, vec2 n1) { + vec2 total = vec2(0.0); + float amplitude = .4; + for (int i = 0; i < ${smokeRingMeta.maxNoiseIterations}; i++) { + if (i >= int(u_noiseIterations)) break; + total.x += valueNoise(n0) * amplitude; + total.y += valueNoise(n1) * amplitude; + n0 *= 1.99; + n1 *= 1.99; + amplitude *= 0.65; + } + return total; +} + +float getNoise(vec2 uv, vec2 pUv, float t) { + vec2 pUvLeft = pUv + .03 * t; + float period = max(abs(u_noiseScale * TWO_PI), 1e-6); + vec2 pUvRight = vec2(fract(pUv.x / period) * period, pUv.y) + .03 * t; + vec2 noise = fbm(pUvLeft, pUvRight); + return mix(noise.y, noise.x, smoothstep(-.25, .25, uv.x)); +} + +float getRingShape(vec2 uv) { + float radius = u_radius; + float thickness = u_thickness; + + float distance = length(uv); + float ringValue = 1. - smoothstep(radius, radius + thickness, distance); + ringValue *= smoothstep(radius - pow(u_innerShape, 3.) * thickness, radius, distance); + + return ringValue; +} + +void main() { + vec2 shape_uv = v_objectUV; + + float t = u_time; + + float cycleDuration = 3.; + float period2 = 2.0 * cycleDuration; + float localTime1 = fract((0.1 * t + cycleDuration) / period2) * period2; + float localTime2 = fract((0.1 * t) / period2) * period2; + float timeBlend = .5 + .5 * sin(.1 * t * PI / cycleDuration - .5 * PI); + + float atg = atan(shape_uv.y, shape_uv.x) + .001; + float l = length(shape_uv); + float radialOffset = .5 * l - inversesqrt(max(1e-4, l)); + vec2 polar_uv1 = vec2(atg, localTime1 - radialOffset) * u_noiseScale; + vec2 polar_uv2 = vec2(atg, localTime2 - radialOffset) * u_noiseScale; + + float noise1 = getNoise(shape_uv, polar_uv1, t); + float noise2 = getNoise(shape_uv, polar_uv2, t); + + float noise = mix(noise1, noise2, timeBlend); + + shape_uv *= (.8 + 1.2 * noise); + + float ringShape = getRingShape(shape_uv); + + float mixer = ringShape * ringShape * (u_colorsCount - 1.); + int idxLast = int(u_colorsCount) - 1; + vec4 gradient = u_colors[idxLast]; + gradient.rgb *= gradient.a; + for (int i = ${smokeRingMeta.maxColorCount} - 2; i >= 0; i--) { + float localT = clamp(mixer - float(idxLast - i - 1), 0., 1.); + vec4 c = u_colors[i]; + c.rgb *= c.a; + gradient = mix(gradient, c, localT); + } + + vec3 color = gradient.rgb * ringShape; + float opacity = gradient.a * ringShape; + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1. - opacity); + opacity = opacity + u_colorBack.a * (1. - opacity); + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +export { + smokeRingFragmentShader, + smokeRingMeta +}; +//# sourceMappingURL=smoke-ring.js.map diff --git a/public/vendor/paper-shaders/shaders/smoke-ring.js.map b/public/vendor/paper-shaders/shaders/smoke-ring.js.map new file mode 100644 index 0000000..0923f68 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/smoke-ring.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/smoke-ring.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, textureRandomizerR, colorBandingFix } from '../shader-utils.js';\n\nexport const smokeRingMeta = {\n maxColorCount: 10,\n maxNoiseIterations: 8,\n} as const;\n\n/**\n * Radial multi-colored gradient shaped with layered noise for a natural, smoky aesthetic.\n *\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colors (vec4[]): Up to 10 gradient colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_thickness (float): Thickness of the ring shape (0.01 to 1)\n * - u_radius (float): Radius of the ring shape (0 to 1)\n * - u_innerShape (float): Ring inner fill amount (0 to 4)\n * - u_noiseIterations (float): Number of noise layers, more layers gives more details (1 to 8)\n * - u_noiseScale (float): Noise frequency (0.01 to 5)\n * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const smokeRingFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${ smokeRingMeta.maxColorCount }];\nuniform float u_colorsCount;\n\nuniform float u_thickness;\nuniform float u_radius;\nuniform float u_innerShape;\nuniform float u_noiseScale;\nuniform float u_noiseIterations;\n\nin vec2 v_objectUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ textureRandomizerR }\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomR(i);\n float b = randomR(i + vec2(1.0, 0.0));\n float c = randomR(i + vec2(0.0, 1.0));\n float d = randomR(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\nvec2 fbm(vec2 n0, vec2 n1) {\n vec2 total = vec2(0.0);\n float amplitude = .4;\n for (int i = 0; i < ${ smokeRingMeta.maxNoiseIterations }; i++) {\n if (i >= int(u_noiseIterations)) break;\n total.x += valueNoise(n0) * amplitude;\n total.y += valueNoise(n1) * amplitude;\n n0 *= 1.99;\n n1 *= 1.99;\n amplitude *= 0.65;\n }\n return total;\n}\n\nfloat getNoise(vec2 uv, vec2 pUv, float t) {\n vec2 pUvLeft = pUv + .03 * t;\n float period = max(abs(u_noiseScale * TWO_PI), 1e-6);\n vec2 pUvRight = vec2(fract(pUv.x / period) * period, pUv.y) + .03 * t;\n vec2 noise = fbm(pUvLeft, pUvRight);\n return mix(noise.y, noise.x, smoothstep(-.25, .25, uv.x));\n}\n\nfloat getRingShape(vec2 uv) {\n float radius = u_radius;\n float thickness = u_thickness;\n\n float distance = length(uv);\n float ringValue = 1. - smoothstep(radius, radius + thickness, distance);\n ringValue *= smoothstep(radius - pow(u_innerShape, 3.) * thickness, radius, distance);\n\n return ringValue;\n}\n\nvoid main() {\n vec2 shape_uv = v_objectUV;\n\n float t = u_time;\n\n float cycleDuration = 3.;\n float period2 = 2.0 * cycleDuration;\n float localTime1 = fract((0.1 * t + cycleDuration) / period2) * period2;\n float localTime2 = fract((0.1 * t) / period2) * period2;\n float timeBlend = .5 + .5 * sin(.1 * t * PI / cycleDuration - .5 * PI);\n\n float atg = atan(shape_uv.y, shape_uv.x) + .001;\n float l = length(shape_uv);\n float radialOffset = .5 * l - inversesqrt(max(1e-4, l));\n vec2 polar_uv1 = vec2(atg, localTime1 - radialOffset) * u_noiseScale;\n vec2 polar_uv2 = vec2(atg, localTime2 - radialOffset) * u_noiseScale;\n \n float noise1 = getNoise(shape_uv, polar_uv1, t);\n float noise2 = getNoise(shape_uv, polar_uv2, t);\n\n float noise = mix(noise1, noise2, timeBlend);\n\n shape_uv *= (.8 + 1.2 * noise);\n\n float ringShape = getRingShape(shape_uv);\n\n float mixer = ringShape * ringShape * (u_colorsCount - 1.);\n int idxLast = int(u_colorsCount) - 1;\n vec4 gradient = u_colors[idxLast];\n gradient.rgb *= gradient.a;\n for (int i = ${ smokeRingMeta.maxColorCount } - 2; i >= 0; i--) {\n float localT = clamp(mixer - float(idxLast - i - 1), 0., 1.);\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, localT);\n }\n\n vec3 color = gradient.rgb * ringShape;\n float opacity = gradient.a * ringShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1. - opacity);\n opacity = opacity + u_colorBack.a * (1. - opacity);\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SmokeRingUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_noiseScale: number;\n u_thickness: number;\n u_radius: number;\n u_innerShape: number;\n u_noiseIterations: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface SmokeRingParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n noiseScale?: number;\n thickness?: number;\n radius?: number;\n innerShape?: number;\n noiseIterations?: number;\n}\n"], + "mappings": ";;;;;AAEA,eAAmE;AACnE,SAAS,WAAW,oBAAoB,uBAAuB;AAExD,MAAM,gBAAgB;AAAA,EAC3B,eAAe;AAAA,EACf,oBAAoB;AACtB;AAqCO,MAAM,0BAAkC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAQtB,cAAc,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAalD,SAAU;AAAA,EACV,kBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAgBG,cAAc,kBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBA4DxC,cAAc,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAczC,eAAgB;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/spiral.d.ts b/public/vendor/paper-shaders/shaders/spiral.d.ts new file mode 100644 index 0000000..ff53cef --- /dev/null +++ b/public/vendor/paper-shaders/shaders/spiral.d.ts @@ -0,0 +1,61 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * A single-colored animated spiral that morphs across a wide range of shapes - + * from crisp, thin-lined geometry to flowing whirlpool forms and wavy, abstract rings. + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_patternUV (vec2): UV coordinates in pixels (scaled by 0.01 for precision), with rotation and offset applied + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_colorBack (vec4): Background color in RGBA + * - u_colorFront (vec4): Foreground (ink) color in RGBA + * - u_density (float): Spacing falloff simulating perspective, 0 = flat spiral (0 to 1) + * - u_distortion (float): Power of shape distortion applied along the spiral (0 to 1) + * - u_strokeWidth (float): Thickness of spiral curve (0 to 1) + * - u_strokeTaper (float): How much stroke loses width away from center, 0 = full visibility (0 to 1) + * - u_strokeCap (float): Extra stroke width at the center, no effect with strokeWidth = 0.5 (0 to 1) + * - u_noise (float): Noise distortion applied over the canvas, no effect with noiseFrequency = 0 (0 to 1) + * - u_noiseFrequency (float): Noise frequency, no effect with noise = 0 (0 to 1) + * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1) + * + */ +export declare const spiralFragmentShader: string; +export interface SpiralUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colorFront: [number, number, number, number]; + u_density: number; + u_distortion: number; + u_strokeWidth: number; + u_strokeTaper: number; + u_strokeCap: number; + u_noise: number; + u_noiseFrequency: number; + u_softness: number; +} +export interface SpiralParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colorFront?: string; + density?: number; + distortion?: number; + strokeWidth?: number; + strokeTaper?: number; + strokeCap?: number; + noise?: number; + noiseFrequency?: number; + softness?: number; +} diff --git a/public/vendor/paper-shaders/shaders/spiral.js b/public/vendor/paper-shaders/shaders/spiral.js new file mode 100644 index 0000000..76152d9 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/spiral.js @@ -0,0 +1,81 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { simplexNoise, declarePI, colorBandingFix } from "../shader-utils.js"; +const spiralFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform vec4 u_colorBack; +uniform vec4 u_colorFront; +uniform float u_density; +uniform float u_distortion; +uniform float u_strokeWidth; +uniform float u_strokeCap; +uniform float u_strokeTaper; +uniform float u_noise; +uniform float u_noiseFrequency; +uniform float u_softness; + +in vec2 v_patternUV; + +out vec4 fragColor; + +${declarePI} +${simplexNoise} + +void main() { + vec2 uv = 2. * v_patternUV; + + float t = u_time; + float l = length(uv); + float density = clamp(u_density, 0., 1.); + l = pow(max(l, 1e-6), density); + float angle = atan(uv.y, uv.x) - t; + float angleNormalised = angle / TWO_PI; + + angleNormalised += .125 * u_noise * snoise(16. * pow(u_noiseFrequency, 3.) * uv); + + float offset = l + angleNormalised; + offset -= u_distortion * (sin(4. * l - .5 * t) * cos(PI + l + .5 * t)); + float stripe = fract(offset); + + float shape = 2. * abs(stripe - .5); + float width = 1. - clamp(u_strokeWidth, .005 * u_strokeTaper, 1.); + + + float wCap = mix(width, (1. - stripe) * (1. - step(.5, stripe)), (1. - clamp(l, 0., 1.))); + width = mix(width, wCap, u_strokeCap); + width *= (1. - clamp(u_strokeTaper, 0., 1.) * l); + + float fw = fwidth(offset); + float fwMult = 4. - 3. * (smoothstep(.05, .4, 2. * u_strokeWidth) * smoothstep(.05, .4, 2. * (1. - u_strokeWidth))); + float pixelSize = mix(fwMult * fw, fwidth(shape), clamp(fw, 0., 1.)); + pixelSize = mix(pixelSize, .002, u_strokeCap * (1. - clamp(l, 0., 1.))); + + float res = smoothstep(width - pixelSize - u_softness, width + pixelSize + u_softness, shape); + + vec3 fgColor = u_colorFront.rgb * u_colorFront.a; + float fgOpacity = u_colorFront.a; + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + float bgOpacity = u_colorBack.a; + + vec3 color = fgColor * res; + float opacity = fgOpacity * res; + + color += bgColor * (1. - opacity); + opacity += bgOpacity * (1. - opacity); + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +export { + spiralFragmentShader +}; +//# sourceMappingURL=spiral.js.map diff --git a/public/vendor/paper-shaders/shaders/spiral.js.map b/public/vendor/paper-shaders/shaders/spiral.js.map new file mode 100644 index 0000000..9b1d972 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/spiral.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/spiral.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { simplexNoise, declarePI, colorBandingFix } from '../shader-utils.js';\n\n/**\n * A single-colored animated spiral that morphs across a wide range of shapes -\n * from crisp, thin-lined geometry to flowing whirlpool forms and wavy, abstract rings.\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_patternUV (vec2): UV coordinates in pixels (scaled by 0.01 for precision), with rotation and offset applied\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colorFront (vec4): Foreground (ink) color in RGBA\n * - u_density (float): Spacing falloff simulating perspective, 0 = flat spiral (0 to 1)\n * - u_distortion (float): Power of shape distortion applied along the spiral (0 to 1)\n * - u_strokeWidth (float): Thickness of spiral curve (0 to 1)\n * - u_strokeTaper (float): How much stroke loses width away from center, 0 = full visibility (0 to 1)\n * - u_strokeCap (float): Extra stroke width at the center, no effect with strokeWidth = 0.5 (0 to 1)\n * - u_noise (float): Noise distortion applied over the canvas, no effect with noiseFrequency = 0 (0 to 1)\n * - u_noiseFrequency (float): Noise frequency, no effect with noise = 0 (0 to 1)\n * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)\n *\n */\n\n// language=GLSL\nexport const spiralFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorFront;\nuniform float u_density;\nuniform float u_distortion;\nuniform float u_strokeWidth;\nuniform float u_strokeCap;\nuniform float u_strokeTaper;\nuniform float u_noise;\nuniform float u_noiseFrequency;\nuniform float u_softness;\n\nin vec2 v_patternUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ simplexNoise }\n\nvoid main() {\n vec2 uv = 2. * v_patternUV;\n\n float t = u_time;\n float l = length(uv);\n float density = clamp(u_density, 0., 1.);\n l = pow(max(l, 1e-6), density);\n float angle = atan(uv.y, uv.x) - t;\n float angleNormalised = angle / TWO_PI;\n\n angleNormalised += .125 * u_noise * snoise(16. * pow(u_noiseFrequency, 3.) * uv);\n\n float offset = l + angleNormalised;\n offset -= u_distortion * (sin(4. * l - .5 * t) * cos(PI + l + .5 * t));\n float stripe = fract(offset);\n\n float shape = 2. * abs(stripe - .5);\n float width = 1. - clamp(u_strokeWidth, .005 * u_strokeTaper, 1.);\n\n\n float wCap = mix(width, (1. - stripe) * (1. - step(.5, stripe)), (1. - clamp(l, 0., 1.)));\n width = mix(width, wCap, u_strokeCap);\n width *= (1. - clamp(u_strokeTaper, 0., 1.) * l);\n\n float fw = fwidth(offset);\n float fwMult = 4. - 3. * (smoothstep(.05, .4, 2. * u_strokeWidth) * smoothstep(.05, .4, 2. * (1. - u_strokeWidth)));\n float pixelSize = mix(fwMult * fw, fwidth(shape), clamp(fw, 0., 1.));\n pixelSize = mix(pixelSize, .002, u_strokeCap * (1. - clamp(l, 0., 1.)));\n\n float res = smoothstep(width - pixelSize - u_softness, width + pixelSize + u_softness, shape);\n\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n vec3 color = fgColor * res;\n float opacity = fgOpacity * res;\n\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SpiralUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colorFront: [number, number, number, number];\n u_density: number;\n u_distortion: number;\n u_strokeWidth: number;\n u_strokeTaper: number;\n u_strokeCap: number;\n u_noise: number;\n u_noiseFrequency: number;\n u_softness: number;\n}\n\nexport interface SpiralParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colorFront?: string;\n density?: number;\n distortion?: number;\n strokeWidth?: number;\n strokeTaper?: number;\n strokeCap?: number;\n noise?: number;\n noiseFrequency?: number;\n softness?: number;\n}\n"], + "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,cAAc,WAAW,uBAAuB;AAsClD,MAAM,uBAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBzC,SAAU;AAAA,EACV,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA4CX,eAAgB;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/static-mesh-gradient.d.ts b/public/vendor/paper-shaders/shaders/static-mesh-gradient.d.ts new file mode 100644 index 0000000..aa8ff75 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/static-mesh-gradient.d.ts @@ -0,0 +1,63 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const staticMeshGradientMeta: { + readonly maxColorCount: 10; +}; +/** + * Multi-point mesh gradient with up to 10 color spots, enhanced by two-direction warping, + * adjustable blend sharpness, and grain controls. + * + * Fragment shader uniforms: + * - u_colors (vec4[]): Up to 10 gradient colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_positions (float): Color spots placement seed (0 to 100) + * - u_waveX (float): Strength of sine wave distortion along X axis (0 to 1) + * - u_waveXShift (float): Phase offset applied to the X-axis wave (0 to 1) + * - u_waveY (float): Strength of sine wave distortion along Y axis (0 to 1) + * - u_waveYShift (float): Phase offset applied to the Y-axis wave (0 to 1) + * - u_mixing (float): Blending behavior, 0 = hard stripes, 0.5 = smooth, 1 = gradual blend (0 to 1) + * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1) + * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const staticMeshGradientFragmentShader: string; +export interface StaticMeshGradientUniforms extends ShaderSizingUniforms { + u_colors: vec4[]; + u_colorsCount: number; + u_positions: number; + u_waveX: number; + u_waveXShift: number; + u_waveY: number; + u_waveYShift: number; + u_mixing: number; + u_grainMixer: number; + u_grainOverlay: number; +} +export interface StaticMeshGradientParams extends ShaderSizingParams, ShaderMotionParams { + colors?: string[]; + positions?: number; + waveX?: number; + waveXShift?: number; + waveY?: number; + waveYShift?: number; + mixing?: number; + grainMixer?: number; + grainOverlay?: number; +} diff --git a/public/vendor/paper-shaders/shaders/static-mesh-gradient.js b/public/vendor/paper-shaders/shaders/static-mesh-gradient.js new file mode 100644 index 0000000..32701a8 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/static-mesh-gradient.js @@ -0,0 +1,128 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import { +} from "../shader-sizing.js"; +import { declarePI, rotation2, proceduralHash21 } from "../shader-utils.js"; +const staticMeshGradientMeta = { + maxColorCount: 10 +}; +const staticMeshGradientFragmentShader = `#version 300 es +precision mediump float; + +uniform vec4 u_colors[${staticMeshGradientMeta.maxColorCount}]; +uniform float u_colorsCount; + +uniform float u_positions; +uniform float u_waveX; +uniform float u_waveXShift; +uniform float u_waveY; +uniform float u_waveYShift; +uniform float u_mixing; +uniform float u_grainMixer; +uniform float u_grainOverlay; + +in vec2 v_objectUV; +out vec4 fragColor; + +${declarePI} +${rotation2} +${proceduralHash21} + +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = hash21(i); + float b = hash21(i + vec2(1.0, 0.0)); + float c = hash21(i + vec2(0.0, 1.0)); + float d = hash21(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} + +float noise(vec2 n, vec2 seedOffset) { + return valueNoise(n + seedOffset); +} + +vec2 getPosition(int i, float t) { + float a = float(i) * .37; + float b = .6 + mod(float(i), 3.) * .3; + float c = .8 + mod(float(i + 1), 4.) * 0.25; + + float x = sin(t * b + a); + float y = cos(t * c + a * 1.5); + + return .5 + .5 * vec2(x, y); +} + +void main() { + vec2 uv = v_objectUV; + uv += .5; + vec2 grainUV = uv * 1000.; + + float grain = noise(grainUV, vec2(0.)); + float mixerGrain = .4 * u_grainMixer * (grain - .5); + + float radius = smoothstep(0., 1., length(uv - .5)); + float center = 1. - radius; + for (float i = 1.; i <= 2.; i++) { + uv.x += u_waveX * center / i * cos(TWO_PI * u_waveXShift + i * 2. * smoothstep(.0, 1., uv.y)); + uv.y += u_waveY * center / i * cos(TWO_PI * u_waveYShift + i * 2. * smoothstep(.0, 1., uv.x)); + } + + vec3 color = vec3(0.); + float opacity = 0.; + float totalWeight = 0.; + float positionSeed = 25. + .33 * u_positions; + + for (int i = 0; i < ${staticMeshGradientMeta.maxColorCount}; i++) { + if (i >= int(u_colorsCount)) break; + + vec2 pos = getPosition(i, positionSeed) + mixerGrain; + float dist = length(uv - pos); + dist = length(uv - pos); + + vec3 colorFraction = u_colors[i].rgb * u_colors[i].a; + float opacityFraction = u_colors[i].a; + + float mixing = pow(u_mixing, .7); + float power = mix(2., 1., mixing); + dist = pow(dist, power); + + float w = 1. / (dist + 1e-3); + float baseSharpness = mix(.0, 8., clamp(w, 0., 1.)); + float sharpness = mix(baseSharpness, 1., mixing); + w = pow(w, sharpness); + color += colorFraction * w; + opacity += opacityFraction * w; + totalWeight += w; + } + + color /= max(1e-4, totalWeight); + opacity /= max(1e-4, totalWeight); + + float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.)); + grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5); + grainOverlay = pow(grainOverlay, 1.3); + + float grainOverlayV = grainOverlay * 2. - 1.; + vec3 grainOverlayColor = vec3(step(0., grainOverlayV)); + float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV); + grainOverlayStrength = pow(grainOverlayStrength, .8); + color = mix(color, grainOverlayColor, .35 * grainOverlayStrength); + + opacity += .5 * grainOverlayStrength; + opacity = clamp(opacity, 0., 1.); + + fragColor = vec4(color, opacity); +} +`; +export { + staticMeshGradientFragmentShader, + staticMeshGradientMeta +}; +//# sourceMappingURL=static-mesh-gradient.js.map diff --git a/public/vendor/paper-shaders/shaders/static-mesh-gradient.js.map b/public/vendor/paper-shaders/shaders/static-mesh-gradient.js.map new file mode 100644 index 0000000..cee90ec --- /dev/null +++ b/public/vendor/paper-shaders/shaders/static-mesh-gradient.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/static-mesh-gradient.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\nexport const staticMeshGradientMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Multi-point mesh gradient with up to 10 color spots, enhanced by two-direction warping,\n * adjustable blend sharpness, and grain controls.\n *\n * Fragment shader uniforms:\n * - u_colors (vec4[]): Up to 10 gradient colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_positions (float): Color spots placement seed (0 to 100)\n * - u_waveX (float): Strength of sine wave distortion along X axis (0 to 1)\n * - u_waveXShift (float): Phase offset applied to the X-axis wave (0 to 1)\n * - u_waveY (float): Strength of sine wave distortion along Y axis (0 to 1)\n * - u_waveYShift (float): Phase offset applied to the Y-axis wave (0 to 1)\n * - u_mixing (float): Blending behavior, 0 = hard stripes, 0.5 = smooth, 1 = gradual blend (0 to 1)\n * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1)\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const staticMeshGradientFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec4 u_colors[${ staticMeshGradientMeta.maxColorCount }];\nuniform float u_colorsCount;\n\nuniform float u_positions;\nuniform float u_waveX;\nuniform float u_waveXShift;\nuniform float u_waveY;\nuniform float u_waveYShift;\nuniform float u_mixing;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\n\nin vec2 v_objectUV;\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\n${ proceduralHash21 }\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat noise(vec2 n, vec2 seedOffset) {\n return valueNoise(n + seedOffset);\n}\n\nvec2 getPosition(int i, float t) {\n float a = float(i) * .37;\n float b = .6 + mod(float(i), 3.) * .3;\n float c = .8 + mod(float(i + 1), 4.) * 0.25;\n\n float x = sin(t * b + a);\n float y = cos(t * c + a * 1.5);\n\n return .5 + .5 * vec2(x, y);\n}\n\nvoid main() {\n vec2 uv = v_objectUV;\n uv += .5;\n vec2 grainUV = uv * 1000.;\n\n float grain = noise(grainUV, vec2(0.));\n float mixerGrain = .4 * u_grainMixer * (grain - .5);\n\n float radius = smoothstep(0., 1., length(uv - .5));\n float center = 1. - radius;\n for (float i = 1.; i <= 2.; i++) {\n uv.x += u_waveX * center / i * cos(TWO_PI * u_waveXShift + i * 2. * smoothstep(.0, 1., uv.y));\n uv.y += u_waveY * center / i * cos(TWO_PI * u_waveYShift + i * 2. * smoothstep(.0, 1., uv.x));\n }\n\n vec3 color = vec3(0.);\n float opacity = 0.;\n float totalWeight = 0.;\n float positionSeed = 25. + .33 * u_positions;\n\n for (int i = 0; i < ${ staticMeshGradientMeta.maxColorCount }; i++) {\n if (i >= int(u_colorsCount)) break;\n\n vec2 pos = getPosition(i, positionSeed) + mixerGrain;\n float dist = length(uv - pos);\n dist = length(uv - pos);\n\n vec3 colorFraction = u_colors[i].rgb * u_colors[i].a;\n float opacityFraction = u_colors[i].a;\n\n float mixing = pow(u_mixing, .7);\n float power = mix(2., 1., mixing);\n dist = pow(dist, power);\n\n float w = 1. / (dist + 1e-3);\n float baseSharpness = mix(.0, 8., clamp(w, 0., 1.));\n float sharpness = mix(baseSharpness, 1., mixing);\n w = pow(w, sharpness);\n color += colorFraction * w;\n opacity += opacityFraction * w;\n totalWeight += w;\n }\n\n color /= max(1e-4, totalWeight);\n opacity /= max(1e-4, totalWeight);\n\n float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.));\n grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\n grainOverlay = pow(grainOverlay, 1.3);\n\n float grainOverlayV = grainOverlay * 2. - 1.;\n vec3 grainOverlayColor = vec3(step(0., grainOverlayV));\n float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV);\n grainOverlayStrength = pow(grainOverlayStrength, .8);\n color = mix(color, grainOverlayColor, .35 * grainOverlayStrength);\n\n opacity += .5 * grainOverlayStrength;\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface StaticMeshGradientUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_positions: number;\n u_waveX: number;\n u_waveXShift: number;\n u_waveY: number;\n u_waveYShift: number;\n u_mixing: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n}\n\nexport interface StaticMeshGradientParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n positions?: number;\n waveX?: number;\n waveXShift?: number;\n waveY?: number;\n waveYShift?: number;\n mixing?: number;\n grainMixer?: number;\n grainOverlay?: number;\n}\n"], + "mappings": ";;;;;AAEA;AAAA,OAGO;AACP,SAAS,WAAW,WAAW,wBAAwB;AAEhD,MAAM,yBAAyB;AAAA,EACpC,eAAe;AACjB;AAqCO,MAAM,mCAA2C;AAAA;AAAA;AAAA,wBAG/B,uBAAuB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAe3D,SAAU;AAAA,EACV,SAAU;AAAA,EACV,gBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAkDK,uBAAuB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/static-radial-gradient.d.ts b/public/vendor/paper-shaders/shaders/static-radial-gradient.d.ts new file mode 100644 index 0000000..6c0cd5b --- /dev/null +++ b/public/vendor/paper-shaders/shaders/static-radial-gradient.d.ts @@ -0,0 +1,72 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const staticRadialGradientMeta: { + readonly maxColorCount: 10; +}; +/** + * Radial gradient with up to 10 blended colors, featuring advanced mixing modes, focal point controls, + * shape distortion, and grain effects. + * + * Fragment shader uniforms: + * - u_colorBack (vec4): Background color in RGBA + * - u_colors (vec4[]): Up to 10 gradient colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_radius (float): Size of the shape (0 to 3) + * - u_focalDistance (float): Distance of the focal point from center (0 to 3) + * - u_focalAngle (float): Angle of the focal point in degrees, effective with focalDistance > 0 (0 to 360) + * - u_falloff (float): Gradient decay, 0 = linear gradient (-1 to 1) + * - u_mixing (float): Blending behavior, 0 = hard stripes, 1 = smooth gradient (0 to 1) + * - u_distortion (float): Strength of radial distortion (0 to 1) + * - u_distortionShift (float): Radial distortion offset, effective with distortion > 0 (-1 to 1) + * - u_distortionFreq (float): Radial distortion frequency, effective with distortion > 0 (0 to 20) + * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1) + * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const staticRadialGradientFragmentShader: string; +export interface StaticRadialGradientUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colors: vec4[]; + u_colorsCount: number; + u_radius: number; + u_focalDistance: number; + u_focalAngle: number; + u_falloff: number; + u_mixing: number; + u_distortion: number; + u_distortionShift: number; + u_distortionFreq: number; + u_grainMixer: number; + u_grainOverlay: number; +} +export interface StaticRadialGradientParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colors?: string[]; + radius?: number; + focalDistance?: number; + focalAngle?: number; + falloff?: number; + mixing?: number; + distortion?: number; + distortionShift?: number; + distortionFreq?: number; + grainMixer?: number; + grainOverlay?: number; +} diff --git a/public/vendor/paper-shaders/shaders/static-radial-gradient.js b/public/vendor/paper-shaders/shaders/static-radial-gradient.js new file mode 100644 index 0000000..b52ae09 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/static-radial-gradient.js @@ -0,0 +1,184 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import { +} from "../shader-sizing.js"; +import { declarePI, rotation2, proceduralHash21 } from "../shader-utils.js"; +const staticRadialGradientMeta = { + maxColorCount: 10 +}; +const staticRadialGradientFragmentShader = `#version 300 es +precision mediump float; + +uniform vec4 u_colorBack; +uniform vec4 u_colors[${staticRadialGradientMeta.maxColorCount}]; +uniform float u_colorsCount; + +uniform float u_radius; +uniform float u_focalDistance; +uniform float u_focalAngle; +uniform float u_falloff; +uniform float u_mixing; +uniform float u_distortion; +uniform float u_distortionShift; +uniform float u_distortionFreq; +uniform float u_grainMixer; +uniform float u_grainOverlay; + +in vec2 v_objectUV; +out vec4 fragColor; + +${declarePI} +${rotation2} +${proceduralHash21} + +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = hash21(i); + float b = hash21(i + vec2(1.0, 0.0)); + float c = hash21(i + vec2(0.0, 1.0)); + float d = hash21(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} + +float noise(vec2 n, vec2 seedOffset) { + return valueNoise(n + seedOffset); +} + +vec2 getPosition(int i, float t) { + float a = float(i) * .37; + float b = .6 + mod(float(i), 3.) * .3; + float c = .8 + mod(float(i + 1), 4.) * 0.25; + + float x = sin(t * b + a); + float y = cos(t * c + a * 1.5); + + return .5 + .5 * vec2(x, y); +} + +void main() { + vec2 uv = 2. * v_objectUV; + vec2 grainUV = uv * 1000.; + + vec2 center = vec2(0.); + float angleRad = -radians(u_focalAngle + 90.); + vec2 focalPoint = vec2(cos(angleRad), sin(angleRad)) * u_focalDistance; + float radius = u_radius; + + vec2 c_to_uv = uv - center; + vec2 f_to_uv = uv - focalPoint; + vec2 f_to_c = center - focalPoint; + float r = length(c_to_uv); + + float fragAngle = atan(c_to_uv.y, c_to_uv.x); + float angleDiff = fract((fragAngle - angleRad + PI) / TWO_PI) * TWO_PI - PI; + + float halfAngle = acos(clamp(radius / max(u_focalDistance, 1e-4), 0.0, 1.0)); + float e0 = 0.6 * PI, e1 = halfAngle; + float lo = min(e0, e1), hi = max(e0, e1); + float s = smoothstep(lo, hi, abs(angleDiff)); + float isInSector = (e1 >= e0) ? (1.0 - s) : s; + + float a = dot(f_to_uv, f_to_uv); + float b = -2.0 * dot(f_to_uv, f_to_c); + float c = dot(f_to_c, f_to_c) - radius * radius; + + float discriminant = b * b - 4.0 * a * c; + float t = 1.0; + + if (discriminant >= 0.0) { + float sqrtD = sqrt(discriminant); + float div = max(1e-4, 2.0 * a); + float t0 = (-b - sqrtD) / div; + float t1 = (-b + sqrtD) / div; + t = max(t0, t1); + if (t < 0.0) t = 0.0; + } + + float dist = length(f_to_uv); + float normalized = dist / max(1e-4, length(f_to_uv * t)); + float shape = clamp(normalized, 0.0, 1.0); + + float falloffMapped = mix(.2 + .8 * max(0., u_falloff + 1.), mix(1., 15., u_falloff * u_falloff), step(.0, u_falloff)); + + float falloffExp = mix(falloffMapped, 1., shape); + shape = pow(shape, falloffExp); + shape = 1. - clamp(shape, 0., 1.); + + + float outerMask = .002; + float outer = 1.0 - smoothstep(radius - outerMask, radius + outerMask, r); + outer = mix(outer, 1., isInSector); + + shape = mix(0., shape, outer); + shape *= 1. - smoothstep(radius - .01, radius, r); + + float angle = atan(f_to_uv.y, f_to_uv.x); + shape -= pow(u_distortion, 2.) * shape * pow(abs(sin(PI * clamp(length(f_to_uv) - 0.2 + u_distortionShift, 0.0, 1.0))), 4.0) * (sin(u_distortionFreq * angle) + cos(floor(0.65 * u_distortionFreq) * angle)); + + float grain = noise(grainUV, vec2(0.)); + float mixerGrain = .4 * u_grainMixer * (grain - .5); + + float mixer = shape * u_colorsCount + mixerGrain; + vec4 gradient = u_colors[0]; + gradient.rgb *= gradient.a; + + float outerShape = 0.; + for (int i = 1; i < ${staticRadialGradientMeta.maxColorCount + 1}; i++) { + if (i > int(u_colorsCount)) break; + float mLinear = clamp(mixer - float(i - 1), 0.0, 1.0); + + float aa = fwidth(mLinear); + float width = min(u_mixing, 0.5); + float t = clamp((mLinear - (0.5 - width - aa)) / (2. * width + 2. * aa), 0., 1.); + float p = mix(2., 1., clamp((u_mixing - 0.5) * 2., 0., 1.)); + float m = t < 0.5 + ? 0.5 * pow(2. * t, p) + : 1. - 0.5 * pow(2. * (1. - t), p); + + float quadBlend = clamp((u_mixing - 0.5) * 2., 0., 1.); + m = mix(m, m * m, 0.5 * quadBlend); + + if (i == 1) { + outerShape = m; + } + + vec4 c = u_colors[i - 1]; + c.rgb *= c.a; + gradient = mix(gradient, c, m); + } + + vec3 color = gradient.rgb * outerShape; + float opacity = gradient.a * outerShape; + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1.0 - opacity); + opacity = opacity + u_colorBack.a * (1.0 - opacity); + + float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.)); + grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5); + grainOverlay = pow(grainOverlay, 1.3); + + float grainOverlayV = grainOverlay * 2. - 1.; + vec3 grainOverlayColor = vec3(step(0., grainOverlayV)); + float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV); + grainOverlayStrength = pow(grainOverlayStrength, .8); + color = mix(color, grainOverlayColor, .35 * grainOverlayStrength); + + opacity += .5 * grainOverlayStrength; + opacity = clamp(opacity, 0., 1.); + + fragColor = vec4(color, opacity); +} +`; +export { + staticRadialGradientFragmentShader, + staticRadialGradientMeta +}; +//# sourceMappingURL=static-radial-gradient.js.map diff --git a/public/vendor/paper-shaders/shaders/static-radial-gradient.js.map b/public/vendor/paper-shaders/shaders/static-radial-gradient.js.map new file mode 100644 index 0000000..c27e009 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/static-radial-gradient.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/static-radial-gradient.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport {\n type ShaderSizingParams,\n type ShaderSizingUniforms,\n} from '../shader-sizing.js';\nimport { declarePI, rotation2, proceduralHash21 } from '../shader-utils.js';\n\nexport const staticRadialGradientMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Radial gradient with up to 10 blended colors, featuring advanced mixing modes, focal point controls,\n * shape distortion, and grain effects.\n *\n * Fragment shader uniforms:\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colors (vec4[]): Up to 10 gradient colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_radius (float): Size of the shape (0 to 3)\n * - u_focalDistance (float): Distance of the focal point from center (0 to 3)\n * - u_focalAngle (float): Angle of the focal point in degrees, effective with focalDistance > 0 (0 to 360)\n * - u_falloff (float): Gradient decay, 0 = linear gradient (-1 to 1)\n * - u_mixing (float): Blending behavior, 0 = hard stripes, 1 = smooth gradient (0 to 1)\n * - u_distortion (float): Strength of radial distortion (0 to 1)\n * - u_distortionShift (float): Radial distortion offset, effective with distortion > 0 (-1 to 1)\n * - u_distortionFreq (float): Radial distortion frequency, effective with distortion > 0 (0 to 20)\n * - u_grainMixer (float): Strength of grain distortion applied to shape edges (0 to 1)\n * - u_grainOverlay (float): Post-processing black/white grain overlay (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const staticRadialGradientFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${ staticRadialGradientMeta.maxColorCount }];\nuniform float u_colorsCount;\n\nuniform float u_radius;\nuniform float u_focalDistance;\nuniform float u_focalAngle;\nuniform float u_falloff;\nuniform float u_mixing;\nuniform float u_distortion;\nuniform float u_distortionShift;\nuniform float u_distortionFreq;\nuniform float u_grainMixer;\nuniform float u_grainOverlay;\n\nin vec2 v_objectUV;\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\n${ proceduralHash21 }\n\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = hash21(i);\n float b = hash21(i + vec2(1.0, 0.0));\n float c = hash21(i + vec2(0.0, 1.0));\n float d = hash21(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\nfloat noise(vec2 n, vec2 seedOffset) {\n return valueNoise(n + seedOffset);\n}\n\nvec2 getPosition(int i, float t) {\n float a = float(i) * .37;\n float b = .6 + mod(float(i), 3.) * .3;\n float c = .8 + mod(float(i + 1), 4.) * 0.25;\n\n float x = sin(t * b + a);\n float y = cos(t * c + a * 1.5);\n\n return .5 + .5 * vec2(x, y);\n}\n\nvoid main() {\n vec2 uv = 2. * v_objectUV;\n vec2 grainUV = uv * 1000.;\n\n vec2 center = vec2(0.);\n float angleRad = -radians(u_focalAngle + 90.);\n vec2 focalPoint = vec2(cos(angleRad), sin(angleRad)) * u_focalDistance;\n float radius = u_radius;\n\n vec2 c_to_uv = uv - center;\n vec2 f_to_uv = uv - focalPoint;\n vec2 f_to_c = center - focalPoint;\n float r = length(c_to_uv);\n\n float fragAngle = atan(c_to_uv.y, c_to_uv.x);\n float angleDiff = fract((fragAngle - angleRad + PI) / TWO_PI) * TWO_PI - PI;\n\n float halfAngle = acos(clamp(radius / max(u_focalDistance, 1e-4), 0.0, 1.0));\n float e0 = 0.6 * PI, e1 = halfAngle;\n float lo = min(e0, e1), hi = max(e0, e1);\n float s = smoothstep(lo, hi, abs(angleDiff));\n float isInSector = (e1 >= e0) ? (1.0 - s) : s;\n\n float a = dot(f_to_uv, f_to_uv);\n float b = -2.0 * dot(f_to_uv, f_to_c);\n float c = dot(f_to_c, f_to_c) - radius * radius;\n\n float discriminant = b * b - 4.0 * a * c;\n float t = 1.0;\n\n if (discriminant >= 0.0) {\n float sqrtD = sqrt(discriminant);\n float div = max(1e-4, 2.0 * a);\n float t0 = (-b - sqrtD) / div;\n float t1 = (-b + sqrtD) / div;\n t = max(t0, t1);\n if (t < 0.0) t = 0.0;\n }\n\n float dist = length(f_to_uv);\n float normalized = dist / max(1e-4, length(f_to_uv * t));\n float shape = clamp(normalized, 0.0, 1.0);\n\n float falloffMapped = mix(.2 + .8 * max(0., u_falloff + 1.), mix(1., 15., u_falloff * u_falloff), step(.0, u_falloff));\n\n float falloffExp = mix(falloffMapped, 1., shape);\n shape = pow(shape, falloffExp);\n shape = 1. - clamp(shape, 0., 1.);\n\n\n float outerMask = .002;\n float outer = 1.0 - smoothstep(radius - outerMask, radius + outerMask, r);\n outer = mix(outer, 1., isInSector);\n\n shape = mix(0., shape, outer);\n shape *= 1. - smoothstep(radius - .01, radius, r);\n\n float angle = atan(f_to_uv.y, f_to_uv.x);\n shape -= pow(u_distortion, 2.) * shape * pow(abs(sin(PI * clamp(length(f_to_uv) - 0.2 + u_distortionShift, 0.0, 1.0))), 4.0) * (sin(u_distortionFreq * angle) + cos(floor(0.65 * u_distortionFreq) * angle));\n\n float grain = noise(grainUV, vec2(0.));\n float mixerGrain = .4 * u_grainMixer * (grain - .5);\n\n float mixer = shape * u_colorsCount + mixerGrain;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n\n float outerShape = 0.;\n for (int i = 1; i < ${ staticRadialGradientMeta.maxColorCount + 1 }; i++) {\n if (i > int(u_colorsCount)) break;\n float mLinear = clamp(mixer - float(i - 1), 0.0, 1.0);\n\n float aa = fwidth(mLinear);\n float width = min(u_mixing, 0.5);\n float t = clamp((mLinear - (0.5 - width - aa)) / (2. * width + 2. * aa), 0., 1.);\n float p = mix(2., 1., clamp((u_mixing - 0.5) * 2., 0., 1.));\n float m = t < 0.5\n ? 0.5 * pow(2. * t, p)\n : 1. - 0.5 * pow(2. * (1. - t), p);\n\n float quadBlend = clamp((u_mixing - 0.5) * 2., 0., 1.);\n m = mix(m, m * m, 0.5 * quadBlend);\n \n if (i == 1) {\n outerShape = m;\n }\n\n vec4 c = u_colors[i - 1];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n vec3 color = gradient.rgb * outerShape;\n float opacity = gradient.a * outerShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1.0 - opacity);\n opacity = opacity + u_colorBack.a * (1.0 - opacity);\n\n float grainOverlay = valueNoise(rotate(grainUV, 1.) + vec2(3.));\n grainOverlay = mix(grainOverlay, valueNoise(rotate(grainUV, 2.) + vec2(-1.)), .5);\n grainOverlay = pow(grainOverlay, 1.3);\n\n float grainOverlayV = grainOverlay * 2. - 1.;\n vec3 grainOverlayColor = vec3(step(0., grainOverlayV));\n float grainOverlayStrength = u_grainOverlay * abs(grainOverlayV);\n grainOverlayStrength = pow(grainOverlayStrength, .8);\n color = mix(color, grainOverlayColor, .35 * grainOverlayStrength);\n\n opacity += .5 * grainOverlayStrength;\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface StaticRadialGradientUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_radius: number;\n u_focalDistance: number;\n u_focalAngle: number;\n u_falloff: number;\n u_mixing: number;\n u_distortion: number;\n u_distortionShift: number;\n u_distortionFreq: number;\n u_grainMixer: number;\n u_grainOverlay: number;\n}\n\nexport interface StaticRadialGradientParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n radius?: number;\n focalDistance?: number;\n focalAngle?: number;\n falloff?: number;\n mixing?: number;\n distortion?: number;\n distortionShift?: number;\n distortionFreq?: number;\n grainMixer?: number;\n grainOverlay?: number;\n}\n"], + "mappings": ";;;;;AAEA;AAAA,OAGO;AACP,SAAS,WAAW,WAAW,wBAAwB;AAEhD,MAAM,2BAA2B;AAAA,EACtC,eAAe;AACjB;AAwCO,MAAM,qCAA6C;AAAA;AAAA;AAAA;AAAA,wBAIjC,yBAAyB,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiB7D,SAAU;AAAA,EACV,SAAU;AAAA,EACV,gBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAkGK,yBAAyB,gBAAgB,CAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/swirl.d.ts b/public/vendor/paper-shaders/shaders/swirl.d.ts new file mode 100644 index 0000000..9d8845f --- /dev/null +++ b/public/vendor/paper-shaders/shaders/swirl.d.ts @@ -0,0 +1,63 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const swirlMeta: { + readonly maxColorCount: 10; +}; +/** + * Animated bands of color twisting and bending, producing spirals, arcs, and flowing circular patterns. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_colorBack (vec4): Background color in RGBA + * - u_colors (vec4[]): Up to 10 stripe colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_bandCount (float): Number of color bands, 0 = concentric ripples (0 to 15) + * - u_twist (float): Vortex power, 0 = straight sectoral shapes (0 to 1) + * - u_center (float): How far from the center the swirl colors begin to appear (0 to 1) + * - u_proportion (float): Blend point between colors, 0.5 = equal distribution (0 to 1) + * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1) + * - u_noise (float): Strength of noise distortion, no effect with noiseFrequency = 0 (0 to 1) + * - u_noiseFrequency (float): Noise frequency, no effect with noise = 0 (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const swirlFragmentShader: string; +export interface SwirlUniforms extends ShaderSizingUniforms { + u_colorBack: [number, number, number, number]; + u_colors: vec4[]; + u_colorsCount: number; + u_bandCount: number; + u_twist: number; + u_center: number; + u_proportion: number; + u_softness: number; + u_noiseFrequency: number; + u_noise: number; +} +export interface SwirlParams extends ShaderSizingParams, ShaderMotionParams { + colorBack?: string; + colors?: string[]; + bandCount?: number; + twist?: number; + center?: number; + proportion?: number; + softness?: number; + noiseFrequency?: number; + noise?: number; +} diff --git a/public/vendor/paper-shaders/shaders/swirl.js b/public/vendor/paper-shaders/shaders/swirl.js new file mode 100644 index 0000000..ed7d21e --- /dev/null +++ b/public/vendor/paper-shaders/shaders/swirl.js @@ -0,0 +1,102 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { simplexNoise, declarePI, rotation2, colorBandingFix } from "../shader-utils.js"; +const swirlMeta = { + maxColorCount: 10 +}; +const swirlFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform vec4 u_colorBack; +uniform vec4 u_colors[${swirlMeta.maxColorCount}]; +uniform float u_colorsCount; +uniform float u_bandCount; +uniform float u_twist; +uniform float u_center; +uniform float u_proportion; +uniform float u_softness; +uniform float u_noise; +uniform float u_noiseFrequency; + +in vec2 v_objectUV; + +out vec4 fragColor; + +${declarePI} +${simplexNoise} +${rotation2} + +void main() { + vec2 shape_uv = v_objectUV; + + float l = length(shape_uv); + l = max(1e-4, l); + + float t = u_time; + + float angle = ceil(u_bandCount) * atan(shape_uv.y, shape_uv.x) + t; + float angle_norm = angle / TWO_PI; + + float twist = 3. * clamp(u_twist, 0., 1.); + float offset = pow(l, -twist) + angle_norm; + + float shape = fract(offset); + shape = 1. - abs(2. * shape - 1.); + shape += u_noise * snoise(15. * pow(u_noiseFrequency, 2.) * shape_uv); + + float mid = smoothstep(.2, .2 + .8 * u_center, pow(l, twist)); + shape = mix(0., shape, mid); + + float proportion = clamp(u_proportion, 0., 1.); + float exponent = mix(.25, 1., proportion * 2.); + exponent = mix(exponent, 10., max(0., proportion * 2. - 1.)); + shape = pow(shape, exponent); + + float mixer = shape * u_colorsCount; + vec4 gradient = u_colors[0]; + gradient.rgb *= gradient.a; + + float outerShape = 0.; + for (int i = 1; i < ${swirlMeta.maxColorCount + 1}; i++) { + if (i > int(u_colorsCount)) break; + + float m = clamp(mixer - float(i - 1), 0., 1.); + float aa = fwidth(m); + m = smoothstep(.5 - .5 * u_softness - aa, .5 + .5 * u_softness + aa, m); + + if (i == 1) { + outerShape = m; + } + + vec4 c = u_colors[i - 1]; + c.rgb *= c.a; + gradient = mix(gradient, c, m); + } + + float midAA = .1 * fwidth(pow(l, -twist)); + float outerMid = smoothstep(.2, .2 + midAA, pow(l, twist)); + outerShape = mix(0., outerShape, outerMid); + + vec3 color = gradient.rgb * outerShape; + float opacity = gradient.a * outerShape; + + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + color = color + bgColor * (1.0 - opacity); + opacity = opacity + u_colorBack.a * (1.0 - opacity); + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +export { + swirlFragmentShader, + swirlMeta +}; +//# sourceMappingURL=swirl.js.map diff --git a/public/vendor/paper-shaders/shaders/swirl.js.map b/public/vendor/paper-shaders/shaders/swirl.js.map new file mode 100644 index 0000000..7a8b2fa --- /dev/null +++ b/public/vendor/paper-shaders/shaders/swirl.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/swirl.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { simplexNoise, declarePI, rotation2, colorBandingFix } from '../shader-utils.js';\n\nexport const swirlMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Animated bands of color twisting and bending, producing spirals, arcs, and flowing circular patterns.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colors (vec4[]): Up to 10 stripe colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_bandCount (float): Number of color bands, 0 = concentric ripples (0 to 15)\n * - u_twist (float): Vortex power, 0 = straight sectoral shapes (0 to 1)\n * - u_center (float): How far from the center the swirl colors begin to appear (0 to 1)\n * - u_proportion (float): Blend point between colors, 0.5 = equal distribution (0 to 1)\n * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)\n * - u_noise (float): Strength of noise distortion, no effect with noiseFrequency = 0 (0 to 1)\n * - u_noiseFrequency (float): Noise frequency, no effect with noise = 0 (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_objectUV (vec2): Object box UV coordinates with global sizing (scale, rotation, offsets, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const swirlFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colors[${ swirlMeta.maxColorCount }];\nuniform float u_colorsCount;\nuniform float u_bandCount;\nuniform float u_twist;\nuniform float u_center;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_noise;\nuniform float u_noiseFrequency;\n\nin vec2 v_objectUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ simplexNoise }\n${ rotation2 }\n\nvoid main() {\n vec2 shape_uv = v_objectUV;\n\n float l = length(shape_uv);\n l = max(1e-4, l);\n\n float t = u_time;\n\n float angle = ceil(u_bandCount) * atan(shape_uv.y, shape_uv.x) + t;\n float angle_norm = angle / TWO_PI;\n\n float twist = 3. * clamp(u_twist, 0., 1.);\n float offset = pow(l, -twist) + angle_norm;\n\n float shape = fract(offset);\n shape = 1. - abs(2. * shape - 1.);\n shape += u_noise * snoise(15. * pow(u_noiseFrequency, 2.) * shape_uv);\n\n float mid = smoothstep(.2, .2 + .8 * u_center, pow(l, twist));\n shape = mix(0., shape, mid);\n\n float proportion = clamp(u_proportion, 0., 1.);\n float exponent = mix(.25, 1., proportion * 2.);\n exponent = mix(exponent, 10., max(0., proportion * 2. - 1.));\n shape = pow(shape, exponent);\n\n float mixer = shape * u_colorsCount;\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n\n float outerShape = 0.;\n for (int i = 1; i < ${ swirlMeta.maxColorCount + 1 }; i++) {\n if (i > int(u_colorsCount)) break;\n\n float m = clamp(mixer - float(i - 1), 0., 1.);\n float aa = fwidth(m);\n m = smoothstep(.5 - .5 * u_softness - aa, .5 + .5 * u_softness + aa, m);\n\n if (i == 1) {\n outerShape = m;\n }\n\n vec4 c = u_colors[i - 1];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n float midAA = .1 * fwidth(pow(l, -twist));\n float outerMid = smoothstep(.2, .2 + midAA, pow(l, twist));\n outerShape = mix(0., outerShape, outerMid);\n\n vec3 color = gradient.rgb * outerShape;\n float opacity = gradient.a * outerShape;\n\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n color = color + bgColor * (1.0 - opacity);\n opacity = opacity + u_colorBack.a * (1.0 - opacity);\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface SwirlUniforms extends ShaderSizingUniforms {\n u_colorBack: [number, number, number, number];\n u_colors: vec4[];\n u_colorsCount: number;\n u_bandCount: number;\n u_twist: number;\n u_center: number;\n u_proportion: number;\n u_softness: number;\n u_noiseFrequency: number;\n u_noise: number;\n}\n\nexport interface SwirlParams extends ShaderSizingParams, ShaderMotionParams {\n colorBack?: string;\n colors?: string[];\n bandCount?: number;\n twist?: number;\n center?: number;\n proportion?: number;\n softness?: number;\n noiseFrequency?: number;\n noise?: number;\n}\n"], + "mappings": ";;;;;AAEA,eAAmE;AACnE,SAAS,cAAc,WAAW,WAAW,uBAAuB;AAE7D,MAAM,YAAY;AAAA,EACvB,eAAe;AACjB;AAqCO,MAAM,sBAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMlB,UAAU,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAc9C,SAAU;AAAA,EACV,YAAa;AAAA,EACb,SAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAiCY,UAAU,gBAAgB,CAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2BhD,eAAgB;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/voronoi.d.ts b/public/vendor/paper-shaders/shaders/voronoi.d.ts new file mode 100644 index 0000000..8d6bc6e --- /dev/null +++ b/public/vendor/paper-shaders/shaders/voronoi.d.ts @@ -0,0 +1,65 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const voronoiMeta: { + readonly maxColorCount: 5; +}; +/** + * Anti-aliased animated Voronoi pattern with smooth and customizable edges. + * + * Double-pass Voronoi pattern cell edges. + * Original algorithm: https://www.shadertoy.com/view/ldl3W8 + * + * Note: gaps can't be removed completely due to natural artifacts of Voronoi cells borders + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_scale (float): Overall zoom level, used for anti-aliasing calculations + * - u_colors (vec4[]): Up to 5 base cell colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_stepsPerColor (float): Number of extra colors between base colors, 1 = N colors, 2 = 2×N, etc. (1 to 3) + * - u_colorGlow (vec4): Color tint for radial inner shadow inside cells in RGBA, effective with glow > 0 + * - u_colorGap (vec4): Color used for cell borders/gaps in RGBA + * - u_distortion (float): Strength of noise-driven displacement of cell centers (0 to 0.5) + * - u_gap (float): Width of the border/gap between cells (0 to 0.1) + * - u_glow (float): Strength of the radial inner shadow inside cells (0 to 1) + * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture + * + * Vertex shader outputs (used in fragment shader): + * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const voronoiFragmentShader: string; +export interface VoronoiUniforms extends ShaderSizingUniforms { + u_colors: vec4[]; + u_colorsCount: number; + u_stepsPerColor: number; + u_colorGap: [number, number, number, number]; + u_colorGlow: [number, number, number, number]; + u_distortion: number; + u_gap: number; + u_glow: number; + u_noiseTexture?: HTMLImageElement; +} +export interface VoronoiParams extends ShaderSizingParams, ShaderMotionParams { + colors?: string[]; + stepsPerColor?: number; + colorGap?: string; + colorGlow?: string; + distortion?: number; + gap?: number; + glow?: number; +} diff --git a/public/vendor/paper-shaders/shaders/voronoi.js b/public/vendor/paper-shaders/shaders/voronoi.js new file mode 100644 index 0000000..82dcc9d --- /dev/null +++ b/public/vendor/paper-shaders/shaders/voronoi.js @@ -0,0 +1,139 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, textureRandomizerGB } from "../shader-utils.js"; +const voronoiMeta = { + maxColorCount: 5 +}; +const voronoiFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform float u_scale; + +uniform sampler2D u_noiseTexture; + +uniform vec4 u_colors[${voronoiMeta.maxColorCount}]; +uniform float u_colorsCount; + +uniform float u_stepsPerColor; +uniform vec4 u_colorGlow; +uniform vec4 u_colorGap; +uniform float u_distortion; +uniform float u_gap; +uniform float u_glow; + +in vec2 v_patternUV; + +out vec4 fragColor; + +${declarePI} +${textureRandomizerGB} + +vec4 voronoi(vec2 x, float t) { + vec2 ip = floor(x); + vec2 fp = fract(x); + + vec2 mg, mr; + float md = 8.; + float rand = 0.; + + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + vec2 g = vec2(float(i), float(j)); + vec2 o = randomGB(ip + g); + float raw_hash = o.x; + o = .5 + u_distortion * sin(t + TWO_PI * o); + vec2 r = g + o - fp; + float d = dot(r, r); + + if (d < md) { + md = d; + mr = r; + mg = g; + rand = raw_hash; + } + } + } + + md = 8.; + for (int j = -2; j <= 2; j++) { + for (int i = -2; i <= 2; i++) { + vec2 g = mg + vec2(float(i), float(j)); + vec2 o = randomGB(ip + g); + o = .5 + u_distortion * sin(t + TWO_PI * o); + vec2 r = g + o - fp; + if (dot(mr - r, mr - r) > .00001) { + md = min(md, dot(.5 * (mr + r), normalize(r - mr))); + } + } + } + + return vec4(md, mr, rand); +} + +void main() { + vec2 shape_uv = v_patternUV; + shape_uv *= 1.25; + + float t = u_time; + + vec4 voronoiRes = voronoi(shape_uv, t); + + float shape = clamp(voronoiRes.w, 0., 1.); + float mixer = shape * (u_colorsCount - 1.); + mixer = (shape - .5 / u_colorsCount) * u_colorsCount; + float steps = max(1., u_stepsPerColor); + + vec4 gradient = u_colors[0]; + gradient.rgb *= gradient.a; + for (int i = 1; i < ${voronoiMeta.maxColorCount}; i++) { + if (i >= int(u_colorsCount)) break; + float localT = clamp(mixer - float(i - 1), 0.0, 1.0); + localT = round(localT * steps) / steps; + vec4 c = u_colors[i]; + c.rgb *= c.a; + gradient = mix(gradient, c, localT); + } + + if ((mixer < 0.) || (mixer > (u_colorsCount - 1.))) { + float localT = mixer + 1.; + if (mixer > (u_colorsCount - 1.)) { + localT = mixer - (u_colorsCount - 1.); + } + localT = round(localT * steps) / steps; + vec4 cFst = u_colors[0]; + cFst.rgb *= cFst.a; + vec4 cLast = u_colors[int(u_colorsCount - 1.)]; + cLast.rgb *= cLast.a; + gradient = mix(cLast, cFst, localT); + } + + vec3 cellColor = gradient.rgb; + float cellOpacity = gradient.a; + + float glows = length(voronoiRes.yz * u_glow); + glows = pow(glows, 1.5); + + vec3 color = mix(cellColor, u_colorGlow.rgb * u_colorGlow.a, u_colorGlow.a * glows); + float opacity = cellOpacity + u_colorGlow.a * glows; + + float edge = voronoiRes.x; + float smoothEdge = .02 / (2. * u_scale) * (1. + .5 * u_gap); + edge = smoothstep(u_gap - smoothEdge, u_gap + smoothEdge, edge); + + color = mix(u_colorGap.rgb * u_colorGap.a, color, edge); + opacity = mix(u_colorGap.a, opacity, edge); + + fragColor = vec4(color, opacity); +} +`; +export { + voronoiFragmentShader, + voronoiMeta +}; +//# sourceMappingURL=voronoi.js.map diff --git a/public/vendor/paper-shaders/shaders/voronoi.js.map b/public/vendor/paper-shaders/shaders/voronoi.js.map new file mode 100644 index 0000000..8f90cb7 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/voronoi.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/voronoi.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, textureRandomizerGB } from '../shader-utils.js';\n\nexport const voronoiMeta = {\n maxColorCount: 5,\n} as const;\n\n/**\n * Anti-aliased animated Voronoi pattern with smooth and customizable edges.\n *\n * Double-pass Voronoi pattern cell edges.\n * Original algorithm: https://www.shadertoy.com/view/ldl3W8\n *\n * Note: gaps can't be removed completely due to natural artifacts of Voronoi cells borders\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_scale (float): Overall zoom level, used for anti-aliasing calculations\n * - u_colors (vec4[]): Up to 5 base cell colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_stepsPerColor (float): Number of extra colors between base colors, 1 = N colors, 2 = 2\u00D7N, etc. (1 to 3)\n * - u_colorGlow (vec4): Color tint for radial inner shadow inside cells in RGBA, effective with glow > 0\n * - u_colorGap (vec4): Color used for cell borders/gaps in RGBA\n * - u_distortion (float): Strength of noise-driven displacement of cell centers (0 to 0.5)\n * - u_gap (float): Width of the border/gap between cells (0 to 0.1)\n * - u_glow (float): Strength of the radial inner shadow inside cells (0 to 1)\n * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const voronoiFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform float u_scale;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colors[${ voronoiMeta.maxColorCount }];\nuniform float u_colorsCount;\n\nuniform float u_stepsPerColor;\nuniform vec4 u_colorGlow;\nuniform vec4 u_colorGap;\nuniform float u_distortion;\nuniform float u_gap;\nuniform float u_glow;\n\nin vec2 v_patternUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ textureRandomizerGB }\n\nvec4 voronoi(vec2 x, float t) {\n vec2 ip = floor(x);\n vec2 fp = fract(x);\n\n vec2 mg, mr;\n float md = 8.;\n float rand = 0.;\n\n for (int j = -1; j <= 1; j++) {\n for (int i = -1; i <= 1; i++) {\n vec2 g = vec2(float(i), float(j));\n vec2 o = randomGB(ip + g);\n float raw_hash = o.x;\n o = .5 + u_distortion * sin(t + TWO_PI * o);\n vec2 r = g + o - fp;\n float d = dot(r, r);\n\n if (d < md) {\n md = d;\n mr = r;\n mg = g;\n rand = raw_hash;\n }\n }\n }\n\n md = 8.;\n for (int j = -2; j <= 2; j++) {\n for (int i = -2; i <= 2; i++) {\n vec2 g = mg + vec2(float(i), float(j));\n vec2 o = randomGB(ip + g);\n o = .5 + u_distortion * sin(t + TWO_PI * o);\n vec2 r = g + o - fp;\n if (dot(mr - r, mr - r) > .00001) {\n md = min(md, dot(.5 * (mr + r), normalize(r - mr)));\n }\n }\n }\n\n return vec4(md, mr, rand);\n}\n\nvoid main() {\n vec2 shape_uv = v_patternUV;\n shape_uv *= 1.25;\n\n float t = u_time;\n\n vec4 voronoiRes = voronoi(shape_uv, t);\n\n float shape = clamp(voronoiRes.w, 0., 1.);\n float mixer = shape * (u_colorsCount - 1.);\n mixer = (shape - .5 / u_colorsCount) * u_colorsCount;\n float steps = max(1., u_stepsPerColor);\n\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n for (int i = 1; i < ${ voronoiMeta.maxColorCount }; i++) {\n if (i >= int(u_colorsCount)) break;\n float localT = clamp(mixer - float(i - 1), 0.0, 1.0);\n localT = round(localT * steps) / steps;\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, localT);\n }\n\n if ((mixer < 0.) || (mixer > (u_colorsCount - 1.))) {\n float localT = mixer + 1.;\n if (mixer > (u_colorsCount - 1.)) {\n localT = mixer - (u_colorsCount - 1.);\n }\n localT = round(localT * steps) / steps;\n vec4 cFst = u_colors[0];\n cFst.rgb *= cFst.a;\n vec4 cLast = u_colors[int(u_colorsCount - 1.)];\n cLast.rgb *= cLast.a;\n gradient = mix(cLast, cFst, localT);\n }\n\n vec3 cellColor = gradient.rgb;\n float cellOpacity = gradient.a;\n\n float glows = length(voronoiRes.yz * u_glow);\n glows = pow(glows, 1.5);\n\n vec3 color = mix(cellColor, u_colorGlow.rgb * u_colorGlow.a, u_colorGlow.a * glows);\n float opacity = cellOpacity + u_colorGlow.a * glows;\n\n float edge = voronoiRes.x;\n float smoothEdge = .02 / (2. * u_scale) * (1. + .5 * u_gap);\n edge = smoothstep(u_gap - smoothEdge, u_gap + smoothEdge, edge);\n\n color = mix(u_colorGap.rgb * u_colorGap.a, color, edge);\n opacity = mix(u_colorGap.a, opacity, edge);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface VoronoiUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_stepsPerColor: number;\n u_colorGap: [number, number, number, number];\n u_colorGlow: [number, number, number, number];\n u_distortion: number;\n u_gap: number;\n u_glow: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface VoronoiParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n stepsPerColor?: number;\n colorGap?: string;\n colorGlow?: string;\n distortion?: number;\n gap?: number;\n glow?: number;\n}\n"], + "mappings": ";;;;;AAEA,eAAmE;AACnE,SAAS,WAAW,2BAA2B;AAExC,MAAM,cAAc;AAAA,EACzB,eAAe;AACjB;AA0CO,MAAM,wBAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBASpB,YAAY,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAchD,SAAU;AAAA,EACV,mBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBA2DE,YAAY,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/warp.d.ts b/public/vendor/paper-shaders/shaders/warp.d.ts new file mode 100644 index 0000000..e70d457 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/warp.d.ts @@ -0,0 +1,72 @@ +import type { vec4 } from '../types.js'; +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +export declare const warpMeta: { + readonly maxColorCount: 10; +}; +/** + * Animated color fields warped by noise and swirls, applied over base patterns + * (checks, stripes, or split edge). Blends up to 10 colors with adjustable distribution, + * softness, distortion, and swirl. Great for fluid, smoky, or marbled effects. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_scale (float): Overall zoom level, used for anti-aliasing calculations + * - u_colors (vec4[]): Up to 10 gradient colors in RGBA + * - u_colorsCount (float): Number of active colors + * - u_proportion (float): Blend point between colors, 0.5 = equal distribution (0 to 1) + * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1) + * - u_shape (float): Base pattern type (0 = checks, 1 = stripes, 2 = edge) + * - u_shapeScale (float): Zoom level of the base pattern (0 to 1) + * - u_distortion (float): Strength of noise-based distortion (0 to 1) + * - u_swirl (float): Strength of the swirl distortion (0 to 1) + * - u_swirlIterations (float): Number of layered swirl passes, effective with swirl > 0 (0 to 20) + * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture + * + * Vertex shader outputs (used in fragment shader): + * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const warpFragmentShader: string; +export interface WarpUniforms extends ShaderSizingUniforms { + u_colors: vec4[]; + u_colorsCount: number; + u_proportion: number; + u_softness: number; + u_shape: (typeof WarpPatterns)[WarpPattern]; + u_shapeScale: number; + u_distortion: number; + u_swirl: number; + u_swirlIterations: number; + u_noiseTexture?: HTMLImageElement; +} +export interface WarpParams extends ShaderSizingParams, ShaderMotionParams { + colors?: string[]; + rotation?: number; + proportion?: number; + softness?: number; + shape?: WarpPattern; + shapeScale?: number; + distortion?: number; + swirl?: number; + swirlIterations?: number; +} +export declare const WarpPatterns: { + readonly checks: 0; + readonly stripes: 1; + readonly edge: 2; +}; +export type WarpPattern = keyof typeof WarpPatterns; diff --git a/public/vendor/paper-shaders/shaders/warp.js b/public/vendor/paper-shaders/shaders/warp.js new file mode 100644 index 0000000..2da4d50 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/warp.js @@ -0,0 +1,132 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, rotation2, colorBandingFix } from "../shader-utils.js"; +const warpMeta = { + maxColorCount: 10 +}; +const warpFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; +uniform float u_scale; + +uniform sampler2D u_noiseTexture; + +uniform vec4 u_colors[${warpMeta.maxColorCount}]; +uniform float u_colorsCount; +uniform float u_proportion; +uniform float u_softness; +uniform float u_shape; +uniform float u_shapeScale; +uniform float u_distortion; +uniform float u_swirl; +uniform float u_swirlIterations; + +in vec2 v_patternUV; + +out vec4 fragColor; + +${declarePI} +${rotation2} +float randomG(vec2 p) { + vec2 uv = floor(p) / 100. + .5; + return texture(u_noiseTexture, fract(uv)).g; +} +float valueNoise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + float a = randomG(i); + float b = randomG(i + vec2(1.0, 0.0)); + float c = randomG(i + vec2(0.0, 1.0)); + float d = randomG(i + vec2(1.0, 1.0)); + vec2 u = f * f * (3.0 - 2.0 * f); + float x1 = mix(a, b, u.x); + float x2 = mix(c, d, u.x); + return mix(x1, x2, u.y); +} + + +void main() { + vec2 uv = v_patternUV; + uv *= .5; + + const float firstFrameOffset = 118.; + float t = 0.0625 * (u_time + firstFrameOffset); + + float n1 = valueNoise(uv * 1. + t); + float n2 = valueNoise(uv * 2. - t); + float angle = n1 * TWO_PI; + uv.x += 4. * u_distortion * n2 * cos(angle); + uv.y += 4. * u_distortion * n2 * sin(angle); + + float swirl = u_swirl; + for (int i = 1; i <= 20; i++) { + if (i >= int(u_swirlIterations)) break; + float iFloat = float(i); + // swirl *= (1. - smoothstep(.0, .25, length(fwidth(uv)))); + uv.x += swirl / iFloat * cos(t + iFloat * 1.5 * uv.y); + uv.y += swirl / iFloat * cos(t + iFloat * 1. * uv.x); + } + + float proportion = clamp(u_proportion, 0., 1.); + + float shape = 0.; + if (u_shape < .5) { + vec2 checksShape_uv = uv * (.5 + 3.5 * u_shapeScale); + shape = .5 + .5 * sin(checksShape_uv.x) * cos(checksShape_uv.y); + shape += .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5); + } else if (u_shape < 1.5) { + vec2 stripesShape_uv = uv * (2. * u_shapeScale); + float f = fract(stripesShape_uv.y); + shape = smoothstep(.0, .55, f) * (1.0 - smoothstep(.45, 1., f)); + shape += .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5); + } else { + float shapeScaling = 5. * (1. - u_shapeScale); + float e0 = 0.45 - shapeScaling; + float e1 = 0.55 + shapeScaling; + shape = smoothstep(min(e0, e1), max(e0, e1), 1.0 - uv.y + 0.3 * (proportion - 0.5)); + } + + float mixer = shape * (u_colorsCount - 1.); + vec4 gradient = u_colors[0]; + gradient.rgb *= gradient.a; + float aa = fwidth(shape); + for (int i = 1; i < ${warpMeta.maxColorCount}; i++) { + if (i >= int(u_colorsCount)) break; + float m = clamp(mixer - float(i - 1), 0.0, 1.0); + + float localMixerStart = floor(m); + float softness = .5 * u_softness + fwidth(m); + float smoothed = smoothstep(max(0., .5 - softness - aa), min(1., .5 + softness + aa), m - localMixerStart); + float stepped = localMixerStart + smoothed; + + m = mix(stepped, m, u_softness); + + vec4 c = u_colors[i]; + c.rgb *= c.a; + gradient = mix(gradient, c, m); + } + + vec3 color = gradient.rgb; + float opacity = gradient.a; + + ${colorBandingFix} + + fragColor = vec4(color, opacity); +} +`; +const WarpPatterns = { + checks: 0, + stripes: 1, + edge: 2 +}; +export { + WarpPatterns, + warpFragmentShader, + warpMeta +}; +//# sourceMappingURL=warp.js.map diff --git a/public/vendor/paper-shaders/shaders/warp.js.map b/public/vendor/paper-shaders/shaders/warp.js.map new file mode 100644 index 0000000..e2acd7a --- /dev/null +++ b/public/vendor/paper-shaders/shaders/warp.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/warp.ts"], + "sourcesContent": ["import type { vec4 } from '../types.js';\nimport type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, colorBandingFix } from '../shader-utils.js';\n\nexport const warpMeta = {\n maxColorCount: 10,\n} as const;\n\n/**\n * Animated color fields warped by noise and swirls, applied over base patterns\n * (checks, stripes, or split edge). Blends up to 10 colors with adjustable distribution,\n * softness, distortion, and swirl. Great for fluid, smoky, or marbled effects.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_scale (float): Overall zoom level, used for anti-aliasing calculations\n * - u_colors (vec4[]): Up to 10 gradient colors in RGBA\n * - u_colorsCount (float): Number of active colors\n * - u_proportion (float): Blend point between colors, 0.5 = equal distribution (0 to 1)\n * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)\n * - u_shape (float): Base pattern type (0 = checks, 1 = stripes, 2 = edge)\n * - u_shapeScale (float): Zoom level of the base pattern (0 to 1)\n * - u_distortion (float): Strength of noise-based distortion (0 to 1)\n * - u_swirl (float): Strength of the swirl distortion (0 to 1)\n * - u_swirlIterations (float): Number of layered swirl passes, effective with swirl > 0 (0 to 20)\n * - u_noiseTexture (sampler2D): Pre-computed randomizer source texture\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const warpFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\nuniform float u_scale;\n\nuniform sampler2D u_noiseTexture;\n\nuniform vec4 u_colors[${ warpMeta.maxColorCount }];\nuniform float u_colorsCount;\nuniform float u_proportion;\nuniform float u_softness;\nuniform float u_shape;\nuniform float u_shapeScale;\nuniform float u_distortion;\nuniform float u_swirl;\nuniform float u_swirlIterations;\n\nin vec2 v_patternUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\nfloat randomG(vec2 p) {\n vec2 uv = floor(p) / 100. + .5;\n return texture(u_noiseTexture, fract(uv)).g;\n}\nfloat valueNoise(vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = randomG(i);\n float b = randomG(i + vec2(1.0, 0.0));\n float c = randomG(i + vec2(0.0, 1.0));\n float d = randomG(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n float x1 = mix(a, b, u.x);\n float x2 = mix(c, d, u.x);\n return mix(x1, x2, u.y);\n}\n\n\nvoid main() {\n vec2 uv = v_patternUV;\n uv *= .5;\n\n const float firstFrameOffset = 118.;\n float t = 0.0625 * (u_time + firstFrameOffset);\n\n float n1 = valueNoise(uv * 1. + t);\n float n2 = valueNoise(uv * 2. - t);\n float angle = n1 * TWO_PI;\n uv.x += 4. * u_distortion * n2 * cos(angle);\n uv.y += 4. * u_distortion * n2 * sin(angle);\n\n float swirl = u_swirl;\n for (int i = 1; i <= 20; i++) {\n if (i >= int(u_swirlIterations)) break;\n float iFloat = float(i);\n // swirl *= (1. - smoothstep(.0, .25, length(fwidth(uv))));\n uv.x += swirl / iFloat * cos(t + iFloat * 1.5 * uv.y);\n uv.y += swirl / iFloat * cos(t + iFloat * 1. * uv.x);\n }\n\n float proportion = clamp(u_proportion, 0., 1.);\n\n float shape = 0.;\n if (u_shape < .5) {\n vec2 checksShape_uv = uv * (.5 + 3.5 * u_shapeScale);\n shape = .5 + .5 * sin(checksShape_uv.x) * cos(checksShape_uv.y);\n shape += .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);\n } else if (u_shape < 1.5) {\n vec2 stripesShape_uv = uv * (2. * u_shapeScale);\n float f = fract(stripesShape_uv.y);\n shape = smoothstep(.0, .55, f) * (1.0 - smoothstep(.45, 1., f));\n shape += .48 * sign(proportion - .5) * pow(abs(proportion - .5), .5);\n } else {\n float shapeScaling = 5. * (1. - u_shapeScale);\n float e0 = 0.45 - shapeScaling;\n float e1 = 0.55 + shapeScaling;\n shape = smoothstep(min(e0, e1), max(e0, e1), 1.0 - uv.y + 0.3 * (proportion - 0.5));\n }\n\n float mixer = shape * (u_colorsCount - 1.);\n vec4 gradient = u_colors[0];\n gradient.rgb *= gradient.a;\n float aa = fwidth(shape);\n for (int i = 1; i < ${ warpMeta.maxColorCount }; i++) {\n if (i >= int(u_colorsCount)) break;\n float m = clamp(mixer - float(i - 1), 0.0, 1.0);\n\n float localMixerStart = floor(m);\n float softness = .5 * u_softness + fwidth(m);\n float smoothed = smoothstep(max(0., .5 - softness - aa), min(1., .5 + softness + aa), m - localMixerStart);\n float stepped = localMixerStart + smoothed;\n\n m = mix(stepped, m, u_softness);\n\n vec4 c = u_colors[i];\n c.rgb *= c.a;\n gradient = mix(gradient, c, m);\n }\n\n vec3 color = gradient.rgb;\n float opacity = gradient.a;\n\n ${ colorBandingFix }\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface WarpUniforms extends ShaderSizingUniforms {\n u_colors: vec4[];\n u_colorsCount: number;\n u_proportion: number;\n u_softness: number;\n u_shape: (typeof WarpPatterns)[WarpPattern];\n u_shapeScale: number;\n u_distortion: number;\n u_swirl: number;\n u_swirlIterations: number;\n u_noiseTexture?: HTMLImageElement;\n}\n\nexport interface WarpParams extends ShaderSizingParams, ShaderMotionParams {\n colors?: string[];\n rotation?: number;\n proportion?: number;\n softness?: number;\n shape?: WarpPattern;\n shapeScale?: number;\n distortion?: number;\n swirl?: number;\n swirlIterations?: number;\n}\n\nexport const WarpPatterns = {\n checks: 0,\n stripes: 1,\n edge: 2,\n} as const;\n\nexport type WarpPattern = keyof typeof WarpPatterns;\n"], + "mappings": ";;;;;AAEA,eAAmE;AACnE,SAAS,WAAW,WAAW,uBAAuB;AAE/C,MAAM,WAAW;AAAA,EACtB,eAAe;AACjB;AAwCO,MAAM,qBAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAQjB,SAAS,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAc7C,SAAU;AAAA,EACV,SAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAgEY,SAAS,aAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAmB3C,eAAgB;AAAA;AAAA;AAAA;AAAA;AA+Bd,MAAM,eAAe;AAAA,EAC1B,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AACR;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/water.d.ts b/public/vendor/paper-shaders/shaders/water.d.ts new file mode 100644 index 0000000..11bff99 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/water.d.ts @@ -0,0 +1,59 @@ +import type { ShaderMotionParams } from '../shader-mount.js'; +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * Water-like surface distortion with natural caustic realism. Works as an image filter or standalone animated texture. + * + * Fragment shader uniforms: + * - u_time (float): Animation time + * - u_image (sampler2D): Optional source image texture + * - u_imageAspectRatio (float): Aspect ratio of the source image + * - u_colorBack (vec4): Background color in RGBA + * - u_colorHighlight (vec4): Highlight color in RGBA + * - u_highlights (float): Coloring added over image/background following caustic shape (0 to 1) + * - u_layering (float): Power of 2nd layer of caustic distortion (0 to 1) + * - u_edges (float): Caustic distortion power on the image edges (0 to 1) + * - u_waves (float): Additional distortion based on simplex noise, independent from caustic (0 to 1) + * - u_caustic (float): Power of caustic distortion (0 to 1) + * - u_size (float): Pattern scale relative to the image (0.01 to 7) + * + * Vertex shader outputs (used in fragment shader): + * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * - u_imageAspectRatio (float): Aspect ratio of the source image + * + */ +export declare const waterFragmentShader: string; +export interface WaterUniforms extends ShaderSizingUniforms { + u_image: HTMLImageElement | string | undefined; + u_colorBack: [number, number, number, number]; + u_colorHighlight: [number, number, number, number]; + u_highlights: number; + u_layering: number; + u_edges: number; + u_caustic: number; + u_waves: number; + u_size: number; +} +export interface WaterParams extends ShaderSizingParams, ShaderMotionParams { + image?: HTMLImageElement | string; + colorBack?: string; + colorHighlight?: string; + highlights?: number; + layering?: number; + edges?: number; + caustic?: number; + waves?: number; + size?: number; +} diff --git a/public/vendor/paper-shaders/shaders/water.js b/public/vendor/paper-shaders/shaders/water.js new file mode 100644 index 0000000..6c0ff86 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/water.js @@ -0,0 +1,120 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI, rotation2, simplexNoise } from "../shader-utils.js"; +const waterFragmentShader = `#version 300 es +precision mediump float; + +uniform float u_time; + +uniform vec4 u_colorBack; +uniform vec4 u_colorHighlight; + +uniform sampler2D u_image; +uniform float u_imageAspectRatio; + +uniform float u_size; +uniform float u_highlights; +uniform float u_layering; +uniform float u_edges; +uniform float u_caustic; +uniform float u_waves; + +in vec2 v_imageUV; + +out vec4 fragColor; + +${declarePI} +${rotation2} +${simplexNoise} + +float getUvFrame(vec2 uv) { + float aax = 2. * fwidth(uv.x); + float aay = 2. * fwidth(uv.y); + + float left = smoothstep(0., aax, uv.x); + float right = 1.0 - smoothstep(1. - aax, 1., uv.x); + float bottom = smoothstep(0., aay, uv.y); + float top = 1.0 - smoothstep(1. - aay, 1., uv.y); + + return left * right * bottom * top; +} + +mat2 rotate2D(float r) { + return mat2(cos(r), sin(r), -sin(r), cos(r)); +} + +float getCausticNoise(vec2 uv, float t, float scale) { + vec2 n = vec2(.1); + vec2 N = vec2(.1); + mat2 m = rotate2D(.5); + for (int j = 0; j < 6; j++) { + uv *= m; + n *= m; + vec2 q = uv * scale + float(j) + n + (.5 + .5 * float(j)) * (mod(float(j), 2.) - 1.) * t; + n += sin(q); + N += cos(q) / scale; + scale *= 1.1; + } + return (N.x + N.y + 1.); +} + +void main() { + vec2 imageUV = v_imageUV; + vec2 patternUV = v_imageUV - .5; + patternUV = (patternUV * vec2(u_imageAspectRatio, 1.)); + patternUV /= (.01 + .09 * u_size); + + float t = u_time; + + float wavesNoise = snoise((.3 + .1 * sin(t)) * .1 * patternUV + vec2(0., .4 * t)); + + float causticNoise = getCausticNoise(patternUV + u_waves * vec2(1., -1.) * wavesNoise, 2. * t, 1.5); + + causticNoise += u_layering * getCausticNoise(patternUV + 2. * u_waves * vec2(1., -1.) * wavesNoise, 1.5 * t, 2.); + causticNoise = causticNoise * causticNoise; + + float edgesDistortion = smoothstep(0., .1, imageUV.x); + edgesDistortion *= smoothstep(0., .1, imageUV.y); + edgesDistortion *= (smoothstep(1., 1.1, imageUV.x) + (1.0 - smoothstep(.8, .95, imageUV.x))); + edgesDistortion *= (1.0 - smoothstep(.9, 1., imageUV.y)); + edgesDistortion = mix(edgesDistortion, 1., u_edges); + + float causticNoiseDistortion = .02 * causticNoise * edgesDistortion; + + float wavesDistortion = .1 * u_waves * wavesNoise; + + imageUV += vec2(wavesDistortion, -wavesDistortion); + imageUV += (u_caustic * causticNoiseDistortion); + + float frame = getUvFrame(imageUV); + + vec4 image = texture(u_image, imageUV); + vec4 backColor = u_colorBack; + backColor.rgb *= backColor.a; + + vec3 color = mix(backColor.rgb, image.rgb, image.a * frame); + float opacity = backColor.a + image.a * frame; + + causticNoise = max(-.2, causticNoise); + + float hightlight = .025 * u_highlights * causticNoise; + hightlight *= u_colorHighlight.a; + color = mix(color, u_colorHighlight.rgb, .05 * u_highlights * causticNoise); + opacity += hightlight; + + color += hightlight * (.5 + .5 * wavesNoise); + opacity += hightlight * (.5 + .5 * wavesNoise); + + opacity = clamp(opacity, 0., 1.); + + fragColor = vec4(color, opacity); +} +`; +export { + waterFragmentShader +}; +//# sourceMappingURL=water.js.map diff --git a/public/vendor/paper-shaders/shaders/water.js.map b/public/vendor/paper-shaders/shaders/water.js.map new file mode 100644 index 0000000..35ced9b --- /dev/null +++ b/public/vendor/paper-shaders/shaders/water.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/water.ts"], + "sourcesContent": ["import type { ShaderMotionParams } from '../shader-mount.js';\nimport { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI, rotation2, simplexNoise } from '../shader-utils.js';\n\n/**\n * Water-like surface distortion with natural caustic realism. Works as an image filter or standalone animated texture.\n *\n * Fragment shader uniforms:\n * - u_time (float): Animation time\n * - u_image (sampler2D): Optional source image texture\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n * - u_colorBack (vec4): Background color in RGBA\n * - u_colorHighlight (vec4): Highlight color in RGBA\n * - u_highlights (float): Coloring added over image/background following caustic shape (0 to 1)\n * - u_layering (float): Power of 2nd layer of caustic distortion (0 to 1)\n * - u_edges (float): Caustic distortion power on the image edges (0 to 1)\n * - u_waves (float): Additional distortion based on simplex noise, independent from caustic (0 to 1)\n * - u_caustic (float): Power of caustic distortion (0 to 1)\n * - u_size (float): Pattern scale relative to the image (0.01 to 7)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_imageUV (vec2): UV coordinates for sampling the source image, with fit, scale, rotation, and offset applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n * - u_imageAspectRatio (float): Aspect ratio of the source image\n *\n */\n\n// language=GLSL\nexport const waterFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform float u_time;\n\nuniform vec4 u_colorBack;\nuniform vec4 u_colorHighlight;\n\nuniform sampler2D u_image;\nuniform float u_imageAspectRatio;\n\nuniform float u_size;\nuniform float u_highlights;\nuniform float u_layering;\nuniform float u_edges;\nuniform float u_caustic;\nuniform float u_waves;\n\nin vec2 v_imageUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n${ rotation2 }\n${ simplexNoise }\n\nfloat getUvFrame(vec2 uv) {\n float aax = 2. * fwidth(uv.x);\n float aay = 2. * fwidth(uv.y);\n\n float left = smoothstep(0., aax, uv.x);\n float right = 1.0 - smoothstep(1. - aax, 1., uv.x);\n float bottom = smoothstep(0., aay, uv.y);\n float top = 1.0 - smoothstep(1. - aay, 1., uv.y);\n\n return left * right * bottom * top;\n}\n\nmat2 rotate2D(float r) {\n return mat2(cos(r), sin(r), -sin(r), cos(r));\n}\n\nfloat getCausticNoise(vec2 uv, float t, float scale) {\n vec2 n = vec2(.1);\n vec2 N = vec2(.1);\n mat2 m = rotate2D(.5);\n for (int j = 0; j < 6; j++) {\n uv *= m;\n n *= m;\n vec2 q = uv * scale + float(j) + n + (.5 + .5 * float(j)) * (mod(float(j), 2.) - 1.) * t;\n n += sin(q);\n N += cos(q) / scale;\n scale *= 1.1;\n }\n return (N.x + N.y + 1.);\n}\n\nvoid main() {\n vec2 imageUV = v_imageUV;\n vec2 patternUV = v_imageUV - .5;\n patternUV = (patternUV * vec2(u_imageAspectRatio, 1.));\n patternUV /= (.01 + .09 * u_size);\n\n float t = u_time;\n\n float wavesNoise = snoise((.3 + .1 * sin(t)) * .1 * patternUV + vec2(0., .4 * t));\n\n float causticNoise = getCausticNoise(patternUV + u_waves * vec2(1., -1.) * wavesNoise, 2. * t, 1.5);\n\n causticNoise += u_layering * getCausticNoise(patternUV + 2. * u_waves * vec2(1., -1.) * wavesNoise, 1.5 * t, 2.);\n causticNoise = causticNoise * causticNoise;\n\n float edgesDistortion = smoothstep(0., .1, imageUV.x);\n edgesDistortion *= smoothstep(0., .1, imageUV.y);\n edgesDistortion *= (smoothstep(1., 1.1, imageUV.x) + (1.0 - smoothstep(.8, .95, imageUV.x)));\n edgesDistortion *= (1.0 - smoothstep(.9, 1., imageUV.y));\n edgesDistortion = mix(edgesDistortion, 1., u_edges);\n\n float causticNoiseDistortion = .02 * causticNoise * edgesDistortion;\n\n float wavesDistortion = .1 * u_waves * wavesNoise;\n\n imageUV += vec2(wavesDistortion, -wavesDistortion);\n imageUV += (u_caustic * causticNoiseDistortion);\n\n float frame = getUvFrame(imageUV);\n\n vec4 image = texture(u_image, imageUV);\n vec4 backColor = u_colorBack;\n backColor.rgb *= backColor.a;\n\n vec3 color = mix(backColor.rgb, image.rgb, image.a * frame);\n float opacity = backColor.a + image.a * frame;\n\n causticNoise = max(-.2, causticNoise);\n\n float hightlight = .025 * u_highlights * causticNoise;\n hightlight *= u_colorHighlight.a;\n color = mix(color, u_colorHighlight.rgb, .05 * u_highlights * causticNoise);\n opacity += hightlight;\n\n color += hightlight * (.5 + .5 * wavesNoise);\n opacity += hightlight * (.5 + .5 * wavesNoise);\n\n opacity = clamp(opacity, 0., 1.);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface WaterUniforms extends ShaderSizingUniforms {\n u_image: HTMLImageElement | string | undefined;\n u_colorBack: [number, number, number, number];\n u_colorHighlight: [number, number, number, number];\n u_highlights: number;\n u_layering: number;\n u_edges: number;\n u_caustic: number;\n u_waves: number;\n u_size: number;\n}\n\nexport interface WaterParams extends ShaderSizingParams, ShaderMotionParams {\n image?: HTMLImageElement | string;\n colorBack?: string;\n colorHighlight?: string;\n highlights?: number;\n layering?: number;\n edges?: number;\n caustic?: number;\n waves?: number;\n size?: number;\n}\n"], + "mappings": ";;;;;AACA,eAAmE;AACnE,SAAS,WAAW,WAAW,oBAAoB;AAsC5C,MAAM,sBAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBxC,SAAU;AAAA,EACV,SAAU;AAAA,EACV,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/shaders/waves.d.ts b/public/vendor/paper-shaders/shaders/waves.d.ts new file mode 100644 index 0000000..e5265fd --- /dev/null +++ b/public/vendor/paper-shaders/shaders/waves.d.ts @@ -0,0 +1,53 @@ +import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js'; +/** + * Static line pattern configurable into textures ranging from sharp zigzags to smooth flowing waves. + * + * Fragment shader uniforms: + * - u_colorFront (vec4): Foreground color in RGBA + * - u_colorBack (vec4): Background color in RGBA + * - u_shape (float): Line shape, 0 = zigzag, 1 = sine, 2-3 = irregular waves, fractional values morph between shapes (0 to 3) + * - u_amplitude (float): Wave amplitude (0 to 1) + * - u_frequency (float): Wave frequency (0 to 2) + * - u_spacing (float): Space between every two wavy lines (0 to 2) + * - u_proportion (float): Blend point between front and back colors, 0.5 = equal distribution (0 to 1) + * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1) + * + * Vertex shader outputs (used in fragment shader): + * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied + * + * Vertex shader uniforms: + * - u_resolution (vec2): Canvas resolution in pixels + * - u_pixelRatio (float): Device pixel ratio + * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1) + * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1) + * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas + * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas + * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover) + * - u_scale (float): Overall zoom level of the graphics (0.01 to 4) + * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360) + * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1) + * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1) + * + */ +export declare const wavesFragmentShader: string; +export interface WavesUniforms extends ShaderSizingUniforms { + u_colorFront: [number, number, number, number]; + u_colorBack: [number, number, number, number]; + u_shape: number; + u_frequency: number; + u_amplitude: number; + u_spacing: number; + u_proportion: number; + u_softness: number; +} +export interface WavesParams extends ShaderSizingParams { + colorFront?: string; + colorBack?: string; + rotation?: number; + shape?: number; + frequency?: number; + amplitude?: number; + spacing?: number; + proportion?: number; + softness?: number; +} diff --git a/public/vendor/paper-shaders/shaders/waves.js b/public/vendor/paper-shaders/shaders/waves.js new file mode 100644 index 0000000..887b7f7 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/waves.js @@ -0,0 +1,66 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import {} from "../shader-sizing.js"; +import { declarePI } from "../shader-utils.js"; +const wavesFragmentShader = `#version 300 es +precision mediump float; + +uniform vec4 u_colorFront; +uniform vec4 u_colorBack; +uniform float u_shape; +uniform float u_frequency; +uniform float u_amplitude; +uniform float u_spacing; +uniform float u_proportion; +uniform float u_softness; + +in vec2 v_patternUV; + +out vec4 fragColor; + +${declarePI} + +void main() { + vec2 shape_uv = v_patternUV; + shape_uv *= 4.; + + float wave = .5 * cos(shape_uv.x * u_frequency * TWO_PI); + float zigzag = 2. * abs(fract(shape_uv.x * u_frequency) - .5); + float irregular = sin(shape_uv.x * .25 * u_frequency * TWO_PI) * cos(shape_uv.x * u_frequency * TWO_PI); + float irregular2 = .75 * (sin(shape_uv.x * u_frequency * TWO_PI) + .5 * cos(shape_uv.x * .5 * u_frequency * TWO_PI)); + + float offset = mix(zigzag, wave, smoothstep(0., 1., u_shape)); + offset = mix(offset, irregular, smoothstep(1., 2., u_shape)); + offset = mix(offset, irregular2, smoothstep(2., 3., u_shape)); + offset *= 2. * u_amplitude; + + float spacing = (.001 + u_spacing); + float shape = .5 + .5 * sin((shape_uv.y + offset) * PI / spacing); + + float aa = .0001 + fwidth(shape); + float dc = 1. - clamp(u_proportion, 0., 1.); + float e0 = dc - u_softness - aa; + float e1 = dc + u_softness + aa; + float res = smoothstep(min(e0, e1), max(e0, e1), shape); + + vec3 fgColor = u_colorFront.rgb * u_colorFront.a; + float fgOpacity = u_colorFront.a; + vec3 bgColor = u_colorBack.rgb * u_colorBack.a; + float bgOpacity = u_colorBack.a; + + vec3 color = fgColor * res; + float opacity = fgOpacity * res; + + color += bgColor * (1. - opacity); + opacity += bgOpacity * (1. - opacity); + + fragColor = vec4(color, opacity); +} +`; +export { + wavesFragmentShader +}; +//# sourceMappingURL=waves.js.map diff --git a/public/vendor/paper-shaders/shaders/waves.js.map b/public/vendor/paper-shaders/shaders/waves.js.map new file mode 100644 index 0000000..0d8ad65 --- /dev/null +++ b/public/vendor/paper-shaders/shaders/waves.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../src/shaders/waves.ts"], + "sourcesContent": ["import { type ShaderSizingParams, type ShaderSizingUniforms } from '../shader-sizing.js';\nimport { declarePI } from '../shader-utils.js';\n\n/**\n * Static line pattern configurable into textures ranging from sharp zigzags to smooth flowing waves.\n *\n * Fragment shader uniforms:\n * - u_colorFront (vec4): Foreground color in RGBA\n * - u_colorBack (vec4): Background color in RGBA\n * - u_shape (float): Line shape, 0 = zigzag, 1 = sine, 2-3 = irregular waves, fractional values morph between shapes (0 to 3)\n * - u_amplitude (float): Wave amplitude (0 to 1)\n * - u_frequency (float): Wave frequency (0 to 2)\n * - u_spacing (float): Space between every two wavy lines (0 to 2)\n * - u_proportion (float): Blend point between front and back colors, 0.5 = equal distribution (0 to 1)\n * - u_softness (float): Color transition sharpness, 0 = hard edge, 1 = smooth gradient (0 to 1)\n *\n * Vertex shader outputs (used in fragment shader):\n * - v_patternUV (vec2): UV coordinates for pattern with global sizing (rotation, scale, offset, etc) applied\n *\n * Vertex shader uniforms:\n * - u_resolution (vec2): Canvas resolution in pixels\n * - u_pixelRatio (float): Device pixel ratio\n * - u_originX (float): Reference point for positioning world width in the canvas (0 to 1)\n * - u_originY (float): Reference point for positioning world height in the canvas (0 to 1)\n * - u_worldWidth (float): Virtual width of the graphic before it's scaled to fit the canvas\n * - u_worldHeight (float): Virtual height of the graphic before it's scaled to fit the canvas\n * - u_fit (float): How to fit the rendered shader into the canvas dimensions (0 = none, 1 = contain, 2 = cover)\n * - u_scale (float): Overall zoom level of the graphics (0.01 to 4)\n * - u_rotation (float): Overall rotation angle of the graphics in degrees (0 to 360)\n * - u_offsetX (float): Horizontal offset of the graphics center (-1 to 1)\n * - u_offsetY (float): Vertical offset of the graphics center (-1 to 1)\n *\n */\n\n// language=GLSL\nexport const wavesFragmentShader: string = `#version 300 es\nprecision mediump float;\n\nuniform vec4 u_colorFront;\nuniform vec4 u_colorBack;\nuniform float u_shape;\nuniform float u_frequency;\nuniform float u_amplitude;\nuniform float u_spacing;\nuniform float u_proportion;\nuniform float u_softness;\n\nin vec2 v_patternUV;\n\nout vec4 fragColor;\n\n${ declarePI }\n\nvoid main() {\n vec2 shape_uv = v_patternUV;\n shape_uv *= 4.;\n\n float wave = .5 * cos(shape_uv.x * u_frequency * TWO_PI);\n float zigzag = 2. * abs(fract(shape_uv.x * u_frequency) - .5);\n float irregular = sin(shape_uv.x * .25 * u_frequency * TWO_PI) * cos(shape_uv.x * u_frequency * TWO_PI);\n float irregular2 = .75 * (sin(shape_uv.x * u_frequency * TWO_PI) + .5 * cos(shape_uv.x * .5 * u_frequency * TWO_PI));\n\n float offset = mix(zigzag, wave, smoothstep(0., 1., u_shape));\n offset = mix(offset, irregular, smoothstep(1., 2., u_shape));\n offset = mix(offset, irregular2, smoothstep(2., 3., u_shape));\n offset *= 2. * u_amplitude;\n\n float spacing = (.001 + u_spacing);\n float shape = .5 + .5 * sin((shape_uv.y + offset) * PI / spacing);\n\n float aa = .0001 + fwidth(shape);\n float dc = 1. - clamp(u_proportion, 0., 1.);\n float e0 = dc - u_softness - aa;\n float e1 = dc + u_softness + aa;\n float res = smoothstep(min(e0, e1), max(e0, e1), shape);\n\n vec3 fgColor = u_colorFront.rgb * u_colorFront.a;\n float fgOpacity = u_colorFront.a;\n vec3 bgColor = u_colorBack.rgb * u_colorBack.a;\n float bgOpacity = u_colorBack.a;\n\n vec3 color = fgColor * res;\n float opacity = fgOpacity * res;\n\n color += bgColor * (1. - opacity);\n opacity += bgOpacity * (1. - opacity);\n\n fragColor = vec4(color, opacity);\n}\n`;\n\nexport interface WavesUniforms extends ShaderSizingUniforms {\n u_colorFront: [number, number, number, number];\n u_colorBack: [number, number, number, number];\n u_shape: number;\n u_frequency: number;\n u_amplitude: number;\n u_spacing: number;\n u_proportion: number;\n u_softness: number;\n}\n\nexport interface WavesParams extends ShaderSizingParams {\n colorFront?: string;\n colorBack?: string;\n rotation?: number;\n shape?: number;\n frequency?: number;\n amplitude?: number;\n spacing?: number;\n proportion?: number;\n softness?: number;\n}\n"], + "mappings": ";;;;;AAAA,eAAmE;AACnE,SAAS,iBAAiB;AAkCnB,MAAM,sBAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBxC,SAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/public/vendor/paper-shaders/types.d.ts b/public/vendor/paper-shaders/types.d.ts new file mode 100644 index 0000000..d675c51 --- /dev/null +++ b/public/vendor/paper-shaders/types.d.ts @@ -0,0 +1,3 @@ +export type vec4 = [number, number, number, number]; +export type vec3 = [number, number, number]; +export type vec2 = [number, number]; diff --git a/public/vendor/paper-shaders/types.js b/public/vendor/paper-shaders/types.js new file mode 100644 index 0000000..32b5358 --- /dev/null +++ b/public/vendor/paper-shaders/types.js @@ -0,0 +1,6 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +//# sourceMappingURL=types.js.map diff --git a/public/vendor/paper-shaders/types.js.map b/public/vendor/paper-shaders/types.js.map new file mode 100644 index 0000000..9865211 --- /dev/null +++ b/public/vendor/paper-shaders/types.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": [], + "sourcesContent": [], + "mappings": "", + "names": [] +} diff --git a/public/vendor/paper-shaders/vertex-shader.d.ts b/public/vendor/paper-shaders/vertex-shader.d.ts new file mode 100644 index 0000000..7cf9b45 --- /dev/null +++ b/public/vendor/paper-shaders/vertex-shader.d.ts @@ -0,0 +1,2 @@ +/** Vertex shader for the shader mount */ +export declare const vertexShaderSource = "#version 300 es\nprecision mediump float;\n\nlayout(location = 0) in vec4 a_position;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\nuniform float u_imageAspectRatio;\nuniform float u_originX;\nuniform float u_originY;\nuniform float u_worldWidth;\nuniform float u_worldHeight;\nuniform float u_fit;\nuniform float u_scale;\nuniform float u_rotation;\nuniform float u_offsetX;\nuniform float u_offsetY;\n\nout vec2 v_objectUV;\nout vec2 v_objectBoxSize;\nout vec2 v_responsiveUV;\nout vec2 v_responsiveBoxGivenSize;\nout vec2 v_patternUV;\nout vec2 v_patternBoxSize;\nout vec2 v_imageUV;\n\nvec3 getBoxSize(float boxRatio, vec2 givenBoxSize) {\n vec2 box = vec2(0.);\n // fit = none\n box.x = boxRatio * min(givenBoxSize.x / boxRatio, givenBoxSize.y);\n float noFitBoxWidth = box.x;\n if (u_fit == 1.) { // fit = contain\n box.x = boxRatio * min(u_resolution.x / boxRatio, u_resolution.y);\n } else if (u_fit == 2.) { // fit = cover\n box.x = boxRatio * max(u_resolution.x / boxRatio, u_resolution.y);\n }\n box.y = box.x / boxRatio;\n return vec3(box, noFitBoxWidth);\n}\n\nvoid main() {\n gl_Position = a_position;\n\n vec2 uv = gl_Position.xy * .5;\n vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);\n vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight);\n givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio;\n float r = u_rotation * 3.14159265358979323846 / 180.;\n mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n\n\n // ===================================================\n\n float fixedRatio = 1.;\n vec2 fixedRatioBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n\n v_objectBoxSize = getBoxSize(fixedRatio, fixedRatioBoxGivenSize).xy;\n vec2 objectWorldScale = u_resolution.xy / v_objectBoxSize;\n\n v_objectUV = uv;\n v_objectUV *= objectWorldScale;\n v_objectUV += boxOrigin * (objectWorldScale - 1.);\n v_objectUV += graphicOffset;\n v_objectUV /= u_scale;\n v_objectUV = graphicRotation * v_objectUV;\n\n // ===================================================\n\n v_responsiveBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n float responsiveRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n vec2 responsiveBoxSize = getBoxSize(responsiveRatio, v_responsiveBoxGivenSize).xy;\n vec2 responsiveBoxScale = u_resolution.xy / responsiveBoxSize;\n\n #ifdef ADD_HELPERS\n v_responsiveHelperBox = uv;\n v_responsiveHelperBox *= responsiveBoxScale;\n v_responsiveHelperBox += boxOrigin * (responsiveBoxScale - 1.);\n #endif\n\n v_responsiveUV = uv;\n v_responsiveUV *= responsiveBoxScale;\n v_responsiveUV += boxOrigin * (responsiveBoxScale - 1.);\n v_responsiveUV += graphicOffset;\n v_responsiveUV /= u_scale;\n v_responsiveUV.x *= responsiveRatio;\n v_responsiveUV = graphicRotation * v_responsiveUV;\n v_responsiveUV.x /= responsiveRatio;\n\n // ===================================================\n\n float patternBoxRatio = givenBoxSize.x / givenBoxSize.y;\n vec2 patternBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n patternBoxRatio = patternBoxGivenSize.x / patternBoxGivenSize.y;\n\n vec3 boxSizeData = getBoxSize(patternBoxRatio, patternBoxGivenSize);\n v_patternBoxSize = boxSizeData.xy;\n float patternBoxNoFitBoxWidth = boxSizeData.z;\n vec2 patternBoxScale = u_resolution.xy / v_patternBoxSize;\n\n v_patternUV = uv;\n v_patternUV += graphicOffset / patternBoxScale;\n v_patternUV += boxOrigin;\n v_patternUV -= boxOrigin / patternBoxScale;\n v_patternUV *= u_resolution.xy;\n v_patternUV /= u_pixelRatio;\n if (u_fit > 0.) {\n v_patternUV *= (patternBoxNoFitBoxWidth / v_patternBoxSize.x);\n }\n v_patternUV /= u_scale;\n v_patternUV = graphicRotation * v_patternUV;\n v_patternUV += boxOrigin / patternBoxScale;\n v_patternUV -= boxOrigin;\n // x100 is a default multiplier between vertex and fragmant shaders\n // we use it to avoid UV presision issues\n v_patternUV *= .01;\n\n // ===================================================\n\n vec2 imageBoxSize;\n if (u_fit == 1.) { // contain\n imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else if (u_fit == 2.) { // cover\n imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else {\n imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio);\n }\n imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio;\n vec2 imageBoxScale = u_resolution.xy / imageBoxSize;\n\n v_imageUV = uv;\n v_imageUV *= imageBoxScale;\n v_imageUV += boxOrigin * (imageBoxScale - 1.);\n v_imageUV += graphicOffset;\n v_imageUV /= u_scale;\n v_imageUV.x *= u_imageAspectRatio;\n v_imageUV = graphicRotation * v_imageUV;\n v_imageUV.x /= u_imageAspectRatio;\n\n v_imageUV += .5;\n v_imageUV.y = 1. - v_imageUV.y;\n}"; diff --git a/public/vendor/paper-shaders/vertex-shader.js b/public/vendor/paper-shaders/vertex-shader.js new file mode 100644 index 0000000..73d7aba --- /dev/null +++ b/public/vendor/paper-shaders/vertex-shader.js @@ -0,0 +1,160 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Paper Shaders * + * https://github.com/paper-design/shaders * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +const vertexShaderSource = `#version 300 es +precision mediump float; + +layout(location = 0) in vec4 a_position; + +uniform vec2 u_resolution; +uniform float u_pixelRatio; +uniform float u_imageAspectRatio; +uniform float u_originX; +uniform float u_originY; +uniform float u_worldWidth; +uniform float u_worldHeight; +uniform float u_fit; +uniform float u_scale; +uniform float u_rotation; +uniform float u_offsetX; +uniform float u_offsetY; + +out vec2 v_objectUV; +out vec2 v_objectBoxSize; +out vec2 v_responsiveUV; +out vec2 v_responsiveBoxGivenSize; +out vec2 v_patternUV; +out vec2 v_patternBoxSize; +out vec2 v_imageUV; + +vec3 getBoxSize(float boxRatio, vec2 givenBoxSize) { + vec2 box = vec2(0.); + // fit = none + box.x = boxRatio * min(givenBoxSize.x / boxRatio, givenBoxSize.y); + float noFitBoxWidth = box.x; + if (u_fit == 1.) { // fit = contain + box.x = boxRatio * min(u_resolution.x / boxRatio, u_resolution.y); + } else if (u_fit == 2.) { // fit = cover + box.x = boxRatio * max(u_resolution.x / boxRatio, u_resolution.y); + } + box.y = box.x / boxRatio; + return vec3(box, noFitBoxWidth); +} + +void main() { + gl_Position = a_position; + + vec2 uv = gl_Position.xy * .5; + vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5); + vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight); + givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio; + float r = u_rotation * 3.14159265358979323846 / 180.; + mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r)); + vec2 graphicOffset = vec2(-u_offsetX, u_offsetY); + + + // =================================================== + + float fixedRatio = 1.; + vec2 fixedRatioBoxGivenSize = vec2( + (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x, + (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y + ); + + v_objectBoxSize = getBoxSize(fixedRatio, fixedRatioBoxGivenSize).xy; + vec2 objectWorldScale = u_resolution.xy / v_objectBoxSize; + + v_objectUV = uv; + v_objectUV *= objectWorldScale; + v_objectUV += boxOrigin * (objectWorldScale - 1.); + v_objectUV += graphicOffset; + v_objectUV /= u_scale; + v_objectUV = graphicRotation * v_objectUV; + + // =================================================== + + v_responsiveBoxGivenSize = vec2( + (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x, + (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y + ); + float responsiveRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y; + vec2 responsiveBoxSize = getBoxSize(responsiveRatio, v_responsiveBoxGivenSize).xy; + vec2 responsiveBoxScale = u_resolution.xy / responsiveBoxSize; + + #ifdef ADD_HELPERS + v_responsiveHelperBox = uv; + v_responsiveHelperBox *= responsiveBoxScale; + v_responsiveHelperBox += boxOrigin * (responsiveBoxScale - 1.); + #endif + + v_responsiveUV = uv; + v_responsiveUV *= responsiveBoxScale; + v_responsiveUV += boxOrigin * (responsiveBoxScale - 1.); + v_responsiveUV += graphicOffset; + v_responsiveUV /= u_scale; + v_responsiveUV.x *= responsiveRatio; + v_responsiveUV = graphicRotation * v_responsiveUV; + v_responsiveUV.x /= responsiveRatio; + + // =================================================== + + float patternBoxRatio = givenBoxSize.x / givenBoxSize.y; + vec2 patternBoxGivenSize = vec2( + (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x, + (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y + ); + patternBoxRatio = patternBoxGivenSize.x / patternBoxGivenSize.y; + + vec3 boxSizeData = getBoxSize(patternBoxRatio, patternBoxGivenSize); + v_patternBoxSize = boxSizeData.xy; + float patternBoxNoFitBoxWidth = boxSizeData.z; + vec2 patternBoxScale = u_resolution.xy / v_patternBoxSize; + + v_patternUV = uv; + v_patternUV += graphicOffset / patternBoxScale; + v_patternUV += boxOrigin; + v_patternUV -= boxOrigin / patternBoxScale; + v_patternUV *= u_resolution.xy; + v_patternUV /= u_pixelRatio; + if (u_fit > 0.) { + v_patternUV *= (patternBoxNoFitBoxWidth / v_patternBoxSize.x); + } + v_patternUV /= u_scale; + v_patternUV = graphicRotation * v_patternUV; + v_patternUV += boxOrigin / patternBoxScale; + v_patternUV -= boxOrigin; + // x100 is a default multiplier between vertex and fragmant shaders + // we use it to avoid UV presision issues + v_patternUV *= .01; + + // =================================================== + + vec2 imageBoxSize; + if (u_fit == 1.) { // contain + imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio; + } else if (u_fit == 2.) { // cover + imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio; + } else { + imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio); + } + imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio; + vec2 imageBoxScale = u_resolution.xy / imageBoxSize; + + v_imageUV = uv; + v_imageUV *= imageBoxScale; + v_imageUV += boxOrigin * (imageBoxScale - 1.); + v_imageUV += graphicOffset; + v_imageUV /= u_scale; + v_imageUV.x *= u_imageAspectRatio; + v_imageUV = graphicRotation * v_imageUV; + v_imageUV.x /= u_imageAspectRatio; + + v_imageUV += .5; + v_imageUV.y = 1. - v_imageUV.y; +}`; +export { + vertexShaderSource +}; +//# sourceMappingURL=vertex-shader.js.map diff --git a/public/vendor/paper-shaders/vertex-shader.js.map b/public/vendor/paper-shaders/vertex-shader.js.map new file mode 100644 index 0000000..07cd0ca --- /dev/null +++ b/public/vendor/paper-shaders/vertex-shader.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../src/vertex-shader.ts"], + "sourcesContent": ["/** Vertex shader for the shader mount */\n// language=GLSL\nexport const vertexShaderSource = `#version 300 es\nprecision mediump float;\n\nlayout(location = 0) in vec4 a_position;\n\nuniform vec2 u_resolution;\nuniform float u_pixelRatio;\nuniform float u_imageAspectRatio;\nuniform float u_originX;\nuniform float u_originY;\nuniform float u_worldWidth;\nuniform float u_worldHeight;\nuniform float u_fit;\nuniform float u_scale;\nuniform float u_rotation;\nuniform float u_offsetX;\nuniform float u_offsetY;\n\nout vec2 v_objectUV;\nout vec2 v_objectBoxSize;\nout vec2 v_responsiveUV;\nout vec2 v_responsiveBoxGivenSize;\nout vec2 v_patternUV;\nout vec2 v_patternBoxSize;\nout vec2 v_imageUV;\n\nvec3 getBoxSize(float boxRatio, vec2 givenBoxSize) {\n vec2 box = vec2(0.);\n // fit = none\n box.x = boxRatio * min(givenBoxSize.x / boxRatio, givenBoxSize.y);\n float noFitBoxWidth = box.x;\n if (u_fit == 1.) { // fit = contain\n box.x = boxRatio * min(u_resolution.x / boxRatio, u_resolution.y);\n } else if (u_fit == 2.) { // fit = cover\n box.x = boxRatio * max(u_resolution.x / boxRatio, u_resolution.y);\n }\n box.y = box.x / boxRatio;\n return vec3(box, noFitBoxWidth);\n}\n\nvoid main() {\n gl_Position = a_position;\n\n vec2 uv = gl_Position.xy * .5;\n vec2 boxOrigin = vec2(.5 - u_originX, u_originY - .5);\n vec2 givenBoxSize = vec2(u_worldWidth, u_worldHeight);\n givenBoxSize = max(givenBoxSize, vec2(1.)) * u_pixelRatio;\n float r = u_rotation * 3.14159265358979323846 / 180.;\n mat2 graphicRotation = mat2(cos(r), sin(r), -sin(r), cos(r));\n vec2 graphicOffset = vec2(-u_offsetX, u_offsetY);\n\n\n // ===================================================\n\n float fixedRatio = 1.;\n vec2 fixedRatioBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n\n v_objectBoxSize = getBoxSize(fixedRatio, fixedRatioBoxGivenSize).xy;\n vec2 objectWorldScale = u_resolution.xy / v_objectBoxSize;\n\n v_objectUV = uv;\n v_objectUV *= objectWorldScale;\n v_objectUV += boxOrigin * (objectWorldScale - 1.);\n v_objectUV += graphicOffset;\n v_objectUV /= u_scale;\n v_objectUV = graphicRotation * v_objectUV;\n\n // ===================================================\n\n v_responsiveBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n float responsiveRatio = v_responsiveBoxGivenSize.x / v_responsiveBoxGivenSize.y;\n vec2 responsiveBoxSize = getBoxSize(responsiveRatio, v_responsiveBoxGivenSize).xy;\n vec2 responsiveBoxScale = u_resolution.xy / responsiveBoxSize;\n\n #ifdef ADD_HELPERS\n v_responsiveHelperBox = uv;\n v_responsiveHelperBox *= responsiveBoxScale;\n v_responsiveHelperBox += boxOrigin * (responsiveBoxScale - 1.);\n #endif\n\n v_responsiveUV = uv;\n v_responsiveUV *= responsiveBoxScale;\n v_responsiveUV += boxOrigin * (responsiveBoxScale - 1.);\n v_responsiveUV += graphicOffset;\n v_responsiveUV /= u_scale;\n v_responsiveUV.x *= responsiveRatio;\n v_responsiveUV = graphicRotation * v_responsiveUV;\n v_responsiveUV.x /= responsiveRatio;\n\n // ===================================================\n\n float patternBoxRatio = givenBoxSize.x / givenBoxSize.y;\n vec2 patternBoxGivenSize = vec2(\n (u_worldWidth == 0.) ? u_resolution.x : givenBoxSize.x,\n (u_worldHeight == 0.) ? u_resolution.y : givenBoxSize.y\n );\n patternBoxRatio = patternBoxGivenSize.x / patternBoxGivenSize.y;\n\n vec3 boxSizeData = getBoxSize(patternBoxRatio, patternBoxGivenSize);\n v_patternBoxSize = boxSizeData.xy;\n float patternBoxNoFitBoxWidth = boxSizeData.z;\n vec2 patternBoxScale = u_resolution.xy / v_patternBoxSize;\n\n v_patternUV = uv;\n v_patternUV += graphicOffset / patternBoxScale;\n v_patternUV += boxOrigin;\n v_patternUV -= boxOrigin / patternBoxScale;\n v_patternUV *= u_resolution.xy;\n v_patternUV /= u_pixelRatio;\n if (u_fit > 0.) {\n v_patternUV *= (patternBoxNoFitBoxWidth / v_patternBoxSize.x);\n }\n v_patternUV /= u_scale;\n v_patternUV = graphicRotation * v_patternUV;\n v_patternUV += boxOrigin / patternBoxScale;\n v_patternUV -= boxOrigin;\n // x100 is a default multiplier between vertex and fragmant shaders\n // we use it to avoid UV presision issues\n v_patternUV *= .01;\n\n // ===================================================\n\n vec2 imageBoxSize;\n if (u_fit == 1.) { // contain\n imageBoxSize.x = min(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else if (u_fit == 2.) { // cover\n imageBoxSize.x = max(u_resolution.x / u_imageAspectRatio, u_resolution.y) * u_imageAspectRatio;\n } else {\n imageBoxSize.x = min(10.0, 10.0 / u_imageAspectRatio * u_imageAspectRatio);\n }\n imageBoxSize.y = imageBoxSize.x / u_imageAspectRatio;\n vec2 imageBoxScale = u_resolution.xy / imageBoxSize;\n\n v_imageUV = uv;\n v_imageUV *= imageBoxScale;\n v_imageUV += boxOrigin * (imageBoxScale - 1.);\n v_imageUV += graphicOffset;\n v_imageUV /= u_scale;\n v_imageUV.x *= u_imageAspectRatio;\n v_imageUV = graphicRotation * v_imageUV;\n v_imageUV.x /= u_imageAspectRatio;\n\n v_imageUV += .5;\n v_imageUV.y = 1. - v_imageUV.y;\n}`;\n"], + "mappings": ";;;;;AAEO,MAAM,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;", + "names": [] +} diff --git a/scripts/serve-dev.mjs b/scripts/serve-dev.mjs index 39fb5ae..ea45c70 100644 --- a/scripts/serve-dev.mjs +++ b/scripts/serve-dev.mjs @@ -21,7 +21,7 @@ function ensureEditsDir() { function isLocalRequest(req) { const host = (req.headers.host || "").split(":")[0]; - const ALLOWED = ["localhost", "127.0.0.1", "dev.mamadaevv.site", "f2ogw6fzi6trnp7cp43x8wsd.31.129.103.242.sslip.io"]; + const ALLOWED = ["localhost", "127.0.0.1", "10.200.201.3", "dev.mamadaevv.site", "f2ogw6fzi6trnp7cp43x8wsd.31.129.103.242.sslip.io"]; return ALLOWED.includes(host); } diff --git a/src/pages/resume.en.html b/src/pages/resume.en.html index 3eddfb1..746630f 100644 --- a/src/pages/resume.en.html +++ b/src/pages/resume.en.html @@ -9,220 +9,23 @@ /> Resume — Nikolai Mamadaev {{> head-links}} - {{> header-minimal}} {{> fab-stack-resume}}
-
-
- Nikolai Mamadaev +
+
+
+ Nikolai Mamadaev

Resume

Nikolai Mamadaev

-

Senior UX/UI Designer, Product Designer

+

Senior UX/UI Designer
+ Product Designer
+ AI Designer

-
- -
+
@@ -230,8 +33,8 @@
mamadaevv.site
-
Salary Range
-
250,000 – 310,000 rubles
+
Salary
+
from 250,000 ₽
Work Format
@@ -245,565 +48,579 @@
Social Media
@mamadaevv
+
+
Languages
+
English (B2)
+
- -
- -

Work Experience

- -
- -
-
-

Astratech

-

January 2026 — Present

-
-

Development and support of digital systems and tools to improve employee productivity.

- -
-
-
-

Responsibilities:

-
    -
  • Worked on interface design
  • -
-
-
-

Achievements:

-
    -
  • Successfully completed assigned tasks
  • -
-
-
-

Stack:

-
    -
  • Figma
  • -
  • HTML/CSS
  • -
  • AI/LLM
  • -
-
-
-
-
+
+

Work Experience

+
    +
  1. +
    +

    Astratech

    +

    — Present

    +
    +

    Development and support of digital systems and tools to improve employee productivity.

    -
    -
    -

    Primo RPA

    -

    February 2023 — November 2025 (2 years and 10 months)·primo-rpa.com

    -
    -

    A Russian enterprise-grade RPA platform: a suite of desktop robot development environments and web services for management, analytics, and ML. I worked as a lead designer on the entire product line, consisting of 6 products.

    - -
    -

    AI Server Platform (SaaS)

    -

    A control panel for training, running, testing, and other operations involving neural networks deployed within the company’s internal network.

    -
    -
    -

    Responsibilities:

    -
      -
    • Designing the model management interface: training, deployment, testing, versioning, and monitoring
    • -
    • Developing workflows for Smart OCR—extracting data from various types of documents
    • -
    • Designing document markup and validating recognition results
    • -
    • Managing the states of long-running processes: model training, progress, errors, rollback
    • -
    • Preparing interactive prototypes and overseeing implementation
    • -
    • Conducting interviews with ML engineers and business users to validate behavioral models
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Packaged complex ML logic into an interface that is understandable not only to ML engineers but also to business analysts without a technical background
    • -
    • Streamlined work with models into a single, predictable 4-step workflow instead of 9 disparate screens
    • -
    • Reduced the time required to configure a new model from 40 to 15 minutes, based on test results
    • -
    • Designed a document annotation interface that reduced support requests on this topic by 60%
    • -
    • Conducted 8 usability sessions and, based on the results, revised the key model training scenario
    • -
    -
    -
    -

    Stack:

    -
      -
    • ML / neural networks
    • -
    • Smart OCR
    • -
    +
    +
    +
    +

    Responsibilities

    +
      +
    • Creating production-ready interfaces
    • +
    • Breaking down ideas and concepts and turning them into design tasks
    • +
    +
    +
    +

    Achievements

    +
      +
    • Set up a configured environment for delivering interfaces to production
    • +
    • Ran more than 15 hypothesis tests on working prototypes without involving the development department
    • +
    • Sped up processing of user testing results by applying AI tools (summarization, classification, transcription)
    • +
    +
    +
    +

    Stack

    +
      +
    • Figma
    • +
    • HTML/CSS/JS
    • +
    • AI/LLM
    • +
    • GitHub
    • +
    • Gitea
    • +
    • Coolify
    • +
    • CI/CD
    • +
    • Hermes Agent
    • +
    • Zed IDE
    • +
    +
    -
    +
  2. -
    - -
    -

    Robot Automation Studio (Windows)

    -

    An environment for developing and debugging robots for business process automation.

    -
    -
    -

    Responsibilities:

    -
      -
    • Designing the interface for a professional desktop IDE: workspace, toolbars, process tree, debugger
    • -
    • Developing the product’s component library and visual language
    • -
    • Designing debugging scenarios: breakpoints, step-by-step execution, variable inspection
    • -
    • Designing support for large activity trees and nesting
    • -
    • Working in tandem with the development team on component implementation, providing author supervision
    • -
    • Conducting regular design reviews within the team
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Updated the product’s visual language to the Windows 10/11 standard—components are now a generation more modern
    • -
    • Redesigned over 80 screens and dialogs, bringing them into a unified grid and typography
    • -
    • Compiled a library of 140 components, covering 90% of the interface’s needs
    • -
    • Converted icons and some illustrations to vector graphics: ensuring correct display at any zoom level and DPI
    • -
    • Reduced the time it took the team to build a new screen from 2 days to 4 hours by using pre-built components
    • -
    -
    -
    -

    Stack:

    -
      -
    • Desktop IDE
    • -
    • Windows 10/11
    • -
    • Component library
    • -
    +
  3. +
    +

    Primo RPA

    +

    — (2 years and 10 months)·primo-rpa.com

    +
    +

    A Russian enterprise-grade B2B RPA platform: a suite of desktop robot development environments and web services for management, analytics, and ML.
    + I worked as a lead designer on the entire product line, consisting of 6 products.

    +
    +

    AI Server Platform (SaaS)

    +

    A control panel for training, running, testing, and other operations involving neural networks deployed within the company’s internal network.

    +
    +
    +
    Responsibilities
    +
      +
    • Design a web interface for the local LLM control panel. Enable the following scenarios: dataset creation, training of narrowly focused models, rapid testing, and administration of deployment on the client’s local infrastructure.
    • +
    • Improve how Smart OCR is configured for more accurate data extraction from documents of various types
    • +
    • Managing the states of long-running processes: model training, progress, errors, rollback
    • +
    • Preparing interactive prototypes and overseeing implementation
    • +
    +
    +
    +
    Achievements
    +
      +
    • Created an intuitive interface for handling the complex setup of LLM models. This significantly lowered the barrier to entry for users without deep neural network expertise.
    • +
    • Reduced setup time from 3–5 days to 2 hours, based on conversations with users
    • +
    • Designed a document annotation interface that reduced support requests on this topic by 60%
    • +
    • Conducted 8 usability sessions and, based on the results, revised the key model training scenario
    • +
    +
    +
    +
    Stack
    +
      +
    • Figma
    • +
    • React
    • +
    • Ant Design
    • +
    • Hope UI
    • +
    • LLM core
    • +
    • Smart OCR
    • +
    +
    -
  4. - -
    - -
    -

    Robot Automation Studio (Linux)

    -

    An environment for developing and debugging robots for business process automation.

    -
    -
    -

    Responsibilities:

    -
      -
    • Adapting the Studio design for the Linux version using the Avalonia framework
    • -
    • Designing components while taking into account the framework’s technical limitations
    • -
    • Synchronizing the visual language between the Windows and Linux versions of the product
    • -
    • Collaborating with the development team to determine what is technically feasible in Avalonia and what requires workarounds
    • -
    • Preparing component behavior specifications for implementation
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Compiled an extensive library of 95 components for Avalonia, which did not exist in the project before I joined
    • -
    • Ensured product consistency across two platforms with different technology stacks
    • -
    • Reduced the number of visual discrepancies between versions from several dozen to just a few isolated cases
    • -
    • Provided the Linux version team with a ready-made set of solutions instead of having to design each screen from scratch
    • -
    -
    -
    -

    Stack:

    -
      -
    • Avalonia
    • -
    • .NET
    • -
    • Linux
    • -
    + +
    +

    Robot Automation Studio (Windows)

    +

    An environment for developing and debugging robots for business process automation.

    +
    +
    +
    Responsibilities
    +
      +
    • Designing the interface for a professional desktop IDE: workspace, toolbars, process tree, debugger
    • +
    • Developing the product’s component library and visual language
    • +
    • Designing debugging scenarios: breakpoints, step-by-step execution, variable inspection
    • +
    • Designing support for large activity trees and nesting
    • +
    • Working in tandem with the development team on component implementation, providing author supervision
    • +
    • Conducting regular design reviews within the team
    • +
    +
    +
    +
    Achievements
    +
      +
    • Updated the product’s visual language to the Windows 11 standard—components are now a generation more modern
    • +
    • Redesigned dozens of screens and modal windows, bringing them into a single token-based design system.
    • +
    • Built a library of more than 50 reusable components, making it possible to design new functionality scenarios while preserving established behavior patterns
    • +
    • Redrew all legacy raster icons as vectors. This produced a library of over 300 unique icons that display perfectly at any UI scale and DPI
    • +
    • Reduced the time it took the team to build a new screen from 2 days to 4 hours by using pre-built components
    • +
    +
    +
    +
    Stack
    +
      +
    • Desktop IDE
    • +
    • Windows 10/11
    • +
    • Figma
    • +
    • AI tools
    • +
    • DevExpress UI
    • +
    • Handy Controls
    • +
    +
    -
    - -
    - -
    -

    Idea Hub Dashboard (SaaS)

    -

    Enables the collection and analysis of employee suggestions, performance evaluation, and monitoring of automation implementation status. Analytics on key performance indicators and a high-level view of automation.

    -
    -
    -

    Responsibilities:

    -
      -
    • Designing dashboards and analytical views for robotization KPIs
    • -
    • Managing the idea lifecycle: submission → evaluation → prioritization → implementation → impact measurement
    • -
    • Section information architecture, working with dense tables, filters, and bulk operations
    • -
    • Designing a role-based model: employee, analyst, manager
    • -
    • Selecting data visualization types for specific management questions
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Integrated high-level analytics and work on a specific idea into a single continuous workflow without switching between sections
    • -
    • Designed data visualizations so that managers can see the impact of automation without having to manually export data to Excel
    • -
    • Reduced the process from idea submission to evaluation from 6 steps to 3
    • -
    • Designed 12 analytics widgets covering customers’ key questions about the ROI of automation
    • -
    -
    -
    -

    Stack:

    -
      -
    • Analytics / Dashboards
    • -
    • KPIs
    • -
    • Data Visualization
    • -
    + +
    +

    Robot Automation Studio (Linux)

    +

    An environment for developing and debugging robots for business process automation.

    +
    +
    +
    Responsibilities
    +
      +
    • Designing the IDE interface for Linux, based on the selected feature set and the already implemented Windows version
    • +
    • Designing components with the technical constraints of the Avalonia Suki UI framework in mind
    • +
    • Preparing component behavior specifications for implementation
    • +
    +
    +
    +
    Achievements
    +
      +
    • Built a component library based on Suki UI that lets me quickly assemble interfaces in Figma and hand them off to developers
    • +
    • Developed a product style that meets international standards, which will support expansion into foreign markets
    • +
    • Handed developers design instructions for assembling screens, so they can build interfaces for new features on their own and ship releases according to the roadmap
    • +
    +
    +
    +
    Stack
    +
      +
    • Avalonia
    • +
    • Suki UI
    • +
    • Figma
    • +
    • AI tools
    • +
    • Cursor IDE
    • +
    • MobaXterm RDP
    • +
    • Linux
    • +
    +
    -
    - -
    - -
    -

    Orchestrator Automation Management Tool (SaaS)

    -

    Ensures seamless system operation and management of hundreds of robots: launching, monitoring, and management.

    -
    -
    -

    Responsibilities:

    -
      -
    • Designing a management interface for a fleet of hundreds of robots: schedules, queues, priorities, and monitoring
    • -
    • Developing state management and error handling for bulk launches
    • -
    • Designing a system for notifications and alerts regarding failures
    • -
    • Working with complex tables: sorting, filtering, bulk actions, saved views
    • -
    • Prototyping complex scenarios for validation prior to development
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Designed a system to manage hundreds of objects so that the operator doesn’t get lost in the scale: from the big picture to a specific robot in just 2 clicks
    • -
    • Aligned the service with a unified design system alongside the rest of the product line’s web products
    • -
    • Reduced operator response time to robot failures from 12 to 4 minutes thanks to a redesigned alert system
    • -
    • Consolidated 5 different robot list views into a single customizable one
    • -
    -
    -
    -

    Stack:

    -
      -
    • Monitoring
    • -
    • Schedules / Queues
    • -
    • Notifications / Alerts
    • -
    + +
    +

    Idea Hub Dashboard (SaaS)

    +

    Enables the collection and analysis of employee suggestions, performance evaluation, and monitoring of automation implementation status. Analytics on key performance indicators and a high-level view of automation.

    +
    +
    +
    Responsibilities
    +
      +
    • Designing dashboards and analytical views for robotization KPIs
    • +
    • Managing the idea lifecycle: submission → evaluation → prioritization → implementation → impact measurement
    • +
    • Section information architecture, working with dense tables, filters, and bulk operations
    • +
    • Designing a role-based model: employee, analyst, manager
    • +
    • Selecting data visualization types for specific management questions
    • +
    +
    +
    +
    Achievements
    +
      +
    • Integrated high-level analytics and work on a specific idea into a single continuous workflow without switching between sections
    • +
    • Designed data visualizations so that managers can see the impact of automation without having to manually export data to Excel
    • +
    • Reduced the process from idea submission to evaluation from 6 steps to 3
    • +
    • Designed 12 analytics widgets covering customers’ key questions about the ROI of automation
    • +
    +
    +
    +
    Stack
    +
      +
    • Figma
    • + + +
    +
    -
    - -
    - -
    -

    Q&A Portal (WEB)

    -

    A platform for posting questions and receiving answers from support

    -
    -
    -

    Responsibilities:

    -
      -
    • Designing workflows for posting questions, providing answers, and searching the accumulated knowledge base
    • -
    • Designing the portal interface based on the company’s common design system
    • -
    • Developing a role model: user, expert, support team, moderator
    • -
    • Designing search mechanics and suggestions for similar questions when posting
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Launched the portal using off-the-shelf components from the design system in 3 weeks instead of the planned 8
    • -
    • Reduced the support workload by approximately 25% by reusing existing answers
    • -
    • The suggestion feature for similar questions reduced duplicate questions by 45%
    • -
    • Ensured visual consistency with other products in the line—the portal does not look like a third-party service
    • -
    -
    -
    -

    Stack:

    -
      -
    • Web portal
    • -
    • Design system
    • -
    • Knowledge base
    • -
    + +
    +

    Orchestrator Automation Management Tool (SaaS)

    +

    Ensures seamless system operation and management of hundreds of robots: launching, monitoring, and management.

    +
    +
    +
    Responsibilities
    +
      +
    • Designing a management interface for a fleet of hundreds of robots: schedules, queues, priorities, and monitoring
    • +
    • Developing state management and error handling for bulk launches
    • +
    • Designing a system for notifications and alerts regarding failures
    • +
    • Working with complex tables: sorting, filtering, bulk actions, saved views
    • +
    • Prototyping complex scenarios for validation prior to development
    • +
    +
    +
    +
    Achievements
    +
      +
    • Designed a system to manage hundreds of objects so that the operator doesn’t get lost in the scale: from the big picture to a specific robot in just 2 clicks
    • +
    • Aligned the service with a unified design system alongside the rest of the product line’s web products
    • +
    • Reduced operator response time to robot failures from 12 to 4 minutes thanks to a redesigned alert system
    • +
    • Consolidated 5 different robot list views into a single customizable one
    • +
    +
    +
    +
    Stack
    +
      +
    • Figma
    • +
    • Ant Design
    • +
    • Storybook
    • +
    • AI tools
    • + + +
    +
    -
    - -
    - -
    -

    Company-wide

    -
      -
    • I initiated a visual refresh of the entire product line—the company decided to redesign the products upon my arrival
    • -
    • I developed and implemented a unified design system for web services, integrating the company’s brand identity with the new interface structure
    • -
    • The design system was adopted by four product teams; it includes 210 components and theming tokens
    • -
    • Reduced the average time to prepare mockups across the product line by 35%
    • -
    • Created and maintained a UI kit to ensure consistency and accelerate development
    • -
    • Conducted more than 20 user studies and usability sessions during my tenure
    • -
    • Oversaw implementation, participated in design reviews, product discussions, and release planning
    • -
    • Served as the sole lead designer for an entire product line consisting of 6 products and two platforms
    • -
    -
    -
    - -
    - -
    -
    -

    Lunacy (Icons8)

    -

    July 2022 – January 2023 (7 months) · icons8.com/lunacy

    -
    -

    A graphic editor for working on interface designs and other mockups. Similar to Sketch App and Figma.

    - -
    -
    -
    -

    Responsibilities:

    -
      -
    • Designing the graphic editor’s interface: tools, property panels, working with the canvas and layers
    • -
    • Documenting solutions as user flows and handing them off to developers
    • -
    • Creating complex interactive prototypes in Protopie to demonstrate tool behavior
    • -
    • Illustrating missing icons in the product’s style
    • -
    • Making suggestions for improving the editor’s functionality
    • -
    • Participating in discussions about product solutions with the team
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Designed 6 functional blocks for the editor, including work with components and styles
    • -
    • Created 15+ prototypes in Protopie, which helped eliminate unsuccessful solutions before development
    • -
    • Some of my feature suggestions were included in product releases
    • -
    • Designed over 60 icons to complement the editor’s system set
    • -
    • Worked on a product for a professional audience of designers, where the requirements for interface details are higher than usual
    • -
    -
    -
    -

    Stack:

    -
      -
    • Protopie
    • -
    • Graphics editor
    • -
    • Icons / Illustrations
    • -
    + +
    +

    Q&A Portal (WEB)

    +

    A platform for posting questions and receiving answers from support

    +
    +
    +
    Responsibilities
    +
      +
    • Designing workflows for posting questions, providing answers, and searching the accumulated knowledge base
    • +
    • Designing the portal interface under the constraints of the PHP engine used for a QnA-type site.
    • +
    • Developing a role model: user, expert, support team, moderator
    • +
    • Designing search mechanics and suggestions for similar questions when posting
    • +
    +
    +
    +
    Achievements
    +
      +
    • Launched the portal using off-the-shelf components from the design system in 3 weeks instead of the planned 8
    • +
    • Reduced the support workload by approximately 25% by reusing existing answers
    • +
    • The suggestion feature for similar questions reduced duplicate questions by 45%
    • +
    • Ensured visual consistency with other products in the line—the portal does not look like a third-party service
    • +
    +
    +
    +
    Stack
    +
      +
    • PHP QnA Engine
    • +
    • Figma
    • +
    • Design system
    • + +
    +
    -
    -
    + +
    +

    Company-wide

    +
      + +
    • I developed and implemented a unified design system for web services, integrating the company’s brand identity with the new interface structure
    • + +
    • Reduced the average time to prepare mockups across the product line by 35%
    • +
    • Created and maintained a UI kit to ensure consistency and accelerate development
    • +
    • Conducted more than 20 user studies and usability sessions during my tenure
    • +
    • Oversaw implementation, participated in design reviews, product discussions, and release planning
    • +
    • Hired 4 interface designers for different company projects
    • +
    • Conducted more than 30 interviews, including introducing the WhiteBoard Interview format
    • +
    • Developed test assignments tailored to the needs of specific roles and tasks
    • +
    • Introduced an internal video call system based on Jitsi within the company’s internal network, together with the technical support department
    • + +
    +
    + -
    - -
    -
    -

    MANU Video Game Maker

    -

    January 2019 — December 2021 (3 years)·manu.co

    -
    -

    A game engine for creating video games without writing code.

    - -
    -
    -
    -

    Responsibilities:

    -
      -
    • End-to-end product design of the engine: from interaction concepts to the final UI
    • -
    • Designing interface behavior for complex user scenarios
    • -
    • User research: observation, in-depth interviews, surveys
    • -
    • Creating user personas and empathy maps
    • -
    • Working with product analytics and metrics tied to user scenarios
    • -
    • Layout of the website’s homepage on Tilda, designing the site’s information architecture together with the team
    • -
    • Supervising the work of the web graphic designer
    • -
    • Hiring for the product department: conducting interviews and selecting specialists
    • -
    • Leading the department and establishing workflows for the entire product team
    • -
    • Overseeing the creation of user documentation
    • -
    • Implementing automation tools (Integromat, Zapier) for internal tasks
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Led a large-scale redesign of the entire program: established a fundamentally new concept and style
    • -
    • Designed the interface behavior for over 100 complex user scenarios
    • -
    • Designed the product’s design system from scratch: 120 components
    • -
    • Received a lot of positive, and in some cases enthusiastic, user feedback—the product became much easier to navigate
    • -
    • Onboarding completion rates increased from 34% to 58% following the redesign
    • -
    • Conducted 25 in-depth interviews and 4 rounds of surveys, and revised 3 key scenarios based on the results
    • -
    • Hired 4 specialists for the product department and grew from a designer to a team lead
    • -
    • Established departmental processes: design reviews, backlog management, and handoff to development
    • -
    • Automated the team’s internal routines, freeing up time for product tasks
    • -
    -
    -
    -

    Stack:

    -
      -
    • Tilda
    • -
    • Integromat / Zapier
    • -
    • User research
    • -
    +
  5. +
    +

    Lunacy (Icons8)

    +

    July 2022 – January 2023 (7 months) · icons8.com/lunacy

    +
    +

    A graphic editor for working on interface designs and other mockups. Similar to Sketch App and Figma.

    +
    +
    +
    +

    Responsibilities

    +
      +
    • Designing the graphic editor’s interface: tools, property panels, working with the canvas and layers
    • +
    • Documenting solutions as user flows and handing them off to developers
    • +
    • Creating complex interactive prototypes in Protopie to demonstrate tool behavior
    • +
    +
    +
    +

    Achievements

    +
      +
    • Designed 6 functional blocks for the editor, including work with components and styles
    • +
    • Built a Protopie prototype for a new feature: editing multi-layered realistic object shadows with complex behavior logic, almost indistinguishable from the production solution. Not counting smaller, simple prototypes.
    • +
    • Validated 3 hypotheses for improving the editor with real users. 1 made it into the release.
    • +
    +
    +
    +

    Stack

    +
      +
    • Protopie
    • +
    • Figma
    • +
    • Design system
    • +
    • Avalonia Custom UI
    • +
    • Windows
    • +
    • Linux
    • +
    • macOS
    • +
    +
    -
  6. -
    + -
    - -
    -
    -

    Aegees Messenger

    -

    October 2017 — December 2018 (1 year and 3 months)·aegees.com

    -
    -

    A cross-platform messenger with secure storage and audio calls.

    - -
    -
    -
    -

    Responsibilities:

    -
      -
    • Designing the messenger for five platforms simultaneously: Android, iOS, macOS, Windows, and Linux
    • -
    • Developing use cases for secure storage and audio calls
    • -
    • Designing scenarios for registration, encryption, and access recovery
    • -
    • Adapting a unified product logic to each platform’s guidelines
    • -
    • Supervising the work of international illustrators in the preparation of graphic assets
    • -
    • Preparation of materials for app stores
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Developed a unified visual language for the product while adhering to the guidelines of five platforms simultaneously
    • -
    • Designed over 90 screens across five platform versions
    • -
    • Ensured a steady flow of graphic assets from external contractors without compromising the design style
    • -
    • Simplified the secure storage workflow so that people without technical training could use it
    • -
    • Worked in the sensitive areas of privacy and security, where a single interface error can cost user trust
    • -
    -
    -
    -

    Stack:

    -
      -
    • Android
    • -
    • iOS
    • -
    • macOS
    • -
    • Windows
    • -
    • Linux
    • -
    +
  7. +
    +

    MANU Video Game Maker

    +

    — (3 years)·manu.co

    +
    +

    A game engine for creating video games without writing code.

    +
    +
    +
    +

    Responsibilities

    +
      +
    • End-to-end product design of the engine: from interaction concepts to the final UI
    • +
    • Designing interface behavior for complex user scenarios
    • +
    • User research: observation, in-depth interviews, surveys
    • +
    • Creating user personas and empathy maps
    • +
    • Working with product analytics and metrics tied to user scenarios
    • +
    • Layout of the website’s homepage on Tilda, designing the site’s information architecture together with the team
    • +
    • Supervising the work of the web graphic designer
    • +
    • Hiring for the product department: conducting interviews and selecting specialists
    • +
    • Leading the department and establishing workflows for the entire product team
    • +
    • Overseeing the creation of user documentation
    • +
    • Implementing automation tools (Integromat, Zapier) for internal tasks
    • +
    +
    +
    +

    Achievements

    +
      +
    • Led a large-scale redesign of the entire program: established a fundamentally new concept and style
    • +
    • Designed the interface behavior for over 100 complex user scenarios
    • +
    • Designed the product’s design system from scratch: 120 components
    • +
    • Received a lot of positive, and in some cases enthusiastic, user feedback—the product became much easier to navigate
    • +
    • Onboarding completion rates increased from 34% to 58% following the redesign
    • +
    • Conducted 25 in-depth interviews and 4 rounds of surveys, and revised 3 key scenarios based on the results
    • +
    • Hired 4 specialists for the product department and grew from a designer to a team lead
    • +
    • Established departmental processes: design reviews, backlog management, and handoff to development
    • +
    • Automated the team’s internal routines, freeing up time for product tasks
    • +
    +
    +
    +

    Stack

    +
      + +
    • Zapier
    • +
    • Figma
    • +
    • Miro
    • +
    • Mailchimp
    • +
    • Windows
    • +
    • Notion
    • +
    • Airtable
    • +
    +
    -
  8. -
    + -
    - -
    -
    -

    Orion Innovation (formerly MERA)

    -

    July 2016 – October 2017 (1 year and 4 months)·orioninnovation.com

    -
    -

    Software development for external clients.

    - -
    -
    -
    -

    Responsibilities:

    -
      -
    • Worked in a design department with up to 8 people
    • -
    • Creating user personas and mapping product functionality based on user needs
    • -
    • Creating wireframes and designing the final UI
    • -
    • Designing use cases, screen maps, and micro-animation demos
    • -
    • Preparing mood boards and design concepts, and discussing them with clients
    • -
    • Preparing and delivering presentations on design concepts
    • -
    • Turning mockups into prototypes, writing scripts, and conducting qualitative usability testing
    • -
    • Making revisions based on test results
    • -
    • Overseeing the implementation of designs by developers
    • -
    • Participating in design reviews: analyzing colleagues’ work and defending your own decisions
    • -
    • Direct communication with international clients to discuss design and receive project briefs
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Conducted 10 usability testing sessions, from writing the test script to implementing changes
    • -
    • Learned to work with the Smart TV platform, which was new to me, and brought the project to successful release in the app store
    • -
    • Communicated directly with international clients, without intermediaries
    • -
    • Actively participated in improving the company’s design department workflow
    • -
    • Completed the full product design cycle on 5 client projects
    • -
    -
    -
    -

    Stack:

    -
      -
    • Smart TV
    • -
    • Usability testing
    • -
    • Mood boards / design concepts
    • -
    +
  9. +
    +

    Aegees Messenger

    +

    — (1 year and 3 months)·aegees.com

    +
    +

    A cross-platform messenger with secure storage and audio calls.

    +
    +
    +
    +

    Responsibilities

    +
      +
    • Designing the messenger for five platforms simultaneously: Android, iOS, macOS, Windows, and Linux
    • +
    • Developing use cases for secure storage and audio calls
    • +
    • Designing scenarios for registration, encryption, and access recovery
    • +
    • Adapting a unified product logic to each platform’s guidelines
    • +
    • Supervising the work of international illustrators in the preparation of graphic assets
    • +
    • Preparation of materials for app stores
    • +
    +
    +
    +

    Achievements

    +
      +
    • Developed a unified visual language for the product while adhering to the guidelines of five platforms simultaneously
    • +
    • Designed over 90 screens across five platform versions
    • +
    • Ensured a steady flow of graphic assets from external contractors without compromising the design style
    • +
    • Simplified the secure storage workflow so that people without technical training could use it
    • +
    • Worked in the sensitive areas of privacy and security, where a single interface error can cost user trust
    • +
    +
    +
    +

    Stack

    +
      +
    • Android
    • +
    • iOS
    • +
    • macOS
    • +
    • Windows
    • +
    • Linux
    • +
    +
    -
  10. -
    + -
    - -
    -
    -

    SpaceshipApps Studio

    -

    June 2015 — June 2016 (1 year and 1 month)·spaceshipapps.ru

    -
    -

    Mobile app and website development studio.

    - -
    -
    -
    -

    Responsibilities:

    -
      -
    • Designing mobile and web products as part of the studio’s workflow
    • -
    • Worked across various sectors: social gaming, beauty services, food delivery, voice interfaces, legal services, and collaborative tools
    • -
    • Responsive web design and design of administrative dashboards
    • -
    • Illustrating icons and preparing graphics for projects
    • -
    • Preparing mockups for handoff to development
    • -
    -
    -
    -

    Achievements:

    -
      -
    • Launched 6 products in one year: a geolocation-based social game with real-world quests, an app for booking beauty professionals, a food delivery service, a voice-based word game, a website and admin panel for a copyright registration service, and a mobile “whiteboard” for collaboration
    • -
    • Learned to quickly get up to speed in new subject areas and maintain quality while keeping up with the studio’s fast pace
    • -
    • Handled the full range of tasks: mobile, web, admin panels, and graphics
    • -
    • Laid the groundwork for transitioning from execution-focused design to product-focused design
    • -
    -
    -
    -

    Stack:

    -
      -
    • Mobile
    • -
    • Web
    • -
    • Dashboards
    • -
    • Graphics
    • -
    +
  11. +
    +

    Orion Innovation (formerly MERA)

    +

    July 2016 – October 2017 (1 year and 4 months)·orioninnovation.com

    +
    +

    Software development for external clients.

    +
    +
    +
    +

    Responsibilities

    +
      +
    • Worked in a design department with up to 8 people
    • +
    • Creating user personas and mapping product functionality based on user needs
    • +
    • Creating wireframes and designing the final UI
    • + +
    • Preparing mood boards and design concepts, and discussing them with clients
    • + +
    • Turning mockups into prototypes, writing scripts, and conducting qualitative usability testing
    • + + +
    • Participating in design reviews: analyzing colleagues’ work and defending your own decisions
    • + +
    +
    +
    +

    Achievements

    +
      +
    • Conducted 10 usability testing sessions, from writing the test script to implementing changes
    • +
    • Learned to work with the Smart TV platform, which was new to me, and brought the project to successful release in the app store
    • +
    • Conducted research and reverse-engineering of app design for the Roku TV platform and created a client app design development workflow that includes: idea formation, clickable prototype development, usability testing on the actual device, and design control in the app built by our developers
    • +
    • Communicated directly with international clients, without intermediaries
    • +
    • Actively participated in improving the company’s design department workflow
    • +
    • Introduced the team to using Miro (ex. Realtime Board) for storing research artifacts
    • + +
    +
    +
    +

    Stack

    +
      +
    • Sketch
    • +
    • Miro
    • +
    • Android
    • +
    • iOS
    • +
    • ROKU TV Box
    • +
    • Smart TV
    • +
    • Marvel App
    • +
    • ProtoPie
    • +
    • Framer
    • + + +
    +
    -
  12. -
    + -
    +
  13. +
    +

    SpaceshipApps Studio

    +

    — (1 year and 1 month)·spaceshipapps.ru

    +
    +

    Mobile app and website development studio.

    +
    +
    +
    +

    Responsibilities

    +
      +
    • Designing mobile and web products as part of the studio’s workflow
    • +
    • Worked across various sectors: social gaming, beauty services, food delivery, voice interfaces, legal services, and collaborative tools
    • +
    • Responsive web design and design of administrative dashboards
    • +
    • Illustrating icons and preparing graphics for projects
    • +
    • Preparing mockups for handoff to development
    • +
    +
    +
    +

    Achievements

    +
      +
    • Launched 6 products in one year: a geolocation-based social game with real-world quests, an app for booking beauty professionals, a food delivery service, a voice-based word game, a website and admin panel for a copyright registration service, and a mobile “whiteboard” for collaboration
    • +
    • Learned to quickly get up to speed in new subject areas and maintain quality while keeping up with the studio’s fast pace
    • +
    • Handled the full range of tasks: mobile, web, admin panels, and graphics
    • +
    • Laid the groundwork for transitioning from execution-focused design to product-focused design
    • +
    +
    +
    +

    Stack

    +
      +
    • Mobile
    • +
    • Web
    • +
    • Adobe Photoshop
    • +
    • After Effects
    • +
    • Adobe Illustrator
    • -

      Skills

      -
      + + +
    +
    +
    +
    +
  14. +
+
+ +
+

Skills

+
-

Design

-

UX/UI, product design, information architecture, responsive design, design systems

+
Design
+
UX/UI, product design, information architecture, responsive design, design systems
-

Research

-

Interviews, surveys, usability testing, JTBD, empathy maps, conversion, retention, NPS

+
Research
+
Interviews, surveys, usability testing, JTBD, empathy maps, conversion, retention, NPS
-

Tools

-

Figma, Protopie, Sketch, Adobe Creative Suite, Tilda, AI tools

+
Management
+
Team building, Agile, Kanban, project supervision, working with stakeholders
-

Management

-

Team building, Agile, Kanban, project supervision, working with stakeholders

+
Tools
+
Figma, Protopie, Sketch, Adobe Creative Suite, Storybook
-
+
+
AI Tools
+
Hermes Agent, Perplexity, Cursor IDE, Zed IDE, Open Design, Codex, Claude Code
+
+ + -
+
+

About Me

+

+ I specialize in SaaS products and desktop applications. I design clear, scalable, and efficient interfaces for complex workflows, so users solve their tasks faster and the product becomes more convenient and useful. +

+

+ I work across every stage of product design: researching user needs, shaping information architecture, designing user flows, building interactive prototypes with vibe coding, and creating scalable design systems with Figma + Storybook. +

+

+ I stay active in the community: I run the Telegram channel “Design Isn’t Pizza,” share resources on Figma Community, spoke about voice skill analytics at a Yandex meetup, and talked about a designer’s toolkit at a business forum. I’ve won hackathons more than once. +

+
-

About Me

-

- I’ve been in design for over 10 years, and in recent years I’ve focused on product design for complex B2B products. I work at the intersection of design and product management: I conduct research, formulate hypotheses and features, help with prioritization, and participate in release planning. -

-

- I love diving into complex topics and making them understandable—which is why I feel right at home with products like IDEs, orchestration tools, and ML dashboards, where a typical designer might find the work boring or intimidating. -

-

- I don’t wait for someone else to solve a problem—I build my own tools. I’ve created a private AI plugin for Figma, automate processes using n8n and PostgreSQL, and turn my ideas into working products through AI-assisted development. My skills for Alisa have attracted over 37,000 and 33,000 users, and the Slider Set file for designers has been downloaded more than 20,000 times. -

-

- I’m active in the community: I run the Telegram channel “Design Isn’t Pizza,” share useful resources on the Figma Community, gave a talk on analytics at a Yandex meetup, and have won hackathons—my most memorable victory was at the Yandex.I’m currently in St. Petersburg working on a QR code payment app. -

-

- English—B1. I work remotely and communicate smoothly with a distributed team. -

-
+
{{> footer}} diff --git a/src/pages/resume.html b/src/pages/resume.html index a23122a..6223175 100644 --- a/src/pages/resume.html +++ b/src/pages/resume.html @@ -9,220 +9,23 @@ /> Резюме — Николай Мамадаев {{> head-links}} - {{> header-minimal}} {{> fab-stack-resume}}
-
-
- Николай Мамадаев +
+
+
+ Николай Мамадаев

Резюме

Николай Мамадаев

-

Senior UX/UI Designer, Product Designer

+

Senior UX/UI Designer
+ Product Designer
+ AI Designer

-
- -
+
@@ -230,8 +33,8 @@
mamadaevv.site
-
Вилка по ЗП
-
250 000 – 310 000 руб.
+
Зарплата
+
от 250 000 ₽
Формат работы
@@ -245,567 +48,579 @@
Соц.сети
@mamadaevv
+
+
Знание языков
+
Английский (B2)
+
- -
- - -

О себе

-

- В дизайне больше 10 лет, последние годы — продуктовый дизайн сложных B2B-продуктов. Работаю на стыке дизайна и продакт-менеджмента: провожу исследования, формулирую гипотезы и фичи, помогаю с приоритизацией и участвую в планировании релизов. -

-

- Люблю разбираться в сложном и делать его понятным — поэтому мне комфортно в продуктах вроде IDE, оркестраторов и ML-панелей, где обычному дизайнеру скучно или страшно. -

-

- Не жду, пока задачу сделает кто-то другой: пишу себе инструменты сам. Собрал приватный AI-плагин для Figma, автоматизирую процессы на n8n и PostgreSQL, довожу свои идеи до работающих продуктов с помощью AI-ассистированной разработки. Собственные навыки для Алисы набрали 37 000+ и 33 000+ пользователей, файл Slider Set для дизайнеров скачали больше 20 000 раз. -

-

- Участвую в жизни сообщества: веду Telegram-канал «Дизайн не пицца», выкладываю полезности в Figma Community, выступал на митапе Яндекса с докладом по аналитике, побеждал на хакатонах — самая приятная победа на хакатоне Яндекс.Денег в Петербурге за приложение оплаты по QR-коду. -

-

- Английский — B1. Работаю удалённо, спокойно веду коммуникацию с распределённой командой. -

- -
-

Опыт работы

- -
- -
-
-

Astratech

-

Январь 2026 — Настоящее время

-
-

Разработка и поддержка цифровых систем и инструментов для повышения эффективности сотрудников.

- -
-
-
-

Обязанности:

-
    -
  • Занимался дизайном интерфейсов
  • -
-
-
-

Достижения:

-
    -
  • Успешно закрывал входящие задачи
  • -
-
-
-

Стэк:

-
    -
  • Figma
  • -
  • HTML/CSS
  • -
  • AI/LLM
  • -
-
-
-
-
+
+

О себе

+

+ Специализируюсь на SaaS-продуктах и desktop-приложениях. Проектирую понятные, масштабируемые и эффективные интерфейсы для сложных рабочих процессов — чтобы пользователи быстрее решали свои задачи, а продукт становился удобнее и полезнее. +

+

+ Работаю на всех этапах продуктового дизайна: изучаю потребности пользователей, формирую информационную архитектуру, проектирую пользовательские сценарии, создаю интерактивные прототипы используя вайбкоддинг и масштабируемые дизайн-системы в связке Figma + Storybook. +

+

+ Участвую в жизни сообщества: веду Telegram-канал «Дизайн не пицца», выкладываю полезности в Figma Community, выступал на митапе Яндекса с докладом про аналитику голосовых навыков и на бизнес-форуме с рассказом про инструменты дизайнера. Неоднократно побеждал на хакатонах. +

+
-
-
-

Primo RPA

-

Февраль 2023 — Ноябрь 2025 (2 года и 10 месяцев)·primo-rpa.com

-
-

Российская RPA-платформа корпоративного уровня: линейка из desktop-среды разработки роботов и веб-сервисов управления, аналитики и ML. Работал ведущим дизайнером на всю продуктовую линейку из 6 продуктов.

+
+

Опыт работы

+
    +
  1. +
    +

    Astratech

    +

    — Настоящее время

    +
    +

    Разработка и поддержка цифровых систем и инструментов для повышения эффективности сотрудников.

    -
    -

    AI Server platform (Saas)

    -

    Панель управления для обучения/запуска/тестирования и других операций с нейросетями развёрнутыми внутри контура компании.

    -
    -
    -

    Обязанности:

    -
      -
    • Проектирование интерфейса управления моделями: обучение, запуск, тестирование, версионирование, мониторинг
    • -
    • Проработка сценариев работы с Smart OCR — извлечение данных из документов различных типов
    • -
    • Проектирование разметки документов и валидации результатов распознавания
    • -
    • Работа с состояниями долгих процессов: обучение модели, прогресс, ошибки, откат
    • -
    • Подготовка интерактивных прототипов и авторский надзор за реализацией
    • -
    • Проведение интервью с ML-инженерами и бизнес-пользователями для сверки моделей поведения
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Упаковал сложную ML-логику в интерфейс, понятный не только ML-инженеру, но и бизнес-аналитику без технического бэкграунда
    • -
    • Свёл работу с моделями к единому предсказуемому сценарию из 4 шагов вместо 9 разрозненных экранов
    • -
    • Сократил время настройки новой модели с 40 до 15 минут по результатам замеров на тестировании
    • -
    • Спроектировал интерфейс разметки документов, который снял 60% обращений в поддержку по этой теме
    • -
    • Провёл 8 юзабилити-сессий, по итогам переработал ключевой сценарий обучения модели
    • -
    -
    -
    -

    Стэк:

    -
      -
    • ML / нейросети
    • -
    • Smart OCR
    • -
    +
    +
    +
    +

    Обязанности

    +
      +
    • Создание готовых к продакшену интерфейсов
    • +
    • Разбор идей и концепций и трансформация их в дизайнерские задачи
    • +
    +
    +
    +

    Достижения

    +
      +
    • Реализовал настроенную среду доставки интерфейсов до продакшена
    • +
    • Провёл более 15 тестов гипотез на рабочих прототипах без привлечения отдела разработки
    • +
    • Ускорил процесс обработки результатов пользовательского тестирования за счёт применения AI инструментов (суммаризация, классификация, трансрибация)
    • +
    +
    +
    +

    Стэк

    +
      +
    • Figma
    • +
    • HTML/CSS/JS
    • +
    • AI/LLM
    • +
    • GitHub
    • +
    • Gitea
    • +
    • Coolify
    • +
    • CI/CD
    • +
    • Hermes Agent
    • +
    • Zed IDE
    • +
    +
    -
    +
  2. -
    - -
    -

    Robot automation studio (Windows)

    -

    Среда для разработки и отладки роботов для автоматизации бизнес-процессов.

    -
    -
    -

    Обязанности:

    -
      -
    • Проектирование интерфейса профессиональной desktop-IDE: рабочая область, панели инструментов, дерево процесса, отладчик
    • -
    • Развитие библиотеки компонентов и визуального языка продукта
    • -
    • Проработка сценариев отладки: точки останова, пошаговое выполнение, просмотр переменных
    • -
    • Проектирование работы с большими деревьями активностей и вложенностью
    • -
    • Работа в паре с разработкой по реализации компонентов, авторский надзор
    • -
    • Ведение регулярных дизайн-ревью внутри команды
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Обновил визуальный язык продукта до уровня Windows 10/11 — компоненты стали на поколение современнее
    • -
    • Переработал 80+ экранов и диалогов, приведя их к единой сетке и типографике
    • -
    • Собрал библиотеку из 140 компонентов, закрывшую 90% потребностей интерфейса
    • -
    • Перевёл иконки и часть иллюстраций на векторную графику: корректное отображение при любом масштабировании и DPI
    • -
    • Сократил время сборки нового экрана командой с 2 дней до 4 часов за счёт готовых компонентов
    • -
    -
    -
    -

    Стэк:

    -
      -
    • Desktop IDE
    • -
    • Windows 10/11
    • -
    • Компонентная библиотека
    • -
    +
  3. +
    +

    Primo RPA

    +

    — (2 года и 10 месяцев)·primo-rpa.com

    +
    +

    Российская B2B RPA-платформа корпоративного уровня: линейка из desktop-среды разработки роботов и веб-сервисов управления, аналитики и ML.
    + Работал ведущим дизайнером на всю продуктовую линейку из 6 продуктов.

    +
    +

    AI Server platform (SaaS)

    +

    Панель управления для обучения/запуска/тестирования и других операций с нейросетями, развёрнутыми внутри контура компании.

    +
    +
    +
    Обязанности
    +
      +
    • Спроектировать web интерфейс панели управления локальных LLM. Реализовать возможность выполнения сценариев: создание датасетов, обучение узконаправленных моделей, быстрое тестирование и администрирование развёртыванием на локальной инфраструктуре клиента.
    • +
    • Улучшить способ конфигурирования работы Smart OCR для более точного извлечения данных из документов различного типа
    • +
    • Работа с состояниями долгих процессов: обучение модели, прогресс, ошибки, откат
    • +
    • Подготовка интерактивных прототипов и авторский надзор за реализацией
    • +
    +
    +
    +
    Достижения
    +
      +
    • Создал интуитивный интерфейс для работы со сложной настройкой LLM моделей. Таким образом существенно снизил порог входа для пользователей без глубокой экспертизы в работе нейросетей.
    • +
    • Сократил время настройки с 3-5 дней до 2 часов по результатам общения с пользователями
    • +
    • Спроектировал интерфейс разметки документов, который снял 60% обращений в поддержку по этой теме
    • +
    • Провёл 8 юзабилити-сессий, по итогам переработал ключевой сценарий обучения модели
    • +
    +
    +
    +
    Стэк
    +
      +
    • Figma
    • +
    • React
    • +
    • Ant Design
    • +
    • Hope UI
    • +
    • LLM core
    • +
    • Smart OCR
    • +
    +
    -
  4. - -
    - -
    -

    Robot automation studio (Linux)

    -

    Среда для разработки и отладки роботов для автоматизации бизнес-процессов.

    -
    -
    -

    Обязанности:

    -
      -
    • Адаптация дизайна Студии под Linux-версию на фреймворке Avalonia
    • -
    • Проектирование компонентов с учётом технических ограничений фреймворка
    • -
    • Синхронизация визуального языка между Windows- и Linux-версиями продукта
    • -
    • Совместная работа с разработкой над тем, что технически реализуемо в Avalonia, а что требует обходных решений
    • -
    • Подготовка спецификаций поведения компонентов для реализации
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Собрал расширенную библиотеку из 95 компонентов для Avalonia, которой в проекте не существовало до меня
    • -
    • Обеспечил единообразие продукта на двух платформах при разных технологических стеках
    • -
    • Сократил количество визуальных расхождений между версиями с нескольких десятков до единичных случаев
    • -
    • Дал команде Linux-версии готовый набор решений вместо дизайна каждого экрана с нуля
    • -
    -
    -
    -

    Стэк:

    -
      -
    • Avalonia
    • -
    • .NET
    • -
    • Linux
    • -
    + +
    +

    Robot automation studio (Windows)

    +

    Среда для разработки и отладки роботов для автоматизации бизнес-процессов.

    +
    +
    +
    Обязанности
    +
      +
    • Проектирование интерфейса профессиональной desktop-IDE: рабочая область, панели инструментов, дерево процесса, отладчик
    • +
    • Развитие библиотеки компонентов и визуального языка продукта
    • +
    • Проработка сценариев отладки: точки останова, пошаговое выполнение, просмотр переменных
    • +
    • Проектирование работы с большими деревьями активностей и вложенностью
    • +
    • Работа в паре с разработкой по реализации компонентов, авторский надзор
    • +
    • Ведение регулярных дизайн-ревью внутри команды
    • +
    +
    +
    +
    Достижения
    +
      +
    • Обновил визуальный язык продукта до уровня Windows 11 — компоненты стали на поколение современнее
    • +
    • Переработал 10-ки экранов и модальных окон, приведя их к единой дизайн-системе основанной на токенах.
    • +
    • Собрал библиотеку из более чем 50-ти компонентов, которые можно переиспользовать и проектировать сценарии нового функционала с сохранением заложенных паттернов поведения
    • +
    • Перерисовал все старые растровые иконки в новые векторные. Таким образом появилась библиотека уникальных иконок количеством более 300 штук. Идеально отображаются на любом масштабе UI и DPI
    • +
    • Сократил время сборки нового экрана командой с 2 дней до 4 часов за счёт готовых компонентов
    • +
    +
    +
    +
    Стэк
    +
      +
    • Desktop IDE
    • +
    • Windows 10/11
    • +
    • Figma
    • +
    • AI инструменты
    • +
    • DevExpress UI
    • +
    • Handy Controls
    • +
    +
    -
    - -
    - -
    -

    Idea Hub Dashboard (Saas)

    -

    Обеспечивает сбор и анализ предложений сотрудников, оценка эффективности и мониторинг статуса реализации автоматизаций. Аналитика по ключевым показателям эффективности и верхнеуровневый взгляд на роботизацию.

    -
    -
    -

    Обязанности:

    -
      -
    • Проектирование дашбордов и аналитических представлений по KPI роботизации
    • -
    • Проработка жизненного цикла идеи: подача → оценка → приоритизация → реализация → замер эффекта
    • -
    • Информационная архитектура раздела, работа с плотными таблицами, фильтрами и массовыми операциями
    • -
    • Проектирование ролевой модели: сотрудник, аналитик, руководитель
    • -
    • Подбор типов визуализации данных под конкретные управленческие вопросы
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Свёл верхнеуровневую аналитику и работу с конкретной идеей в один непрерывный сценарий без переключения между разделами
    • -
    • Спроектировал представление данных так, что руководитель видит эффект от роботизации без ручных выгрузок в Excel
    • -
    • Сократил путь от подачи идеи до её оценки с 6 шагов до 3
    • -
    • Спроектировал 12 виджетов аналитики, покрывающих основные вопросы заказчиков о ROI роботизации
    • -
    -
    -
    -

    Стэк:

    -
      -
    • Аналитика / дашборды
    • -
    • KPI
    • -
    • Визуализация данных
    • -
    + +
    +

    Robot automation studio (Linux)

    +

    Среда для разработки и отладки роботов для автоматизации бизнес-процессов.

    +
    +
    +
    Обязанности
    +
      +
    • Разработка интерфейса IDE для платформы Linux с учётом выбранного функционала на основе реализованной версии под Windows
    • +
    • Проектирование компонентов с учётом технических ограничений фреймворка Avalonia Suki UI
    • +
    • Подготовка спецификаций поведения компонентов для реализации
    • +
    +
    +
    +
    Достижения
    +
      +
    • Собрал библиотеку компонентов основанную на библиотеке Suki UI из которых можно быстро собирать интерфейс в Figma и передавать разработчикам
    • +
    • Разработал стиль программы, которая соответствует международному уровню, что положительно скажется при экспансии зарубежных рынков
    • +
    • Передал дизайн-инструкции по сборке экранов разработчикам, чтобы они могли самостоятельно собирать интерфейс под новые функции и выпускать релизы согласно роадмэп
    • +
    +
    +
    +
    Стэк
    +
      +
    • Avalonia
    • +
    • Suki UI
    • +
    • Figma
    • +
    • AI инструменты
    • +
    • Cursor IDE
    • +
    • MobaXterm RDP
    • +
    • Linux
    • +
    +
    -
    - -
    - -
    -

    Orchestrator Automation Management Tool (Saas)

    -

    Обеспечивает слаженную работу системы и управление сотнями роботов: запуски, мониторинг и менеджмент.

    -
    -
    -

    Обязанности:

    -
      -
    • Проектирование интерфейса управления парком из сотен роботов: расписания, очереди, приоритеты, мониторинг
    • -
    • Проработка состояний и обработки ошибок при массовых запусках
    • -
    • Проектирование системы уведомлений и алертов о сбоях
    • -
    • Работа со сложными таблицами: сортировка, фильтрация, массовые действия, сохранённые представления
    • -
    • Прототипирование сложных сценариев для валидации до начала разработки
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Спроектировал управление сотнями объектов так, что оператор не теряется в масштабе: от общей картины до конкретного робота за 2 клика
    • -
    • Привёл сервис к единой дизайн-системе вместе с остальными веб-продуктами линейки
    • -
    • Сократил время реакции оператора на сбой робота с 12 до 4 минут за счёт переработанной системы алертов
    • -
    • Свёл 5 разных представлений списка роботов к одному настраиваемому
    • -
    -
    -
    -

    Стэк:

    -
      -
    • Мониторинг
    • -
    • Расписания / очереди
    • -
    • Уведомления / алерты
    • -
    + +
    +

    Idea Hub Dashboard (SaaS)

    +

    Обеспечивает сбор и анализ предложений сотрудников, оценка эффективности и мониторинг статуса реализации автоматизаций. Аналитика по ключевым показателям эффективности и верхнеуровневый взгляд на роботизацию.

    +
    +
    +
    Обязанности
    +
      +
    • Проектирование дашбордов и аналитических представлений по KPI роботизации
    • +
    • Проработка жизненного цикла идеи: подача → оценка → приоритизация → реализация → замер эффекта
    • +
    • Информационная архитектура раздела, работа с плотными таблицами, фильтрами и массовыми операциями
    • +
    • Проектирование ролевой модели: сотрудник, аналитик, руководитель
    • +
    • Подбор типов визуализации данных под конкретные управленческие вопросы
    • +
    +
    +
    +
    Достижения
    +
      +
    • Свёл верхнеуровневую аналитику и работу с конкретной идеей в один непрерывный сценарий без переключения между разделами
    • +
    • Спроектировал представление данных так, что руководитель видит эффект от роботизации без ручных выгрузок в Excel
    • +
    • Сократил путь от подачи идеи до её оценки с 6 шагов до 3
    • +
    • Спроектировал 12 виджетов аналитики, покрывающих основные вопросы заказчиков о ROI роботизации
    • +
    +
    +
    +
    Стэк
    +
      +
    • Figma
    • + + +
    +
    -
    - -
    - -
    -

    QnA portal (WEB)

    -

    Площадка для постинга вопросов и получения ответов от службы поддержки

    -
    -
    -

    Обязанности:

    -
      -
    • Проектирование сценариев публикации вопроса, ответа и поиска по накопленной базе знаний
    • -
    • Дизайн интерфейса портала на базе общей дизайн-системы компании
    • -
    • Проработка ролевой модели: пользователь, эксперт, служба поддержки, модератор
    • -
    • Проектирование механики поиска и подсказок похожих вопросов при публикации
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Запустил портал на готовых компонентах дизайн-системы за 3 недели вместо запланированных 8
    • -
    • Снизил нагрузку на поддержку примерно на 25% за счёт переиспользования уже данных ответов
    • -
    • Механика подсказок похожих вопросов сократила количество дублей на 45%
    • -
    • Обеспечил визуальную преемственность с продуктами линейки — портал не выглядит сторонним сервисом
    • -
    -
    -
    -

    Стэк:

    -
      -
    • Веб-портал
    • -
    • Дизайн-система
    • -
    • База знаний
    • -
    + +
    +

    Orchestrator Automation Management Tool (SaaS)

    +

    Обеспечивает слаженную работу системы и управление сотнями роботов: запуски, мониторинг и менеджмент.

    +
    +
    +
    Обязанности
    +
      +
    • Проектирование интерфейса управления парком из сотен роботов: расписания, очереди, приоритеты, мониторинг
    • +
    • Проработка состояний и обработки ошибок при массовых запусках
    • +
    • Проектирование системы уведомлений и алертов о сбоях
    • +
    • Работа со сложными таблицами: сортировка, фильтрация, массовые действия, сохранённые представления
    • +
    • Прототипирование сложных сценариев для валидации до начала разработки
    • +
    +
    +
    +
    Достижения
    +
      +
    • Спроектировал управление сотнями объектов так, что оператор не теряется в масштабе: от общей картины до конкретного робота за 2 клика
    • +
    • Привёл сервис к единой дизайн-системе вместе с остальными веб-продуктами линейки
    • +
    • Сократил время реакции оператора на сбой робота с 12 до 4 минут за счёт переработанной системы алертов
    • +
    • Свёл 5 разных представлений списка роботов к одному настраиваемому
    • +
    +
    +
    +
    Стэк
    +
      +
    • Figma
    • +
    • Ant Design
    • +
    • Storybook
    • +
    • AI инструменты
    • + + +
    +
    -
    - -
    - -
    -

    Общее по компании

    -
      -
    • Инициировал обновление внешнего вида всей продуктовой линейки — решение о редизайне компания приняла с моим приходом
    • -
    • Собрал и внедрил единую дизайн-систему для веб-сервисов, соединив фирменный стиль компании с новой структурой интерфейсов
    • -
    • Дизайн-систему приняли 4 продуктовые команды; она включает 210 компонентов и токены темизации
    • -
    • Сократил среднее время подготовки макетов по линейке на 35%
    • -
    • Создавал и поддерживал UI-kit для консистентности и ускорения разработки
    • -
    • Провёл более 20 пользовательских исследований и юзабилити-сессий за время работы
    • -
    • Вёл авторский надзор за реализацией, участвовал в дизайн-ревью, продуктовых обсуждениях и планировании релизов
    • -
    • Был единственным ведущим дизайнером на всю линейку из 6 продуктов и двух платформ
    • -
    -
    -
    - -
    - -
    -
    -

    Lunacy (Icons8)

    -

    Июль 2022 — Январь 2023 (7 месяцев)·icons8.com/lunacy

    -
    -

    Графический редактор для работы над дизайном интерфейсов и прочих макетов. Аналог Sketch App и Figma.

    - -
    -
    -
    -

    Обязанности:

    -
      -
    • Проектирование интерфейса графического редактора: инструменты, панели свойств, работа с холстом и слоями
    • -
    • Оформление решений в виде юзерфлоу и передача разработчикам
    • -
    • Создание сложных интерактивных прототипов в Protopie для демонстрации поведения инструментов
    • -
    • Отрисовка недостающих иконок в стилистике продукта
    • -
    • Внесение предложений по улучшению функциональности редактора
    • -
    • Участие в обсуждениях продуктовых решений с командой
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Спроектировал 6 функциональных блоков редактора, включая работу с компонентами и стилями
    • -
    • Собрал 15+ прототипов в Protopie, что позволило отсечь неудачные решения до разработки
    • -
    • Часть моих предложений по функциональности вошла в релизы продукта
    • -
    • Отрисовал более 60 иконок, дополнивших системный набор редактора
    • -
    • Работал над продуктом для профессиональной аудитории дизайнеров, где требования к деталям интерфейса выше обычных
    • -
    -
    -
    -

    Стэк:

    -
      -
    • Protopie
    • -
    • Графический редактор
    • -
    • Иконки / иллюстрации
    • -
    + +
    +

    QnA portal (WEB)

    +

    Площадка для постинга вопросов и получения ответов от службы поддержки

    +
    +
    +
    Обязанности
    +
      +
    • Проектирование сценариев публикации вопроса, ответа и поиска по накопленной базе знаний
    • +
    • Дизайн интерфейса портала в условиях ограничения используемого PHP движка для QnA типа сайта.
    • +
    • Проработка ролевой модели: пользователь, эксперт, служба поддержки, модератор
    • +
    • Проектирование механики поиска и подсказок похожих вопросов при публикации
    • +
    +
    +
    +
    Достижения
    +
      +
    • Запустил портал на готовых компонентах дизайн-системы за 3 недели вместо запланированных 8
    • +
    • Снизил нагрузку на поддержку примерно на 25% за счёт переиспользования уже данных ответов
    • +
    • Механика подсказок похожих вопросов сократила количество дублей на 45%
    • +
    • Обеспечил визуальную преемственность с продуктами линейки — портал не выглядит сторонним сервисом
    • +
    +
    +
    +
    Стэк
    +
      +
    • PHP QnA Engine
    • +
    • Figma
    • +
    • Дизайн-система
    • + +
    +
    -
    -
    + +
    +

    Общее по компании

    +
      + +
    • Собрал и внедрил единую дизайн-систему для веб-сервисов, соединив фирменный стиль компании с новой структурой интерфейсов
    • + +
    • Сократил среднее время подготовки макетов по линейке на 35%
    • +
    • Создавал и поддерживал UI-kit для консистентности и ускорения разработки
    • +
    • Провёл более 20 пользовательских исследований и юзабилити-сессий за время работы
    • +
    • Вёл авторский надзор за реализацией, участвовал в дизайн-ревью, продуктовых обсуждениях и планировании релизов
    • +
    • Нанял 4 дизайнеров интерфейсов для разных проектов компании
    • +
    • Провел больше 30 собеседований, в том числе внедрил формат WhiteBoard Interview
    • +
    • Разрабатывал тестовые задания с учётом потребностей конкретной должности и задач
    • +
    • Внедрил внутреннюю систему видеозвонков на основе Jitsi во внутреннем контуре компании совместно с отделом технической поддержки компании
    • + +
    +
    + -
    - -
    -
    -

    MANU Video Game Maker

    -

    Январь 2019 — Декабрь 2021 (3 года)·manu.co

    -
    -

    Игровой движок для создания видеоигр без написания кода.

    - -
    -
    -
    -

    Обязанности:

    -
      -
    • Продуктовый дизайн движка целиком: от концепции взаимодействия до конечного UI
    • -
    • Проектирование поведения интерфейса для сложных пользовательских сценариев
    • -
    • Исследования пользователей: наблюдение, глубинные интервью, опросы
    • -
    • Составление портретов потенциальных пользователей и карт эмпатии
    • -
    • Работа с продуктовой аналитикой и метриками, завязанными на пользовательские сценарии
    • -
    • Вёрстка главной страницы сайта на Tilda, проектирование информационной архитектуры сайта вместе с командой
    • -
    • Контроль работы веб-графического дизайнера
    • -
    • Найм в продуктовый отдел: проведение собеседований и отбор специалистов
    • -
    • Руководство отделом, построение рабочих процессов всей продуктовой команды
    • -
    • Кураторство работ по созданию пользовательской документации
    • -
    • Внедрение инструментов автоматизации (Integromat, Zapier) для внутренних задач
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Провёл масштабный редизайн всей программы: заложил принципиально новую концепцию работы и стилистику
    • -
    • Спроектировал поведение интерфейса для 100+ сложных пользовательских сценариев
    • -
    • Спроектировал дизайн-систему продукта с нуля: 120 компонентов
    • -
    • Получили массу положительных, местами восторженных отзывов пользователей — в продукте стало значительно легче ориентироваться
    • -
    • Завершаемость онбординга выросла с 34% до 58% после редизайна
    • -
    • Провёл 25 глубинных интервью и 4 волны опросов, на их основе переработал 3 ключевых сценария
    • -
    • Нанял 4 специалистов в продуктовый отдел и вырос из дизайнера в тимлида команды
    • -
    • Выстроил процессы отдела: дизайн-ревью, работа с бэклогом, передача в разработку
    • -
    • Автоматизировал внутренние рутины команды, освободив время на продуктовые задачи
    • -
    -
    -
    -

    Стэк:

    -
      -
    • Tilda
    • -
    • Integromat / Zapier
    • -
    • Исследования пользователей
    • -
    +
  5. +
    +

    Lunacy (Icons8)

    +

    — (7 месяцев)·icons8.com/lunacy

    +
    +

    Графический редактор для работы над дизайном интерфейсов и прочих макетов. Аналог Sketch App и Figma.

    +
    +
    +
    +

    Обязанности

    +
      +
    • Проектирование интерфейса графического редактора: инструменты, панели свойств, работа с холстом и слоями
    • +
    • Оформление решений в виде юзерфлоу и передача разработчикам
    • +
    • Создание сложных интерактивных прототипов в Protopie для демонстрации поведения инструментов
    • +
    +
    +
    +

    Достижения

    +
      +
    • Спроектировал 6 функциональных блоков редактора, включая работу с компонентами и стилями
    • +
    • Собрал прототип нового функционала в Protopie для редактирования многослойной реалистичной тени от объекта со сложной логикой поведения, практически не отличимый от решения в продакшене. Не считая мелких простых прототипов.
    • +
    • Провалидировал 3 гипотезы относительно улучшений работы редактора на реальных пользователях. 1 ушла в релиз.
    • +
    +
    +
    +

    Стэк

    +
      +
    • Protopie
    • +
    • Figma
    • +
    • Дизайн-система
    • +
    • Avalonia Custom UI
    • +
    • Windows
    • +
    • Linux
    • +
    • macOS
    • +
    +
    -
  6. -
    + -
    - -
    -
    -

    Aegees Messenger

    -

    Октябрь 2017 — Декабрь 2018 (1 год и 3 месяца)·aegees.com

    -
    -

    Кроссплатформенный мессенджер с защищенным хранилищем и аудиозвонками.

    - -
    -
    -
    -

    Обязанности:

    -
      -
    • Дизайн мессенджера сразу под пять платформ: Android, iOS, macOS, Windows, Linux
    • -
    • Проработка сценариев защищённого хранилища и аудиозвонков
    • -
    • Проектирование сценариев регистрации, шифрования и восстановления доступа
    • -
    • Адаптация единой продуктовой логики под гайдлайны каждой платформы
    • -
    • Контроль работы зарубежных иллюстраторов по подготовке графических материалов
    • -
    • Подготовка материалов для магазинов приложений
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Выстроил единый визуальный язык продукта при соблюдении гайдлайнов пяти платформ одновременно
    • -
    • Спроектировал более 90 экранов в пяти платформенных вариантах
    • -
    • Обеспечил стабильный поток графических материалов от внешних подрядчиков без потери стилистики
    • -
    • Упростил сценарий защищённого хранилища так, чтобы им пользовались люди без технической подготовки
    • -
    • Работал в чувствительной области приватности и безопасности, где ошибка в интерфейсе стоит доверия пользователя
    • -
    -
    -
    -

    Стэк:

    -
      -
    • Android
    • -
    • iOS
    • -
    • macOS
    • -
    • Windows
    • -
    • Linux
    • -
    +
  7. +
    +

    MANU Video Game Maker

    +

    — (3 года)·manu.co

    +
    +

    Игровой движок для создания видеоигр без написания кода.

    +
    +
    +
    +

    Обязанности

    +
      +
    • Продуктовый дизайн движка целиком: от концепции взаимодействия до конечного UI
    • +
    • Проектирование поведения интерфейса для сложных пользовательских сценариев
    • +
    • Исследования пользователей: наблюдение, глубинные интервью, опросы
    • +
    • Составление портретов потенциальных пользователей и карт эмпатии
    • +
    • Работа с продуктовой аналитикой и метриками, завязанными на пользовательские сценарии
    • +
    • Вёрстка главной страницы сайта на Tilda, проектирование информационной архитектуры сайта вместе с командой
    • +
    • Контроль работы веб-графического дизайнера
    • +
    • Найм в продуктовый отдел: проведение собеседований и отбор специалистов
    • +
    • Руководство отделом, построение рабочих процессов всей продуктовой команды
    • +
    • Кураторство работ по созданию пользовательской документации
    • +
    • Внедрение инструментов автоматизации (Integromat, Zapier) для внутренних задач
    • +
    +
    +
    +

    Достижения

    +
      +
    • Провёл масштабный редизайн всей программы: заложил принципиально новую концепцию работы и стилистику
    • +
    • Спроектировал поведение интерфейса для 100+ сложных пользовательских сценариев
    • +
    • Спроектировал дизайн-систему продукта с нуля: 120 компонентов
    • +
    • Получили массу положительных, местами восторженных отзывов пользователей — в продукте стало значительно легче ориентироваться
    • +
    • Завершаемость онбординга выросла с 34% до 58% после редизайна
    • +
    • Провёл 25 глубинных интервью и 4 волны опросов, на их основе переработал 3 ключевых сценария
    • +
    • Нанял 4 специалистов в продуктовый отдел и вырос из дизайнера в тимлида команды
    • +
    • Выстроил процессы отдела: дизайн-ревью, работа с бэклогом, передача в разработку
    • +
    • Автоматизировал внутренние рутины команды, освободив время на продуктовые задачи
    • +
    +
    +
    +

    Стэк

    +
      + +
    • Zapier
    • +
    • Figma
    • +
    • Miro
    • +
    • Mailchimp
    • +
    • Windows
    • +
    • Notion
    • +
    • Airtable
    • +
    +
    -
  8. -
    + -
    - -
    -
    -

    Orion Innovation (ex MERA)

    -

    Июль 2016 — Октябрь 2017 (1 год и 4 месяца)·orioninnovation.com

    -
    -

    Разработка ПО для внешних заказчиков.

    - -
    -
    -
    -

    Обязанности:

    -
      -
    • Работа в дизайн-отделе численностью до 8 человек
    • -
    • Создание анкет персон и составление карты функциональности продукта в соответствии с потребностями персон
    • -
    • Вайрфреймы и отрисовка конечного UI интерфейсов
    • -
    • Проектирование юзкейсов, карт экранов, демонстрации микроанимаций
    • -
    • Подготовка мудбордов и дизайн-концепций, обсуждение их с клиентами
    • -
    • Подготовка и проведение презентаций дизайн-концепций
    • -
    • Линковка мокапов в прототипы, написание сценариев и проведение качественных юзабилити-тестирований
    • -
    • Внесение правок по результатам тестирований
    • -
    • Авторский надзор над реализацией дизайна разработчиками
    • -
    • Участие в дизайн-ревью: разбор работ коллег и защита собственных решений
    • -
    • Прямая коммуникация с зарубежными клиентами для обсуждения дизайна и получения задач
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Провёл 10 сессий юзабилити-тестирования от написания сценария до внесения правок
    • -
    • Освоил новую для себя платформу Smart TV и довёл проект до успешной публикации в магазине приложений
    • -
    • Вёл коммуникацию с зарубежными заказчиками напрямую, без посредников
    • -
    • Активно участвовал в улучшении рабочего процесса дизайн-отдела компании
    • -
    • Прошёл полный цикл продуктового дизайна на 5 клиентских проектах
    • -
    -
    -
    -

    Стэк:

    -
      -
    • Smart TV
    • -
    • Юзабилити-тестирование
    • -
    • Мудборды / дизайн-концепции
    • -
    +
  9. +
    +

    Aegees Messenger

    +

    — (1 год и 3 месяца)·aegees.com

    +
    +

    Кроссплатформенный мессенджер с защищенным хранилищем и аудиозвонками.

    +
    +
    +
    +

    Обязанности

    +
      +
    • Дизайн мессенджера сразу под пять платформ: Android, iOS, macOS, Windows, Linux
    • +
    • Проработка сценариев защищённого хранилища и аудиозвонков
    • +
    • Проектирование сценариев регистрации, шифрования и восстановления доступа
    • +
    • Адаптация единой продуктовой логики под гайдлайны каждой платформы
    • +
    • Контроль работы зарубежных иллюстраторов по подготовке графических материалов
    • +
    • Подготовка материалов для магазинов приложений
    • +
    +
    +
    +

    Достижения

    +
      +
    • Выстроил единый визуальный язык продукта при соблюдении гайдлайнов пяти платформ одновременно
    • +
    • Спроектировал более 90 экранов в пяти платформенных вариантах
    • +
    • Обеспечил стабильный поток графических материалов от внешних подрядчиков без потери стилистики
    • +
    • Упростил сценарий защищённого хранилища так, чтобы им пользовались люди без технической подготовки
    • +
    • Работал в чувствительной области приватности и безопасности, где ошибка в интерфейсе стоит доверия пользователя
    • +
    +
    +
    +

    Стэк

    +
      +
    • Android
    • +
    • iOS
    • +
    • macOS
    • +
    • Windows
    • +
    • Linux
    • +
    +
    -
  10. -
    + -
    - -
    -
    -

    SpaceshipApps Studio

    -

    Июнь 2015 — Июнь 2016 (1 год и 1 месяц)·spaceshipapps.ru

    -
    -

    Студия разработки мобильных приложений и сайтов.

    - -
    -
    -
    -

    Обязанности:

    -
      -
    • Дизайн мобильных и веб-продуктов в потоковом режиме студии
    • -
    • Работа с разными доменами: соцгейминг, бьюти-услуги, доставка еды, голосовые интерфейсы, легал-сервисы, коллаборативные инструменты
    • -
    • Адаптивный веб-дизайн и дизайн административных панелей
    • -
    • Отрисовка иконок и подготовка графики для проектов
    • -
    • Подготовка макетов к передаче в разработку
    • -
    -
    -
    -

    Достижения:

    -
      -
    • Выпустил 6 продуктов за год: геолокационная социальная игра с квестами в реальном мире, приложение записи к бьюти-специалистам, сервис доставки еды, голосовая игра со словами, сайт и админка сервиса регистрации авторского права, мобильная «белая доска» для совместной работы
    • -
    • Научился быстро входить в новую предметную область и держать качество при высоком темпе студии
    • -
    • Закрыл весь спектр задач: мобайл, веб, админ-панели, графика
    • -
    • Заложил базу для перехода от исполнительского дизайна к продуктовому
    • -
    -
    -
    -

    Стэк:

    -
      -
    • Мобайл
    • -
    • Веб
    • -
    • Админ-панели
    • -
    • Графика
    • -
    +
  11. +
    +

    Orion Innovation (ex MERA)

    +

    — (1 год и 4 месяца)·orioninnovation.com

    +
    +

    Разработка ПО для внешних заказчиков.

    +
    +
    +
    +

    Обязанности

    +
      +
    • Работа в дизайн-отделе численностью до 8 человек
    • +
    • Создание анкет персон и составление карты функциональности продукта в соответствии с потребностями персон
    • +
    • Вайрфреймы и отрисовка конечного UI интерфейсов
    • + +
    • Подготовка мудбордов и дизайн-концепций, обсуждение их с клиентами
    • + +
    • Линковка мокапов в прототипы, написание сценариев и проведение качественных юзабилити-тестирований
    • + + +
    • Участие в дизайн-ревью: разбор работ коллег и защита собственных решений
    • + +
    +
    +
    +

    Достижения

    +
      +
    • Провёл 10 сессий юзабилити-тестирования от написания сценария до внесения правок
    • +
    • Освоил новую для себя платформу Smart TV и довёл проект до успешной публикации в магазине приложений
    • +
    • Провёл исследования и реверс-инжиниринг дизайна приложений для платформы Roku TV и создал рабочий процесс разработки дизайна приложения заказчика, включающий: формирование идеи, разработку кликабельных прототипов, тестирование юзабилити на рабочем устройстве, контроль дизайна в приложении, разработанном нашими разработчиками
    • +
    • Вёл коммуникацию с зарубежными заказчиками напрямую, без посредников
    • +
    • Активно участвовал в улучшении рабочего процесса дизайн-отдела компании
    • +
    • Внедрил в команду использование Miro (ex. Realtime Board) для хранения артефактов исследований
    • + +
    +
    +
    +

    Стэк

    +
      +
    • Sketch
    • +
    • Miro
    • +
    • Android
    • +
    • iOS
    • +
    • ROKU TV Box
    • +
    • Smart TV
    • +
    • Marvel App
    • +
    • ProtoPie
    • +
    • Framer
    • + + +
    +
    -
  12. -
    + -
    +
  13. +
    +

    SpaceshipApps Studio

    +

    — (1 год и 1 месяц)·spaceshipapps.ru

    +
    +

    Студия разработки мобильных приложений и сайтов.

    +
    +
    +
    +

    Обязанности

    +
      +
    • Дизайн мобильных и веб-продуктов в потоковом режиме студии
    • +
    • Работа с разными доменами: соцгейминг, бьюти-услуги, доставка еды, голосовые интерфейсы, легал-сервисы, коллаборативные инструменты
    • +
    • Адаптивный веб-дизайн и дизайн административных панелей
    • +
    • Отрисовка иконок и подготовка графики для проектов
    • +
    • Подготовка макетов к передаче в разработку
    • +
    +
    +
    +

    Достижения

    +
      +
    • Выпустил 6 продуктов за год: геолокационная социальная игра с квестами в реальном мире, приложение записи к бьюти-специалистам, сервис доставки еды, голосовая игра со словами, сайт и админка сервиса регистрации авторского права, мобильная «белая доска» для совместной работы
    • +
    • Научился быстро входить в новую предметную область и держать качество при высоком темпе студии
    • +
    • Закрыл весь спектр задач: мобайл, веб, админ-панели, графика
    • +
    • Заложил базу для перехода от исполнительского дизайна к продуктовому
    • +
    +
    +
    +

    Стэк

    +
      +
    • Мобайл
    • +
    • Веб
    • +
    • Adobe Photoshop
    • +
    • After Effects
    • +
    • Adobe Illustrator
    • -

      Навыки

      -
      + + +
    +
    +
    +
    +
  14. +
+
+ +
+

Навыки

+
-

Проектирование

-

UX/UI, product design, информационная архитектура, адаптивный дизайн, дизайн-системы

+
Проектирование
+
UX/UI, product design, информационная архитектура, адаптивный дизайн, дизайн-системы
-

Исследования

-

Интервью, опросы, usability testing, JTBD, карты эмпатии, конверсия, retention, NPS

+
Исследования
+
Интервью, опросы, usability testing, JTBD, карты эмпатии, конверсия, retention, NPS
-

Инструменты

-

Figma, Protopie, Sketch, Adobe Creative Suite, Tilda, AI-инструменты

+
Управление
+
Построение команды, Agile, Kanban, авторский надзор, работа со стейкхолдерами
-

Управление

-

Построение команды, Agile, Kanban, авторский надзор, работа со стейкхолдерами

+
Инструменты
+
Figma, Protopie, Sketch, Adobe Creative Suite, Storybook
-
+
+
AI инструменты
+
Hermes Agent, Perplexity, Cursor IDE, Zed IDE, Open Design, Codex, Claude Code
+
+ + - -
+
{{> footer}} diff --git a/src/partials/head-links.html b/src/partials/head-links.html index 2501e41..7c62c3f 100644 --- a/src/partials/head-links.html +++ b/src/partials/head-links.html @@ -7,6 +7,7 @@ + {{> head-seo}} {{> json-ld}} diff --git a/texts/content-edits/pending.json b/texts/content-edits/pending.json index 6d3f77d..2bed88e 100644 --- a/texts/content-edits/pending.json +++ b/texts/content-edits/pending.json @@ -1,6 +1,6 @@ { "version": 2, - "updatedAt": "2026-08-15T20:32:52.216Z", + "updatedAt": "2026-09-24T19:32:33.153Z", "items": [], "lastAppliedAt": "2026-06-06T11:54:50.470Z" }