@needle-tools/engine 4.6.1-next.1e3d612 → 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.
Files changed (68) hide show
  1. package/dist/{needle-engine.bundle-Dx31Hfkf.js → needle-engine.bundle-BBxAzv6J.js} +6556 -6485
  2. package/dist/{needle-engine.bundle-BYdv-DmQ.min.js → needle-engine.bundle-BwVJc8bG.min.js} +170 -158
  3. package/dist/{needle-engine.bundle-DsI9yYlp.umd.cjs → needle-engine.bundle-cvRBTPLl.umd.cjs} +173 -161
  4. package/dist/needle-engine.js +48 -48
  5. package/dist/needle-engine.min.js +1 -1
  6. package/dist/needle-engine.umd.cjs +1 -1
  7. package/lib/engine/engine_context.d.ts +2 -1
  8. package/lib/engine/engine_context.js +3 -2
  9. package/lib/engine/engine_context.js.map +1 -1
  10. package/lib/engine/engine_three_utils.d.ts +17 -14
  11. package/lib/engine/engine_three_utils.js +106 -53
  12. package/lib/engine/engine_three_utils.js.map +1 -1
  13. package/lib/engine/engine_tonemapping.d.ts +4 -0
  14. package/lib/engine/engine_tonemapping.js +21 -18
  15. package/lib/engine/engine_tonemapping.js.map +1 -1
  16. package/lib/engine/engine_utils.js.map +1 -1
  17. package/lib/engine/webcomponents/needle-engine.d.ts +4 -1
  18. package/lib/engine/webcomponents/needle-engine.extras.js +3 -3
  19. package/lib/engine/webcomponents/needle-engine.extras.js.map +1 -1
  20. package/lib/engine/webcomponents/needle-engine.js +11 -21
  21. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  22. package/lib/engine-components/CameraUtils.js.map +1 -1
  23. package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +1 -1
  24. package/lib/engine-components/postprocessing/Effects/BloomEffect.js +2 -2
  25. package/lib/engine-components/postprocessing/Effects/ColorAdjustments.d.ts +8 -0
  26. package/lib/engine-components/postprocessing/Effects/ColorAdjustments.js +27 -8
  27. package/lib/engine-components/postprocessing/Effects/ColorAdjustments.js.map +1 -1
  28. package/lib/engine-components/postprocessing/Effects/Sharpening.d.ts +1 -1
  29. package/lib/engine-components/postprocessing/Effects/Sharpening.js +2 -2
  30. package/lib/engine-components/postprocessing/Effects/Sharpening.js.map +1 -1
  31. package/lib/engine-components/postprocessing/Effects/Tonemapping.d.ts +2 -9
  32. package/lib/engine-components/postprocessing/Effects/Tonemapping.js +22 -57
  33. package/lib/engine-components/postprocessing/Effects/Tonemapping.js.map +1 -1
  34. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.d.ts +13 -0
  35. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.js +52 -0
  36. package/lib/engine-components/postprocessing/Effects/Tonemapping.utils.js.map +1 -0
  37. package/lib/engine-components/postprocessing/PostProcessingEffect.d.ts +7 -7
  38. package/lib/engine-components/postprocessing/PostProcessingEffect.js +7 -7
  39. package/lib/engine-components/postprocessing/PostProcessingEffect.js.map +1 -1
  40. package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +6 -1
  41. package/lib/engine-components/postprocessing/PostProcessingHandler.js +107 -16
  42. package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
  43. package/lib/engine-components/postprocessing/Volume.js +19 -24
  44. package/lib/engine-components/postprocessing/Volume.js.map +1 -1
  45. package/lib/engine-components/postprocessing/index.d.ts +1 -1
  46. package/lib/engine-components/postprocessing/index.js +1 -1
  47. package/lib/engine-components/postprocessing/index.js.map +1 -1
  48. package/lib/engine-components/postprocessing/utils.d.ts +6 -6
  49. package/lib/engine-components/postprocessing/utils.js +26 -25
  50. package/lib/engine-components/postprocessing/utils.js.map +1 -1
  51. package/package.json +1 -1
  52. package/src/engine/engine_context.ts +5 -3
  53. package/src/engine/engine_three_utils.ts +134 -58
  54. package/src/engine/engine_tonemapping.ts +23 -24
  55. package/src/engine/engine_utils.ts +2 -2
  56. package/src/engine/webcomponents/needle-engine.extras.ts +3 -3
  57. package/src/engine/webcomponents/needle-engine.ts +14 -25
  58. package/src/engine-components/CameraUtils.ts +3 -3
  59. package/src/engine-components/postprocessing/Effects/BloomEffect.ts +2 -2
  60. package/src/engine-components/postprocessing/Effects/ColorAdjustments.ts +24 -13
  61. package/src/engine-components/postprocessing/Effects/Sharpening.ts +2 -2
  62. package/src/engine-components/postprocessing/Effects/Tonemapping.ts +24 -66
  63. package/src/engine-components/postprocessing/Effects/Tonemapping.utils.ts +60 -0
  64. package/src/engine-components/postprocessing/PostProcessingEffect.ts +7 -7
  65. package/src/engine-components/postprocessing/PostProcessingHandler.ts +122 -20
  66. package/src/engine-components/postprocessing/Volume.ts +19 -26
  67. package/src/engine-components/postprocessing/index.ts +1 -1
  68. package/src/engine-components/postprocessing/utils.ts +27 -26
