summaryrefslogtreecommitdiff
path: root/apps/web/src/services/renderer/nodes/composite-effect-node.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-03-01 01:32:09 +0100
committerMaze Winther <mazewinther@gmail.com>2026-03-01 01:32:09 +0100
commit5e7c27f542e94ffee5d2106ce318b6f43cc7e738 (patch)
treeb33c3370c01b9b2ee685f1039543bc02f8764f7b /apps/web/src/services/renderer/nodes/composite-effect-node.ts
parent744419469da403be57d30b5b19f6b43246caa57d (diff)
parent354f4dec67ef7eb39d34fca941ca34025a68136b (diff)
downloadopencut-5e7c27f542e94ffee5d2106ce318b6f43cc7e738.tar.gz
Merge branch 'dev' into ripple-editing
Diffstat (limited to 'apps/web/src/services/renderer/nodes/composite-effect-node.ts')
-rw-r--r--apps/web/src/services/renderer/nodes/composite-effect-node.ts77
1 files changed, 77 insertions, 0 deletions
diff --git a/apps/web/src/services/renderer/nodes/composite-effect-node.ts b/apps/web/src/services/renderer/nodes/composite-effect-node.ts
new file mode 100644
index 00000000..3fbfffc1
--- /dev/null
+++ b/apps/web/src/services/renderer/nodes/composite-effect-node.ts
@@ -0,0 +1,77 @@
+import type { CanvasRenderer } from "../canvas-renderer";
+import { createOffscreenCanvas } from "../canvas-utils";
+import { getEffect } from "@/lib/effects";
+import type { EffectParamValues } from "@/types/effects";
+import { BaseNode } from "./base-node";
+import { webglEffectRenderer } from "../webgl-effect-renderer";
+
+export type CompositeEffectNodeParams = {
+ contentNodes: BaseNode[];
+ effectType: string;
+ effectParams: EffectParamValues;
+ scale: number;
+};
+
+export class CompositeEffectNode extends BaseNode<CompositeEffectNodeParams> {
+ async render({
+ renderer,
+ time,
+ }: {
+ renderer: CanvasRenderer;
+ time: number;
+ }): Promise<void> {
+ const offscreen = createOffscreenCanvas({
+ width: renderer.width,
+ height: renderer.height,
+ });
+ const offscreenCtx = offscreen.getContext("2d") as OffscreenCanvasRenderingContext2D | null;
+ if (!offscreenCtx) {
+ throw new Error("failed to get offscreen canvas context");
+ }
+
+ const originalContext = renderer.context;
+ renderer.context = offscreenCtx;
+
+ for (const node of this.params.contentNodes) {
+ await node.render({ renderer, time });
+ }
+
+ renderer.context = originalContext;
+
+ const effectDefinition = getEffect({ effectType: this.params.effectType });
+ const scale = this.params.scale;
+ const scaledWidth = renderer.width * scale;
+ const scaledHeight = renderer.height * scale;
+ const offsetX = (renderer.width - scaledWidth) / 2;
+ const offsetY = (renderer.height - scaledHeight) / 2;
+
+ const passes = effectDefinition.renderer.passes.map((pass) => ({
+ fragmentShader: pass.fragmentShader,
+ uniforms: pass.uniforms({
+ effectParams: this.params.effectParams,
+ width: renderer.width,
+ height: renderer.height,
+ }),
+ }));
+ const effectResult = webglEffectRenderer.applyEffect({
+ source: offscreen as CanvasImageSource,
+ width: renderer.width,
+ height: renderer.height,
+ passes,
+ });
+
+ renderer.context.save();
+ renderer.context.drawImage(
+ effectResult,
+ 0,
+ 0,
+ renderer.width,
+ renderer.height,
+ offsetX,
+ offsetY,
+ scaledWidth,
+ scaledHeight,
+ );
+ renderer.context.restore();
+ }
+}