@needle-tools/engine 4.6.1-next.f9f2e7d → 4.6.2-next.fb486b2
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/{needle-engine.bundle-DoQP-VX2.min.js → needle-engine.bundle-CJ4jhuta.min.js} +173 -161
- package/dist/{needle-engine.bundle-ntX9QTqT.js → needle-engine.bundle-CQzZighj.js} +7390 -7295
- package/dist/{needle-engine.bundle-CbE5i73R.umd.cjs → needle-engine.bundle-CdAK5p8o.umd.cjs} +177 -165
- package/dist/needle-engine.js +48 -48
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/engine_context.d.ts +2 -1
- package/lib/engine/engine_context.js +3 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_three_utils.d.ts +17 -14
- package/lib/engine/engine_three_utils.js +106 -53
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/engine_tonemapping.d.ts +4 -0
- package/lib/engine/engine_tonemapping.js +21 -18
- package/lib/engine/engine_tonemapping.js.map +1 -1
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +4 -1
- package/lib/engine/webcomponents/needle-engine.extras.js +3 -3
- package/lib/engine/webcomponents/needle-engine.extras.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.js +11 -21
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/CameraUtils.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +1 -1
- package/lib/engine-components/postprocessing/Effects/BloomEffect.js +2 -5
- package/lib/engine-components/postprocessing/Effects/BloomEffect.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/ColorAdjustments.d.ts +8 -0
- package/lib/engine-components/postprocessing/Effects/ColorAdjustments.js +27 -8
- package/lib/engine-components/postprocessing/Effects/ColorAdjustments.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.js +1 -0
- package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Sharpening.d.ts +1 -0
- package/lib/engine-components/postprocessing/Effects/Sharpening.js +4 -0
- package/lib/engine-components/postprocessing/Effects/Sharpening.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.d.ts +2 -9
- package/lib/engine-components/postprocessing/Effects/Tonemapping.js +23 -71
- package/lib/engine-components/postprocessing/Effects/Tonemapping.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +13 -0
- package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.js +52 -0
- package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.js.map +1 -0
- package/lib/engine-components/postprocessing/PostProcessingEffect.d.ts +7 -7
- package/lib/engine-components/postprocessing/PostProcessingEffect.js +9 -9
- package/lib/engine-components/postprocessing/PostProcessingEffect.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +10 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +152 -23
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/postprocessing/Volume.js +19 -24
- package/lib/engine-components/postprocessing/Volume.js.map +1 -1
- package/lib/engine-components/postprocessing/index.d.ts +1 -1
- package/lib/engine-components/postprocessing/index.js +1 -1
- package/lib/engine-components/postprocessing/index.js.map +1 -1
- package/lib/engine-components/postprocessing/utils.d.ts +13 -7
- package/lib/engine-components/postprocessing/utils.js +37 -53
- package/lib/engine-components/postprocessing/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/engine_context.ts +5 -3
- package/src/engine/engine_three_utils.ts +134 -58
- package/src/engine/engine_tonemapping.ts +23 -24
- package/src/engine/engine_utils.ts +2 -2
- package/src/engine/webcomponents/needle-engine.extras.ts +3 -3
- package/src/engine/webcomponents/needle-engine.ts +14 -25
- package/src/engine-components/CameraUtils.ts +3 -3
- package/src/engine-components/postprocessing/Effects/BloomEffect.ts +2 -4
- package/src/engine-components/postprocessing/Effects/ColorAdjustments.ts +24 -13
- package/src/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.ts +1 -0
- package/src/engine-components/postprocessing/Effects/Sharpening.ts +5 -0
- package/src/engine-components/postprocessing/Effects/Tonemapping.ts +26 -80
- package/src/engine-components/postprocessing/Effects/Tonemapping.utils.ts +60 -0
- package/src/engine-components/postprocessing/PostProcessingEffect.ts +9 -9
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +174 -27
- package/src/engine-components/postprocessing/Volume.ts +19 -26
- package/src/engine-components/postprocessing/index.ts +2 -2
- package/src/engine-components/postprocessing/utils.ts +41 -56
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { Effect, EffectComposer, Pass, ToneMappingEffect as _TonemappingEffect } from "postprocessing";
|
|
2
|
-
import { Camera as Camera3, HalfFloatType, NoToneMapping, Scene } from "three";
|
|
2
|
+
import { Camera as Camera3, DepthTexture, HalfFloatType, LinearFilter, NoToneMapping, Scene, Texture, ToneMapping, WebGLRenderTarget } from "three";
|
|
3
3
|
|
|
4
4
|
import { showBalloonWarning } from "../../engine/debug/index.js";
|
|
5
5
|
// import { internal_SetSharpeningEffectModule } from "./Effects/Sharpening.js";
|
|
6
6
|
import { MODULES } from "../../engine/engine_modules.js";
|
|
7
7
|
import { Context } from "../../engine/engine_setup.js";
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
8
|
+
import { Graphics } from "../../engine/engine_three_utils.js";
|
|
9
|
+
import { Constructor } from "../../engine/engine_types.js";
|
|
10
|
+
import { getParam } from "../../engine/engine_utils.js";
|
|
10
11
|
import { Camera } from "../Camera.js";
|
|
12
|
+
import { threeToneMappingToEffectMode } from "./Effects/Tonemapping.utils.js";
|
|
11
13
|
import { PostProcessingEffect, PostProcessingEffectContext } from "./PostProcessingEffect.js";
|
|
12
|
-
import { orderEffects, PostprocessingEffectData } from "./utils.js";
|
|
14
|
+
import { orderEffects, PostprocessingEffectData, PostProcessingEffectOrder } from "./utils.js";
|
|
13
15
|
|
|
14
16
|
declare const NEEDLE_USE_POSTPROCESSING: boolean;
|
|
15
17
|
globalThis["NEEDLE_USE_POSTPROCESSING"] = globalThis["NEEDLE_USE_POSTPROCESSING"] !== undefined ? globalThis["NEEDLE_USE_POSTPROCESSING"] : true;
|
|
@@ -19,6 +21,7 @@ const debug = getParam("debugpost");
|
|
|
19
21
|
|
|
20
22
|
const activeKey = Symbol("needle:postprocessing-handler");
|
|
21
23
|
const autoclearSetting = Symbol("needle:previous-autoclear-state");
|
|
24
|
+
const previousToneMapping = Symbol("needle:previous-tone-mapping");
|
|
22
25
|
|
|
23
26
|
/**
|
|
24
27
|
* PostProcessingHandler is responsible for applying post processing effects to the scene. It is internally used by the {@link Volume} component
|
|
@@ -29,6 +32,14 @@ export class PostProcessingHandler {
|
|
|
29
32
|
private _lastVolumeComponents?: PostProcessingEffect[];
|
|
30
33
|
private readonly _effects: Array<PostprocessingEffectData> = [];
|
|
31
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Returns true if a specific effect is currently active in the post processing stack.
|
|
37
|
+
*/
|
|
38
|
+
getEffectIsActive(effect: Effect): boolean {
|
|
39
|
+
if (!effect) return false;
|
|
40
|
+
return this._isActive && this._effects.some(e => e.effect === effect);
|
|
41
|
+
}
|
|
42
|
+
|
|
32
43
|
get isActive() {
|
|
33
44
|
return this._isActive;
|
|
34
45
|
}
|
|
@@ -78,6 +89,9 @@ export class PostProcessingHandler {
|
|
|
78
89
|
if (typeof context.renderer[autoclearSetting] === "boolean") {
|
|
79
90
|
context.renderer.autoClear = context.renderer[autoclearSetting];
|
|
80
91
|
}
|
|
92
|
+
if (typeof context.renderer[previousToneMapping] === "number") {
|
|
93
|
+
context.renderer.toneMapping = context.renderer[previousToneMapping] as ToneMapping;
|
|
94
|
+
}
|
|
81
95
|
}
|
|
82
96
|
|
|
83
97
|
this._composer?.removeAllPasses();
|
|
@@ -153,14 +167,16 @@ export class PostProcessingHandler {
|
|
|
153
167
|
for (const effect of res) {
|
|
154
168
|
this._effects.push({
|
|
155
169
|
effect,
|
|
156
|
-
|
|
170
|
+
typeName: component.typeName,
|
|
171
|
+
priority: component.order
|
|
157
172
|
});
|
|
158
173
|
}
|
|
159
174
|
}
|
|
160
175
|
else {
|
|
161
176
|
this._effects.push({
|
|
162
177
|
effect: res,
|
|
163
|
-
|
|
178
|
+
typeName: component.typeName,
|
|
179
|
+
priority: component.order
|
|
164
180
|
});
|
|
165
181
|
}
|
|
166
182
|
}
|
|
@@ -171,14 +187,6 @@ export class PostProcessingHandler {
|
|
|
171
187
|
}
|
|
172
188
|
}
|
|
173
189
|
|
|
174
|
-
// Ensure that we have a tonemapping effect if the renderer is set to use a tone mapping
|
|
175
|
-
if (this.context.renderer.toneMapping != NoToneMapping) {
|
|
176
|
-
if (!this._effects.find(e => e instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect)) {
|
|
177
|
-
const tonemapping = new MODULES.POSTPROCESSING.MODULE.ToneMappingEffect();
|
|
178
|
-
this._effects.push({ effect: tonemapping });
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
190
|
this.applyEffects(context);
|
|
183
191
|
}
|
|
184
192
|
|
|
@@ -191,6 +199,18 @@ export class PostProcessingHandler {
|
|
|
191
199
|
get hasSmaaEffect() { return this._hasSmaaEffect; }
|
|
192
200
|
|
|
193
201
|
|
|
202
|
+
|
|
203
|
+
private _customInputBuffer: WebGLRenderTarget<Texture> | null = null;
|
|
204
|
+
private _customInputBufferId = 0;
|
|
205
|
+
private _multisampling: number = 0;
|
|
206
|
+
set multisampling(value: number) {
|
|
207
|
+
this._multisampling = value;
|
|
208
|
+
}
|
|
209
|
+
get multisampling() {
|
|
210
|
+
return this._multisampling;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
|
|
194
214
|
/** Build composer passes */
|
|
195
215
|
private applyEffects(context: Context) {
|
|
196
216
|
// Reset state
|
|
@@ -198,8 +218,7 @@ export class PostProcessingHandler {
|
|
|
198
218
|
this._anyPassHasNormal = false;
|
|
199
219
|
this._hasSmaaEffect = false;
|
|
200
220
|
|
|
201
|
-
|
|
202
|
-
if (effectsOrPasses.length <= 0) {
|
|
221
|
+
if (this._effects.length <= 0) {
|
|
203
222
|
return;
|
|
204
223
|
}
|
|
205
224
|
|
|
@@ -217,6 +236,25 @@ export class PostProcessingHandler {
|
|
|
217
236
|
}
|
|
218
237
|
renderer[autoclearSetting] = renderer.autoClear;
|
|
219
238
|
|
|
239
|
+
if (typeof renderer[previousToneMapping] === "number") {
|
|
240
|
+
renderer.toneMapping = renderer[previousToneMapping] as ToneMapping;
|
|
241
|
+
}
|
|
242
|
+
renderer[previousToneMapping] = renderer.toneMapping;
|
|
243
|
+
|
|
244
|
+
// Ensure that we have a tonemapping effect if the renderer is set to use a tone mapping
|
|
245
|
+
if (renderer.toneMapping != NoToneMapping) {
|
|
246
|
+
if (!this._effects.find(e => e instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect)) {
|
|
247
|
+
const tonemapping = new MODULES.POSTPROCESSING.MODULE.ToneMappingEffect();
|
|
248
|
+
tonemapping.name = `ToneMapping (${renderer.toneMapping})`;
|
|
249
|
+
tonemapping.mode = threeToneMappingToEffectMode(renderer.toneMapping);
|
|
250
|
+
this._effects.push({
|
|
251
|
+
typeName: "ToneMapping",
|
|
252
|
+
effect: tonemapping,
|
|
253
|
+
priority: PostProcessingEffectOrder.ToneMapping
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
220
258
|
// create composer and set active on context
|
|
221
259
|
if (!this._composer) {
|
|
222
260
|
// const hdrRenderTarget = new WebGLRenderTarget(window.innerWidth, window.innerHeight, { type: HalfFloatType });
|
|
@@ -236,7 +274,7 @@ export class PostProcessingHandler {
|
|
|
236
274
|
composer.setRenderer(renderer);
|
|
237
275
|
composer.setMainScene(scene);
|
|
238
276
|
composer.autoRenderToScreen = true; // Must be enabled because it might be disabled during addPass by the composer itself (depending on the effect's settings or order)
|
|
239
|
-
composer.multisampling = 0;
|
|
277
|
+
composer.multisampling = 0; // Disable multisampling by default
|
|
240
278
|
|
|
241
279
|
for (const prev of composer.passes)
|
|
242
280
|
prev.dispose();
|
|
@@ -244,17 +282,96 @@ export class PostProcessingHandler {
|
|
|
244
282
|
|
|
245
283
|
// Render to screen pass
|
|
246
284
|
const screenpass = new MODULES.POSTPROCESSING.MODULE.RenderPass(scene, cam);
|
|
247
|
-
screenpass.name = "
|
|
285
|
+
screenpass.name = "RenderPass";
|
|
248
286
|
screenpass.mainScene = scene;
|
|
249
287
|
composer.addPass(screenpass);
|
|
250
288
|
|
|
289
|
+
const screenPassRender = screenpass.render;
|
|
290
|
+
this._customInputBuffer?.dispose();
|
|
291
|
+
this._customInputBuffer = null;
|
|
292
|
+
screenpass.render = (renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) => {
|
|
293
|
+
if (!inputBuffer) return;
|
|
294
|
+
|
|
295
|
+
// screenPassRender.call(screenpass, renderer, inputBuffer, outputBuffer, deltaTime, stencilTest);
|
|
296
|
+
// return;
|
|
297
|
+
|
|
298
|
+
// Make sure multisampling is disabled on the composer buffers. Technically a user could still set multisampling directly on the composer so this is to override that and make sure these textures do NOT use multisampling
|
|
299
|
+
inputBuffer.samples = 0;
|
|
300
|
+
if (outputBuffer) {
|
|
301
|
+
outputBuffer.samples = 0;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Make sure the input buffer is a WebGLRenderTarget with the correct settings
|
|
305
|
+
if (!this._customInputBuffer
|
|
306
|
+
|| this._customInputBuffer.width !== inputBuffer.width
|
|
307
|
+
|| this._customInputBuffer.height !== inputBuffer.height
|
|
308
|
+
|| this._customInputBuffer.samples !== this._multisampling
|
|
309
|
+
|| this._customInputBuffer.texture.format !== inputBuffer.texture.format
|
|
310
|
+
|| this._customInputBuffer.texture.type !== HalfFloatType
|
|
311
|
+
) {
|
|
312
|
+
this._customInputBuffer?.dispose();
|
|
313
|
+
|
|
314
|
+
this._customInputBuffer = new WebGLRenderTarget(inputBuffer.width, inputBuffer.height, {
|
|
315
|
+
format: inputBuffer.texture.format,
|
|
316
|
+
type: HalfFloatType,
|
|
317
|
+
depthBuffer: inputBuffer.depthBuffer,
|
|
318
|
+
depthTexture: inputBuffer.depthTexture
|
|
319
|
+
? new DepthTexture(inputBuffer.width, inputBuffer.height)
|
|
320
|
+
: undefined,
|
|
321
|
+
stencilBuffer: inputBuffer.stencilBuffer,
|
|
322
|
+
samples: Math.max(0, this._multisampling),
|
|
323
|
+
minFilter: inputBuffer.texture.minFilter ?? LinearFilter,
|
|
324
|
+
magFilter: inputBuffer.texture.magFilter ?? LinearFilter,
|
|
325
|
+
generateMipmaps: inputBuffer.texture.generateMipmaps,
|
|
326
|
+
});
|
|
327
|
+
this._customInputBufferId++;
|
|
328
|
+
this._customInputBuffer.texture.name = `CustomInputBuffer-${this._customInputBufferId}`;
|
|
329
|
+
if (this._customInputBuffer.depthTexture && inputBuffer.depthTexture) {
|
|
330
|
+
this._customInputBuffer.depthTexture.format = inputBuffer.depthTexture.format;
|
|
331
|
+
this._customInputBuffer.depthTexture.type = inputBuffer.depthTexture.type;
|
|
332
|
+
}
|
|
333
|
+
// https://github.com/pmndrs/postprocessing/blob/ad338df710ef41fee4e5d10ad2c2c299030d46ef/src/core/EffectComposer.js#L366
|
|
334
|
+
if (this._customInputBuffer.samples > 0)
|
|
335
|
+
(this._customInputBuffer as any).ignoreDepthForMultisampleCopy = false;
|
|
336
|
+
|
|
337
|
+
if (debug) console.warn(`[PostProcessing] Input buffer created with size ${this._customInputBuffer.width}x${this._customInputBuffer.height} and samples ${this._customInputBuffer.samples}`);
|
|
338
|
+
}
|
|
339
|
+
// Calling the original render function with the input buffer
|
|
340
|
+
screenPassRender.call(screenpass, renderer, this._customInputBuffer, outputBuffer, deltaTime, stencilTest);
|
|
341
|
+
// Blit the resulting buffer to the input buffer passed in by the composer so it's used for subsequent effects
|
|
342
|
+
Graphics.blit(this._customInputBuffer.texture, inputBuffer, {
|
|
343
|
+
renderer,
|
|
344
|
+
depthTexture: this._customInputBuffer.depthTexture,
|
|
345
|
+
depthWrite: true,
|
|
346
|
+
depthTest: true,
|
|
347
|
+
});
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
|
|
251
351
|
try {
|
|
252
352
|
orderEffects(this._effects);
|
|
253
353
|
|
|
354
|
+
let foundTonemappingEffect = false;
|
|
355
|
+
let activeTonemappingEffect: _TonemappingEffect | null = null;
|
|
356
|
+
for (let i = this._effects.length - 1; i >= 0; i--) {
|
|
357
|
+
const ef = this._effects[i].effect;
|
|
358
|
+
if (ef instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect) {
|
|
359
|
+
// If we already have a tonemapping effect, we can skip this one
|
|
360
|
+
if (foundTonemappingEffect) {
|
|
361
|
+
if (debug) console.warn(`[PostProcessing] Found multiple tonemapping effects in the scene: ${ef.name} and ${activeTonemappingEffect?.name}. Only the last one added will be used.`);
|
|
362
|
+
this._effects.splice(i, 1);
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
activeTonemappingEffect = ef;
|
|
366
|
+
foundTonemappingEffect = true;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
254
370
|
const effectsToMerge: Array<Effect> = [];
|
|
255
371
|
let hasConvolutionEffectInArray = false;
|
|
256
372
|
|
|
257
|
-
for (
|
|
373
|
+
for (let i = 0; i < this._effects.length; i++) {
|
|
374
|
+
const entry = this._effects[i];
|
|
258
375
|
const ef = entry.effect;
|
|
259
376
|
|
|
260
377
|
if (ef instanceof MODULES.POSTPROCESSING.MODULE.SMAAEffect) {
|
|
@@ -264,16 +381,30 @@ export class PostProcessingHandler {
|
|
|
264
381
|
this._anyPassHasNormal = true;
|
|
265
382
|
}
|
|
266
383
|
|
|
384
|
+
// There can be only one tonemapping effect in the scene, so we skip all others
|
|
385
|
+
if (ef instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect && activeTonemappingEffect !== ef) {
|
|
386
|
+
// If we already have a tonemapping effect, we can skip this one
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// We can also not merge multiple effects of the same type in one pass
|
|
391
|
+
// So we first need to create a new pass with whatever effects we have so far
|
|
392
|
+
// TODO: this seems to work fine for some effects (like ColorAdjustments) and only caused issues with multiple Tonemapping effects so far which is handled above
|
|
393
|
+
// const constructor = ef.constructor;
|
|
394
|
+
// if (effectsToMerge.find(e => e.constructor === constructor)) {
|
|
395
|
+
// this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
396
|
+
// }
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
|
|
267
400
|
if (ef instanceof MODULES.POSTPROCESSING.MODULE.Effect) {
|
|
268
401
|
const attributes = ef.getAttributes();
|
|
269
402
|
const convolution = MODULES.POSTPROCESSING.MODULE.EffectAttribute.CONVOLUTION;
|
|
270
403
|
if (attributes & convolution) {
|
|
271
|
-
if(debug) console.log("[PostProcessing] Convolution effect
|
|
404
|
+
if (debug) console.log("[PostProcessing] Convolution effect: " + ef.name);
|
|
272
405
|
if (hasConvolutionEffectInArray) {
|
|
273
|
-
|
|
274
|
-
if(debug) console.log("[PostProcessing] Merging effects with convolution effect in array", effectsToMerge.map(e => e.name).join(", "));
|
|
406
|
+
if (debug) console.log("[PostProcessing] Merging effects with convolution", effectsToMerge.map(e => e.name).join(", "));
|
|
275
407
|
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
276
|
-
|
|
277
408
|
}
|
|
278
409
|
hasConvolutionEffectInArray = true;
|
|
279
410
|
}
|
|
@@ -293,28 +424,44 @@ export class PostProcessingHandler {
|
|
|
293
424
|
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
294
425
|
composer.addPass(ef);
|
|
295
426
|
}
|
|
427
|
+
|
|
296
428
|
}
|
|
297
429
|
|
|
298
430
|
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
299
431
|
}
|
|
300
432
|
catch (e) {
|
|
301
433
|
console.error("Error while applying postprocessing effects", e);
|
|
434
|
+
composer.passes.forEach(p => p.dispose());
|
|
302
435
|
composer.removeAllPasses();
|
|
303
436
|
}
|
|
304
437
|
|
|
305
438
|
// The last pass is the one that renders to the screen, so we need to set the gamma correction for it (and enable it for all others)
|
|
306
|
-
|
|
439
|
+
let foundEnabled = false;
|
|
440
|
+
for (let i = composer.passes.length - 1; i >= 0; i--) {
|
|
307
441
|
const pass = composer.passes[i];
|
|
308
|
-
|
|
442
|
+
let gammaCorrect = false;
|
|
443
|
+
let renderToScreen = false;
|
|
444
|
+
if (pass.enabled) {
|
|
445
|
+
if (!foundEnabled) {
|
|
446
|
+
gammaCorrect = true;
|
|
447
|
+
renderToScreen = true;
|
|
448
|
+
}
|
|
449
|
+
foundEnabled = true;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
pass.renderToScreen = renderToScreen;
|
|
453
|
+
|
|
309
454
|
if ((pass as any)?.configuration !== undefined) {
|
|
310
|
-
(pass as any).configuration.gammaCorrection =
|
|
455
|
+
(pass as any).configuration.gammaCorrection = gammaCorrect;
|
|
311
456
|
}
|
|
312
457
|
else if ("autosetGamma" in pass) {
|
|
313
458
|
// Some effects have a autosetGamma property that we can use to set the gamma correction
|
|
314
|
-
pass.autosetGamma =
|
|
459
|
+
pass.autosetGamma = gammaCorrect;
|
|
315
460
|
}
|
|
316
461
|
|
|
462
|
+
|
|
317
463
|
this._anyPassHasDepth ||= pass.needsDepthTexture;
|
|
464
|
+
|
|
318
465
|
}
|
|
319
466
|
|
|
320
467
|
// DEBUG LAND BELOW
|
|
@@ -187,14 +187,12 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
187
187
|
|
|
188
188
|
this.context.composer.setMainScene(this.context.scene);
|
|
189
189
|
|
|
190
|
-
const composer = this.context.composer as EffectComposer;
|
|
191
|
-
|
|
192
190
|
if (this.multisampling === "auto") {
|
|
193
191
|
|
|
194
192
|
// If the postprocessing handler is using depth+normals (e.g. with SMAA) we ALWAYS disable multisampling to avoid ugly edges
|
|
195
193
|
if (this._postprocessing && (this._postprocessing.hasSmaaEffect)) {
|
|
196
|
-
if (
|
|
197
|
-
|
|
194
|
+
if (this._postprocessing.multisampling !== 0) {
|
|
195
|
+
this._postprocessing.multisampling = 0;
|
|
198
196
|
if (debug || isDevEnvironment()) {
|
|
199
197
|
console.warn(`[PostProcessing] Disabling multisampling you're using SMAA and have set the 'multisampling: auto' on your PostprocessingManager/Volume component. If you need anti-aliasing, consider adding an Antialiasing component or SMAA effect to the PostprocessingManager.`);
|
|
200
198
|
}
|
|
@@ -206,45 +204,41 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
206
204
|
if (this.context.time.realtimeSinceStartup - this._componentEnabledTime > 2
|
|
207
205
|
&& timeSinceLastChange > .5
|
|
208
206
|
) {
|
|
209
|
-
const prev =
|
|
207
|
+
const prev = this._postprocessing.multisampling;
|
|
210
208
|
|
|
211
|
-
if (
|
|
209
|
+
if (this._postprocessing.multisampling > 0 && this.context.time.smoothedFps <= 50) {
|
|
212
210
|
this._multisampleAutoChangeTime = this.context.time.realtimeSinceStartup;
|
|
213
211
|
this._multisampleAutoDecreaseTime = this.context.time.realtimeSinceStartup;
|
|
214
|
-
let newMultiSample =
|
|
212
|
+
let newMultiSample = this._postprocessing.multisampling * .5;
|
|
215
213
|
newMultiSample = Math.floor(newMultiSample);
|
|
216
|
-
if (newMultiSample !=
|
|
217
|
-
|
|
214
|
+
if (newMultiSample != this._postprocessing.multisampling) {
|
|
215
|
+
this._postprocessing.multisampling = newMultiSample;
|
|
218
216
|
}
|
|
219
|
-
if (debug) console.debug(`[PostProcessing] Reduced multisampling from ${prev} to ${
|
|
217
|
+
if (debug) console.debug(`[PostProcessing] Reduced multisampling from ${prev} to ${this._postprocessing.multisampling}`);
|
|
220
218
|
}
|
|
221
219
|
// if performance is good for a while try increasing multisampling again
|
|
222
220
|
else if (timeSinceLastChange > 1
|
|
223
221
|
&& this.context.time.smoothedFps >= 59
|
|
224
|
-
&&
|
|
222
|
+
&& this._postprocessing.multisampling < this.context.renderer.capabilities.maxSamples
|
|
225
223
|
&& this.context.time.realtimeSinceStartup - this._multisampleAutoDecreaseTime > 10
|
|
226
224
|
) {
|
|
227
225
|
this._multisampleAutoChangeTime = this.context.time.realtimeSinceStartup;
|
|
228
|
-
let newMultiSample =
|
|
226
|
+
let newMultiSample = this._postprocessing.multisampling <= 0 ? 1 : this._postprocessing.multisampling * 2;
|
|
229
227
|
newMultiSample = Math.floor(newMultiSample);
|
|
230
|
-
if (newMultiSample !==
|
|
231
|
-
|
|
228
|
+
if (newMultiSample !== this._postprocessing.multisampling) {
|
|
229
|
+
this._postprocessing.multisampling = newMultiSample;
|
|
232
230
|
}
|
|
233
|
-
if (debug) console.debug(`[PostProcessing] Increased multisampling from ${prev} to ${
|
|
231
|
+
if (debug) console.debug(`[PostProcessing] Increased multisampling from ${prev} to ${this._postprocessing.multisampling}`);
|
|
234
232
|
}
|
|
235
233
|
}
|
|
236
234
|
}
|
|
237
235
|
}
|
|
238
236
|
else {
|
|
239
237
|
const newMultiSample = Math.max(0, Math.min(this.multisampling, this.context.renderer.capabilities.maxSamples));
|
|
240
|
-
if (newMultiSample !==
|
|
241
|
-
|
|
238
|
+
if (newMultiSample !== this._postprocessing.multisampling)
|
|
239
|
+
this._postprocessing.multisampling = newMultiSample;
|
|
242
240
|
}
|
|
243
241
|
|
|
244
|
-
// Fix multisampling for the composer, it ONLY needs to be done for the input buffer
|
|
245
|
-
// this is super important for performance a negative visual impact
|
|
246
|
-
composer.outputBuffer.samples = 0;
|
|
247
|
-
|
|
248
242
|
// only set the main camera if any pass has a different camera
|
|
249
243
|
// trying to avoid doing this regularly since it involves doing potentially unnecessary work
|
|
250
244
|
// https://github.com/pmndrs/postprocessing/blob/3d3df0576b6d49aec9e763262d5a1ff7429fd91a/src/core/EffectComposer.js#L406
|
|
@@ -309,17 +303,16 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
309
303
|
|
|
310
304
|
this._applyPostQueue();
|
|
311
305
|
|
|
312
|
-
|
|
313
|
-
if (composer) {
|
|
306
|
+
if (this._postprocessing) {
|
|
314
307
|
if (this.multisampling === "auto") {
|
|
315
|
-
|
|
308
|
+
this._postprocessing.multisampling = DeviceUtilities.isMobileDevice()
|
|
316
309
|
? 2
|
|
317
310
|
: 4;
|
|
318
311
|
}
|
|
319
312
|
else {
|
|
320
|
-
|
|
313
|
+
this._postprocessing.multisampling = Math.max(0, Math.min(this.multisampling, this.context.renderer.capabilities.maxSamples));
|
|
321
314
|
}
|
|
322
|
-
if (debug) console.debug(`[PostProcessing] Set multisampling to ${
|
|
315
|
+
if (debug) console.debug(`[PostProcessing] Set multisampling to ${this._postprocessing.multisampling} (Is Mobile: ${DeviceUtilities.isMobileDevice()})`);
|
|
323
316
|
}
|
|
324
317
|
else if (debug) {
|
|
325
318
|
console.warn(`[PostProcessing] No composer found`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./PostProcessingEffect.js";
|
|
2
2
|
export * from "./PostProcessingHandler.js"
|
|
3
|
+
export { PostProcessingEffectOrder } from "./utils.js";
|
|
3
4
|
export { PostProcessingManager } from "./Volume.js"
|
|
4
5
|
export * from "./VolumeParameter.js"
|
|
5
|
-
export * from "./VolumeProfile.js";
|
|
6
|
-
export { PostProcessingEffectPriority } from "./utils.js";
|
|
6
|
+
export * from "./VolumeProfile.js";
|
|
@@ -61,28 +61,32 @@ export function getPostProcessingManager(effect: PostProcessingEffect): IPostPro
|
|
|
61
61
|
export type PostprocessingEffectData = {
|
|
62
62
|
effect: Effect | Pass;
|
|
63
63
|
priority?: number,
|
|
64
|
+
typeName?: string | null | undefined,
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
/**
|
|
67
|
-
* Default
|
|
68
|
-
* E.g. in your custom effect, you can set `
|
|
69
|
-
* OR `
|
|
68
|
+
* Default order for post-processing effects. This can be used to sort effects by their rendering order when creating custom effects.
|
|
69
|
+
* E.g. in your custom effect, you can set `order: PostProcessingEffectOrder.Bloom + 1;` to ensure it gets rendered after the bloom effect.
|
|
70
|
+
* OR `order: PostProcessingEffectOrder.Bloom - 1;` to ensure it gets rendered before the bloom effect.
|
|
70
71
|
* @example
|
|
71
72
|
* ```typescript
|
|
72
|
-
* import {
|
|
73
|
-
*
|
|
73
|
+
* import { PostProcessingEffectOrder } from "@needle-tools/engine"
|
|
74
|
+
*
|
|
74
75
|
* export class MyCustomEffect extends PostProcessingEffect {
|
|
75
|
-
*
|
|
76
|
+
* order: PostProcessingEffectPriority.Bloom + 1; // render after bloom
|
|
76
77
|
*
|
|
77
78
|
* // ... your effect code
|
|
78
79
|
* }
|
|
79
80
|
* ```
|
|
80
81
|
*/
|
|
81
|
-
export const
|
|
82
|
+
export const PostProcessingEffectOrder = {
|
|
83
|
+
/** Used to render effects at the start of the post-processing chain */
|
|
84
|
+
AT_START: -10_000,
|
|
85
|
+
|
|
82
86
|
NormalPass: 0,
|
|
83
87
|
DepthDownsamplingPass: 10,
|
|
84
|
-
|
|
85
|
-
|
|
88
|
+
SSAO: 20,
|
|
89
|
+
SMAA: 30,
|
|
86
90
|
TiltShift: 40,
|
|
87
91
|
DepthOfField: 50,
|
|
88
92
|
ChromaticAberration: 60,
|
|
@@ -92,7 +96,10 @@ export const PostProcessingEffectPriority = {
|
|
|
92
96
|
ToneMapping: 100,
|
|
93
97
|
HueSaturation: 110,
|
|
94
98
|
BrightnessContrast: 120,
|
|
95
|
-
|
|
99
|
+
Sharpening: 130,
|
|
100
|
+
|
|
101
|
+
/** Used to render effects at the end of the post-processing chain, e.g. for final adjustments or overlays. */
|
|
102
|
+
AT_END: 10_000,
|
|
96
103
|
}
|
|
97
104
|
// let effectsOrder: Array<Constructor<Effect | Pass>> | null = null;
|
|
98
105
|
|
|
@@ -103,65 +110,43 @@ export function orderEffects(effects: Array<PostprocessingEffectData>) {
|
|
|
103
110
|
|
|
104
111
|
if (!builtinOrder) {
|
|
105
112
|
builtinOrder = new Map<Constructor<Effect | Pass>, number>();
|
|
106
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.NormalPass,
|
|
107
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.DepthDownsamplingPass,
|
|
108
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.SMAAEffect,
|
|
109
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.SSAOEffect,
|
|
110
|
-
builtinOrder.set(MODULES.
|
|
111
|
-
builtinOrder.set(MODULES.
|
|
112
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.
|
|
113
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.
|
|
114
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.
|
|
115
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.
|
|
116
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.
|
|
117
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.
|
|
118
|
-
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.
|
|
113
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.NormalPass, PostProcessingEffectOrder.NormalPass);
|
|
114
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.DepthDownsamplingPass, PostProcessingEffectOrder.DepthDownsamplingPass);
|
|
115
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.SMAAEffect, PostProcessingEffectOrder.SMAA);
|
|
116
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.SSAOEffect, PostProcessingEffectOrder.SSAO);
|
|
117
|
+
builtinOrder.set(MODULES.POSTPROCESSING_AO.MODULE.N8AOPostPass, PostProcessingEffectOrder.SSAO);
|
|
118
|
+
builtinOrder.set(MODULES.POSTPROCESSING_AO.MODULE.N8AOPass, PostProcessingEffectOrder.SSAO);
|
|
119
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.TiltShiftEffect, PostProcessingEffectOrder.TiltShift);
|
|
120
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.DepthOfFieldEffect, PostProcessingEffectOrder.DepthOfField);
|
|
121
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.ChromaticAberrationEffect, PostProcessingEffectOrder.ChromaticAberration);
|
|
122
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.BloomEffect, PostProcessingEffectOrder.Bloom);
|
|
123
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.SelectiveBloomEffect, PostProcessingEffectOrder.Bloom);
|
|
124
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.VignetteEffect, PostProcessingEffectOrder.Vignette);
|
|
125
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.PixelationEffect, PostProcessingEffectOrder.Pixelation);
|
|
126
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.ToneMappingEffect, PostProcessingEffectOrder.ToneMapping);
|
|
127
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.HueSaturationEffect, PostProcessingEffectOrder.HueSaturation);
|
|
128
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.BrightnessContrastEffect, PostProcessingEffectOrder.BrightnessContrast);
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
// enforce correct order of effects (e.g. DOF before Bloom)
|
|
122
132
|
effects.sort((a, b) => {
|
|
123
133
|
// we use find index here because sometimes constructor names are prefixed with `_`
|
|
124
134
|
// TODO: find a more robust solution that isnt name based (not sure if that exists tho... maybe we must give effect TYPES some priority/index)
|
|
125
|
-
const aidx = typeof a.priority === "number" ? a.priority : builtinOrder!.get(a.effect.constructor as Constructor<Effect | Pass>)
|
|
126
|
-
const bidx = typeof b.priority === "number" ? b.priority : builtinOrder!.get(b.effect.constructor as Constructor<Effect | Pass>)
|
|
135
|
+
const aidx = typeof a.priority === "number" ? a.priority : builtinOrder!.get(a.effect.constructor as Constructor<Effect | Pass>) ?? Number.NEGATIVE_INFINITY;
|
|
136
|
+
const bidx = typeof b.priority === "number" ? b.priority : builtinOrder!.get(b.effect.constructor as Constructor<Effect | Pass>) ?? Number.NEGATIVE_INFINITY;
|
|
127
137
|
|
|
128
138
|
// Unknown effects should be rendered first
|
|
129
|
-
if (aidx
|
|
130
|
-
if (debug) console.warn("Unknown effect found: ", a.constructor.name);
|
|
131
|
-
return -1;
|
|
132
|
-
}
|
|
133
|
-
else if (bidx < 0) {
|
|
134
|
-
if (debug) console.warn("Unknown effect found: ", b.constructor.name);
|
|
139
|
+
if (aidx === Number.NEGATIVE_INFINITY) {
|
|
140
|
+
if (debug) console.warn("Unknown effect found: ", a.constructor.name, a);
|
|
135
141
|
return 1;
|
|
136
142
|
}
|
|
137
|
-
|
|
138
|
-
|
|
143
|
+
else if (bidx === Number.NEGATIVE_INFINITY) {
|
|
144
|
+
if (debug) console.warn("Unknown effect found: ", b.constructor.name, b);
|
|
145
|
+
return -1;
|
|
146
|
+
}
|
|
139
147
|
return aidx - bidx;
|
|
140
148
|
});
|
|
141
149
|
|
|
142
|
-
// effects.sort((a, b) => {
|
|
143
|
-
// if (a.beforeEffect) {
|
|
144
|
-
// const beforeA = effectsOrder!.findIndex(e => a.beforeEffect!.constructor.name.endsWith(e.name));
|
|
145
|
-
// if (beforeA >= 0) {
|
|
146
|
-
// return -1; // before effect should be rendered first
|
|
147
|
-
// }
|
|
148
|
-
// else {
|
|
149
|
-
// return 1; // no before effect, so we can keep the order
|
|
150
|
-
// }
|
|
151
|
-
// }
|
|
152
|
-
// else if (b.beforeEffect) {
|
|
153
|
-
// const beforeB = effectsOrder!.findIndex(e => b.beforeEffect!.constructor.name.endsWith(e.name));
|
|
154
|
-
// if (beforeB >= 0) {
|
|
155
|
-
// return 1; // before effect should be rendered first
|
|
156
|
-
// }
|
|
157
|
-
// else if (a.beforeEffect) {
|
|
158
|
-
// return -1; // no before effect, so we can keep the order
|
|
159
|
-
// }
|
|
160
|
-
|
|
161
|
-
// }
|
|
162
|
-
// return 0; // no before effect, so we can keep the order
|
|
163
|
-
// });
|
|
164
|
-
|
|
165
150
|
|
|
166
151
|
if (debug === "verbose") console.debug("After ordering effects", [...effects]);
|
|
167
152
|
}
|