@@ -1,70 +1,18 @@
1
1
  import type { ToneMappingEffect as _TonemappingEffect, ToneMappingMode } from "postprocessing";
2
- import { ACESFilmicToneMapping, AgXToneMapping, LinearToneMapping, NeutralToneMapping, NoToneMapping, ReinhardToneMapping, WebGLRenderer } from "three";
3
2
 
4
3
  import { MODULES } from "../../../engine/engine_modules.js";
5
4
  import { serializable } from "../../../engine/engine_serialization.js";
5
+ import { nameToThreeTonemapping } from "../../../engine/engine_tonemapping.js";
6
6
  import { getParam } from "../../../engine/engine_utils.js";
7
7
  import { EffectProviderResult, PostProcessingEffect } from "../PostProcessingEffect.js";
8
8
  import { findPostProcessingManager } from "../utils.js";
9
9
  import { VolumeParameter } from "../VolumeParameter.js";
10
10
  import { registerCustomEffectType } from "../VolumeProfile.js";
11
+ import { NEToneMappingMode, NEToneMappingModeNames, threeToNeedleToneMapping, threeToneMappingToEffectMode, toThreeToneMapping } from "./Tonemapping.utils.js";
11
12
 
12
13
  const debug = getParam("debugpost");
13
14
 
14
15
 
