@needle-tools/engine 4.6.1-next.26cf56c → 4.6.1-next.50abb78
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-BcXxWyWw.js} +8161 -8114
- package/dist/{needle-engine.bundle-B0Kku0-E.umd.cjs → needle-engine.bundle-DTkya1lV.umd.cjs} +146 -146
- package/dist/{needle-engine.bundle-B23LP9xh.min.js → needle-engine.bundle-DxbvIKfK.min.js} +170 -170
- package/dist/needle-engine.js +342 -341
- 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.js +4 -1
- package/lib/engine/engine_context.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 +1 -1
- package/lib/engine-components/postprocessing/Effects/BloomEffect.js +3 -0
- package/lib/engine-components/postprocessing/Effects/BloomEffect.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingEffect.d.ts +18 -0
- package/lib/engine-components/postprocessing/PostProcessingEffect.js +18 -0
- package/lib/engine-components/postprocessing/PostProcessingEffect.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +4 -3
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +83 -119
- 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 +48 -36
- 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 +36 -0
- package/lib/engine-components/postprocessing/utils.js +98 -0
- package/lib/engine-components/postprocessing/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/engine_context.ts +3 -1
- package/src/engine-components/postprocessing/Effects/Antialiasing.ts +2 -1
- package/src/engine-components/postprocessing/Effects/BloomEffect.ts +4 -2
- package/src/engine-components/postprocessing/PostProcessingEffect.ts +19 -0
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +94 -123
- package/src/engine-components/postprocessing/Volume.ts +52 -42
- package/src/engine-components/postprocessing/index.ts +1 -0
- package/src/engine-components/postprocessing/utils.ts +116 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isDevEnvironment } from "../../engine/debug/index.js";
|
|
2
2
|
import { addComponent } from "../../engine/engine_components.js";
|
|
3
3
|
import { foreachComponentEnumerator } from "../../engine/engine_gameobject.js";
|
|
4
|
+
import { MODULES } from "../../engine/engine_modules.js";
|
|
4
5
|
import { getParam } from "../../engine/engine_utils.js";
|
|
5
6
|
export const debug = getParam("debugpost");
|
|
6
7
|
let PostprocessingManagerType = null;
|
|
@@ -35,4 +36,101 @@ export function getPostProcessingManager(effect) {
|
|
|
35
36
|
}
|
|
36
37
|
return manager;
|
|
37
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Default priority for post-processing effects. This can be used to sort effects by their rendering order when creating custom effects.
|
|
41
|
+
* E.g. in your custom effect, you can set `priority: PostProcessingEffectPriority.Bloom + 1;` to ensure it gets rendered after the bloom effect.
|
|
42
|
+
* OR `priority: PostProcessingEffectPriority.Bloom - 1;` to ensure it gets rendered before the bloom effect.
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* import { PostProcessingEffectPriority } from "@needle-tools/engine"
|
|
46
|
+
*
|
|
47
|
+
* export class MyCustomEffect extends PostProcessingEffect {
|
|
48
|
+
* priority: PostProcessingEffectPriority.Bloom + 1; // render after bloom
|
|
49
|
+
*
|
|
50
|
+
* // ... your effect code
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const PostProcessingEffectPriority = {
|
|
55
|
+
NormalPass: 0,
|
|
56
|
+
DepthDownsamplingPass: 10,
|
|
57
|
+
SMAA: 20,
|
|
58
|
+
SSAO: 30,
|
|
59
|
+
TiltShift: 40,
|
|
60
|
+
DepthOfField: 50,
|
|
61
|
+
ChromaticAberration: 60,
|
|
62
|
+
Bloom: 70,
|
|
63
|
+
Vignette: 80,
|
|
64
|
+
Pixelation: 90,
|
|
65
|
+
ToneMapping: 100,
|
|
66
|
+
HueSaturation: 110,
|
|
67
|
+
BrightnessContrast: 120,
|
|
68
|
+
// Sharpening: 130,
|
|
69
|
+
};
|
|
70
|
+
// let effectsOrder: Array<Constructor<Effect | Pass>> | null = null;
|
|
71
|
+
let builtinOrder = null;
|
|
72
|
+
export function orderEffects(effects) {
|
|
73
|
+
if (debug === "verbose")
|
|
74
|
+
console.debug("Before ordering effects", [...effects]);
|
|
75
|
+
if (!builtinOrder) {
|
|
76
|
+
builtinOrder = new Map();
|
|
77
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.NormalPass, PostProcessingEffectPriority.NormalPass);
|
|
78
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.DepthDownsamplingPass, PostProcessingEffectPriority.DepthDownsamplingPass);
|
|
79
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.SMAAEffect, PostProcessingEffectPriority.SMAA);
|
|
80
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.SSAOEffect, PostProcessingEffectPriority.SSAO);
|
|
81
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.TiltShiftEffect, PostProcessingEffectPriority.TiltShift);
|
|
82
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.DepthOfFieldEffect, PostProcessingEffectPriority.DepthOfField);
|
|
83
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.ChromaticAberrationEffect, PostProcessingEffectPriority.ChromaticAberration);
|
|
84
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.BloomEffect, PostProcessingEffectPriority.Bloom);
|
|
85
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.VignetteEffect, PostProcessingEffectPriority.Vignette);
|
|
86
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.PixelationEffect, PostProcessingEffectPriority.Pixelation);
|
|
87
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.ToneMappingEffect, PostProcessingEffectPriority.ToneMapping);
|
|
88
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.HueSaturationEffect, PostProcessingEffectPriority.HueSaturation);
|
|
89
|
+
builtinOrder.set(MODULES.POSTPROCESSING.MODULE.BrightnessContrastEffect, PostProcessingEffectPriority.BrightnessContrast);
|
|
90
|
+
}
|
|
91
|
+
// enforce correct order of effects (e.g. DOF before Bloom)
|
|
92
|
+
effects.sort((a, b) => {
|
|
93
|
+
// we use find index here because sometimes constructor names are prefixed with `_`
|
|
94
|
+
// 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)
|
|
95
|
+
const aidx = typeof a.priority === "number" ? a.priority : builtinOrder.get(a.effect.constructor) || -1;
|
|
96
|
+
const bidx = typeof b.priority === "number" ? b.priority : builtinOrder.get(b.effect.constructor) || -1;
|
|
97
|
+
// Unknown effects should be rendered first
|
|
98
|
+
if (aidx < 0) {
|
|
99
|
+
if (debug)
|
|
100
|
+
console.warn("Unknown effect found: ", a.constructor.name);
|
|
101
|
+
return -1;
|
|
102
|
+
}
|
|
103
|
+
else if (bidx < 0) {
|
|
104
|
+
if (debug)
|
|
105
|
+
console.warn("Unknown effect found: ", b.constructor.name);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
// if (aidx < 0) return 1;
|
|
109
|
+
// if (bidx < 0) return -1;
|
|
110
|
+
return aidx - bidx;
|
|
111
|
+
});
|
|
112
|
+
// effects.sort((a, b) => {
|
|
113
|
+
// if (a.beforeEffect) {
|
|
114
|
+
// const beforeA = effectsOrder!.findIndex(e => a.beforeEffect!.constructor.name.endsWith(e.name));
|
|
115
|
+
// if (beforeA >= 0) {
|
|
116
|
+
// return -1; // before effect should be rendered first
|
|
117
|
+
// }
|
|
118
|
+
// else {
|
|
119
|
+
// return 1; // no before effect, so we can keep the order
|
|
120
|
+
// }
|
|
121
|
+
// }
|
|
122
|
+
// else if (b.beforeEffect) {
|
|
123
|
+
// const beforeB = effectsOrder!.findIndex(e => b.beforeEffect!.constructor.name.endsWith(e.name));
|
|
124
|
+
// if (beforeB >= 0) {
|
|
125
|
+
// return 1; // before effect should be rendered first
|
|
126
|
+
// }
|
|
127
|
+
// else if (a.beforeEffect) {
|
|
128
|
+
// return -1; // no before effect, so we can keep the order
|
|
129
|
+
// }
|
|
130
|
+
// }
|
|
131
|
+
// return 0; // no before effect, so we can keep the order
|
|
132
|
+
// });
|
|
133
|
+
if (debug === "verbose")
|
|
134
|
+
console.debug("After ordering effects", [...effects]);
|
|
135
|
+
}
|
|
38
136
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/engine-components/postprocessing/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/engine-components/postprocessing/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/engine",
|
|
3
|
-
"version": "4.6.1-next.
|
|
3
|
+
"version": "4.6.1-next.50abb78",
|
|
4
4
|
"description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.",
|
|
5
5
|
"main": "dist/needle-engine.min.js",
|
|
6
6
|
"exports": {
|
|
@@ -33,7 +33,7 @@ import { NetworkConnection } from './engine_networking.js';
|
|
|
33
33
|
import { Physics } from './engine_physics.js';
|
|
34
34
|
import { PlayerViewManager } from './engine_playerview.js';
|
|
35
35
|
import { RendererData as SceneLighting } from './engine_scenelighting.js';
|
|
36
|
-
import { logHierarchy } from './engine_three_utils.js';
|
|
36
|
+
import { getTempColor, logHierarchy } from './engine_three_utils.js';
|
|
37
37
|
import { Time } from './engine_time.js';
|
|
38
38
|
import { patchTonemapping } from './engine_tonemapping.js';
|
|
39
39
|
import type { CoroutineData, ICamera, IComponent, IContext, ILight, LoadedModel, Model, Vec2 } from "./engine_types.js";
|
|
@@ -1612,6 +1612,8 @@ export class Context implements IContext {
|
|
|
1612
1612
|
if (currentPassesCamera != camera)
|
|
1613
1613
|
this.composer.setMainCamera(camera);
|
|
1614
1614
|
}
|
|
1615
|
+
const backgroundColor = this.renderer.getClearColor(getTempColor());
|
|
1616
|
+
if(backgroundColor) this.renderer.setClearColor(backgroundColor.convertSRGBToLinear());
|
|
1615
1617
|
this.composer.render(this.time.deltaTime);
|
|
1616
1618
|
}
|
|
1617
1619
|
else if (camera) {
|
|
@@ -34,7 +34,8 @@ export class Antialiasing extends PostProcessingEffect {
|
|
|
34
34
|
const effect = new MODULES.POSTPROCESSING.MODULE.SMAAEffect({
|
|
35
35
|
preset: MODULES.POSTPROCESSING.MODULE.SMAAPreset.HIGH,
|
|
36
36
|
edgeDetectionMode: MODULES.POSTPROCESSING.MODULE.EdgeDetectionMode.LUMA,
|
|
37
|
-
|
|
37
|
+
// Keep predication mode disabled (default) since it looks better
|
|
38
|
+
// predicationMode: MODULES.POSTPROCESSING.MODULE.PredicationMode.DEPTH,
|
|
38
39
|
});
|
|
39
40
|
|
|
40
41
|
this.preset.onValueChanged = (newValue) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { BloomEffect as _BloomEffect, EffectAttribute } from "postprocessing";
|
|
2
2
|
import { MathUtils } from "three";
|
|
3
3
|
|
|
4
4
|
import { MODULES } from "../../../engine/engine_modules.js";
|
|
@@ -91,8 +91,10 @@ export class BloomEffect extends PostProcessingEffect {
|
|
|
91
91
|
luminanceThreshold: this.threshold.value,
|
|
92
92
|
luminanceSmoothing: this.scatter.value,
|
|
93
93
|
radius: 0.85, // default value
|
|
94
|
-
intensity: this.intensity.value,
|
|
94
|
+
intensity: this.intensity.value,
|
|
95
95
|
});
|
|
96
|
+
// @ts-ignore Well... it's protected but bloom needs this :(
|
|
97
|
+
bloom.setAttributes(EffectAttribute.CONVOLUTION);
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
this.intensity.onValueChanged = newValue => {
|
|
@@ -55,6 +55,25 @@ export abstract class PostProcessingEffect extends Component implements IEffectP
|
|
|
55
55
|
|
|
56
56
|
get isPostProcessingEffect() { return true; }
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* The priority of this effect. The higher the priority the later the effect will be applied in the post processing stack.
|
|
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)
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* import { PostProcessingEffectPriority } from "@needle-tools/engine"
|
|
67
|
+
*
|
|
68
|
+
* export class MyCustomEffect extends PostProcessingEffect {
|
|
69
|
+
* priority: PostProcessingEffectPriority.Bloom + 1; // render after bloom
|
|
70
|
+
*
|
|
71
|
+
* // ... the rest of your effect code
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
priority: number | undefined = undefined;
|
|
76
|
+
|
|
58
77
|
constructor(params: any = undefined) {
|
|
59
78
|
super();
|
|
60
79
|
if (params) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Effect, EffectComposer, Pass, ToneMappingEffect as _TonemappingEffect } from "postprocessing";
|
|
2
|
-
import { Camera as Camera3, HalfFloatType, NoToneMapping } from "three";
|
|
2
|
+
import { Camera as Camera3, HalfFloatType, NoToneMapping, Scene } from "three";
|
|
3
3
|
|
|
4
4
|
import { showBalloonWarning } from "../../engine/debug/index.js";
|
|
5
5
|
// import { internal_SetSharpeningEffectModule } from "./Effects/Sharpening.js";
|
|
@@ -9,13 +9,13 @@ import type { Constructor } from "../../engine/engine_types.js";
|
|
|
9
9
|
import { DeviceUtilities, getParam } from "../../engine/engine_utils.js";
|
|
10
10
|
import { Camera } from "../Camera.js";
|
|
11
11
|
import { PostProcessingEffect, PostProcessingEffectContext } from "./PostProcessingEffect.js";
|
|
12
|
+
import { orderEffects, PostprocessingEffectData } from "./utils.js";
|
|
12
13
|
|
|
13
14
|
declare const NEEDLE_USE_POSTPROCESSING: boolean;
|
|
14
15
|
globalThis["NEEDLE_USE_POSTPROCESSING"] = globalThis["NEEDLE_USE_POSTPROCESSING"] !== undefined ? globalThis["NEEDLE_USE_POSTPROCESSING"] : true;
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
const debug = getParam("debugpost");
|
|
18
|
-
const dontMergePasses = getParam("debugpostpasses");
|
|
19
19
|
|
|
20
20
|
const activeKey = Symbol("needle:postprocessing-handler");
|
|
21
21
|
const autoclearSetting = Symbol("needle:previous-autoclear-state");
|
|
@@ -27,7 +27,7 @@ export class PostProcessingHandler {
|
|
|
27
27
|
|
|
28
28
|
private _composer: EffectComposer | null = null;
|
|
29
29
|
private _lastVolumeComponents?: PostProcessingEffect[];
|
|
30
|
-
private _effects: Array<
|
|
30
|
+
private readonly _effects: Array<PostprocessingEffectData> = [];
|
|
31
31
|
|
|
32
32
|
get isActive() {
|
|
33
33
|
return this._isActive;
|
|
@@ -60,7 +60,7 @@ export class PostProcessingHandler {
|
|
|
60
60
|
return this.onApply(this.context, components);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
unapply() {
|
|
63
|
+
unapply(dispose: boolean = true) {
|
|
64
64
|
if (debug) console.log("Unapplying postprocessing effects");
|
|
65
65
|
this._isActive = false;
|
|
66
66
|
if (this._lastVolumeComponents) {
|
|
@@ -73,21 +73,26 @@ export class PostProcessingHandler {
|
|
|
73
73
|
const active = context[activeKey] as PostProcessingHandler | null;
|
|
74
74
|
if (active === this) {
|
|
75
75
|
delete context[activeKey];
|
|
76
|
+
|
|
77
|
+
// Restore the auto clear setting
|
|
78
|
+
if (typeof context.renderer[autoclearSetting] === "boolean") {
|
|
79
|
+
context.renderer.autoClear = context.renderer[autoclearSetting];
|
|
80
|
+
}
|
|
76
81
|
}
|
|
82
|
+
|
|
83
|
+
this._composer?.removeAllPasses();
|
|
84
|
+
if (dispose) this._composer?.dispose();
|
|
85
|
+
|
|
77
86
|
if (context.composer === this._composer) {
|
|
78
|
-
context.composer?.dispose();
|
|
79
87
|
context.composer = null;
|
|
80
88
|
}
|
|
81
|
-
if (typeof context.renderer[autoclearSetting] === "boolean") {
|
|
82
|
-
context.renderer.autoClear = context.renderer[autoclearSetting];
|
|
83
|
-
}
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
dispose() {
|
|
87
|
-
this.unapply();
|
|
92
|
+
this.unapply(true);
|
|
88
93
|
|
|
89
94
|
for (const effect of this._effects) {
|
|
90
|
-
effect.dispose();
|
|
95
|
+
effect.effect.dispose();
|
|
91
96
|
}
|
|
92
97
|
this._effects.length = 0;
|
|
93
98
|
this._composer = null;
|
|
@@ -105,6 +110,7 @@ export class PostProcessingHandler {
|
|
|
105
110
|
// import("./Effects/Sharpening.effect")
|
|
106
111
|
]);
|
|
107
112
|
|
|
113
|
+
|
|
108
114
|
// try {
|
|
109
115
|
// internal_SetSharpeningEffectModule(modules[2]);
|
|
110
116
|
// }
|
|
@@ -141,10 +147,22 @@ export class PostProcessingHandler {
|
|
|
141
147
|
// apply or collect effects
|
|
142
148
|
const res = component.apply(ctx);
|
|
143
149
|
if (!res) continue;
|
|
150
|
+
|
|
151
|
+
|
|
144
152
|
if (Array.isArray(res)) {
|
|
145
|
-
|
|
153
|
+
for (const effect of res) {
|
|
154
|
+
this._effects.push({
|
|
155
|
+
effect,
|
|
156
|
+
priority: component.priority
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
this._effects.push({
|
|
162
|
+
effect: res,
|
|
163
|
+
priority: component.priority
|
|
164
|
+
});
|
|
146
165
|
}
|
|
147
|
-
else this._effects.push(res);
|
|
148
166
|
}
|
|
149
167
|
}
|
|
150
168
|
else {
|
|
@@ -157,7 +175,7 @@ export class PostProcessingHandler {
|
|
|
157
175
|
if (this.context.renderer.toneMapping != NoToneMapping) {
|
|
158
176
|
if (!this._effects.find(e => e instanceof MODULES.POSTPROCESSING.MODULE.ToneMappingEffect)) {
|
|
159
177
|
const tonemapping = new MODULES.POSTPROCESSING.MODULE.ToneMappingEffect();
|
|
160
|
-
this._effects.push(tonemapping);
|
|
178
|
+
this._effects.push({ effect: tonemapping });
|
|
161
179
|
}
|
|
162
180
|
}
|
|
163
181
|
|
|
@@ -175,9 +193,16 @@ export class PostProcessingHandler {
|
|
|
175
193
|
|
|
176
194
|
/** Build composer passes */
|
|
177
195
|
private applyEffects(context: Context) {
|
|
196
|
+
// Reset state
|
|
197
|
+
this._anyPassHasDepth = false;
|
|
198
|
+
this._anyPassHasNormal = false;
|
|
199
|
+
this._hasSmaaEffect = false;
|
|
178
200
|
|
|
179
201
|
const effectsOrPasses = this._effects;
|
|
180
|
-
if (effectsOrPasses.length <= 0)
|
|
202
|
+
if (effectsOrPasses.length <= 0) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
181
206
|
const camera = context.mainCameraComponent as Camera;
|
|
182
207
|
const renderer = context.renderer;
|
|
183
208
|
const scene = context.scene;
|
|
@@ -186,18 +211,11 @@ export class PostProcessingHandler {
|
|
|
186
211
|
// Store the auto clear setting because the postprocessing composer just disables it
|
|
187
212
|
// and when we disable postprocessing we want to restore the original setting
|
|
188
213
|
// https://github.com/pmndrs/postprocessing/blob/271944b74b543a5b743a62803a167b60cc6bb4ee/src/core/EffectComposer.js#L230C12-L230C12
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this._anyPassHasDepth = false;
|
|
193
|
-
this._anyPassHasNormal = false;
|
|
194
|
-
this._hasSmaaEffect = false;
|
|
195
|
-
|
|
196
|
-
for (const ef of effectsOrPasses) {
|
|
197
|
-
if (ef instanceof MODULES.POSTPROCESSING.MODULE.SMAAEffect) {
|
|
198
|
-
this._hasSmaaEffect = true;
|
|
199
|
-
}
|
|
214
|
+
// First we need to get the previously set autoClear setting, if it exists
|
|
215
|
+
if (typeof renderer[autoclearSetting] === "boolean") {
|
|
216
|
+
renderer.autoClear = renderer[autoclearSetting];
|
|
200
217
|
}
|
|
218
|
+
renderer[autoclearSetting] = renderer.autoClear;
|
|
201
219
|
|
|
202
220
|
// create composer and set active on context
|
|
203
221
|
if (!this._composer) {
|
|
@@ -218,6 +236,7 @@ export class PostProcessingHandler {
|
|
|
218
236
|
composer.setRenderer(renderer);
|
|
219
237
|
composer.setMainScene(scene);
|
|
220
238
|
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;
|
|
221
240
|
|
|
222
241
|
for (const prev of composer.passes)
|
|
223
242
|
prev.dispose();
|
|
@@ -229,63 +248,58 @@ export class PostProcessingHandler {
|
|
|
229
248
|
screenpass.mainScene = scene;
|
|
230
249
|
composer.addPass(screenpass);
|
|
231
250
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
this.orderEffects();
|
|
251
|
+
try {
|
|
252
|
+
orderEffects(this._effects);
|
|
236
253
|
|
|
237
|
-
|
|
254
|
+
const effectsToMerge: Array<Effect> = [];
|
|
255
|
+
let hasConvolutionEffectInArray = false;
|
|
238
256
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
effects.push(ef as Effect);
|
|
242
|
-
}
|
|
243
|
-
else if (ef instanceof MODULES.POSTPROCESSING.MODULE.Pass) {
|
|
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
|
-
}
|
|
257
|
+
for (const entry of effectsOrPasses) {
|
|
258
|
+
const ef = entry.effect;
|
|
253
259
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
pass.enabled = true;
|
|
260
|
-
pass.renderToScreen = false;
|
|
261
|
-
composer.addPass(pass);
|
|
260
|
+
if (ef instanceof MODULES.POSTPROCESSING.MODULE.SMAAEffect) {
|
|
261
|
+
this._hasSmaaEffect = true;
|
|
262
|
+
}
|
|
263
|
+
else if (ef instanceof MODULES.POSTPROCESSING.MODULE.NormalPass) {
|
|
264
|
+
this._anyPassHasNormal = true;
|
|
262
265
|
}
|
|
263
|
-
}
|
|
264
|
-
catch (e) {
|
|
265
|
-
console.error("Error while applying postprocessing effects", e);
|
|
266
|
-
composer.removeAllPasses();
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
else {
|
|
270
|
-
// we still want to sort passes, but we do not want to merge them for debugging
|
|
271
|
-
if (automaticEffectsOrdering)
|
|
272
|
-
this.orderEffects();
|
|
273
266
|
|
|
274
|
-
for (const ef of effectsOrPasses) {
|
|
275
267
|
if (ef instanceof MODULES.POSTPROCESSING.MODULE.Effect) {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
268
|
+
const attributes = ef.getAttributes();
|
|
269
|
+
const convolution = MODULES.POSTPROCESSING.MODULE.EffectAttribute.CONVOLUTION;
|
|
270
|
+
if (attributes & convolution) {
|
|
271
|
+
if(debug) console.log("[PostProcessing] Convolution effect detected: " + ef.name, hasConvolutionEffectInArray);
|
|
272
|
+
if (hasConvolutionEffectInArray) {
|
|
273
|
+
hasConvolutionEffectInArray = false;
|
|
274
|
+
if(debug) console.log("[PostProcessing] Merging effects with convolution effect in array", effectsToMerge.map(e => e.name).join(", "));
|
|
275
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
276
|
+
|
|
277
|
+
}
|
|
278
|
+
hasConvolutionEffectInArray = true;
|
|
279
|
+
}
|
|
280
|
+
// Otherwise we can merge it
|
|
281
|
+
effectsToMerge.push(ef as Effect);
|
|
279
282
|
}
|
|
280
283
|
else if (ef instanceof MODULES.POSTPROCESSING.MODULE.Pass) {
|
|
284
|
+
hasConvolutionEffectInArray = false;
|
|
285
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
281
286
|
ef.renderToScreen = false;
|
|
282
287
|
composer.addPass(ef as Pass);
|
|
283
288
|
}
|
|
284
|
-
else
|
|
289
|
+
else {
|
|
285
290
|
// seems some effects are not correctly typed, but three can deal with them,
|
|
286
|
-
// so we just pass them through
|
|
291
|
+
// so we might need to just pass them through
|
|
292
|
+
hasConvolutionEffectInArray = false;
|
|
293
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
287
294
|
composer.addPass(ef);
|
|
295
|
+
}
|
|
288
296
|
}
|
|
297
|
+
|
|
298
|
+
this.createPassForMergeableEffects(effectsToMerge, composer, cam, scene);
|
|
299
|
+
}
|
|
300
|
+
catch (e) {
|
|
301
|
+
console.error("Error while applying postprocessing effects", e);
|
|
302
|
+
composer.removeAllPasses();
|
|
289
303
|
}
|
|
290
304
|
|
|
291
305
|
// 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 +315,6 @@ export class PostProcessingHandler {
|
|
|
301
315
|
}
|
|
302
316
|
|
|
303
317
|
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
318
|
}
|
|
311
319
|
|
|
312
320
|
// DEBUG LAND BELOW
|
|
@@ -314,56 +322,20 @@ export class PostProcessingHandler {
|
|
|
314
322
|
if (debug) this._onCreateEffectsDebug(this._composer!, cam);
|
|
315
323
|
}
|
|
316
324
|
|
|
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
325
|
|
|
364
|
-
if (debug === "verbose") console.debug("After ordering effects", [...this._effects]);
|
|
365
|
-
}
|
|
366
326
|
|
|
327
|
+
/** Should be called before `composer.addPass()` to create an effect pass with all previously collected effects that can be merged up to that point */
|
|
328
|
+
private createPassForMergeableEffects(effects: Array<Effect>, composer: EffectComposer, camera: Camera3, scene: Scene) {
|
|
329
|
+
if (effects.length > 0) {
|
|
330
|
+
const pass = new MODULES.POSTPROCESSING.MODULE.EffectPass(camera, ...effects);
|
|
331
|
+
pass.name = effects.map(e => e.name).join(", ");
|
|
332
|
+
pass.mainScene = scene;
|
|
333
|
+
pass.enabled = true;
|
|
334
|
+
pass.renderToScreen = false;
|
|
335
|
+
composer.addPass(pass);
|
|
336
|
+
effects.length = 0; // Clear effects after adding them to the pass
|
|
337
|
+
}
|
|
338
|
+
}
|
|
367
339
|
|
|
368
340
|
|
|
369
341
|
|
|
@@ -439,4 +411,3 @@ export class PostProcessingHandler {
|
|
|
439
411
|
|
|
440
412
|
}
|
|
441
413
|
|
|
442
|
-
let effectsOrder: Array<Constructor<Effect | Pass>> | null = null;
|