@needle-tools/engine 4.6.1 → 4.6.2
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/generateMeshBVH.worker-BaNp_Xtp.js +25 -0
- package/dist/{gltf-progressive-Bm9eEfgu.min.js → gltf-progressive-Bl4okF1b.min.js} +1 -1
- package/dist/{gltf-progressive-GjIqwSG3.js → gltf-progressive-DSpdn0QT.js} +2 -2
- package/dist/{gltf-progressive-Dn6o99rH.umd.cjs → gltf-progressive-P8b8a0qY.umd.cjs} +1 -1
- package/dist/needle-engine.bundle-D0XWaCQs.min.js +1575 -0
- package/dist/{needle-engine.bundle-BVg46UWZ.js → needle-engine.bundle-DGcStTA7.js} +9715 -9555
- package/dist/needle-engine.bundle-DmYMLdWP.umd.cjs +1575 -0
- package/dist/needle-engine.js +352 -351
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-CRQa6Qxn.umd.cjs → postprocessing-CjW23fio.umd.cjs} +18 -18
- package/dist/{postprocessing-D6W1EyZ-.js → postprocessing-DYLNOL3W.js} +4 -3
- package/dist/{postprocessing-DF8AlRgW.min.js → postprocessing-xYQWCHFu.min.js} +26 -26
- package/dist/{three-DMrv-4ar.umd.cjs → three-B_hneGZr.umd.cjs} +4 -4
- package/dist/{three-Bz6X1mrw.js → three-DrqIzZTH.js} +4198 -4198
- package/dist/{three-Boa-jOq-.min.js → three-DuDKwKB8.min.js} +33 -33
- package/dist/{three-examples-GggCDHv0.js → three-examples-B50TT3Iu.js} +5 -5
- package/dist/{three-examples-DuVhxqft.min.js → three-examples-DaDLBuy6.min.js} +14 -14
- package/dist/{three-examples-C7ryg8vN.umd.cjs → three-examples-X3OadjXB.umd.cjs} +3 -3
- package/dist/{three-mesh-ui-CY6Izc7C.min.js → three-mesh-ui-B3p3gyUz.min.js} +1 -1
- package/dist/{three-mesh-ui-CwlN0FUC.umd.cjs → three-mesh-ui-CQiIQIlA.umd.cjs} +1 -1
- package/dist/{three-mesh-ui-CLNOfsWn.js → three-mesh-ui-CxuWt7m-.js} +1 -1
- package/dist/{vendor-zxXa3Dmr.min.js → vendor-BlSxe9JJ.min.js} +3 -3
- package/dist/{vendor-BSD1RQIh.js → vendor-BmYIgaS1.js} +3 -3
- package/dist/{vendor-DHr4aqIZ.umd.cjs → vendor-Cavtu3CP.umd.cjs} +3 -3
- 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/engine_utils_screenshot.d.ts +1 -1
- package/lib/engine/engine_utils_screenshot.js +11 -2
- package/lib/engine/engine_utils_screenshot.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 +3 -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 +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 +18 -0
- package/lib/engine-components/postprocessing/PostProcessingEffect.js +22 -3
- package/lib/engine-components/postprocessing/PostProcessingEffect.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +20 -4
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +209 -112
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/postprocessing/Volume.d.ts +2 -1
- package/lib/engine-components/postprocessing/Volume.js +51 -33
- 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 +43 -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/engine_utils_screenshot.ts +13 -3
- 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 +3 -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 +26 -80
- package/src/engine-components/postprocessing/Effects/Tonemapping.utils.ts +60 -0
- package/src/engine-components/postprocessing/PostProcessingEffect.ts +23 -3
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +239 -119
- package/src/engine-components/postprocessing/Volume.ts +54 -38
- package/src/engine-components/postprocessing/index.ts +2 -1
- package/src/engine-components/postprocessing/utils.ts +102 -2
- package/dist/generateMeshBVH.worker-Cdfpaq5W.js +0 -25
- package/dist/needle-engine.bundle-AOXFIsYk.umd.cjs +0 -1563
- package/dist/needle-engine.bundle-Dt52m2jf.min.js +0 -1563
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
import type { Effect, EffectComposer, Pass, ToneMappingEffect as _TonemappingEffect } from "postprocessing";
|
|
2
|
-
import { HalfFloatType, NoToneMapping,
|
|
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,15 @@ 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> = [];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Returns true if a specific effect is currently active in the post processing stack.
|
|
36
|
+
*/
|
|
37
|
+
getEffectIsActive(effect: Effect): boolean {
|
|
38
|
+
if (!effect) return false;
|
|
39
|
+
return this._isActive && this._effects.some(e => e.effect === effect);
|
|
40
|
+
}
|
|
31
41
|
|
|
32
42
|
get isActive() {
|
|
33
43
|
return this._isActive;
|
|
@@ -60,7 +70,7 @@ export class PostProcessingHandler {
|
|
|
60
70
|
return this.onApply(this.context, components);
|
|
61
71
|
}
|
|
62
72
|
|
|
63
|
-
unapply() {
|
|
73
|
+
unapply(dispose: boolean = true) {
|
|
64
74
|
if (debug) console.log("Unapplying postprocessing effects");
|
|
65
75
|
this._isActive = false;
|
|
66
76
|
if (this._lastVolumeComponents) {
|
|
@@ -73,21 +83,29 @@ export class PostProcessingHandler {
|
|
|
73
83
|
const active = context[activeKey] as PostProcessingHandler | null;
|
|
74
84
|
if (active === this) {
|
|
75
85
|
delete context[activeKey];
|
|
86
|
+
|
|
87
|
+
// Restore the auto clear setting
|
|
88
|
+
if (typeof context.renderer[autoclearSetting] === "boolean") {
|
|
89
|
+
context.renderer.autoClear = context.renderer[autoclearSetting];
|
|
90
|
+
}
|
|
91
|
+
if (typeof context.renderer[previousToneMapping] === "number") {
|
|
92
|
+
context.renderer.toneMapping = context.renderer[previousToneMapping] as ToneMapping;
|
|
93
|
+
}
|
|
76
94
|
}
|
|
95
|
+
|
|
96
|
+
this._composer?.removeAllPasses();
|
|
97
|
+
if (dispose) this._composer?.dispose();
|
|
98
|
+
|
|
77
99
|
if (context.composer === this._composer) {
|
|
78
|
-
context.composer?.dispose();
|
|
79
100
|
context.composer = null;
|
|
80
101
|
}
|
|
81
|
-
if (typeof context.renderer[autoclearSetting] === "boolean") {
|
|
82
|
-
context.renderer.autoClear = context.renderer[autoclearSetting];
|
|
83
|
-
}
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
dispose() {
|
|
87
|
-
this.unapply();
|
|
105
|
+
this.unapply(true);
|
|
88
106
|
|
|
89
107
|
for (const effect of this._effects) {
|
|
90
|
-
effect.dispose();
|
|
108
|
+
effect.effect.dispose();
|
|
91
109
|
}
|
|
92
110
|
this._effects.length = 0;
|
|
93
111
|
this._composer = null;
|
|
@@ -105,6 +123,7 @@ export class PostProcessingHandler {
|
|
|
105
123
|
// import("./Effects/Sharpening.effect")
|
|
106
124
|
]);
|
|
107
125
|
|
|
126
|
+
|
|
108
127
|
// try {
|
|
109
128
|
// internal_SetSharpeningEffectModule(modules[2]);
|
|
110
129
|
// }
|
|
@@ -141,10 +160,22 @@ export class PostProcessingHandler {
|
|
|
141
160
|
// apply or collect effects
|
|
142
161
|
const res = component.apply(ctx);
|
|
143
162
|
if (!res) continue;
|
|
163
|
+
|
|
164
|
+
|
|
144
165
|
if (Array.isArray(res)) {
|
|
145
|
-
|
|
166
|
+
for (const effect of res) {
|
|
167
|
+
this._effects.push({
|
|
168
|
+
effect,
|
|
169
|
+
priority: component.order
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
this._effects.push({
|
|
175
|
+
effect: res,
|
|
176
|
+
priority: component.order
|
|
177
|
+
});
|
|
146
178
|
}
|
|
147
|
-
else this._effects.push(res);
|
|
148
179
|
}
|
|
149
180
|
}
|
|
150
181
|
else {
|
|
@@ -153,23 +184,41 @@ export class PostProcessingHandler {
|
|
|
153
184
|
}
|
|
154
185
|
}
|
|
155
186
|
|
|
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
187
|
this.applyEffects(context);
|
|
165
188
|
}
|
|
166
189
|
|
|
190
|
+
private _anyPassHasDepth = false;
|
|
191
|
+
private _anyPassHasNormal = false;
|
|
192
|
+
private _hasSmaaEffect = false;
|
|
193
|
+
|
|
194
|
+
get anyPassHasDepth() { return this._anyPassHasDepth; }
|
|
195
|
+
get anyPassHasNormal() { return this._anyPassHasNormal; }
|
|
196
|
+
get hasSmaaEffect() { return this._hasSmaaEffect; }
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
private _customInputBuffer: WebGLRenderTarget<Texture> | null = null;
|
|
201
|
+
private _customInputBufferId = 0;
|
|
202
|
+
private _multisampling: number = 0;
|
|
203
|
+
set multisampling(value: number) {
|
|
204
|
+
this._multisampling = value;
|
|
205
|
+
}
|
|
206
|
+
get multisampling() {
|
|
207
|
+
return this._multisampling;
|
|
208
|
+
}
|
|
209
|
+
|
|
167
210
|
|
|
168
211
|
/** Build composer passes */
|
|
169
212
|
private applyEffects(context: Context) {
|
|
213
|
+
// Reset state
|
|
214
|
+
this._anyPassHasDepth = false;
|
|
215
|
+
this._anyPassHasNormal = false;
|
|
216
|
+
this._hasSmaaEffect = false;
|
|
217
|
+
|
|
218
|
+
if (this._effects.length <= 0) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
170
221
|
|
|
171
|
-
const effectsOrPasses = this._effects;
|
|
172
|
-
if (effectsOrPasses.length <= 0) return;
|
|
173
222
|
const camera = context.mainCameraComponent as Camera;
|
|
174
223
|
const renderer = context.renderer;
|
|
175
224
|
const scene = context.scene;
|
|
@@ -178,8 +227,27 @@ export class PostProcessingHandler {
|
|
|
178
227
|
// Store the auto clear setting because the postprocessing composer just disables it
|
|
179
228
|
// and when we disable postprocessing we want to restore the original setting
|
|
180
229
|
// https://github.com/pmndrs/postprocessing/blob/271944b74b543a5b743a62803a167b60cc6bb4ee/src/core/EffectComposer.js#L230C12-L230C12
|
|
230
|
+
// First we need to get the previously set autoClear setting, if it exists
|
|
231
|
+
if (typeof renderer[autoclearSetting] === "boolean") {
|
|
232
|
+
renderer.autoClear = renderer[autoclearSetting];
|
|
233
|
+
}
|
|
181
234
|
renderer[autoclearSetting] = renderer.autoClear;
|
|
182
235
|
|
|
236
|
+
if (typeof renderer[previousToneMapping] === "number") {
|
|
237
|
+
renderer.toneMapping = renderer[previousToneMapping] as ToneMapping;
|
|
238
|
+
}
|
|
239
|
+
renderer[previousToneMapping] = renderer.toneMapping;
|
|
240
|
+
|
|
241
|
+
// Ensure that we have a tonemapping effect if the renderer is set to use a tone mapping
|
|
242
|
+
if (renderer.toneMapping != NoToneMapping) {
|
|
243
|
+
if (!this._effects.find(e => e instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect)) {
|
|
244
|
+
const tonemapping = new MODULES.POSTPROCESSING.MODULE.ToneMappingEffect();
|
|
245
|
+
tonemapping.name = `ToneMapping (${renderer.toneMapping})`;
|
|
246
|
+
tonemapping.mode = threeToneMappingToEffectMode(renderer.toneMapping);
|
|
247
|
+
this._effects.push({ effect: tonemapping, priority: PostProcessingEffectOrder.ToneMapping });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
183
251
|
// create composer and set active on context
|
|
184
252
|
if (!this._composer) {
|
|
185
253
|
// const hdrRenderTarget = new WebGLRenderTarget(window.innerWidth, window.innerHeight, { type: HalfFloatType });
|
|
@@ -192,12 +260,14 @@ export class PostProcessingHandler {
|
|
|
192
260
|
if (context.composer && context.composer !== this._composer) {
|
|
193
261
|
console.warn("There's already an active EffectComposer in your scene: replacing it with a new one. This might cause unexpected behaviour. Make sure to only use one PostprocessingManager/Volume in your scene.");
|
|
194
262
|
}
|
|
263
|
+
|
|
195
264
|
context.composer = this._composer;
|
|
196
265
|
const composer = context.composer;
|
|
197
266
|
composer.setMainCamera(cam);
|
|
198
267
|
composer.setRenderer(renderer);
|
|
199
268
|
composer.setMainScene(scene);
|
|
200
269
|
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)
|
|
270
|
+
composer.multisampling = 0; // Disable multisampling by default
|
|
201
271
|
|
|
202
272
|
for (const prev of composer.passes)
|
|
203
273
|
prev.dispose();
|
|
@@ -205,81 +275,168 @@ export class PostProcessingHandler {
|
|
|
205
275
|
|
|
206
276
|
// Render to screen pass
|
|
207
277
|
const screenpass = new MODULES.POSTPROCESSING.MODULE.RenderPass(scene, cam);
|
|
208
|
-
screenpass.name = "
|
|
278
|
+
screenpass.name = "RenderPass";
|
|
209
279
|
screenpass.mainScene = scene;
|
|
210
280
|
composer.addPass(screenpass);
|
|
211
281
|
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
282
|
+
const screenPassRender = screenpass.render;
|
|
283
|
+
this._customInputBuffer?.dispose();
|
|
284
|
+
this._customInputBuffer = null;
|
|
285
|
+
screenpass.render = (renderer, inputBuffer, outputBuffer, deltaTime, stencilTest) => {
|
|
286
|
+
if (!inputBuffer) return;
|
|
216
287
|
|
|
217
|
-
|
|
288
|
+
// screenPassRender.call(screenpass, renderer, inputBuffer, outputBuffer, deltaTime, stencilTest);
|
|
289
|
+
// return;
|
|
218
290
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
ef.renderToScreen = false;
|
|
225
|
-
composer.addPass(ef as Pass);
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
// seems some effects are not correctly typed, but three can deal with them,
|
|
229
|
-
// so we might need to just pass them through
|
|
230
|
-
composer.addPass(ef);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
291
|
+
// 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
|
|
292
|
+
inputBuffer.samples = 0;
|
|
293
|
+
if (outputBuffer) {
|
|
294
|
+
outputBuffer.samples = 0;
|
|
295
|
+
}
|
|
233
296
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
297
|
+
// Make sure the input buffer is a WebGLRenderTarget with the correct settings
|
|
298
|
+
if (!this._customInputBuffer
|
|
299
|
+
|| this._customInputBuffer.width !== inputBuffer.width
|
|
300
|
+
|| this._customInputBuffer.height !== inputBuffer.height
|
|
301
|
+
|| this._customInputBuffer.samples !== this._multisampling
|
|
302
|
+
|| this._customInputBuffer.texture.format !== inputBuffer.texture.format
|
|
303
|
+
|| this._customInputBuffer.texture.type !== HalfFloatType
|
|
304
|
+
) {
|
|
305
|
+
this._customInputBuffer?.dispose();
|
|
306
|
+
|
|
307
|
+
this._customInputBuffer = new WebGLRenderTarget(inputBuffer.width, inputBuffer.height, {
|
|
308
|
+
format: inputBuffer.texture.format,
|
|
309
|
+
type: HalfFloatType,
|
|
310
|
+
depthBuffer: inputBuffer.depthBuffer,
|
|
311
|
+
depthTexture: inputBuffer.depthTexture
|
|
312
|
+
? new DepthTexture(inputBuffer.width, inputBuffer.height)
|
|
313
|
+
: undefined,
|
|
314
|
+
stencilBuffer: inputBuffer.stencilBuffer,
|
|
315
|
+
samples: Math.max(0, this._multisampling),
|
|
316
|
+
minFilter: inputBuffer.texture.minFilter ?? LinearFilter,
|
|
317
|
+
magFilter: inputBuffer.texture.magFilter ?? LinearFilter,
|
|
318
|
+
generateMipmaps: inputBuffer.texture.generateMipmaps,
|
|
319
|
+
});
|
|
320
|
+
this._customInputBufferId++;
|
|
321
|
+
this._customInputBuffer.texture.name = `CustomInputBuffer-${this._customInputBufferId}`;
|
|
322
|
+
if (this._customInputBuffer.depthTexture && inputBuffer.depthTexture) {
|
|
323
|
+
this._customInputBuffer.depthTexture.format = inputBuffer.depthTexture.format;
|
|
324
|
+
this._customInputBuffer.depthTexture.type = inputBuffer.depthTexture.type;
|
|
242
325
|
}
|
|
326
|
+
// https://github.com/pmndrs/postprocessing/blob/ad338df710ef41fee4e5d10ad2c2c299030d46ef/src/core/EffectComposer.js#L366
|
|
327
|
+
if (this._customInputBuffer.samples > 0)
|
|
328
|
+
(this._customInputBuffer as any).ignoreDepthForMultisampleCopy = false;
|
|
329
|
+
|
|
330
|
+
if (debug) console.warn(`[PostProcessing] Input buffer created with size ${this._customInputBuffer.width}x${this._customInputBuffer.height} and samples ${this._customInputBuffer.samples}`);
|
|
243
331
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
332
|
+
// Calling the original render function with the input buffer
|
|
333
|
+
screenPassRender.call(screenpass, renderer, this._customInputBuffer, outputBuffer, deltaTime, stencilTest);
|
|
334
|
+
// Blit the resulting buffer to the input buffer passed in by the composer so it's used for subsequent effects
|
|
335
|
+
Graphics.blit(this._customInputBuffer.texture, inputBuffer, {
|
|
336
|
+
renderer,
|
|
337
|
+
depthTexture: this._customInputBuffer.depthTexture,
|
|
338
|
+
depthWrite: true,
|
|
339
|
+
depthTest: true,
|
|
340
|
+
});
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
orderEffects(this._effects);
|
|
346
|
+
|
|
347
|
+
let foundTonemappingEffect = false;
|
|
348
|
+
let activeTonemappingEffect: _TonemappingEffect | null = null;
|
|
349
|
+
for (let i = this._effects.length - 1; i >= 0; i--) {
|
|
350
|
+
const ef = this._effects[i].effect;
|
|
351
|
+
if (ef instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect) {
|
|
352
|
+
// If we already have a tonemapping effect, we can skip this one
|
|
353
|
+
if (foundTonemappingEffect) {
|
|
354
|
+
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.`);
|
|
355
|
+
this._effects.splice(i, 1);
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
activeTonemappingEffect = ef;
|
|
359
|
+
foundTonemappingEffect = true;
|
|
360
|
+
}
|
|
247
361
|
}
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
// we still want to sort passes, but we do not want to merge them for debugging
|
|
251
|
-
if (automaticEffectsOrdering)
|
|
252
|
-
this.orderEffects();
|
|
253
362
|
|
|
254
|
-
|
|
363
|
+
const effectsToMerge: Array<Effect> = [];
|
|
364
|
+
let hasConvolutionEffectInArray = false;
|
|
365
|
+
|
|
366
|
+
for (const entry of this._effects) {
|
|
367
|
+
const ef = entry.effect;
|
|
368
|
+
|
|
369
|
+
if (ef instanceof MODULES.POSTPROCESSING.MODULE.SMAAEffect) {
|
|
370
|
+
this._hasSmaaEffect = true;
|
|
371
|
+
}
|
|
372
|
+
else if (ef instanceof MODULES.POSTPROCESSING.MODULE.NormalPass) {
|
|
373
|
+
this._anyPassHasNormal = true;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
// There can be only one tonemapping effect in the scene, so we can skip all others
|
|
378
|
+
if (ef instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect && activeTonemappingEffect !== ef) {
|
|
379
|
+
// If we already have a tonemapping effect, we can skip this one
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// We can also not merge multiple effects of the same type in one pass
|
|
384
|
+
// So we first need to create a new pass with whatever effects we have so far
|
|
385
|
+
// 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
|
|
386
|
+
// const constructor = ef.constructor as Constructor<Effect | Pass>;
|
|
387
|
+
// if(effectsToMerge.find(e => e.constructor === constructor)) {
|
|
388
|
+
// // this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
389
|
+
// }
|
|
390
|
+
|
|
255
391
|
if (ef instanceof MODULES.POSTPROCESSING.MODULE.Effect) {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
392
|
+
const attributes = ef.getAttributes();
|
|
393
|
+
const convolution = MODULES.POSTPROCESSING.MODULE.EffectAttribute.CONVOLUTION;
|
|
394
|
+
if (attributes & convolution) {
|
|
395
|
+
if (debug) console.log("[PostProcessing] Convolution effect: " + ef.name);
|
|
396
|
+
if (hasConvolutionEffectInArray) {
|
|
397
|
+
if (debug) console.log("[PostProcessing] Merging effects with convolution", effectsToMerge.map(e => e.name).join(", "));
|
|
398
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
399
|
+
}
|
|
400
|
+
hasConvolutionEffectInArray = true;
|
|
401
|
+
}
|
|
402
|
+
// Otherwise we can merge it
|
|
403
|
+
effectsToMerge.push(ef as Effect);
|
|
259
404
|
}
|
|
260
405
|
else if (ef instanceof MODULES.POSTPROCESSING.MODULE.Pass) {
|
|
406
|
+
hasConvolutionEffectInArray = false;
|
|
407
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
261
408
|
ef.renderToScreen = false;
|
|
262
409
|
composer.addPass(ef as Pass);
|
|
263
410
|
}
|
|
264
|
-
else
|
|
411
|
+
else {
|
|
265
412
|
// seems some effects are not correctly typed, but three can deal with them,
|
|
266
|
-
// so we just pass them through
|
|
413
|
+
// so we might need to just pass them through
|
|
414
|
+
hasConvolutionEffectInArray = false;
|
|
415
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
267
416
|
composer.addPass(ef);
|
|
417
|
+
}
|
|
268
418
|
}
|
|
269
|
-
}
|
|
270
419
|
|
|
420
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
421
|
+
}
|
|
422
|
+
catch (e) {
|
|
423
|
+
console.error("Error while applying postprocessing effects", e);
|
|
424
|
+
composer.removeAllPasses();
|
|
425
|
+
}
|
|
271
426
|
|
|
272
427
|
// 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)
|
|
273
428
|
for (let i = 0; i < composer.passes.length; i++) {
|
|
274
|
-
const pass = composer.passes[i]
|
|
429
|
+
const pass = composer.passes[i];
|
|
275
430
|
const isLast = i === composer.passes.length - 1;
|
|
276
|
-
if (pass?.configuration !== undefined) {
|
|
277
|
-
pass.configuration.gammaCorrection = isLast;
|
|
431
|
+
if ((pass as any)?.configuration !== undefined) {
|
|
432
|
+
(pass as any).configuration.gammaCorrection = isLast;
|
|
278
433
|
}
|
|
279
434
|
else if ("autosetGamma" in pass) {
|
|
280
435
|
// Some effects have a autosetGamma property that we can use to set the gamma correction
|
|
281
436
|
pass.autosetGamma = isLast;
|
|
282
437
|
}
|
|
438
|
+
|
|
439
|
+
this._anyPassHasDepth ||= pass.needsDepthTexture;
|
|
283
440
|
}
|
|
284
441
|
|
|
285
442
|
// DEBUG LAND BELOW
|
|
@@ -287,56 +444,20 @@ export class PostProcessingHandler {
|
|
|
287
444
|
if (debug) this._onCreateEffectsDebug(this._composer!, cam);
|
|
288
445
|
}
|
|
289
446
|
|
|
290
|
-
private orderEffects() {
|
|
291
|
-
if (debug === "verbose") console.debug("Before ordering effects", [...this._effects]);
|
|
292
|
-
|
|
293
|
-
// Order of effects for correct results.
|
|
294
|
-
// Aligned with https://github.com/pmndrs/postprocessing/wiki/Effect-Merging#effect-execution-order
|
|
295
|
-
// We can not put this into global scope because then the module might not yet be initialized
|
|
296
|
-
effectsOrder ??= [
|
|
297
|
-
MODULES.POSTPROCESSING.MODULE.NormalPass,
|
|
298
|
-
MODULES.POSTPROCESSING.MODULE.DepthDownsamplingPass,
|
|
299
|
-
MODULES.POSTPROCESSING.MODULE.SMAAEffect,
|
|
300
|
-
MODULES.POSTPROCESSING.MODULE.SSAOEffect,
|
|
301
|
-
MODULES.POSTPROCESSING_AO.MODULE.N8AOPostPass,
|
|
302
|
-
MODULES.POSTPROCESSING.MODULE.TiltShiftEffect,
|
|
303
|
-
MODULES.POSTPROCESSING.MODULE.DepthOfFieldEffect,
|
|
304
|
-
MODULES.POSTPROCESSING.MODULE.ChromaticAberrationEffect,
|
|
305
|
-
MODULES.POSTPROCESSING.MODULE.BloomEffect,
|
|
306
|
-
MODULES.POSTPROCESSING.MODULE.SelectiveBloomEffect,
|
|
307
|
-
MODULES.POSTPROCESSING.MODULE.VignetteEffect,
|
|
308
|
-
MODULES.POSTPROCESSING.MODULE.PixelationEffect,
|
|
309
|
-
MODULES.POSTPROCESSING.MODULE.ToneMappingEffect,
|
|
310
|
-
MODULES.POSTPROCESSING.MODULE.HueSaturationEffect,
|
|
311
|
-
MODULES.POSTPROCESSING.MODULE.BrightnessContrastEffect,
|
|
312
|
-
// __SHARPENING_MODULE._SharpeningEffect,
|
|
313
|
-
];
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
// TODO: enforce correct order of effects (e.g. DOF before Bloom)
|
|
317
|
-
const effects = this._effects;
|
|
318
|
-
effects.sort((a, b) => {
|
|
319
|
-
// we use find index here because sometimes constructor names are prefixed with `_`
|
|
320
|
-
// 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)
|
|
321
|
-
const aidx = effectsOrder!.findIndex(e => a.constructor.name.endsWith(e.name));
|
|
322
|
-
const bidx = effectsOrder!.findIndex(e => b.constructor.name.endsWith(e.name));
|
|
323
|
-
// Unknown effects should be rendered first
|
|
324
|
-
if (aidx < 0) {
|
|
325
|
-
if (debug) console.warn("Unknown effect found: ", a.constructor.name);
|
|
326
|
-
return -1;
|
|
327
|
-
}
|
|
328
|
-
else if (bidx < 0) {
|
|
329
|
-
if (debug) console.warn("Unknown effect found: ", b.constructor.name);
|
|
330
|
-
return 1;
|
|
331
|
-
}
|
|
332
|
-
// if (aidx < 0) return 1;
|
|
333
|
-
// if (bidx < 0) return -1;
|
|
334
|
-
return aidx - bidx;
|
|
335
|
-
});
|
|
336
447
|
|
|
337
|
-
if (debug === "verbose") console.debug("After ordering effects", [...this._effects]);
|
|
338
|
-
}
|
|
339
448
|
|
|
449
|
+
/** Should be called before `composer.addPass()` to create an effect pass with all previously collected effects that can be merged up to that point */
|
|
450
|
+
private createPassForMergeableEffects(effects: Array<Effect>, composer: EffectComposer, camera: Camera3, scene: Scene) {
|
|
451
|
+
if (effects.length > 0) {
|
|
452
|
+
const pass = new MODULES.POSTPROCESSING.MODULE.EffectPass(camera, ...effects);
|
|
453
|
+
pass.name = effects.map(e => e.name).join(", ");
|
|
454
|
+
pass.mainScene = scene;
|
|
455
|
+
pass.enabled = true;
|
|
456
|
+
pass.renderToScreen = false;
|
|
457
|
+
composer.addPass(pass);
|
|
458
|
+
effects.length = 0; // Clear effects after adding them to the pass
|
|
459
|
+
}
|
|
460
|
+
}
|
|
340
461
|
|
|
341
462
|
|
|
342
463
|
|
|
@@ -412,4 +533,3 @@ export class PostProcessingHandler {
|
|
|
412
533
|
|
|
413
534
|
}
|
|
414
535
|
|
|
415
|
-
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";
|
|
@@ -56,6 +56,9 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
56
56
|
return this._activeEffects;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
get dirty() { return this._isDirty; }
|
|
60
|
+
set dirty(value: boolean) { this._isDirty = value; }
|
|
61
|
+
|
|
59
62
|
@serializeable(VolumeProfile)
|
|
60
63
|
sharedProfile?: VolumeProfile;
|
|
61
64
|
|
|
@@ -184,38 +187,56 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
184
187
|
|
|
185
188
|
this.context.composer.setMainScene(this.context.scene);
|
|
186
189
|
|
|
187
|
-
const composer = this.context.composer;
|
|
188
190
|
if (this.multisampling === "auto") {
|
|
189
191
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (composer.multisampling > 0 && this.context.time.smoothedFps <= 50) {
|
|
198
|
-
this._multisampleAutoChangeTime = this.context.time.realtimeSinceStartup;
|
|
199
|
-
this._multisampleAutoDecreaseTime = this.context.time.realtimeSinceStartup;
|
|
200
|
-
composer.multisampling *= .5;
|
|
201
|
-
composer.multisampling = Math.floor(composer.multisampling);
|
|
202
|
-
if (debug) console.debug(`[PostProcessing] Reduced multisampling from ${prev} to ${composer.multisampling}`);
|
|
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
|
+
}
|
|
203
199
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const timeSinceLastChange = this.context.time.realtimeSinceStartup - this._multisampleAutoChangeTime;
|
|
203
|
+
|
|
204
|
+
if (this.context.time.realtimeSinceStartup - this._componentEnabledTime > 2
|
|
205
|
+
&& timeSinceLastChange > .5
|
|
209
206
|
) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
+
}
|
|
214
233
|
}
|
|
215
234
|
}
|
|
216
235
|
}
|
|
217
236
|
else {
|
|
218
|
-
|
|
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;
|
|
219
240
|
}
|
|
220
241
|
|
|
221
242
|
// only set the main camera if any pass has a different camera
|
|
@@ -244,7 +265,7 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
244
265
|
private _isDirty: boolean = false;
|
|
245
266
|
|
|
246
267
|
private apply() {
|
|
247
|
-
if (debug) console.log(`Apply PostProcessing "${this.name}"`);
|
|
268
|
+
if (debug) console.log(`Apply PostProcessing "${this.name || "unnamed"}"`);
|
|
248
269
|
|
|
249
270
|
if (isDevEnvironment()) {
|
|
250
271
|
if (this._lastApplyTime !== undefined && Date.now() - this._lastApplyTime < 100) {
|
|
@@ -256,7 +277,6 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
256
277
|
}
|
|
257
278
|
|
|
258
279
|
this._isDirty = false;
|
|
259
|
-
this.unapply();
|
|
260
280
|
|
|
261
281
|
this._activeEffects.length = 0;
|
|
262
282
|
// get from profile
|
|
@@ -283,32 +303,28 @@ export class Volume extends Behaviour implements IEditorModificationReceiver, IP
|
|
|
283
303
|
|
|
284
304
|
this._applyPostQueue();
|
|
285
305
|
|
|
286
|
-
|
|
287
|
-
if (composer) {
|
|
306
|
+
if (this._postprocessing) {
|
|
288
307
|
if (this.multisampling === "auto") {
|
|
289
|
-
|
|
308
|
+
this._postprocessing.multisampling = DeviceUtilities.isMobileDevice()
|
|
290
309
|
? 2
|
|
291
310
|
: 4;
|
|
292
311
|
}
|
|
293
312
|
else {
|
|
294
|
-
|
|
313
|
+
this._postprocessing.multisampling = Math.max(0, Math.min(this.multisampling, this.context.renderer.capabilities.maxSamples));
|
|
295
314
|
}
|
|
296
|
-
if (debug) console.debug(`[PostProcessing] Set multisampling to ${
|
|
315
|
+
if (debug) console.debug(`[PostProcessing] Set multisampling to ${this._postprocessing.multisampling} (Is Mobile: ${DeviceUtilities.isMobileDevice()})`);
|
|
297
316
|
}
|
|
298
317
|
else if (debug) {
|
|
299
318
|
console.warn(`[PostProcessing] No composer found`);
|
|
300
319
|
}
|
|
301
320
|
})
|
|
302
|
-
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
this._postprocessing?.unapply(false);
|
|
303
324
|
}
|
|
304
325
|
|
|
305
326
|
}
|
|
306
327
|
|
|
307
|
-
private unapply() {
|
|
308
|
-
this._postprocessing?.unapply();
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
328
|
private _applyPostQueue() {
|
|
313
329
|
if (this._modificationQueue) {
|
|
314
330
|
for (const entry of this._modificationQueue.values()) this.onEditorModification(entry);
|