15
- enum NEToneMappingMode {
16
- None = 0,
17
- Neutral = 1, // Neutral tonemapper, close to Reinhard
18
- ACES = 2, // ACES Filmic reference tonemapper (custom approximation)
19
- AgX = 3, // AgX Filmic tonemapper
20
- KhronosNeutral = 4, // PBR Neural tonemapper
21
- }
22
-
23
- type NEToneMappingModeNames = keyof typeof NEToneMappingMode;
24
-
25
-
26
- function toThreeToneMapping(mode: NEToneMappingMode | undefined) {
27
- switch (mode) {
28
- case NEToneMappingMode.None:
29
- return LinearToneMapping;
30
- case NEToneMappingMode.Neutral:
31
- return ReinhardToneMapping;
32
- case NEToneMappingMode.ACES:
33
- return ACESFilmicToneMapping;
34
- case NEToneMappingMode.AgX:
35
- return AgXToneMapping;
36
- case NEToneMappingMode.KhronosNeutral:
37
- return NeutralToneMapping;
38
- default:
39
- if(debug) console.warn("[Postprocessing] Unknown tone mapping mode", mode);
40
- return NeutralToneMapping;
41
- }
42
- }
43
-
44
- function threeToNeToneMapping(mode: number | undefined): NEToneMappingMode {
45
- switch (mode) {
46
- case LinearToneMapping: return NEToneMappingMode.None;
47
- case ACESFilmicToneMapping: return NEToneMappingMode.ACES;
48
- case AgXToneMapping: return NEToneMappingMode.AgX;
49
- case NeutralToneMapping: return NEToneMappingMode.Neutral;
50
- case ReinhardToneMapping: return NEToneMappingMode.Neutral;
51
- default: return NEToneMappingMode.None;
52
- }
53
-
54
- }
55
-
56
-
57
- function threeToneMappingToEffectMode(mode: number | undefined): ToneMappingMode {
58
- switch (mode) {
59
- case LinearToneMapping: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.LINEAR;
60
- case ACESFilmicToneMapping: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.ACES_FILMIC;
61
- case AgXToneMapping: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.AGX;
62
- case NeutralToneMapping: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.NEUTRAL;
63
- case ReinhardToneMapping: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.REINHARD;
64
- default: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.LINEAR;
65
- }
66
- }
67
-
68
16
  /**
69
17
  * @category Effects
70
18
  * @group Components
@@ -102,6 +50,7 @@ export class ToneMappingEffect extends PostProcessingEffect {
102
50
  super.onEffectEnabled(ppmanager);
103
51
  }
104
52
 
53
+ private _didCreateEffect = false;
105
54
  onCreateEffect(): EffectProviderResult | undefined {
106
55
 
107
56
  // TODO: this should be done in the PostProcessingHandler
@@ -118,10 +67,12 @@ export class ToneMappingEffect extends PostProcessingEffect {
118
67
  }
119
68
 
120
69
 
70
+ this._didCreateEffect = true;
71
+
121
72
  // ensure the effect tonemapping value is initialized
122
73
  if (this.mode.isInitialized == false) {
123
- const mode = threeToNeToneMapping(this.context.renderer.toneMapping);
124
- if(debug) console.log("[PostProcessing] Initializing ToneMapping mode to renderer.toneMapping", this.context.renderer.toneMapping + " → " + mode);
74
+ const mode = threeToNeedleToneMapping(this.context.renderer.toneMapping);
75
+ if (debug) console.log("[PostProcessing] Initializing ToneMapping mode to renderer.toneMapping", this.context.renderer.toneMapping + " → " + mode);
125
76
  this.mode.initialize(mode);
126
77
  }
127
78
 
@@ -130,26 +81,33 @@ export class ToneMappingEffect extends PostProcessingEffect {
130
81
  mode: threeToneMappingToEffectMode(threeMode),
131
82
  });
132
83
  this.mode.onValueChanged = (newValue) => {
133
- const threeMode = toThreeToneMapping(newValue);
134
- tonemapping.mode = threeToneMappingToEffectMode(threeMode);
84
+ if (typeof newValue === "string") {
85
+ newValue = nameToThreeTonemapping(newValue);
86
+ tonemapping.mode = threeToneMappingToEffectMode(newValue);
87
+ }
88
+ else {
89
+ const threeMode = toThreeToneMapping(newValue);
90
+ tonemapping.mode = threeToneMappingToEffectMode(threeMode);
91
+ }
92
+ tonemapping.name = "ToneMapping (" + NEToneMappingMode[newValue] + ")";
135
93
  if (debug) console.log("[PostProcessing] ToneMapping mode changed to", NEToneMappingMode[newValue], threeMode, tonemapping.mode);
136
94
  };
137
95
  if (debug) console.log("[PostProcessing] Use ToneMapping", NEToneMappingMode[this.mode.value], threeMode, tonemapping.mode, "renderer.tonemapping: " + this.context.renderer.toneMapping);
138
96
 
139
97
 
140
- this.exposure.onValueChanged = (newValue) => {
141
- this.context.renderer.toneMappingExposure = newValue;
142
- };
143
-
144
98
  return tonemapping;
145
99
  }
146
100
 
147
101
 
148
102
  onBeforeRender(): void {
149
- if (this.mode.overrideState)
150
- this.context.renderer.toneMapping = toThreeToneMapping(this.mode.value);
151
- if (this.exposure.overrideState && this.exposure.value !== undefined)
152
- this.context.renderer.toneMappingExposure = this.exposure.value
103
+ if (this._didCreateEffect) {
104
+ if (this.mode.overrideState)
105
+ this.context.renderer.toneMapping = toThreeToneMapping(this.mode.value);
106
+ if (this.exposure.overrideState && this.exposure.value !== undefined) {
107
+ const newValue = Math.max(0.01, this.exposure.value);
108
+ this.context.renderer.toneMappingExposure = newValue;
109
+ }
110
+ }
153
111
  }
154
112
 
155
113
 
@@ -0,0 +1,60 @@
1
+ import type { ToneMappingMode } from "postprocessing";
2
+ import { ACESFilmicToneMapping, AgXToneMapping, LinearToneMapping, NeutralToneMapping, ReinhardToneMapping, ToneMapping } from "three";
3
+
4
+ import { MODULES } from "../../../engine/engine_modules.js";
5
+
6
+ export enum NEToneMappingMode {
7
+ None = 0,
8
+ Neutral = 1, // Neutral tonemapper, close to Reinhard
9
+ ACES = 2, // ACES Filmic reference tonemapper (custom approximation)
10
+ AgX = 3, // AgX Filmic tonemapper
11
+ KhronosNeutral = 4, // PBR Neural tonemapper
12
+ }
13
+ export type NEToneMappingModeNames = keyof typeof NEToneMappingMode;
14
+
15
+ const unknownTonemappingWarning = new Map<any, boolean>();
16
+
17
+ export function toThreeToneMapping(mode: NEToneMappingMode | undefined) {
18
+ switch (mode) {
19
+ case NEToneMappingMode.None:
20
+ return LinearToneMapping;
21
+ case NEToneMappingMode.Neutral:
22
+ return ReinhardToneMapping;
23
+ case NEToneMappingMode.ACES:
24
+ return ACESFilmicToneMapping;
25
+ case NEToneMappingMode.AgX:
26
+ return AgXToneMapping;
27
+ case NEToneMappingMode.KhronosNeutral:
28
+ return NeutralToneMapping;
29
+ default:
30
+ if(!unknownTonemappingWarning.has(mode)) {
31
+ unknownTonemappingWarning.set(mode, true);
32
+ console.warn("[Postprocessing] Unknown tone mapping mode", mode);
33
+ }
34
+ return NeutralToneMapping;
35
+ }
36
+ }
37
+
38
+ export function threeToNeedleToneMapping(mode: ToneMapping | number | undefined): NEToneMappingMode {
39
+ switch (mode) {
40
+ case LinearToneMapping: return NEToneMappingMode.None;
41
+ case ACESFilmicToneMapping: return NEToneMappingMode.ACES;
42
+ case AgXToneMapping: return NEToneMappingMode.AgX;
43
+ case NeutralToneMapping: return NEToneMappingMode.Neutral;
44
+ case ReinhardToneMapping: return NEToneMappingMode.Neutral;
45
+ default: return NEToneMappingMode.None;
46
+ }
47
+
48
+ }
49
+
50
+
51
+ export function threeToneMappingToEffectMode(mode: number | undefined): ToneMappingMode {
52
+ switch (mode) {
53
+ case LinearToneMapping: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.LINEAR;
54
+ case ACESFilmicToneMapping: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.ACES_FILMIC;
55
+ case AgXToneMapping: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.AGX;
56
+ case NeutralToneMapping: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.NEUTRAL;
57
+ case ReinhardToneMapping: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.REINHARD;
58
+ default: return MODULES.POSTPROCESSING.MODULE.ToneMappingMode.LINEAR;
59
+ }
60
+ }
@@ -56,23 +56,23 @@ export abstract class PostProcessingEffect extends Component implements IEffectP
56
56
  get isPostProcessingEffect() { return true; }
57
57
 
58
58
  /**
59
- * The priority of this effect. The higher the priority the later the effect will be applied in the post processing stack.
59
+ * The order of this effect. The higher the order the later the effect will be applied in the post processing stack.
60
60
  * This can be used to control the order of effects when multiple effects are applied.
61
- * It is recommended to use the PostProcessingEffectPriority constant to order your custom effects before or after built-in effects.
62
- * @default `undefined` (no specific priority set, will be applied in the order of registration)
61
+ * It is recommended to use the PostProcessingEffectOrder constant to order your custom effects before or after built-in effects.
62
+ * @default `undefined` (no specific order set, will be applied in the order of registration)
63
63
  *
64
64
  * @example
65
65
  * ```typescript
66
- * import { PostProcessingEffectPriority } from "@needle-tools/engine"
66
+ * import { PostProcessingEffectOrder } from "@needle-tools/engine"
67
67
  *
68
68
  * export class MyCustomEffect extends PostProcessingEffect {
69
- * priority: PostProcessingEffectPriority.Bloom + 1; // render after bloom
70
- *
69
+ * order: PostProcessingEffectOrder.Bloom + 1; // render after bloom
70
+ * // This will ensure that the effect is applied after the bloom effect in the post processing stack.
71
71
  * // ... the rest of your effect code
72
72
  * }
73
73
  * ```
74
74
  */
