From 3e89d2998588f8e35c524e523ba2ee853f0f6475 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Mon, 20 Apr 2026 11:31:17 +0200 Subject: refactor: restructure files to their domains, new preview overlay system, and dep graph --- apps/web/src/effects/definitions/blur.ts | 110 ++++++++++++++++++++++++++++++ apps/web/src/effects/definitions/index.ts | 13 ++++ 2 files changed, 123 insertions(+) create mode 100644 apps/web/src/effects/definitions/blur.ts create mode 100644 apps/web/src/effects/definitions/index.ts (limited to 'apps/web/src/effects/definitions') diff --git a/apps/web/src/effects/definitions/blur.ts b/apps/web/src/effects/definitions/blur.ts new file mode 100644 index 00000000..194196e9 --- /dev/null +++ b/apps/web/src/effects/definitions/blur.ts @@ -0,0 +1,110 @@ +import type { EffectDefinition, EffectPass } from "@/effects/types"; + +export const GAUSSIAN_BLUR_SHADER = "gaussian-blur"; + +const MAX_SINGLE_PASS_SIGMA = 10; +const MAX_STEP = 4; +const MAX_EFFECTIVE_SIGMA = MAX_SINGLE_PASS_SIGMA * MAX_STEP; +const MAX_ITERATIONS = 8; + +export function buildGaussianBlurPasses({ + sigmaX, + sigmaY, +}: { + sigmaX: number; + sigmaY: number; +}): EffectPass[] { + const maxSigma = Math.max(sigmaX, sigmaY); + if (maxSigma < 0.001) return []; + + const iterations = Math.min( + MAX_ITERATIONS, + Math.max( + 1, + Math.ceil( + (maxSigma * maxSigma) / + (MAX_EFFECTIVE_SIGMA * MAX_EFFECTIVE_SIGMA), + ), + ), + ); + const perPassSigmaX = sigmaX / Math.sqrt(iterations); + const perPassSigmaY = sigmaY / Math.sqrt(iterations); + const stepX = Math.max(1, perPassSigmaX / MAX_SINGLE_PASS_SIGMA); + const stepY = Math.max(1, perPassSigmaY / MAX_SINGLE_PASS_SIGMA); + + const passes: EffectPass[] = []; + for (let i = 0; i < iterations; i++) { + passes.push({ + shader: GAUSSIAN_BLUR_SHADER, + uniforms: { + u_sigma: perPassSigmaX, + u_step: stepX, + u_direction: [1, 0], + }, + }); + passes.push({ + shader: GAUSSIAN_BLUR_SHADER, + uniforms: { + u_sigma: perPassSigmaY, + u_step: stepY, + u_direction: [0, 1], + }, + }); + } + return passes; +} + +export const INTENSITY_TO_SIGMA_DIVISOR = 5; + +export function intensityToSigma({ intensity, resolution, reference }: { intensity: number; resolution: number; reference: number }): number { + return (intensity / INTENSITY_TO_SIGMA_DIVISOR) * (resolution / reference); +} + +function parseIntensity(effectParams: Record): number { + const raw = effectParams.intensity; + return typeof raw === "number" ? raw : Number.parseFloat(String(raw)); +} + +export const blurEffectDefinition: EffectDefinition = { + type: "blur", + name: "Blur", + keywords: ["blur", "soft", "defocus"], + params: [ + { + key: "intensity", + label: "Intensity", + type: "number", + default: 15, + min: 0, + max: 100, + step: 1, + }, + ], + renderer: { + passes: [ + { + shader: GAUSSIAN_BLUR_SHADER, + uniforms: ({ effectParams, width }) => ({ + u_sigma: Math.max(intensityToSigma({ intensity: parseIntensity(effectParams), resolution: width, reference: 1920 }), 0.001), + u_step: 1, + u_direction: [1, 0], + }), + }, + { + shader: GAUSSIAN_BLUR_SHADER, + uniforms: ({ effectParams, height }) => ({ + u_sigma: Math.max(intensityToSigma({ intensity: parseIntensity(effectParams), resolution: height, reference: 1080 }), 0.001), + u_step: 1, + u_direction: [0, 1], + }), + }, + ], + buildPasses: ({ effectParams, width, height }) => { + const intensity = parseIntensity(effectParams); + return buildGaussianBlurPasses({ + sigmaX: intensityToSigma({ intensity, resolution: width, reference: 1920 }), + sigmaY: intensityToSigma({ intensity, resolution: height, reference: 1080 }), + }); + }, + }, +}; diff --git a/apps/web/src/effects/definitions/index.ts b/apps/web/src/effects/definitions/index.ts new file mode 100644 index 00000000..edc8c5e2 --- /dev/null +++ b/apps/web/src/effects/definitions/index.ts @@ -0,0 +1,13 @@ +import { effectsRegistry } from "../registry"; +import { blurEffectDefinition } from "./blur"; + +const defaultEffects = [blurEffectDefinition]; + +export function registerDefaultEffects(): void { + for (const definition of defaultEffects) { + if (effectsRegistry.has(definition.type)) { + continue; + } + effectsRegistry.register(definition.type, definition); + } +} -- cgit v1.3.1