@xperimntl/vue-threejs-postprocessing 1.1.0 → 1.1.1
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/CHANGELOG.md +6 -0
- package/dist/declarations/src/EffectComposer.d.ts +60 -0
- package/dist/declarations/src/context.d.ts +19 -0
- package/dist/declarations/src/effects/Bloom.d.ts +40 -0
- package/dist/declarations/src/effects/BrightnessContrast.d.ts +22 -0
- package/dist/declarations/src/effects/DepthOfField.d.ts +31 -0
- package/dist/declarations/src/effects/HueSaturation.d.ts +22 -0
- package/dist/declarations/src/effects/LUT.d.ts +23 -0
- package/dist/declarations/src/effects/Noise.d.ts +24 -0
- package/dist/declarations/src/effects/ToneMapping.d.ts +42 -0
- package/dist/declarations/src/effects/Vignette.d.ts +22 -0
- package/dist/declarations/src/effects/index.d.ts +8 -0
- package/dist/declarations/src/index.d.ts +6 -0
- package/dist/declarations/src/plugin.d.ts +7 -0
- package/dist/xperimntl-vue-threejs-postprocessing.cjs.d.ts +2 -2
- package/dist/xperimntl-vue-threejs-postprocessing.cjs.dev.js +555 -0
- package/dist/xperimntl-vue-threejs-postprocessing.cjs.js +6 -15
- package/dist/xperimntl-vue-threejs-postprocessing.cjs.prod.js +555 -0
- package/dist/xperimntl-vue-threejs-postprocessing.esm.js +539 -0
- package/package.json +1 -1
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import { defineComponent, shallowRef, watch, provide, onBeforeUnmount, inject } from 'vue';
|
|
2
|
+
import { EffectComposer as EffectComposer$1, RenderPass, EffectPass, BloomEffect, BrightnessContrastEffect, HueSaturationEffect, LUT3DEffect, ToneMappingMode, ToneMappingEffect, DepthOfFieldEffect, BlendFunction, NoiseEffect, VignetteEffect } from 'postprocessing';
|
|
3
|
+
import { useThree, useFrame, defineFiberPlugin, withPluginOptions } from '@xperimntl/vue-threejs';
|
|
4
|
+
|
|
5
|
+
/** Injection key for the EffectComposer context. */
|
|
6
|
+
const COMPOSER_CONTEXT = Symbol('v3f-composer-context');
|
|
7
|
+
/** Injection key for plugin-level defaults. */
|
|
8
|
+
const POSTPROCESSING_DEFAULTS = Symbol('v3f-postprocessing-defaults');
|
|
9
|
+
|
|
10
|
+
const EffectComposer = defineComponent({
|
|
11
|
+
name: 'EffectComposer',
|
|
12
|
+
props: {
|
|
13
|
+
enabled: {
|
|
14
|
+
type: Boolean,
|
|
15
|
+
default: true
|
|
16
|
+
},
|
|
17
|
+
multisampling: {
|
|
18
|
+
type: Number,
|
|
19
|
+
default: 8
|
|
20
|
+
},
|
|
21
|
+
autoClear: {
|
|
22
|
+
type: Boolean,
|
|
23
|
+
default: true
|
|
24
|
+
},
|
|
25
|
+
resolutionScale: {
|
|
26
|
+
type: Number,
|
|
27
|
+
default: 1
|
|
28
|
+
},
|
|
29
|
+
depthBuffer: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: true
|
|
32
|
+
},
|
|
33
|
+
stencilBuffer: {
|
|
34
|
+
type: Boolean,
|
|
35
|
+
default: false
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
setup(props, {
|
|
39
|
+
slots
|
|
40
|
+
}) {
|
|
41
|
+
const state = useThree();
|
|
42
|
+
const composerRef = shallowRef(null);
|
|
43
|
+
|
|
44
|
+
// Ordered list of registered effects
|
|
45
|
+
const effects = [];
|
|
46
|
+
|
|
47
|
+
// Track the current EffectPass so we can dispose/replace it
|
|
48
|
+
let currentEffectPass = null;
|
|
49
|
+
let currentRenderPass = null;
|
|
50
|
+
|
|
51
|
+
// Store the original autoClear value so we can restore on unmount
|
|
52
|
+
let originalAutoClear = null;
|
|
53
|
+
function disposeComposer() {
|
|
54
|
+
// Dispose composer
|
|
55
|
+
if (composerRef.value) {
|
|
56
|
+
composerRef.value.dispose();
|
|
57
|
+
composerRef.value = null;
|
|
58
|
+
}
|
|
59
|
+
currentEffectPass = null;
|
|
60
|
+
currentRenderPass = null;
|
|
61
|
+
}
|
|
62
|
+
function createComposer() {
|
|
63
|
+
const {
|
|
64
|
+
gl,
|
|
65
|
+
scene,
|
|
66
|
+
camera
|
|
67
|
+
} = state.value;
|
|
68
|
+
if (!gl || !scene || !camera) return null;
|
|
69
|
+
|
|
70
|
+
// Dispose previous composer if it exists
|
|
71
|
+
disposeComposer();
|
|
72
|
+
const composer = new EffectComposer$1(gl, {
|
|
73
|
+
multisampling: props.multisampling > 0 ? props.multisampling : 0,
|
|
74
|
+
depthBuffer: props.depthBuffer,
|
|
75
|
+
stencilBuffer: props.stencilBuffer
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Create render pass
|
|
79
|
+
currentRenderPass = new RenderPass(scene, camera);
|
|
80
|
+
composer.addPass(currentRenderPass);
|
|
81
|
+
|
|
82
|
+
// Create effect pass if we already have effects
|
|
83
|
+
if (effects.length > 0) {
|
|
84
|
+
const sortedEffects = [...effects].sort((a, b) => a.priority - b.priority).map(e => e.effect);
|
|
85
|
+
currentEffectPass = new EffectPass(camera, ...sortedEffects);
|
|
86
|
+
composer.addPass(currentEffectPass);
|
|
87
|
+
}
|
|
88
|
+
composerRef.value = composer;
|
|
89
|
+
|
|
90
|
+
// Disable default renderer autoClear
|
|
91
|
+
if (originalAutoClear === null) {
|
|
92
|
+
originalAutoClear = gl.autoClear;
|
|
93
|
+
}
|
|
94
|
+
gl.autoClear = false;
|
|
95
|
+
return composer;
|
|
96
|
+
}
|
|
97
|
+
function rebuildEffectPass() {
|
|
98
|
+
const composer = composerRef.value;
|
|
99
|
+
if (!composer) return;
|
|
100
|
+
const {
|
|
101
|
+
camera
|
|
102
|
+
} = state.value;
|
|
103
|
+
|
|
104
|
+
// Remove old effect pass
|
|
105
|
+
if (currentEffectPass) {
|
|
106
|
+
composer.removePass(currentEffectPass);
|
|
107
|
+
currentEffectPass.dispose();
|
|
108
|
+
currentEffectPass = null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Build new effect pass with current effects
|
|
112
|
+
if (effects.length > 0) {
|
|
113
|
+
const sortedEffects = [...effects].sort((a, b) => a.priority - b.priority).map(e => e.effect);
|
|
114
|
+
currentEffectPass = new EffectPass(camera, ...sortedEffects);
|
|
115
|
+
composer.addPass(currentEffectPass);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
watch(() => [state.value.gl, state.value.scene, state.value.camera], ([gl, scene, camera]) => {
|
|
119
|
+
if (!gl || !scene || !camera) {
|
|
120
|
+
disposeComposer();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
createComposer();
|
|
124
|
+
}, {
|
|
125
|
+
immediate: true
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Provide the context so child effect components can register
|
|
129
|
+
const composerContext = {
|
|
130
|
+
addEffect(effect, priority = 0) {
|
|
131
|
+
effects.push({
|
|
132
|
+
effect,
|
|
133
|
+
priority
|
|
134
|
+
});
|
|
135
|
+
rebuildEffectPass();
|
|
136
|
+
return () => composerContext.removeEffect(effect);
|
|
137
|
+
},
|
|
138
|
+
removeEffect(effect) {
|
|
139
|
+
const index = effects.findIndex(e => e.effect === effect);
|
|
140
|
+
if (index !== -1) {
|
|
141
|
+
effects.splice(index, 1);
|
|
142
|
+
rebuildEffectPass();
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
composer: composerRef
|
|
146
|
+
};
|
|
147
|
+
provide(COMPOSER_CONTEXT, composerContext);
|
|
148
|
+
|
|
149
|
+
// Watch for camera changes — update render pass and effect pass
|
|
150
|
+
watch(() => state.value.camera, camera => {
|
|
151
|
+
if (currentRenderPass) {
|
|
152
|
+
currentRenderPass.mainCamera = camera;
|
|
153
|
+
}
|
|
154
|
+
if (currentEffectPass) {
|
|
155
|
+
currentEffectPass.mainCamera = camera;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Watch for size changes — update composer size
|
|
160
|
+
watch(() => state.value.size, size => {
|
|
161
|
+
const composer = composerRef.value;
|
|
162
|
+
if (composer) {
|
|
163
|
+
composer.setSize(size.width, size.height, false);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Watch for multisampling changes — recreate composer
|
|
168
|
+
watch(() => props.multisampling, () => {
|
|
169
|
+
if (createComposer()) rebuildEffectPass();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Use useFrame with priority 1 to take over rendering
|
|
173
|
+
useFrame((_frameState, delta) => {
|
|
174
|
+
if (!props.enabled || !composerRef.value) return;
|
|
175
|
+
composerRef.value.render(delta);
|
|
176
|
+
}, 1);
|
|
177
|
+
|
|
178
|
+
// Cleanup on unmount
|
|
179
|
+
onBeforeUnmount(() => {
|
|
180
|
+
const {
|
|
181
|
+
gl
|
|
182
|
+
} = state.value;
|
|
183
|
+
|
|
184
|
+
// Restore original autoClear
|
|
185
|
+
if (originalAutoClear !== null) {
|
|
186
|
+
gl.autoClear = originalAutoClear;
|
|
187
|
+
}
|
|
188
|
+
disposeComposer();
|
|
189
|
+
effects.length = 0;
|
|
190
|
+
});
|
|
191
|
+
return () => slots.default == null ? void 0 : slots.default();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const Bloom = defineComponent({
|
|
196
|
+
name: 'Bloom',
|
|
197
|
+
props: {
|
|
198
|
+
intensity: {
|
|
199
|
+
type: Number,
|
|
200
|
+
default: 1
|
|
201
|
+
},
|
|
202
|
+
luminanceThreshold: {
|
|
203
|
+
type: Number,
|
|
204
|
+
default: 0.9
|
|
205
|
+
},
|
|
206
|
+
luminanceSmoothing: {
|
|
207
|
+
type: Number,
|
|
208
|
+
default: 0.025
|
|
209
|
+
},
|
|
210
|
+
mipmapBlur: {
|
|
211
|
+
type: Boolean,
|
|
212
|
+
default: true
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
setup(props) {
|
|
216
|
+
const composerCtx = inject(COMPOSER_CONTEXT);
|
|
217
|
+
if (!composerCtx) {
|
|
218
|
+
throw new Error('Bloom must be a child of EffectComposer');
|
|
219
|
+
}
|
|
220
|
+
const effect = new BloomEffect({
|
|
221
|
+
intensity: props.intensity,
|
|
222
|
+
luminanceThreshold: props.luminanceThreshold,
|
|
223
|
+
luminanceSmoothing: props.luminanceSmoothing,
|
|
224
|
+
mipmapBlur: props.mipmapBlur
|
|
225
|
+
});
|
|
226
|
+
const removeEffect = composerCtx.addEffect(effect, 0);
|
|
227
|
+
watch(() => props.intensity, value => {
|
|
228
|
+
effect.intensity = value;
|
|
229
|
+
});
|
|
230
|
+
watch(() => props.luminanceThreshold, value => {
|
|
231
|
+
effect.luminanceMaterial.threshold = value;
|
|
232
|
+
});
|
|
233
|
+
watch(() => props.luminanceSmoothing, value => {
|
|
234
|
+
effect.luminanceMaterial.smoothing = value;
|
|
235
|
+
});
|
|
236
|
+
watch(() => props.mipmapBlur, value => {
|
|
237
|
+
effect.mipmapBlurPass.enabled = value;
|
|
238
|
+
});
|
|
239
|
+
onBeforeUnmount(() => {
|
|
240
|
+
removeEffect();
|
|
241
|
+
effect.dispose();
|
|
242
|
+
});
|
|
243
|
+
return () => null;
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const BrightnessContrast = defineComponent({
|
|
248
|
+
name: 'BrightnessContrast',
|
|
249
|
+
props: {
|
|
250
|
+
brightness: {
|
|
251
|
+
type: Number,
|
|
252
|
+
default: 0
|
|
253
|
+
},
|
|
254
|
+
contrast: {
|
|
255
|
+
type: Number,
|
|
256
|
+
default: 0
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
setup(props) {
|
|
260
|
+
const composerCtx = inject(COMPOSER_CONTEXT);
|
|
261
|
+
if (!composerCtx) {
|
|
262
|
+
throw new Error('BrightnessContrast must be a child of EffectComposer');
|
|
263
|
+
}
|
|
264
|
+
const effect = new BrightnessContrastEffect({
|
|
265
|
+
brightness: props.brightness,
|
|
266
|
+
contrast: props.contrast
|
|
267
|
+
});
|
|
268
|
+
const removeEffect = composerCtx.addEffect(effect, 0);
|
|
269
|
+
watch(() => props.brightness, value => {
|
|
270
|
+
effect.brightness = value;
|
|
271
|
+
});
|
|
272
|
+
watch(() => props.contrast, value => {
|
|
273
|
+
effect.contrast = value;
|
|
274
|
+
});
|
|
275
|
+
onBeforeUnmount(() => {
|
|
276
|
+
removeEffect();
|
|
277
|
+
effect.dispose();
|
|
278
|
+
});
|
|
279
|
+
return () => null;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const HueSaturation = defineComponent({
|
|
284
|
+
name: 'HueSaturation',
|
|
285
|
+
props: {
|
|
286
|
+
hue: {
|
|
287
|
+
type: Number,
|
|
288
|
+
default: 0
|
|
289
|
+
},
|
|
290
|
+
saturation: {
|
|
291
|
+
type: Number,
|
|
292
|
+
default: 0
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
setup(props) {
|
|
296
|
+
const composerCtx = inject(COMPOSER_CONTEXT);
|
|
297
|
+
if (!composerCtx) {
|
|
298
|
+
throw new Error('HueSaturation must be a child of EffectComposer');
|
|
299
|
+
}
|
|
300
|
+
const effect = new HueSaturationEffect({
|
|
301
|
+
hue: props.hue,
|
|
302
|
+
saturation: props.saturation
|
|
303
|
+
});
|
|
304
|
+
const removeEffect = composerCtx.addEffect(effect, 0);
|
|
305
|
+
watch(() => props.hue, value => {
|
|
306
|
+
effect.hue = value;
|
|
307
|
+
});
|
|
308
|
+
watch(() => props.saturation, value => {
|
|
309
|
+
effect.saturation = value;
|
|
310
|
+
});
|
|
311
|
+
onBeforeUnmount(() => {
|
|
312
|
+
removeEffect();
|
|
313
|
+
effect.dispose();
|
|
314
|
+
});
|
|
315
|
+
return () => null;
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const LUT = defineComponent({
|
|
320
|
+
name: 'LUT',
|
|
321
|
+
props: {
|
|
322
|
+
lut: {
|
|
323
|
+
type: Object,
|
|
324
|
+
required: true
|
|
325
|
+
},
|
|
326
|
+
tetrahedralInterpolation: {
|
|
327
|
+
type: Boolean,
|
|
328
|
+
default: false
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
setup(props) {
|
|
332
|
+
const composerCtx = inject(COMPOSER_CONTEXT);
|
|
333
|
+
if (!composerCtx) {
|
|
334
|
+
throw new Error('LUT must be a child of EffectComposer');
|
|
335
|
+
}
|
|
336
|
+
const effect = new LUT3DEffect(props.lut, {
|
|
337
|
+
tetrahedralInterpolation: props.tetrahedralInterpolation
|
|
338
|
+
});
|
|
339
|
+
const removeEffect = composerCtx.addEffect(effect, 0);
|
|
340
|
+
watch(() => props.lut, value => {
|
|
341
|
+
effect.lut = value;
|
|
342
|
+
});
|
|
343
|
+
watch(() => props.tetrahedralInterpolation, value => {
|
|
344
|
+
effect.tetrahedralInterpolation = value;
|
|
345
|
+
});
|
|
346
|
+
onBeforeUnmount(() => {
|
|
347
|
+
removeEffect();
|
|
348
|
+
effect.dispose();
|
|
349
|
+
});
|
|
350
|
+
return () => null;
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const ToneMapping = defineComponent({
|
|
355
|
+
name: 'ToneMapping',
|
|
356
|
+
props: {
|
|
357
|
+
mode: {
|
|
358
|
+
type: Number,
|
|
359
|
+
default: ToneMappingMode.AGX
|
|
360
|
+
},
|
|
361
|
+
resolution: {
|
|
362
|
+
type: Number,
|
|
363
|
+
default: 256
|
|
364
|
+
},
|
|
365
|
+
whitePoint: {
|
|
366
|
+
type: Number,
|
|
367
|
+
default: 4
|
|
368
|
+
},
|
|
369
|
+
middleGrey: {
|
|
370
|
+
type: Number,
|
|
371
|
+
default: 0.6
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
setup(props) {
|
|
375
|
+
const composerCtx = inject(COMPOSER_CONTEXT);
|
|
376
|
+
if (!composerCtx) {
|
|
377
|
+
throw new Error('ToneMapping must be a child of EffectComposer');
|
|
378
|
+
}
|
|
379
|
+
const effect = new ToneMappingEffect({
|
|
380
|
+
mode: props.mode,
|
|
381
|
+
resolution: props.resolution,
|
|
382
|
+
whitePoint: props.whitePoint,
|
|
383
|
+
middleGrey: props.middleGrey
|
|
384
|
+
});
|
|
385
|
+
const removeEffect = composerCtx.addEffect(effect, 0);
|
|
386
|
+
watch(() => props.mode, value => {
|
|
387
|
+
effect.mode = value;
|
|
388
|
+
});
|
|
389
|
+
watch(() => props.resolution, value => {
|
|
390
|
+
effect.resolution.width = value;
|
|
391
|
+
effect.resolution.height = value;
|
|
392
|
+
});
|
|
393
|
+
watch(() => props.whitePoint, value => {
|
|
394
|
+
effect.whitePoint = value;
|
|
395
|
+
});
|
|
396
|
+
watch(() => props.middleGrey, value => {
|
|
397
|
+
effect.middleGrey = value;
|
|
398
|
+
});
|
|
399
|
+
onBeforeUnmount(() => {
|
|
400
|
+
removeEffect();
|
|
401
|
+
effect.dispose();
|
|
402
|
+
});
|
|
403
|
+
return () => null;
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const DepthOfField = defineComponent({
|
|
408
|
+
name: 'DepthOfField',
|
|
409
|
+
props: {
|
|
410
|
+
focusDistance: {
|
|
411
|
+
type: Number,
|
|
412
|
+
default: 0
|
|
413
|
+
},
|
|
414
|
+
focalLength: {
|
|
415
|
+
type: Number,
|
|
416
|
+
default: 0.1
|
|
417
|
+
},
|
|
418
|
+
bokehScale: {
|
|
419
|
+
type: Number,
|
|
420
|
+
default: 2
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
setup(props) {
|
|
424
|
+
const composerCtx = inject(COMPOSER_CONTEXT);
|
|
425
|
+
if (!composerCtx) {
|
|
426
|
+
throw new Error('DepthOfField must be a child of EffectComposer');
|
|
427
|
+
}
|
|
428
|
+
const state = useThree();
|
|
429
|
+
const {
|
|
430
|
+
camera
|
|
431
|
+
} = state.value;
|
|
432
|
+
const effect = new DepthOfFieldEffect(camera, {
|
|
433
|
+
focusDistance: props.focusDistance,
|
|
434
|
+
focalLength: props.focalLength,
|
|
435
|
+
bokehScale: props.bokehScale
|
|
436
|
+
});
|
|
437
|
+
const removeEffect = composerCtx.addEffect(effect, 0);
|
|
438
|
+
watch(() => props.focusDistance, value => {
|
|
439
|
+
effect.cocMaterial.focusDistance = value;
|
|
440
|
+
});
|
|
441
|
+
watch(() => props.focalLength, value => {
|
|
442
|
+
effect.cocMaterial.focalLength = value;
|
|
443
|
+
});
|
|
444
|
+
watch(() => props.bokehScale, value => {
|
|
445
|
+
effect.bokehScale = value;
|
|
446
|
+
});
|
|
447
|
+
onBeforeUnmount(() => {
|
|
448
|
+
removeEffect();
|
|
449
|
+
effect.dispose();
|
|
450
|
+
});
|
|
451
|
+
return () => null;
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const Noise = defineComponent({
|
|
456
|
+
name: 'Noise',
|
|
457
|
+
props: {
|
|
458
|
+
premultiply: {
|
|
459
|
+
type: Boolean,
|
|
460
|
+
default: false
|
|
461
|
+
},
|
|
462
|
+
blendFunction: {
|
|
463
|
+
type: Number,
|
|
464
|
+
default: BlendFunction.SCREEN
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
setup(props) {
|
|
468
|
+
const composerCtx = inject(COMPOSER_CONTEXT);
|
|
469
|
+
if (!composerCtx) {
|
|
470
|
+
throw new Error('Noise must be a child of EffectComposer');
|
|
471
|
+
}
|
|
472
|
+
const effect = new NoiseEffect({
|
|
473
|
+
premultiply: props.premultiply,
|
|
474
|
+
blendFunction: props.blendFunction
|
|
475
|
+
});
|
|
476
|
+
const removeEffect = composerCtx.addEffect(effect, 0);
|
|
477
|
+
watch(() => props.premultiply, value => {
|
|
478
|
+
effect.premultiply = value;
|
|
479
|
+
});
|
|
480
|
+
watch(() => props.blendFunction, value => {
|
|
481
|
+
effect.blendMode.blendFunction = value;
|
|
482
|
+
});
|
|
483
|
+
onBeforeUnmount(() => {
|
|
484
|
+
removeEffect();
|
|
485
|
+
effect.dispose();
|
|
486
|
+
});
|
|
487
|
+
return () => null;
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
const Vignette = defineComponent({
|
|
492
|
+
name: 'Vignette',
|
|
493
|
+
props: {
|
|
494
|
+
offset: {
|
|
495
|
+
type: Number,
|
|
496
|
+
default: 0.5
|
|
497
|
+
},
|
|
498
|
+
darkness: {
|
|
499
|
+
type: Number,
|
|
500
|
+
default: 0.5
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
setup(props) {
|
|
504
|
+
const composerCtx = inject(COMPOSER_CONTEXT);
|
|
505
|
+
if (!composerCtx) {
|
|
506
|
+
throw new Error('Vignette must be a child of EffectComposer');
|
|
507
|
+
}
|
|
508
|
+
const effect = new VignetteEffect({
|
|
509
|
+
offset: props.offset,
|
|
510
|
+
darkness: props.darkness
|
|
511
|
+
});
|
|
512
|
+
const removeEffect = composerCtx.addEffect(effect, 0);
|
|
513
|
+
watch(() => props.offset, value => {
|
|
514
|
+
effect.offset = value;
|
|
515
|
+
});
|
|
516
|
+
watch(() => props.darkness, value => {
|
|
517
|
+
effect.darkness = value;
|
|
518
|
+
});
|
|
519
|
+
onBeforeUnmount(() => {
|
|
520
|
+
removeEffect();
|
|
521
|
+
effect.dispose();
|
|
522
|
+
});
|
|
523
|
+
return () => null;
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
const postprocessingFiberPlugin = defineFiberPlugin({
|
|
528
|
+
name: '@xperimntl/vue-threejs-postprocessing',
|
|
529
|
+
setup(ctx, options) {
|
|
530
|
+
if (options) {
|
|
531
|
+
ctx.provide(POSTPROCESSING_DEFAULTS, options);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
function createPostprocessingPlugin(options) {
|
|
536
|
+
return withPluginOptions(postprocessingFiberPlugin, options);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export { Bloom, BrightnessContrast, COMPOSER_CONTEXT, DepthOfField, EffectComposer, HueSaturation, LUT, Noise, POSTPROCESSING_DEFAULTS, ToneMapping, Vignette, createPostprocessingPlugin, postprocessingFiberPlugin };
|