75
- priority: number | undefined = undefined;
75
+ order: number | undefined = undefined;
76
76
 
77
77
  constructor(params: any = undefined) {
78
78
  super();
@@ -1,15 +1,16 @@
1
- import type { Effect, EffectComposer, Pass, ToneMappingEffect as _TonemappingEffect } from "postprocessing";
2
- import { Camera as Camera3, HalfFloatType, NoToneMapping, Scene } 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 type { Constructor } from "../../engine/engine_types.js";
9
- import { DeviceUtilities, getParam } from "../../engine/engine_utils.js";
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";
12
- import { orderEffects, PostprocessingEffectData } from "./utils.js";
13
+ import { orderEffects, PostprocessingEffectData, PostProcessingEffectOrder } from "./utils.js";
13
14
 
14
15
  declare const NEEDLE_USE_POSTPROCESSING: boolean;
15
16
  globalThis["NEEDLE_USE_POSTPROCESSING"] = globalThis["NEEDLE_USE_POSTPROCESSING"] !== undefined ? globalThis["NEEDLE_USE_POSTPROCESSING"] : true;
@@ -19,6 +20,7 @@ const debug = getParam("debugpost");
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
@@ -78,6 +80,9 @@ export class PostProcessingHandler {
78
80
  if (typeof context.renderer[autoclearSetting] === "boolean") {
79
81
  context.renderer.autoClear = context.renderer[autoclearSetting];
80
82
  }
83
+ if (typeof context.renderer[previousToneMapping] === "number") {
84
+ context.renderer.toneMapping = context.renderer[previousToneMapping] as ToneMapping;
85
+ }
81
86
  }
82
87
 
83
88
  this._composer?.removeAllPasses();
@@ -153,14 +158,14 @@ export class PostProcessingHandler {
153
158
  for (const effect of res) {
154
159
  this._effects.push({
155
160
  effect,
156
- priority: component.priority
161
+ priority: component.order
157
162
  });
158
163
  }
159
164
  }
160
165
  else {
161
166
  this._effects.push({
162
167
  effect: res,
163
- priority: component.priority
168
+ priority: component.order
164
169
  });
165
170
  }
166
171
  }
