@triforge/compositor-core 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/glslLoopBound.d.ts +17 -0
- package/dist/core/glslLoopBound.d.ts.map +1 -0
- package/dist/core/glslLoopBound.js +25 -0
- package/dist/passes/GlareStreaks.d.ts.map +1 -1
- package/dist/passes/GlareStreaks.js +41 -35
- package/dist/passes/LensFlare.d.ts.map +1 -1
- package/dist/passes/LensFlare.js +55 -53
- package/dist/passes/MotionBlur.d.ts.map +1 -1
- package/dist/passes/MotionBlur.js +29 -26
- package/package.json +61 -54
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time GLSL loop-bound sanitizer.
|
|
3
|
+
*
|
|
4
|
+
* Several passes bake a JS number directly into a GLSL `for` loop bound, e.g.
|
|
5
|
+
* `for (int i = 0; i < ${n}; i++)`. Per the root ecosystem rule ("ALWAYS cap
|
|
6
|
+
* GLSL loop iterations to a compile-time constant"), that number must be a
|
|
7
|
+
* finite integer within a sane range before it reaches the shader source:
|
|
8
|
+
*
|
|
9
|
+
* - `NaN`/`Infinity` would produce a shader that fails to compile.
|
|
10
|
+
* - An absurdly large value (e.g. a mistyped `samples: 1e6`) would compile a
|
|
11
|
+
* loop that hangs the GPU.
|
|
12
|
+
*
|
|
13
|
+
* This clamps to [min, max] and rounds to an integer, so the emitted bound is
|
|
14
|
+
* always a small compile-time constant.
|
|
15
|
+
*/
|
|
16
|
+
export declare function glslLoopBound(value: number, min: number, max: number): number;
|
|
17
|
+
//# sourceMappingURL=glslLoopBound.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glslLoopBound.d.ts","sourceRoot":"","sources":["../../src/core/glslLoopBound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAM7E"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time GLSL loop-bound sanitizer.
|
|
3
|
+
*
|
|
4
|
+
* Several passes bake a JS number directly into a GLSL `for` loop bound, e.g.
|
|
5
|
+
* `for (int i = 0; i < ${n}; i++)`. Per the root ecosystem rule ("ALWAYS cap
|
|
6
|
+
* GLSL loop iterations to a compile-time constant"), that number must be a
|
|
7
|
+
* finite integer within a sane range before it reaches the shader source:
|
|
8
|
+
*
|
|
9
|
+
* - `NaN`/`Infinity` would produce a shader that fails to compile.
|
|
10
|
+
* - An absurdly large value (e.g. a mistyped `samples: 1e6`) would compile a
|
|
11
|
+
* loop that hangs the GPU.
|
|
12
|
+
*
|
|
13
|
+
* This clamps to [min, max] and rounds to an integer, so the emitted bound is
|
|
14
|
+
* always a small compile-time constant.
|
|
15
|
+
*/
|
|
16
|
+
export function glslLoopBound(value, min, max) {
|
|
17
|
+
if (!Number.isFinite(value))
|
|
18
|
+
return min;
|
|
19
|
+
const rounded = Math.round(value);
|
|
20
|
+
if (rounded < min)
|
|
21
|
+
return min;
|
|
22
|
+
if (rounded > max)
|
|
23
|
+
return max;
|
|
24
|
+
return rounded;
|
|
25
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GlareStreaks.d.ts","sourceRoot":"","sources":["../../src/passes/GlareStreaks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"GlareStreaks.d.ts","sourceRoot":"","sources":["../../src/passes/GlareStreaks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAGvD,MAAM,WAAW,mBAAmB;IAClC,qGAAqG;IACrG,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;GAMG;AACH,qBAAa,YAAa,SAAQ,QAAQ;IACxC,QAAQ,CAAC,QAAQ,kBAAiB;IAClC,UAAU,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;gBAErG,IAAI,GAAE,mBAAwB;IAY1C,cAAc;IAEd,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO;CAmEhE"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BasePass } from '../core/BasePass.js';
|
|
2
|
+
import { glslLoopBound } from '../core/glslLoopBound.js';
|
|
2
3
|
/**
|
|
3
4
|
* GlareStreaks — anamorphic lens streak effect on bright areas.
|
|
4
5
|
* Blender Compositor: Glare node (Streaks type).
|
|
@@ -24,7 +25,12 @@ export class GlareStreaks extends BasePass {
|
|
|
24
25
|
const ShaderPass = reg['ShaderPass'];
|
|
25
26
|
if (!ShaderPass)
|
|
26
27
|
throw new Error('GlareStreaks: ShaderPass not found.');
|
|
27
|
-
const { threshold, strength,
|
|
28
|
+
const { threshold, strength, length, angle, fade } = this.parameters;
|
|
29
|
+
// Cap both baked loop bounds: streak count (1..16) and per-streak sample
|
|
30
|
+
// length (1..128) — both are interpolated into generated GLSL / unrolled
|
|
31
|
+
// JS below, so both must be finite compile-time-bounded integers.
|
|
32
|
+
const streaks = glslLoopBound(this.parameters.streaks, 1, 16);
|
|
33
|
+
const sampleLength = glslLoopBound(length, 1, 128);
|
|
28
34
|
// Build direction vectors for each streak
|
|
29
35
|
const dirs = [];
|
|
30
36
|
for (let i = 0; i < streaks; i++) {
|
|
@@ -40,41 +46,41 @@ export class GlareStreaks extends BasePass {
|
|
|
40
46
|
fade: { value: fade },
|
|
41
47
|
length: { value: length },
|
|
42
48
|
},
|
|
43
|
-
vertexShader: `
|
|
44
|
-
varying vec2 vUv;
|
|
45
|
-
void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
|
|
49
|
+
vertexShader: `
|
|
50
|
+
varying vec2 vUv;
|
|
51
|
+
void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
|
|
46
52
|
`,
|
|
47
|
-
fragmentShader: `
|
|
48
|
-
uniform sampler2D tDiffuse;
|
|
49
|
-
uniform vec2 resolution;
|
|
50
|
-
uniform float threshold;
|
|
51
|
-
uniform float strength;
|
|
52
|
-
uniform float fade;
|
|
53
|
-
uniform float length;
|
|
54
|
-
varying vec2 vUv;
|
|
55
|
-
|
|
56
|
-
float
|
|
57
|
-
|
|
58
|
-
vec4 sampleStreak(vec2 dir) {
|
|
59
|
-
vec4 acc = vec4(0.0);
|
|
60
|
-
float att = 1.0;
|
|
61
|
-
vec2 step = dir / resolution;
|
|
62
|
-
for (int i = 1; i <= ${
|
|
63
|
-
vec2 uv = vUv + step * float(i) * strength;
|
|
64
|
-
vec4 samp = texture2D(tDiffuse, clamp(uv, 0.0, 1.0));
|
|
65
|
-
float lum =
|
|
66
|
-
if (lum > threshold) acc += samp * att;
|
|
67
|
-
att *= fade;
|
|
68
|
-
}
|
|
69
|
-
return acc;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
void main() {
|
|
73
|
-
vec4 base = texture2D(tDiffuse, vUv);
|
|
74
|
-
vec4 glare = vec4(0.0);
|
|
75
|
-
${dirs.map(d => `glare += sampleStreak(${d});`).join('\n ')}
|
|
76
|
-
gl_FragColor = base + glare * (1.0 / ${Math.max(1, streaks)}.0);
|
|
77
|
-
}
|
|
53
|
+
fragmentShader: `
|
|
54
|
+
uniform sampler2D tDiffuse;
|
|
55
|
+
uniform vec2 resolution;
|
|
56
|
+
uniform float threshold;
|
|
57
|
+
uniform float strength;
|
|
58
|
+
uniform float fade;
|
|
59
|
+
uniform float length;
|
|
60
|
+
varying vec2 vUv;
|
|
61
|
+
|
|
62
|
+
float stLuma(vec3 c) { return dot(c, vec3(0.299, 0.587, 0.114)); }
|
|
63
|
+
|
|
64
|
+
vec4 sampleStreak(vec2 dir) {
|
|
65
|
+
vec4 acc = vec4(0.0);
|
|
66
|
+
float att = 1.0;
|
|
67
|
+
vec2 step = dir / resolution;
|
|
68
|
+
for (int i = 1; i <= ${sampleLength}; i++) {
|
|
69
|
+
vec2 uv = vUv + step * float(i) * strength;
|
|
70
|
+
vec4 samp = texture2D(tDiffuse, clamp(uv, 0.0, 1.0));
|
|
71
|
+
float lum = stLuma(samp.rgb);
|
|
72
|
+
if (lum > threshold) acc += samp * att;
|
|
73
|
+
att *= fade;
|
|
74
|
+
}
|
|
75
|
+
return acc;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
void main() {
|
|
79
|
+
vec4 base = texture2D(tDiffuse, vUv);
|
|
80
|
+
vec4 glare = vec4(0.0);
|
|
81
|
+
${dirs.map(d => `glare += sampleStreak(${d});`).join('\n ')}
|
|
82
|
+
gl_FragColor = base + glare * (1.0 / ${Math.max(1, streaks)}.0);
|
|
83
|
+
}
|
|
78
84
|
`,
|
|
79
85
|
};
|
|
80
86
|
return new ShaderPass(shader);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LensFlare.d.ts","sourceRoot":"","sources":["../../src/passes/LensFlare.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"LensFlare.d.ts","sourceRoot":"","sources":["../../src/passes/LensFlare.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAGvD,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,qBAAa,SAAU,SAAQ,QAAQ;IACrC,QAAQ,CAAC,QAAQ,eAAc;IAC/B,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;gBAE7F,IAAI,GAAE,gBAAqB;IAWvC,cAAc;IAEd,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO;CA0EhE"}
|
package/dist/passes/LensFlare.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BasePass } from '../core/BasePass.js';
|
|
2
|
+
import { glslLoopBound } from '../core/glslLoopBound.js';
|
|
2
3
|
/**
|
|
3
4
|
* LensFlare — Blender Compositor: Lens Flare / Glare node (lens reflections type).
|
|
4
5
|
*
|
|
@@ -23,7 +24,8 @@ export class LensFlare extends BasePass {
|
|
|
23
24
|
if (!ShaderPass)
|
|
24
25
|
throw new Error('LensFlare: ShaderPass not found.');
|
|
25
26
|
const { ghosts, strength, threshold, haloWidth, chromatic } = this.parameters;
|
|
26
|
-
|
|
27
|
+
// Cap the baked loop bound: finite, integer, 1..32 ghost reflections.
|
|
28
|
+
const n = glslLoopBound(ghosts, 1, 32);
|
|
27
29
|
const shader = {
|
|
28
30
|
uniforms: {
|
|
29
31
|
tDiffuse: { value: null },
|
|
@@ -32,59 +34,59 @@ export class LensFlare extends BasePass {
|
|
|
32
34
|
haloWidth: { value: haloWidth },
|
|
33
35
|
chromatic: { value: chromatic },
|
|
34
36
|
},
|
|
35
|
-
vertexShader: `
|
|
36
|
-
varying vec2 vUv;
|
|
37
|
-
void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
|
|
37
|
+
vertexShader: `
|
|
38
|
+
varying vec2 vUv;
|
|
39
|
+
void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
|
|
38
40
|
`,
|
|
39
|
-
fragmentShader: `
|
|
40
|
-
uniform sampler2D tDiffuse;
|
|
41
|
-
uniform float strength;
|
|
42
|
-
uniform float threshold;
|
|
43
|
-
uniform float haloWidth;
|
|
44
|
-
uniform float chromatic;
|
|
45
|
-
varying vec2 vUv;
|
|
46
|
-
|
|
47
|
-
float
|
|
48
|
-
|
|
49
|
-
vec3 sampleChromatic(vec2 uv, vec2 offset) {
|
|
50
|
-
vec2 r = clamp(uv + offset * (1.0 + chromatic * 0.1), 0.0, 1.0);
|
|
51
|
-
vec2 b = clamp(uv + offset * (1.0 - chromatic * 0.1), 0.0, 1.0);
|
|
52
|
-
return vec3(
|
|
53
|
-
texture2D(tDiffuse, r).r,
|
|
54
|
-
texture2D(tDiffuse, clamp(uv + offset, 0.0, 1.0)).g,
|
|
55
|
-
texture2D(tDiffuse, b).b
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
void main() {
|
|
60
|
-
vec4 base = texture2D(tDiffuse, vUv);
|
|
61
|
-
vec3 flare = vec3(0.0);
|
|
62
|
-
vec2 ghost = vec2(0.5) - vUv; // vector to screen centre
|
|
63
|
-
|
|
64
|
-
// Ghost reflections along the screen diagonal
|
|
65
|
-
for (int i = 1; i <= ${n}; i++) {
|
|
66
|
-
float fi = float(i);
|
|
67
|
-
float disp = 0.3 + fi * 0.25;
|
|
68
|
-
vec2 guv = vUv + ghost * disp;
|
|
69
|
-
vec3 samp = sampleChromatic(guv, ghost * 0.01 * fi);
|
|
70
|
-
float lum =
|
|
71
|
-
if (lum > threshold) {
|
|
72
|
-
vec3 tint = mix(vec3(1.0, 0.8, 0.6), vec3(0.6, 0.8, 1.0), fract(fi * 0.37));
|
|
73
|
-
flare += samp * tint * (lum - threshold) * strength / fi;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Halo ring
|
|
78
|
-
float dist = length(ghost);
|
|
79
|
-
float haloBias = abs(dist - haloWidth);
|
|
80
|
-
float halo = smoothstep(0.05, 0.0, haloBias) * strength * 0.5;
|
|
81
|
-
vec4 haloCentre = texture2D(tDiffuse, clamp(vec2(0.5) + normalize(-ghost) * haloWidth, 0.0, 1.0));
|
|
82
|
-
if (
|
|
83
|
-
flare += haloCentre.rgb * halo;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
gl_FragColor = vec4(base.rgb + flare, base.a);
|
|
87
|
-
}
|
|
41
|
+
fragmentShader: `
|
|
42
|
+
uniform sampler2D tDiffuse;
|
|
43
|
+
uniform float strength;
|
|
44
|
+
uniform float threshold;
|
|
45
|
+
uniform float haloWidth;
|
|
46
|
+
uniform float chromatic;
|
|
47
|
+
varying vec2 vUv;
|
|
48
|
+
|
|
49
|
+
float stLuma(vec3 c) { return dot(c, vec3(0.299, 0.587, 0.114)); }
|
|
50
|
+
|
|
51
|
+
vec3 sampleChromatic(vec2 uv, vec2 offset) {
|
|
52
|
+
vec2 r = clamp(uv + offset * (1.0 + chromatic * 0.1), 0.0, 1.0);
|
|
53
|
+
vec2 b = clamp(uv + offset * (1.0 - chromatic * 0.1), 0.0, 1.0);
|
|
54
|
+
return vec3(
|
|
55
|
+
texture2D(tDiffuse, r).r,
|
|
56
|
+
texture2D(tDiffuse, clamp(uv + offset, 0.0, 1.0)).g,
|
|
57
|
+
texture2D(tDiffuse, b).b
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
void main() {
|
|
62
|
+
vec4 base = texture2D(tDiffuse, vUv);
|
|
63
|
+
vec3 flare = vec3(0.0);
|
|
64
|
+
vec2 ghost = vec2(0.5) - vUv; // vector to screen centre
|
|
65
|
+
|
|
66
|
+
// Ghost reflections along the screen diagonal
|
|
67
|
+
for (int i = 1; i <= ${n}; i++) {
|
|
68
|
+
float fi = float(i);
|
|
69
|
+
float disp = 0.3 + fi * 0.25;
|
|
70
|
+
vec2 guv = vUv + ghost * disp;
|
|
71
|
+
vec3 samp = sampleChromatic(guv, ghost * 0.01 * fi);
|
|
72
|
+
float lum = stLuma(samp);
|
|
73
|
+
if (lum > threshold) {
|
|
74
|
+
vec3 tint = mix(vec3(1.0, 0.8, 0.6), vec3(0.6, 0.8, 1.0), fract(fi * 0.37));
|
|
75
|
+
flare += samp * tint * (lum - threshold) * strength / fi;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Halo ring
|
|
80
|
+
float dist = length(ghost);
|
|
81
|
+
float haloBias = abs(dist - haloWidth);
|
|
82
|
+
float halo = smoothstep(0.05, 0.0, haloBias) * strength * 0.5;
|
|
83
|
+
vec4 haloCentre = texture2D(tDiffuse, clamp(vec2(0.5) + normalize(-ghost) * haloWidth, 0.0, 1.0));
|
|
84
|
+
if (stLuma(haloCentre.rgb) > threshold) {
|
|
85
|
+
flare += haloCentre.rgb * halo;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
gl_FragColor = vec4(base.rgb + flare, base.a);
|
|
89
|
+
}
|
|
88
90
|
`,
|
|
89
91
|
};
|
|
90
92
|
return new ShaderPass(shader);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MotionBlur.d.ts","sourceRoot":"","sources":["../../src/passes/MotionBlur.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"MotionBlur.d.ts","sourceRoot":"","sources":["../../src/passes/MotionBlur.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAGvD,MAAM,WAAW,iBAAiB;IAChC,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;GAQG;AACH,qBAAa,UAAW,SAAQ,QAAQ;IACtC,QAAQ,CAAC,QAAQ,gBAAe;IAChC,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;gBAE7D,IAAI,GAAE,iBAAsB;IASxC,cAAc;IAEd,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO;CA+ChE"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BasePass } from '../core/BasePass.js';
|
|
2
|
+
import { glslLoopBound } from '../core/glslLoopBound.js';
|
|
2
3
|
/**
|
|
3
4
|
* MotionBlur — screen-space radial/directional motion blur.
|
|
4
5
|
* Blender Compositor: Vector Blur node.
|
|
@@ -24,7 +25,9 @@ export class MotionBlur extends BasePass {
|
|
|
24
25
|
if (!ShaderPass)
|
|
25
26
|
throw new Error('MotionBlur: ShaderPass not found.');
|
|
26
27
|
const { samples, velocityScale, maxRadius } = this.parameters;
|
|
27
|
-
|
|
28
|
+
// Cap the baked loop bound: finite, integer, 1..64 (matches typical
|
|
29
|
+
// real-time motion-blur sample budgets; guards against NaN / huge values).
|
|
30
|
+
const n = glslLoopBound(samples, 1, 64);
|
|
28
31
|
const shader = {
|
|
29
32
|
uniforms: {
|
|
30
33
|
tDiffuse: { value: null },
|
|
@@ -32,32 +35,32 @@ export class MotionBlur extends BasePass {
|
|
|
32
35
|
velocityScale: { value: velocityScale },
|
|
33
36
|
maxRadius: { value: maxRadius },
|
|
34
37
|
},
|
|
35
|
-
vertexShader: `
|
|
36
|
-
varying vec2 vUv;
|
|
37
|
-
void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
|
|
38
|
+
vertexShader: `
|
|
39
|
+
varying vec2 vUv;
|
|
40
|
+
void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
|
|
38
41
|
`,
|
|
39
|
-
fragmentShader: `
|
|
40
|
-
uniform sampler2D tDiffuse;
|
|
41
|
-
uniform vec2 resolution;
|
|
42
|
-
uniform float velocityScale;
|
|
43
|
-
uniform float maxRadius;
|
|
44
|
-
varying vec2 vUv;
|
|
45
|
-
|
|
46
|
-
void main() {
|
|
47
|
-
// Screen-space radial blur toward centre
|
|
48
|
-
vec2 centre = vec2(0.5);
|
|
49
|
-
vec2 vel = (centre - vUv) * velocityScale;
|
|
50
|
-
float len = length(vel * resolution);
|
|
51
|
-
if (len > maxRadius) vel = vel * (maxRadius / max(len, 0.001));
|
|
52
|
-
|
|
53
|
-
vec4 acc = vec4(0.0);
|
|
54
|
-
for (int i = 0; i < ${n}; i++) {
|
|
55
|
-
float t = float(i) / float(${n - 1});
|
|
56
|
-
vec2 uv = clamp(vUv + vel * t / resolution, 0.0, 1.0);
|
|
57
|
-
acc += texture2D(tDiffuse, uv);
|
|
58
|
-
}
|
|
59
|
-
gl_FragColor = acc / float(${n});
|
|
60
|
-
}
|
|
42
|
+
fragmentShader: `
|
|
43
|
+
uniform sampler2D tDiffuse;
|
|
44
|
+
uniform vec2 resolution;
|
|
45
|
+
uniform float velocityScale;
|
|
46
|
+
uniform float maxRadius;
|
|
47
|
+
varying vec2 vUv;
|
|
48
|
+
|
|
49
|
+
void main() {
|
|
50
|
+
// Screen-space radial blur toward centre
|
|
51
|
+
vec2 centre = vec2(0.5);
|
|
52
|
+
vec2 vel = (centre - vUv) * velocityScale;
|
|
53
|
+
float len = length(vel * resolution);
|
|
54
|
+
if (len > maxRadius) vel = vel * (maxRadius / max(len, 0.001));
|
|
55
|
+
|
|
56
|
+
vec4 acc = vec4(0.0);
|
|
57
|
+
for (int i = 0; i < ${n}; i++) {
|
|
58
|
+
float t = float(i) / float(${n - 1});
|
|
59
|
+
vec2 uv = clamp(vUv + vel * t / resolution, 0.0, 1.0);
|
|
60
|
+
acc += texture2D(tDiffuse, uv);
|
|
61
|
+
}
|
|
62
|
+
gl_FragColor = acc / float(${n});
|
|
63
|
+
}
|
|
61
64
|
`,
|
|
62
65
|
};
|
|
63
66
|
return new ShaderPass(shader);
|
package/package.json
CHANGED
|
@@ -1,54 +1,61 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@triforge/compositor-core",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Blender-matched compositor
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/index.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"files": [
|
|
9
|
-
"dist",
|
|
10
|
-
"package.json"
|
|
11
|
-
],
|
|
12
|
-
"scripts": {
|
|
13
|
-
"build": "tsc",
|
|
14
|
-
"dev": "tsc --watch",
|
|
15
|
-
"test": "node test/run-tests.js"
|
|
16
|
-
},
|
|
17
|
-
"keywords": [
|
|
18
|
-
"three.js",
|
|
19
|
-
"post-processing",
|
|
20
|
-
"compositor",
|
|
21
|
-
"blender",
|
|
22
|
-
"effects"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@triforge/compositor-core",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Blender-matched post-processing compositor for Three.js — 28 passes (Bloom, Depth of Field, SSAO, SSR, LUT, Film Grain), backend-agnostic",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"package.json"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"test": "node test/run-tests.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"three.js",
|
|
19
|
+
"post-processing",
|
|
20
|
+
"compositor",
|
|
21
|
+
"blender",
|
|
22
|
+
"effects",
|
|
23
|
+
"threejs",
|
|
24
|
+
"webgl",
|
|
25
|
+
"bloom",
|
|
26
|
+
"postprocessing",
|
|
27
|
+
"ssao",
|
|
28
|
+
"typescript",
|
|
29
|
+
"vfx"
|
|
30
|
+
],
|
|
31
|
+
"author": "Aftab Ibrahim Kazi",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"three": ">=0.128.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependenciesMeta": {
|
|
37
|
+
"postprocessing": {
|
|
38
|
+
"optional": true
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/three": "^0.186.0",
|
|
43
|
+
"three": "^0.186.0",
|
|
44
|
+
"typescript": "^5.3.0"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/AftabIbrahimKazi/triforge.git",
|
|
49
|
+
"directory": "st-compositor-core"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/AftabIbrahimKazi/triforge#readme",
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"exports": {
|
|
56
|
+
".": {
|
|
57
|
+
"import": "./dist/index.js",
|
|
58
|
+
"types": "./dist/index.d.ts"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|