Needle Engine

Changes between version 3.41.2-beta.2 and 3.41.2-beta.3
Files changed (3) hide show
  1. src/engine/engine_context.ts +4 -1
  2. src/engine/engine_element.ts +1 -1
  3. src/engine/engine_tonemapping.ts +70 -0
src/engine/engine_context.ts CHANGED
@@ -32,7 +32,8 @@
32
32
  import * as utils from "./engine_utils.js";
33
33
  import { delay, getParam } from './engine_utils.js';
34
34
  import type { INeedleXRSessionEventReceiver, NeedleXRSession } from './engine_xr.js';
35
- import { NeedleMenu, NeedleMenuElement } from './webcomponents/needle menu/needle-menu.js';
35
+ import { NeedleMenu } from './webcomponents/needle menu/needle-menu.js';
36
+ import { patchTonemapping } from './engine_tonemapping.js';
36
37
 
37
38
 
38
39
  const debug = utils.getParam("debugcontext");
@@ -1335,6 +1336,8 @@
1335
1336
  this._isRendering = true;
1336
1337
  this.renderRequiredTextures();
1337
1338
 
1339
+ if (this.renderer.toneMapping !== NoToneMapping)
1340
+ patchTonemapping(this);
1338
1341
 
1339
1342
  if (this.composer && !this.isInXR) {
1340
1343
  this.composer.render(this.time.deltaTime);
src/engine/engine_element.ts CHANGED
@@ -468,7 +468,7 @@
468
468
  if (!this._context?.renderer) return;
469
469
 
470
470
  // set tonemapping if configured
471
- const attribute = this.getAttribute("tone-mapping") as TonemappingAttributeOptions | null | undefined;
471
+ const attribute = this.getAttribute("tonemapping") || this.getAttribute("tone-mapping") as TonemappingAttributeOptions | null | undefined;
472
472
  switch (attribute?.toLowerCase()) {
473
473
  case "none":
474
474
  this._context.renderer.toneMapping = NoToneMapping;
src/engine/engine_tonemapping.ts ADDED
@@ -0,0 +1,70 @@
1
+ import { Mesh, ShaderChunk } from "three";
2
+ import type { Context } from "./engine_setup";
3
+
4
+ const neutralTonemappingStart = `vec3 NeutralToneMapping( vec3 color ) {`
5
+ const neutralTonemappingEnd = `return mix(color, vec3(1, 1, 1), g);
6
+ }`;
7
+
8
+ // From https://github.com/google/model-viewer/pull/4495
9
+ // Emmett's new 3D Commerce tone mapping function
10
+ const commerceToneMapping = `
11
+ float startCompression = 0.8;
12
+ float desaturation = 0.5;
13
+ // Patched tonemapping function
14
+ vec3 NeutralToneMapping( vec3 color ) {
15
+ color *= toneMappingExposure;
16
+
17
+ float d = 1. - startCompression;
18
+ // float peak = dot(color, vec3(0.299, 0.587, 0.114));
19
+ float peak = max(color.r, max(color.g, color.b));
20
+ if (peak < startCompression) return color;
21
+ float newPeak = 1. - d * d / (peak + d - startCompression);
22
+ float invPeak = 1. / peak;
23
+
24
+ float extraBrightness = dot(color * (1. - startCompression * invPeak), vec3(1, 1, 1));
25
+
26
+ color *= newPeak * invPeak;
27
+ float g = 1. - 3. / (desaturation * extraBrightness + 3.);
28
+ return mix(color, vec3(1, 1, 1), g);
29
+ }
30
+ `;
31
+
32
+
33
+
34
+
35
+ let patchedTonemapping = false;
36
+
37
+ export function patchTonemapping(_ctx?: Context) {
38
+ if (patchedTonemapping) return;
39
+ patchedTonemapping = true;
40
+
41
+ const neutralTonemappingStartIndex = ShaderChunk.tonemapping_pars_fragment.indexOf(neutralTonemappingStart);
42
+ const neutralTonemappingEndIndex = ShaderChunk.tonemapping_pars_fragment.indexOf(neutralTonemappingEnd, neutralTonemappingStartIndex);
43
+ if (neutralTonemappingStartIndex >= 0 && neutralTonemappingEndIndex >= 0) {
44
+ // get the old tonemapping
45
+ const existingNeutralTonemapping = ShaderChunk.tonemapping_pars_fragment.substring(neutralTonemappingStartIndex, neutralTonemappingEndIndex + neutralTonemappingEnd.length);
46
+ // replace it with the new one
47
+ ShaderChunk.tonemapping_pars_fragment = ShaderChunk.tonemapping_pars_fragment.replace(existingNeutralTonemapping, commerceToneMapping);
48
+ // console.log("Patched tonemapping\n", ShaderChunk.tonemapping_pars_fragment);
49
+ // if (_ctx) resetShaders(_ctx);
50
+ }
51
+ }
52
+ patchTonemapping();
53
+
54
+ // function resetShaders(ctx: Context) {
55
+ // const scene = ctx.scene;
56
+ // const gl = ctx.renderer;
57
+ // scene.traverse(object => {
58
+ // if ((object as Mesh).isMesh) {
59
+ // const mesh = object as Mesh;
60
+ // if (Array.isArray(mesh.material)) {
61
+ // mesh.material.forEach(mat => gl.properties.remove(mat));
62
+ // }
63
+ // else {
64
+ // gl.properties.remove(mesh.material);
65
+ // }
66
+ // }
67
+ // })
68
+ // if (gl.info?.programs)
69
+ // gl.info.programs.length = 0;
70
+ // }