@@ -171,14 +176,6 @@ export class PostProcessingHandler {
171
176
  }
172
177
  }
173
178
 
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
179
  this.applyEffects(context);
183
180
  }
184
181
 
@@ -191,6 +188,18 @@ export class PostProcessingHandler {
191
188
  get hasSmaaEffect() { return this._hasSmaaEffect; }
192
189
 
193
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
+
194
203
  /** Build composer passes */
195
204
  private applyEffects(context: Context) {
196
205
  // Reset state
@@ -198,8 +207,7 @@ export class PostProcessingHandler {
198
207
  this._anyPassHasNormal = false;
199
208
  this._hasSmaaEffect = false;
200
209
 
201
- const effectsOrPasses = this._effects;
202
- if (effectsOrPasses.length <= 0) {
210
+ if (this._effects.length <= 0) {
203
211
  return;
204
212
  }
205
213
 
@@ -217,6 +225,21 @@ export class PostProcessingHandler {
217
225
  }
218
226
  renderer[autoclearSetting] = renderer.autoClear;
219
227
 
228
+ if (typeof renderer[previousToneMapping] === "number") {
229
+ renderer.toneMapping = renderer[previousToneMapping] as ToneMapping;
230
+ }
231
+ renderer[previousToneMapping] = renderer.toneMapping;
232
+
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 });
240
+ }
241
+ }
242
+
220
243
  // create composer and set active on context
