diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-01 01:32:09 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-01 01:32:09 +0100 |
| commit | 5e7c27f542e94ffee5d2106ce318b6f43cc7e738 (patch) | |
| tree | b33c3370c01b9b2ee685f1039543bc02f8764f7b /apps/web/src/lib/effects | |
| parent | 744419469da403be57d30b5b19f6b43246caa57d (diff) | |
| parent | 354f4dec67ef7eb39d34fca941ca34025a68136b (diff) | |
| download | opencut-5e7c27f542e94ffee5d2106ce318b6f43cc7e738.tar.gz | |
Merge branch 'dev' into ripple-editing
Diffstat (limited to 'apps/web/src/lib/effects')
| -rw-r--r-- | apps/web/src/lib/effects/definitions/blur.frag.glsl | 25 | ||||
| -rw-r--r-- | apps/web/src/lib/effects/definitions/blur.ts | 50 | ||||
| -rw-r--r-- | apps/web/src/lib/effects/definitions/index.ts | 13 | ||||
| -rw-r--r-- | apps/web/src/lib/effects/effect.vert.glsl | 7 | ||||
| -rw-r--r-- | apps/web/src/lib/effects/index.ts | 34 | ||||
| -rw-r--r-- | apps/web/src/lib/effects/registry.ts | 31 |
6 files changed, 160 insertions, 0 deletions
diff --git a/apps/web/src/lib/effects/definitions/blur.frag.glsl b/apps/web/src/lib/effects/definitions/blur.frag.glsl new file mode 100644 index 00000000..2c015725 --- /dev/null +++ b/apps/web/src/lib/effects/definitions/blur.frag.glsl @@ -0,0 +1,25 @@ +precision mediump float; + +uniform sampler2D u_texture; +uniform vec2 u_resolution; +uniform float u_sigma; +uniform vec2 u_direction; + +varying vec2 v_texCoord; + +void main() { + vec2 texelSize = 1.0 / u_resolution; + + vec4 color = vec4(0.0); + float totalWeight = 0.0; + + // step=1 texel — scaling step size instead causes discrete ghosting artifacts + for (int i = -30; i <= 30; i++) { + float fi = float(i); + float weight = exp(-(fi * fi) / (2.0 * u_sigma * u_sigma)); + color += texture2D(u_texture, v_texCoord + texelSize * u_direction * fi) * weight; + totalWeight += weight; + } + + gl_FragColor = color / totalWeight; +} diff --git a/apps/web/src/lib/effects/definitions/blur.ts b/apps/web/src/lib/effects/definitions/blur.ts new file mode 100644 index 00000000..3a8f691a --- /dev/null +++ b/apps/web/src/lib/effects/definitions/blur.ts @@ -0,0 +1,50 @@ +import type { EffectDefinition } from "@/types/effects"; +import blurFragmentShader from "./blur.frag.glsl"; + +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: { + type: "webgl", + passes: [ + { + fragmentShader: blurFragmentShader, + uniforms: ({ effectParams }) => { + const intensity = + typeof effectParams.intensity === "number" + ? effectParams.intensity + : Number.parseFloat(String(effectParams.intensity)); + return { + u_sigma: Math.max(intensity / 5, 0.001), + u_direction: [1, 0], + }; + }, + }, + { + fragmentShader: blurFragmentShader, + uniforms: ({ effectParams }) => { + const intensity = + typeof effectParams.intensity === "number" + ? effectParams.intensity + : Number.parseFloat(String(effectParams.intensity)); + return { + u_sigma: Math.max(intensity / 5, 0.001), + u_direction: [0, 1], + }; + }, + }, + ], + }, +}; diff --git a/apps/web/src/lib/effects/definitions/index.ts b/apps/web/src/lib/effects/definitions/index.ts new file mode 100644 index 00000000..1742b1d7 --- /dev/null +++ b/apps/web/src/lib/effects/definitions/index.ts @@ -0,0 +1,13 @@ +import { hasEffect, registerEffect } from "../registry"; +import { blurEffectDefinition } from "./blur"; + +const defaultEffects = [blurEffectDefinition]; + +export function registerDefaultEffects(): void { + for (const definition of defaultEffects) { + if (hasEffect({ effectType: definition.type })) { + continue; + } + registerEffect({ definition }); + } +} diff --git a/apps/web/src/lib/effects/effect.vert.glsl b/apps/web/src/lib/effects/effect.vert.glsl new file mode 100644 index 00000000..5febc835 --- /dev/null +++ b/apps/web/src/lib/effects/effect.vert.glsl @@ -0,0 +1,7 @@ +attribute vec2 a_position; +varying vec2 v_texCoord; + +void main() { + v_texCoord = a_position * 0.5 + 0.5; + gl_Position = vec4(a_position, 0.0, 1.0); +} diff --git a/apps/web/src/lib/effects/index.ts b/apps/web/src/lib/effects/index.ts new file mode 100644 index 00000000..14d06d84 --- /dev/null +++ b/apps/web/src/lib/effects/index.ts @@ -0,0 +1,34 @@ +import { generateUUID } from "@/utils/id"; +import { getEffect } from "./registry"; +import type { Effect, EffectParamValues } from "@/types/effects"; +import type { VisualElement } from "@/types/timeline"; + +export { getEffect, getAllEffects, hasEffect, registerEffect } from "./registry"; +export { registerDefaultEffects } from "./definitions"; + +export const EFFECT_TARGET_ELEMENT_TYPES: VisualElement["type"][] = [ + "video", + "image", + "text", + "sticker", +]; + +export function buildDefaultEffectInstance({ + effectType, +}: { + effectType: string; +}): Effect { + const definition = getEffect({ effectType }); + + const params: EffectParamValues = {}; + for (const paramDef of definition.params) { + params[paramDef.key] = paramDef.default; + } + + return { + id: generateUUID(), + type: effectType, + params, + enabled: true, + }; +} diff --git a/apps/web/src/lib/effects/registry.ts b/apps/web/src/lib/effects/registry.ts new file mode 100644 index 00000000..568b8924 --- /dev/null +++ b/apps/web/src/lib/effects/registry.ts @@ -0,0 +1,31 @@ +import type { EffectDefinition } from "@/types/effects"; + +const effectDefinitions = new Map<string, EffectDefinition>(); + +export function registerEffect({ + definition, +}: { + definition: EffectDefinition; +}): void { + effectDefinitions.set(definition.type, definition); +} + +export function hasEffect({ effectType }: { effectType: string }): boolean { + return effectDefinitions.has(effectType); +} + +export function getEffect({ + effectType, +}: { + effectType: string; +}): EffectDefinition { + const definition = effectDefinitions.get(effectType); + if (!definition) { + throw new Error(`Unknown effect type: ${effectType}`); + } + return definition; +} + +export function getAllEffects(): EffectDefinition[] { + return Array.from(effectDefinitions.values()); +} |
