@needle-tools/engine 4.6.1-next.26cf56c → 4.6.1-next.2b1af6a
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/{needle-engine.bundle-gJVfKnbJ.js → needle-engine.bundle-BBxAzv6J.js} +9228 -9100
- package/dist/{needle-engine.bundle-B23LP9xh.min.js → needle-engine.bundle-BwVJc8bG.min.js} +213 -201
- package/dist/{needle-engine.bundle-B0Kku0-E.umd.cjs → needle-engine.bundle-cvRBTPLl.umd.cjs} +190 -178
- package/dist/needle-engine.js +351 -350
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-BPhSPjAn.umd.cjs → postprocessing-CjW23fio.umd.cjs} +17 -17
- package/dist/{postprocessing-XKNCD6By.js → postprocessing-DYLNOL3W.js} +1 -0
- package/dist/{postprocessing-DoBd3XIR.min.js → postprocessing-xYQWCHFu.min.js} +26 -26
- package/lib/engine/engine_context.d.ts +4 -1
- package/lib/engine/engine_context.js +9 -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/Antialiasing.js +2 -1
- package/lib/engine-components/postprocessing/Effects/Antialiasing.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +2 -2
- package/lib/engine-components/postprocessing/Effects/BloomEffect.js +5 -2
- 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 +22 -57
- 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 +18 -0
- package/lib/engine-components/postprocessing/PostProcessingEffect.js +20 -2
- package/lib/engine-components/postprocessing/PostProcessingEffect.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +10 -4
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +180 -126
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/postprocessing/Volume.d.ts +0 -1
- package/lib/engine-components/postprocessing/Volume.js +49 -42
- package/lib/engine-components/postprocessing/Volume.js.map +1 -1
- package/lib/engine-components/postprocessing/index.d.ts +1 -0
- package/lib/engine-components/postprocessing/index.js +1 -0
- package/lib/engine-components/postprocessing/index.js.map +1 -1
- package/lib/engine-components/postprocessing/utils.d.ts +41 -0
- package/lib/engine-components/postprocessing/utils.js +82 -0
- package/lib/engine-components/postprocessing/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/engine_context.ts +13 -4
- 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/Antialiasing.ts +2 -1
- package/src/engine-components/postprocessing/Effects/BloomEffect.ts +6 -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 +24 -66
- package/src/engine-components/postprocessing/Effects/Tonemapping.utils.ts +60 -0
- package/src/engine-components/postprocessing/PostProcessingEffect.ts +21 -2
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +201 -130
- package/src/engine-components/postprocessing/Volume.ts +50 -47
- package/src/engine-components/postprocessing/index.ts +2 -1
- package/src/engine-components/postprocessing/utils.ts +100 -2
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Camera as Camera3, HalfFloatType, NoToneMapping } from "three";
|
|
1
|
+
import { Effect, EffectComposer, Pass, ToneMappingEffect as _TonemappingEffect } from "postprocessing";
|
|
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 { getParam } from "../../engine/engine_utils.js";
|
|
10
10
|
import { Camera } from "../Camera.js";
|
|
11
|
+
import { threeToneMappingToEffectMode } from "./Effects/Tonemapping.utils.js";
|
|
11
12
|
import { PostProcessingEffect, PostProcessingEffectContext } from "./PostProcessingEffect.js";
|
|
13
|
+
import { orderEffects, PostprocessingEffectData, PostProcessingEffectOrder } from "./utils.js";
|
|
12
14
|
|
|
13
15
|
declare const NEEDLE_USE_POSTPROCESSING: boolean;
|
|
14
16
|
globalThis["NEEDLE_USE_POSTPROCESSING"] = globalThis["NEEDLE_USE_POSTPROCESSING"] !== undefined ? globalThis["NEEDLE_USE_POSTPROCESSING"] : true;
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
const debug = getParam("debugpost");
|
|
18
|
-
const dontMergePasses = getParam("debugpostpasses");
|
|
19
20
|
|
|
20
21
|
const activeKey = Symbol("needle:postprocessing-handler");
|
|
21
22
|
const autoclearSetting = Symbol("needle:previous-autoclear-state");
|
|
23
|
+
const previousToneMapping = Symbol("needle:previous-tone-mapping");
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* PostProcessingHandler is responsible for applying post processing effects to the scene. It is internally used by the {@link Volume} component
|
|
@@ -27,7 +29,7 @@ export class PostProcessingHandler {
|
|
|
27
29
|
|
|
28
30
|
private _composer: EffectComposer | null = null;
|
|
29
31
|
private _lastVolumeComponents?: PostProcessingEffect[];
|
|
30
|
-
private _effects: Array<
|
|
32
|
+
private readonly _effects: Array<PostprocessingEffectData> = [];
|
|
31
33
|
|
|
32
34
|
get isActive() {
|
|
33
35
|
return this._isActive;
|
|
@@ -60,7 +62,7 @@ export class PostProcessingHandler {
|
|
|
60
62
|
return this.onApply(this.context, components);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
unapply() {
|
|
65
|
+
unapply(dispose: boolean = true) {
|
|
64
66
|
if (debug) console.log("Unapplying postprocessing effects");
|
|
65
67
|
this._isActive = false;
|
|
66
68
|
if (this._lastVolumeComponents) {
|
|
@@ -73,21 +75,29 @@ export class PostProcessingHandler {
|
|
|
73
75
|
const active = context[activeKey] as PostProcessingHandler | null;
|
|
74
76
|
if (active === this) {
|
|
75
77
|
delete context[activeKey];
|
|
78
|
+
|
|
79
|
+
// Restore the auto clear setting
|
|
80
|
+
if (typeof context.renderer[autoclearSetting] === "boolean") {
|
|
81
|
+
context.renderer.autoClear = context.renderer[autoclearSetting];
|
|
82
|
+
}
|
|
83
|
+
if (typeof context.renderer[previousToneMapping] === "number") {
|
|
84
|
+
context.renderer.toneMapping = context.renderer[previousToneMapping] as ToneMapping;
|
|
85
|
+
}
|
|
76
86
|
}
|
|
87
|
+
|
|
88
|
+
this._composer?.removeAllPasses();
|
|
89
|
+
if (dispose) this._composer?.dispose();
|
|
90
|
+
|
|
77
91
|
if (context.composer === this._composer) {
|
|
78
|
-
context.composer?.dispose();
|
|
79
92
|
context.composer = null;
|
|
80
93
|
}
|
|
81
|
-
if (typeof context.renderer[autoclearSetting] === "boolean") {
|
|
82
|
-
context.renderer.autoClear = context.renderer[autoclearSetting];
|
|
83
|
-
}
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
dispose() {
|
|
87
|
-
this.unapply();
|
|
97
|
+
this.unapply(true);
|
|
88
98
|
|
|
89
99
|
for (const effect of this._effects) {
|
|
90
|
-
effect.dispose();
|
|
100
|
+
effect.effect.dispose();
|
|
91
101
|
}
|
|
92
102
|
this._effects.length = 0;
|
|
93
103
|
this._composer = null;
|
|
@@ -105,6 +115,7 @@ export class PostProcessingHandler {
|
|
|
105
115
|
// import("./Effects/Sharpening.effect")
|
|
106
116
|
]);
|
|
107
117
|
|
|
118
|
+
|
|
108
119
|
// try {
|
|
109
120
|
// internal_SetSharpeningEffectModule(modules[2]);
|
|
110
121
|
// }
|
|
@@ -141,10 +152,22 @@ export class PostProcessingHandler {
|
|
|
141
152
|
// apply or collect effects
|
|
142
153
|
const res = component.apply(ctx);
|
|
143
154
|
if (!res) continue;
|
|
155
|
+
|
|
156
|
+
|
|
144
157
|
if (Array.isArray(res)) {
|
|
145
|
-
|
|
158
|
+
for (const effect of res) {
|
|
159
|
+
this._effects.push({
|
|
160
|
+
effect,
|
|
161
|
+
priority: component.order
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
this._effects.push({
|
|
167
|
+
effect: res,
|
|
168
|
+
priority: component.order
|
|
169
|
+
});
|
|
146
170
|
}
|
|
147
|
-
else this._effects.push(res);
|
|
148
171
|
}
|
|
149
172
|
}
|
|
150
173
|
else {
|
|
@@ -153,14 +176,6 @@ export class PostProcessingHandler {
|
|
|
153
176
|
}
|
|
154
177
|
}
|
|
155
178
|
|
|
156
|
-
// Ensure that we have a tonemapping effect if the renderer is set to use a tone mapping
|
|
157
|
-
if (this.context.renderer.toneMapping != NoToneMapping) {
|
|
158
|
-
if (!this._effects.find(e => e instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect)) {
|
|
159
|
-
const tonemapping = new MODULES.POSTPROCESSING.MODULE.ToneMappingEffect();
|
|
160
|
-
this._effects.push(tonemapping);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
179
|
this.applyEffects(context);
|
|
165
180
|
}
|
|
166
181
|
|
|
@@ -173,11 +188,29 @@ export class PostProcessingHandler {
|
|
|
173
188
|
get hasSmaaEffect() { return this._hasSmaaEffect; }
|
|
174
189
|
|
|
175
190
|
|
|
191
|
+
|
|
192
|
+
private _customInputBuffer: WebGLRenderTarget<Texture> | null = null;
|
|
193
|
+
private _customInputBufferId = 0;
|
|
194
|
+
private _multisampling: number = 0;
|
|
195
|
+
set multisampling(value: number) {
|
|
196
|
+
this._multisampling = value;
|
|
197
|
+
}
|
|
198
|
+
get multisampling() {
|
|
199
|
+
return this._multisampling;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
176
203
|
/** Build composer passes */
|
|
177
204
|
private applyEffects(context: Context) {
|
|
205
|
+
// Reset state
|
|
206
|
+
this._anyPassHasDepth = false;
|
|
207
|
+
this._anyPassHasNormal = false;
|
|
208
|
+
this._hasSmaaEffect = false;
|
|
209
|
+
|
|
210
|
+
if (this._effects.length <= 0) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
178
213
|
|
|
179
|
-
const effectsOrPasses = this._effects;
|
|
180
|
-
if (effectsOrPasses.length <= 0) return;
|
|
181
214
|
const camera = context.mainCameraComponent as Camera;
|
|
182
215
|
const renderer = context.renderer;
|
|
183
216
|
const scene = context.scene;
|
|
@@ -186,16 +219,24 @@ export class PostProcessingHandler {
|
|
|
186
219
|
// Store the auto clear setting because the postprocessing composer just disables it
|
|
187
220
|
// and when we disable postprocessing we want to restore the original setting
|
|
188
221
|
// https://github.com/pmndrs/postprocessing/blob/271944b74b543a5b743a62803a167b60cc6bb4ee/src/core/EffectComposer.js#L230C12-L230C12
|
|
222
|
+
// First we need to get the previously set autoClear setting, if it exists
|
|
223
|
+
if (typeof renderer[autoclearSetting] === "boolean") {
|
|
224
|
+
renderer.autoClear = renderer[autoclearSetting];
|
|
225
|
+
}
|
|
189
226
|
renderer[autoclearSetting] = renderer.autoClear;
|
|
190
227
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
228
|
+
if (typeof renderer[previousToneMapping] === "number") {
|
|
229
|
+
renderer.toneMapping = renderer[previousToneMapping] as ToneMapping;
|
|
230
|
+
}
|
|
231
|
+
renderer[previousToneMapping] = renderer.toneMapping;
|
|
195
232
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
233
|
+
// Ensure that we have a tonemapping effect if the renderer is set to use a tone mapping
|
|
234
|
+
if (renderer.toneMapping != NoToneMapping) {
|
|
235
|
+
if (!this._effects.find(e => e instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect)) {
|
|
236
|
+
const tonemapping = new MODULES.POSTPROCESSING.MODULE.ToneMappingEffect();
|
|
237
|
+
tonemapping.name = `ToneMapping (${renderer.toneMapping})`;
|
|
238
|
+
tonemapping.mode = threeToneMappingToEffectMode(renderer.toneMapping);
|
|
239
|
+
this._effects.push({ effect: tonemapping, priority: PostProcessingEffectOrder.ToneMapping });
|
|
199
240
|
}
|
|
200
241
|
}
|
|
201
242
|
|
|
@@ -218,6 +259,7 @@ export class PostProcessingHandler {
|
|
|
218
259
|
composer.setRenderer(renderer);
|
|
219
260
|
composer.setMainScene(scene);
|
|
220
261
|
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)
|
|
262
|
+
composer.multisampling = 0; // Disable multisampling by default
|
|
221
263
|
|
|
222
264
|
for (const prev of composer.passes)
|
|
223
265
|
prev.dispose();
|
|
@@ -225,67 +267,139 @@ export class PostProcessingHandler {
|
|
|
225
267
|
|
|
226
268
|
// Render to screen pass
|
|
227
269
|
const screenpass = new MODULES.POSTPROCESSING.MODULE.RenderPass(scene, cam);
|
|
228
|
-
screenpass.name = "
|
|
270
|
+
screenpass.name = "RenderPass";
|
|
229
271
|
screenpass.mainScene = scene;
|
|
230
272
|
composer.addPass(screenpass);
|
|
231
273
|
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
274
|
+
const screenPassRender = screenpass.render;
|
|
275
|
+
this._customInputBuffer?.dispose();
|
|
276
|
+
this._customInputBuffer = null;
|
|
277
|
+
screenpass.render = (renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) => {
|
|
278
|
+
if (!inputBuffer) return;
|
|
236
279
|
|
|
237
|
-
|
|
280
|
+
// screenPassRender.call(screenpass, renderer, inputBuffer, outputBuffer, deltaTime, stencilTest);
|
|
281
|
+
// return;
|
|
238
282
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
ef.renderToScreen = false;
|
|
245
|
-
composer.addPass(ef as Pass);
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
// seems some effects are not correctly typed, but three can deal with them,
|
|
249
|
-
// so we might need to just pass them through
|
|
250
|
-
composer.addPass(ef);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
283
|
+
// 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
|
|
284
|
+
inputBuffer.samples = 0;
|
|
285
|
+
if (outputBuffer) {
|
|
286
|
+
outputBuffer.samples = 0;
|
|
287
|
+
}
|
|
253
288
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
289
|
+
// Make sure the input buffer is a WebGLRenderTarget with the correct settings
|
|
290
|
+
if (!this._customInputBuffer
|
|
291
|
+
|| this._customInputBuffer.width !== inputBuffer.width
|
|
292
|
+
|| this._customInputBuffer.height !== inputBuffer.height
|
|
293
|
+
|| this._customInputBuffer.samples !== this._multisampling
|
|
294
|
+
|| this._customInputBuffer.texture.format !== inputBuffer.texture.format
|
|
295
|
+
|| this._customInputBuffer.texture.type !== HalfFloatType
|
|
296
|
+
) {
|
|
297
|
+
this._customInputBuffer?.dispose();
|
|
298
|
+
|
|
299
|
+
this._customInputBuffer = new WebGLRenderTarget(inputBuffer.width, inputBuffer.height, {
|
|
300
|
+
format: inputBuffer.texture.format,
|
|
301
|
+
type: HalfFloatType,
|
|
302
|
+
depthBuffer: inputBuffer.depthBuffer,
|
|
303
|
+
depthTexture: inputBuffer.depthTexture
|
|
304
|
+
? new DepthTexture(inputBuffer.width, inputBuffer.height)
|
|
305
|
+
: undefined,
|
|
306
|
+
stencilBuffer: inputBuffer.stencilBuffer,
|
|
307
|
+
samples: Math.max(0, this._multisampling),
|
|
308
|
+
minFilter: inputBuffer.texture.minFilter ?? LinearFilter,
|
|
309
|
+
magFilter: inputBuffer.texture.magFilter ?? LinearFilter,
|
|
310
|
+
generateMipmaps: inputBuffer.texture.generateMipmaps,
|
|
311
|
+
});
|
|
312
|
+
this._customInputBufferId++;
|
|
313
|
+
this._customInputBuffer.texture.name = `CustomInputBuffer-${this._customInputBufferId}`;
|
|
314
|
+
if (this._customInputBuffer.depthTexture && inputBuffer.depthTexture) {
|
|
315
|
+
this._customInputBuffer.depthTexture.format = inputBuffer.depthTexture.format;
|
|
316
|
+
this._customInputBuffer.depthTexture.type = inputBuffer.depthTexture.type;
|
|
262
317
|
}
|
|
318
|
+
// https://github.com/pmndrs/postprocessing/blob/ad338df710ef41fee4e5d10ad2c2c299030d46ef/src/core/EffectComposer.js#L366
|
|
319
|
+
if (this._customInputBuffer.samples > 0)
|
|
320
|
+
(this._customInputBuffer as any).ignoreDepthForMultisampleCopy = false;
|
|
321
|
+
|
|
322
|
+
if (debug) console.warn(`[PostProcessing] Input buffer created with size ${this._customInputBuffer.width}x${this._customInputBuffer.height} and samples ${this._customInputBuffer.samples}`);
|
|
263
323
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
324
|
+
// Calling the original render function with the input buffer
|
|
325
|
+
screenPassRender.call(screenpass, renderer, this._customInputBuffer, outputBuffer, deltaTime, stencilTest);
|
|
326
|
+
// Blit the resulting buffer to the input buffer passed in by the composer so it's used for subsequent effects
|
|
327
|
+
Graphics.blit(this._customInputBuffer.texture, inputBuffer, {
|
|
328
|
+
renderer,
|
|
329
|
+
depthTexture: this._customInputBuffer.depthTexture,
|
|
330
|
+
depthWrite: true,
|
|
331
|
+
depthTest: true,
|
|
332
|
+
});
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
let foundTonemappingEffect = false;
|
|
337
|
+
try {
|
|
338
|
+
orderEffects(this._effects);
|
|
339
|
+
|
|
340
|
+
const effectsToMerge: Array<Effect> = [];
|
|
341
|
+
let hasConvolutionEffectInArray = false;
|
|
342
|
+
|
|
343
|
+
for (const entry of this._effects) {
|
|
344
|
+
const ef = entry.effect;
|
|
345
|
+
|
|
346
|
+
if (ef instanceof MODULES.POSTPROCESSING.MODULE.SMAAEffect) {
|
|
347
|
+
this._hasSmaaEffect = true;
|
|
348
|
+
}
|
|
349
|
+
else if (ef instanceof MODULES.POSTPROCESSING.MODULE.NormalPass) {
|
|
350
|
+
this._anyPassHasNormal = true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
// There can be only one tonemapping effect in the scene, so we can skip all others
|
|
355
|
+
if (ef instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect) {
|
|
356
|
+
// If we already have a tonemapping effect, we can skip this one
|
|
357
|
+
if (foundTonemappingEffect) continue;
|
|
358
|
+
foundTonemappingEffect = true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// We can also not merge multiple effects of the same type in one pass
|
|
362
|
+
// So we first need to create a new pass with whatever effects we have so far
|
|
363
|
+
// 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
|
|
364
|
+
// const constructor = ef.constructor as Constructor<Effect | Pass>;
|
|
365
|
+
// if(effectsToMerge.find(e => e.constructor === constructor)) {
|
|
366
|
+
// // this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
367
|
+
// }
|
|
273
368
|
|
|
274
|
-
for (const ef of effectsOrPasses) {
|
|
275
369
|
if (ef instanceof MODULES.POSTPROCESSING.MODULE.Effect) {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
370
|
+
const attributes = ef.getAttributes();
|
|
371
|
+
const convolution = MODULES.POSTPROCESSING.MODULE.EffectAttribute.CONVOLUTION;
|
|
372
|
+
if (attributes & convolution) {
|
|
373
|
+
if (debug) console.log("[PostProcessing] Convolution effect: " + ef.name);
|
|
374
|
+
if (hasConvolutionEffectInArray) {
|
|
375
|
+
if (debug) console.log("[PostProcessing] Merging effects with convolution", effectsToMerge.map(e => e.name).join(", "));
|
|
376
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
377
|
+
}
|
|
378
|
+
hasConvolutionEffectInArray = true;
|
|
379
|
+
}
|
|
380
|
+
// Otherwise we can merge it
|
|
381
|
+
effectsToMerge.push(ef as Effect);
|
|
279
382
|
}
|
|
280
383
|
else if (ef instanceof MODULES.POSTPROCESSING.MODULE.Pass) {
|
|
384
|
+
hasConvolutionEffectInArray = false;
|
|
385
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
281
386
|
ef.renderToScreen = false;
|
|
282
387
|
composer.addPass(ef as Pass);
|
|
283
388
|
}
|
|
284
|
-
else
|
|
389
|
+
else {
|
|
285
390
|
// seems some effects are not correctly typed, but three can deal with them,
|
|
286
|
-
// so we just pass them through
|
|
391
|
+
// so we might need to just pass them through
|
|
392
|
+
hasConvolutionEffectInArray = false;
|
|
393
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
287
394
|
composer.addPass(ef);
|
|
395
|
+
}
|
|
288
396
|
}
|
|
397
|
+
|
|
398
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
399
|
+
}
|
|
400
|
+
catch (e) {
|
|
401
|
+
console.error("Error while applying postprocessing effects", e);
|
|
402
|
+
composer.removeAllPasses();
|
|
289
403
|
}
|
|
290
404
|
|
|
291
405
|
// 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)
|
|
@@ -301,12 +415,6 @@ export class PostProcessingHandler {
|
|
|
301
415
|
}
|
|
302
416
|
|
|
303
417
|
this._anyPassHasDepth ||= pass.needsDepthTexture;
|
|
304
|
-
if (pass instanceof MODULES.POSTPROCESSING.MODULE.NormalPass) {
|
|
305
|
-
this._anyPassHasNormal = true;
|
|
306
|
-
}
|
|
307
|
-
if (pass instanceof MODULES.POSTPROCESSING.MODULE.SMAAEffect) {
|
|
308
|
-
this._hasSmaaEffect = true;
|
|
309
|
-
}
|
|
310
418
|
}
|
|
311
419
|
|
|
312
420
|
// DEBUG LAND BELOW
|
|
@@ -314,56 +422,20 @@ export class PostProcessingHandler {
|
|
|
314
422
|
if (debug) this._onCreateEffectsDebug(this._composer!, cam);
|
|
315
423
|
}
|
|
316
424
|
|
|
317
|
-
private orderEffects() {
|
|
318
|
-
if (debug === "verbose") console.debug("Before ordering effects", [...this._effects]);
|
|
319
|
-
|
|
320
|
-
// Order of effects for correct results.
|
|
321
|
-
// Aligned with https://github.com/pmndrs/postprocessing/wiki/Effect-Merging#effect-execution-order
|
|
322
|
-
// We can not put this into global scope because then the module might not yet be initialized
|
|
323
|
-
effectsOrder ??= [
|
|
324
|
-
MODULES.POSTPROCESSING.MODULE.NormalPass,
|
|
325
|
-
MODULES.POSTPROCESSING.MODULE.DepthDownsamplingPass,
|
|
326
|
-
MODULES.POSTPROCESSING.MODULE.SMAAEffect,
|
|
327
|
-
MODULES.POSTPROCESSING.MODULE.SSAOEffect,
|
|
328
|
-
MODULES.POSTPROCESSING_AO.MODULE.N8AOPostPass,
|
|
329
|
-
MODULES.POSTPROCESSING.MODULE.TiltShiftEffect,
|
|
330
|
-
MODULES.POSTPROCESSING.MODULE.DepthOfFieldEffect,
|
|
331
|
-
MODULES.POSTPROCESSING.MODULE.ChromaticAberrationEffect,
|
|
332
|
-
MODULES.POSTPROCESSING.MODULE.BloomEffect,
|
|
333
|
-
MODULES.POSTPROCESSING.MODULE.SelectiveBloomEffect,
|
|
334
|
-
MODULES.POSTPROCESSING.MODULE.VignetteEffect,
|
|
335
|
-
MODULES.POSTPROCESSING.MODULE.PixelationEffect,
|
|
336
|
-
MODULES.POSTPROCESSING.MODULE.ToneMappingEffect,
|
|
337
|
-
MODULES.POSTPROCESSING.MODULE.HueSaturationEffect,
|
|
338
|
-
MODULES.POSTPROCESSING.MODULE.BrightnessContrastEffect,
|
|
339
|
-
// __SHARPENING_MODULE._SharpeningEffect,
|
|
340
|
-
];
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
// TODO: enforce correct order of effects (e.g. DOF before Bloom)
|
|
344
|
-
const effects = this._effects;
|
|
345
|
-
effects.sort((a, b) => {
|
|
346
|
-
// we use find index here because sometimes constructor names are prefixed with `_`
|
|
347
|
-
// 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)
|
|
348
|
-
const aidx = effectsOrder!.findIndex(e => a.constructor.name.endsWith(e.name));
|
|
349
|
-
const bidx = effectsOrder!.findIndex(e => b.constructor.name.endsWith(e.name));
|
|
350
|
-
// Unknown effects should be rendered first
|
|
351
|
-
if (aidx < 0) {
|
|
352
|
-
if (debug) console.warn("Unknown effect found: ", a.constructor.name);
|
|
353
|
-
return -1;
|
|
354
|
-
}
|
|
355
|
-
else if (bidx < 0) {
|
|
356
|
-
if (debug) console.warn("Unknown effect found: ", b.constructor.name);
|
|
357
|
-
return 1;
|
|
358
|
-
}
|
|
359
|
-
// if (aidx < 0) return 1;
|
|
360
|
-
// if (bidx < 0) return -1;
|
|
361
|
-
return aidx - bidx;
|
|
362
|
-
});
|
|
363
425
|
|
|
364
|
-
if (debug === "verbose") console.debug("After ordering effects", [...this._effects]);
|
|
365
|
-
}
|
|
366
426
|
|
|
427
|
+
/** Should be called before `composer.addPass()` to create an effect pass with all previously collected effects that can be merged up to that point */
|
|
428
|
+
private createPassForMergeableEffects(effects: Array<Effect>, composer: EffectComposer, camera: Camera3, scene: Scene) {
|
|
429
|
+
if (effects.length > 0) {
|
|
430
|
+
const pass = new MODULES.POSTPROCESSING.MODULE.EffectPass(camera, ...effects);
|
|
431
|
+
pass.name = effects.map(e => e.name).join(", ");
|
|
432
|
+
pass.mainScene = scene;
|
|
433
|
+
pass.enabled = true;
|
|
434
|
+
pass.renderToScreen = false;
|
|
435
|
+
composer.addPass(pass);
|
|
436
|
+
effects.length = 0; // Clear effects after adding them to the pass
|
|
437
|
+
}
|
|
438
|
+
}
|
|
367
439
|
|
|
368
440
|
|
|
369
441
|
|
|
@@ -439,4 +511,3 @@ export class PostProcessingHandler {
|
|
|
439
511
|
|
|
440
512
|
}
|
|
441
513
|
|
|
442
|
-
let effectsOrder: Array<Constructor<Effect | Pass>> | null = null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Effect } from "postprocessing";
|
|
1
|
+
import type { Effect, EffectComposer } from "postprocessing";
|
|
2
2
|
|
|
3
3
|
import { isDevEnvironment, showBalloonMessage } from "../../engine/debug/index.js";
|
|
4
4
|
import { Context } from "../../engine/engine_context.js";
|
|
@@ -187,48 +187,56 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
187
187
|
|
|
188
188
|
this.context.composer.setMainScene(this.context.scene);
|
|
189
189
|
|
|
190
|
-
|
|
190
|
+
if (this.multisampling === "auto") {
|
|
191
191
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
192
|
+
// If the postprocessing handler is using depth+normals (e.g. with SMAA) we ALWAYS disable multisampling to avoid ugly edges
|
|
193
|
+
if (this._postprocessing && (this._postprocessing.hasSmaaEffect)) {
|
|
194
|
+
if (this._postprocessing.multisampling !== 0) {
|
|
195
|
+
this._postprocessing.multisampling = 0;
|
|
196
|
+
if (debug || isDevEnvironment()) {
|
|
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.`);
|
|
198
|
+
}
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const timeSinceLastChange = this.context.time.realtimeSinceStartup - this._multisampleAutoChangeTime;
|
|
204
|
-
|
|
205
|
-
if (this.context.time.realtimeSinceStartup - this._componentEnabledTime > 2
|
|
206
|
-
&& timeSinceLastChange > .5
|
|
207
|
-
) {
|
|
208
|
-
const prev = composer.multisampling;
|
|
201
|
+
else {
|
|
202
|
+
const timeSinceLastChange = this.context.time.realtimeSinceStartup - this._multisampleAutoChangeTime;
|
|
209
203
|
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
this._multisampleAutoDecreaseTime = this.context.time.realtimeSinceStartup;
|
|
213
|
-
composer.multisampling *= .5;
|
|
214
|
-
composer.multisampling = Math.floor(composer.multisampling);
|
|
215
|
-
if (debug) console.debug(`[PostProcessing] Reduced multisampling from ${prev} to ${composer.multisampling}`);
|
|
216
|
-
}
|
|
217
|
-
// if performance is good for a while try increasing multisampling again
|
|
218
|
-
else if (timeSinceLastChange > 1
|
|
219
|
-
&& this.context.time.smoothedFps >= 59
|
|
220
|
-
&& composer.multisampling < this.context.renderer.capabilities.maxSamples
|
|
221
|
-
&& this.context.time.realtimeSinceStartup - this._multisampleAutoDecreaseTime > 10
|
|
204
|
+
if (this.context.time.realtimeSinceStartup - this._componentEnabledTime > 2
|
|
205
|
+
&& timeSinceLastChange > .5
|
|
222
206
|
) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
207
|
+
const prev = this._postprocessing.multisampling;
|
|
208
|
+
|
|
209
|
+
if (this._postprocessing.multisampling > 0 && this.context.time.smoothedFps <= 50) {
|
|
210
|
+
this._multisampleAutoChangeTime = this.context.time.realtimeSinceStartup;
|
|
211
|
+
this._multisampleAutoDecreaseTime = this.context.time.realtimeSinceStartup;
|
|
212
|
+
let newMultiSample = this._postprocessing.multisampling * .5;
|
|
213
|
+
newMultiSample = Math.floor(newMultiSample);
|
|
214
|
+
if (newMultiSample != this._postprocessing.multisampling) {
|
|
215
|
+
this._postprocessing.multisampling = newMultiSample;
|
|
216
|
+
}
|
|
217
|
+
if (debug) console.debug(`[PostProcessing] Reduced multisampling from ${prev} to ${this._postprocessing.multisampling}`);
|
|
218
|
+
}
|
|
219
|
+
// if performance is good for a while try increasing multisampling again
|
|
220
|
+
else if (timeSinceLastChange > 1
|
|
221
|
+
&& this.context.time.smoothedFps >= 59
|
|
222
|
+
&& this._postprocessing.multisampling < this.context.renderer.capabilities.maxSamples
|
|
223
|
+
&& this.context.time.realtimeSinceStartup - this._multisampleAutoDecreaseTime > 10
|
|
224
|
+
) {
|
|
225
|
+
this._multisampleAutoChangeTime = this.context.time.realtimeSinceStartup;
|
|
226
|
+
let newMultiSample = this._postprocessing.multisampling <= 0 ? 1 : this._postprocessing.multisampling * 2;
|
|
227
|
+
newMultiSample = Math.floor(newMultiSample);
|
|
228
|
+
if (newMultiSample !== this._postprocessing.multisampling) {
|
|
229
|
+
this._postprocessing.multisampling = newMultiSample;
|
|
230
|
+
}
|
|
231
|
+
if (debug) console.debug(`[PostProcessing] Increased multisampling from ${prev} to ${this._postprocessing.multisampling}`);
|
|
232
|
+
}
|
|
227
233
|
}
|
|
228
234
|
}
|
|
229
235
|
}
|
|
230
236
|
else {
|
|
231
|
-
|
|
237
|
+
const newMultiSample = Math.max(0, Math.min(this.multisampling, this.context.renderer.capabilities.maxSamples));
|
|
238
|
+
if (newMultiSample !== this._postprocessing.multisampling)
|
|
239
|
+
this._postprocessing.multisampling = newMultiSample;
|
|
232
240
|
}
|
|
233
241
|
|
|
234
242
|
// only set the main camera if any pass has a different camera
|
|
@@ -257,7 +265,7 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
257
265
|
private _isDirty: boolean = false;
|
|
258
266
|
|
|
259
267
|
private apply() {
|
|
260
|
-
if (debug) console.log(`Apply PostProcessing "${this.name}"`);
|
|
268
|
+
if (debug) console.log(`Apply PostProcessing "${this.name || "unnamed"}"`);
|
|
261
269
|
|
|
262
270
|
if (isDevEnvironment()) {
|
|
263
271
|
if (this._lastApplyTime !== undefined && Date.now() - this._lastApplyTime < 100) {
|
|
@@ -269,7 +277,6 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
269
277
|
}
|
|
270
278
|
|
|
271
279
|
this._isDirty = false;
|
|
272
|
-
this.unapply();
|
|
273
280
|
|
|
274
281
|
this._activeEffects.length = 0;
|
|
275
282
|
// get from profile
|
|
@@ -296,32 +303,28 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
296
303
|
|
|
297
304
|
this._applyPostQueue();
|
|
298
305
|
|
|
299
|
-
|
|
300
|
-
if (composer) {
|
|
306
|
+
if (this._postprocessing) {
|
|
301
307
|
if (this.multisampling === "auto") {
|
|
302
|
-
|
|
308
|
+
this._postprocessing.multisampling = DeviceUtilities.isMobileDevice()
|
|
303
309
|
? 2
|
|
304
310
|
: 4;
|
|
305
311
|
}
|
|
306
312
|
else {
|
|
307
|
-
|
|
313
|
+
this._postprocessing.multisampling = Math.max(0, Math.min(this.multisampling, this.context.renderer.capabilities.maxSamples));
|
|
308
314
|
}
|
|
309
|
-
if (debug) console.debug(`[PostProcessing] Set multisampling to ${
|
|
315
|
+
if (debug) console.debug(`[PostProcessing] Set multisampling to ${this._postprocessing.multisampling} (Is Mobile: ${DeviceUtilities.isMobileDevice()})`);
|
|
310
316
|
}
|
|
311
317
|
else if (debug) {
|
|
312
318
|
console.warn(`[PostProcessing] No composer found`);
|
|
313
319
|
}
|
|
314
320
|
})
|
|
315
|
-
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
this._postprocessing?.unapply(false);
|
|
316
324
|
}
|
|
317
325
|
|
|
318
326
|
}
|
|
319
327
|
|
|
320
|
-
private unapply() {
|
|
321
|
-
this._postprocessing?.unapply();
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
325
328
|
private _applyPostQueue() {
|
|
326
329
|
if (this._modificationQueue) {
|
|
327
330
|
for (const entry of this._modificationQueue.values()) this.onEditorModification(entry);
|
|
@@ -1,5 +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 * from "./VolumeProfile.js";
|