221
244
  if (!this._composer) {
222
245
  // const hdrRenderTarget = new WebGLRenderTarget(window.innerWidth, window.innerHeight, { type: HalfFloatType });
@@ -236,7 +259,7 @@ export class PostProcessingHandler {
236
259
  composer.setRenderer(renderer);
237
260
  composer.setMainScene(scene);
238
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)
239
- composer.multisampling = 0;
262
+ composer.multisampling = 0; // Disable multisampling by default
240
263
 
241
264
  for (const prev of composer.passes)
242
265
  prev.dispose();
@@ -244,17 +267,80 @@ export class PostProcessingHandler {
244
267
 
245
268
  // Render to screen pass
246
269
  const screenpass = new MODULES.POSTPROCESSING.MODULE.RenderPass(scene, cam);
247
- screenpass.name = "Render To Screen";
270
+ screenpass.name = "RenderPass";
248
271
  screenpass.mainScene = scene;
249
272
  composer.addPass(screenpass);
250
273
 
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;
279
+
280
+ // screenPassRender.call(screenpass, renderer, inputBuffer, outputBuffer, deltaTime, stencilTest);
281
+ // return;
282
+
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
+ }
288
+
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;
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}`);
323
+ }
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;
251
337
  try {
252
338
  orderEffects(this._effects);
253
339
 
254
340
  const effectsToMerge: Array<Effect> = [];
255
341
  let hasConvolutionEffectInArray = false;
256
342
 
257
- for (const entry of effectsOrPasses) {
343
+ for (const entry of this._effects) {
258
344
  const ef = entry.effect;
259
345
 
260
346
  if (ef instanceof MODULES.POSTPROCESSING.MODULE.SMAAEffect) {
@@ -264,6 +350,22 @@ export class PostProcessingHandler {
264
350
  this._anyPassHasNormal = true;
265
351
  }
266
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
+ // }
368
+
267
369
  if (ef instanceof MODULES.POSTPROCESSING.MODULE.Effect) {
268
370
  const attributes = ef.getAttributes();
269
371
  const convolution = MODULES.POSTPROCESSING.MODULE.EffectAttribute.CONVOLUTION;
@@ -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 (composer.multisampling !== 0) {
197
- composer.multisampling = 0;
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 = composer.multisampling;
207
+ const prev = this._postprocessing.multisampling;
210
208
 
211
- if (composer.multisampling > 0 && this.context.time.smoothedFps <= 50) {
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 = composer.multisampling * .5;
212
+ let newMultiSample = this._postprocessing.multisampling * .5;
215
213
  newMultiSample = Math.floor(newMultiSample);
216
- if (newMultiSample != composer.multisampling) {
217
- composer.multisampling = newMultiSample;
214
+ if (newMultiSample != this._postprocessing.multisampling) {
215
+ this._postprocessing.multisampling = newMultiSample;
218
216
  }
219
- if (debug) console.debug(`[PostProcessing] Reduced multisampling from ${prev} to ${composer.multisampling}`);
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
- && composer.multisampling < this.context.renderer.capabilities.maxSamples
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 = composer.multisampling <= 0 ? 1 : composer.multisampling * 2;
226
+ let newMultiSample = this._postprocessing.multisampling <= 0 ? 1 : this._postprocessing.multisampling * 2;
229
227
  newMultiSample = Math.floor(newMultiSample);
230
- if (newMultiSample !== composer.multisampling) {
231
- composer.multisampling = newMultiSample;
228
+ if (newMultiSample !== this._postprocessing.multisampling) {
229
+ this._postprocessing.multisampling = newMultiSample;
232
230
  }
233
- if (debug) console.debug(`[PostProcessing] Increased multisampling from ${prev} to ${composer.multisampling}`);
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 !== composer.multisampling)
241
- composer.multisampling = newMultiSample;
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
- const composer = this._postprocessing?.composer;
313
- if (composer) {
306
+ if (this._postprocessing) {
314
307
  if (this.multisampling === "auto") {
315
- composer.multisampling = DeviceUtilities.isMobileDevice()
308
+ this._postprocessing.multisampling = DeviceUtilities.isMobileDevice()
316
309
  ? 2
317
310
  : 4;
318
311
  }
319
312
  else {
320
- composer.multisampling = Math.max(0, Math.min(this.multisampling, this.context.renderer.capabilities.maxSamples));
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 ${composer.multisampling} (Is Mobile: ${DeviceUtilities.isMobileDevice()})`);
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 { PostProcessingEffectPriority } from "./utils.js";
3
+ export { PostProcessingEffectOrder } from "./utils.js";
4
4
  export { PostProcessingManager } from "./Volume.js"
5
5
  export * from "./VolumeParameter.js"
6
6
  export * from "./VolumeProfile.js";