@gnsx/three 0.186.4 → 0.186.6

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 (27) hide show
  1. package/build/three.webgpu.js +1563 -761
  2. package/build/three.webgpu.nodes.js +1563 -761
  3. package/examples/jsm/inspector/tabs/Settings.js +64 -3
  4. package/package.json +1 -1
  5. package/src/materials/nodes/MeshBasicNodeMaterial.js +13 -0
  6. package/src/materials/nodes/MeshLambertNodeMaterial.js +13 -0
  7. package/src/materials/nodes/MeshMatcapNodeMaterial.js +13 -0
  8. package/src/materials/nodes/MeshPhongNodeMaterial.js +13 -0
  9. package/src/materials/nodes/MeshPhysicalNodeMaterial.js +25 -0
  10. package/src/materials/nodes/MeshSSSNodeMaterial.js +15 -0
  11. package/src/materials/nodes/MeshStandardNodeMaterial.js +13 -0
  12. package/src/materials/nodes/MeshToonNodeMaterial.js +13 -0
  13. package/src/materials/nodes/NodeMaterial.js +107 -7
  14. package/src/materials/nodes/ShadowNodeMaterial.js +13 -0
  15. package/src/materials/nodes/VolumeNodeMaterial.js +13 -0
  16. package/src/nodes/display/BufferDebug.js +143 -0
  17. package/src/nodes/display/ComplexityDebug.js +188 -34
  18. package/src/nodes/display/DrawCallDebug.js +119 -0
  19. package/src/nodes/display/LightingDebug.js +109 -0
  20. package/src/renderers/common/Background.js +5 -2
  21. package/src/renderers/common/RenderObject.js +1 -1
  22. package/src/renderers/common/RenderPipeline.js +34 -5
  23. package/src/renderers/common/Renderer.js +11 -9
  24. package/src/renderers/common/nodes/NodeManager.js +13 -3
  25. package/src/renderers/webgl-fallback/WebGLBackend.js +4 -1
  26. package/src/renderers/webgl-fallback/utils/WebGLState.js +6 -2
  27. package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +4 -1
@@ -4,7 +4,8 @@
4
4
 
5
5
  import Node from '../core/Node.js';
6
6
  import { float, vec3, vec4, If, Fn } from '../tsl/TSLCore.js';
7
- import { fract, min, mix } from '../math/MathNode.js';
7
+ import { fract, min, mix, round } from '../math/MathNode.js';
8
+ import { DEBUG_VIEW_BUFFER } from './BufferDebug.js';
8
9
 
9
10
  /**
10
11
  * Shaded output. No complexity debug view.
@@ -27,30 +28,57 @@ export const DEBUG_VIEW_SHADER_COMPLEXITY = 'shaderComplexity';
27
28
  */
28
29
  export const DEBUG_VIEW_LIGHTING_COMPLEXITY = 'lightingComplexity';
29
30
 
31
+ /**
32
+ * Per-pixel overdraw heatmap. Each fragment that passes the depth test adds one.
33
+ * This is the portable stand-in for Unreal's 2×2 quad-coverage count.
34
+ *
35
+ * @type {string}
36
+ */
37
+ export const DEBUG_VIEW_QUAD_OVERDRAW = 'quadOverdraw';
38
+
39
+ /**
40
+ * Shader cost multiplied by the number of fragments that shaded the pixel, then the shader-complexity ramp.
41
+ * One opaque surface matches {@link DEBUG_VIEW_SHADER_COMPLEXITY}. Each extra overlapping fragment scales the summed cost.
42
+ * WebGPU cannot count wasted lanes in a 2×2 quad, so the count is fragments that pass the depth test.
43
+ *
44
+ * @type {string}
45
+ */
46
+ export const DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS = 'shaderComplexityAndQuads';
47
+
30
48
  /**
31
49
  * Proxy budget that fills the shader-complexity ramp.
32
- * A plain lit material stays in the green stops. Transmission plus several
33
- * texture samples climbs toward red.
50
+ * Unlit and basic stay green. Phong and standard move through yellow into red.
51
+ * Transmission plus several texture samples climbs toward white.
34
52
  *
35
53
  * @type {number}
36
54
  */
37
55
  export const DEFAULT_SHADER_COMPLEXITY_BUDGET = 800;
38
56
 
39
- /** @type {number} */
40
- export const SHADER_COMPLEXITY_BASE_UNLIT = 20;
57
+ /**
58
+ * Overlapping fragments that fill the quad-overdraw ramp.
59
+ * Unreal's stair reaches white at ten layers (`1 / 16` stored, then scaled by 1.6).
60
+ *
61
+ * @type {number}
62
+ */
63
+ export const DEFAULT_QUAD_OVERDRAW_BUDGET = 10;
41
64
 
42
- /** @type {number} */
43
- export const SHADER_COMPLEXITY_BASE_LIT = 40;
65
+ /**
66
+ * Storage scale for the overdraw count in {@link DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS}.
67
+ * Each fragment adds `1 / scale`, so an 8-bit target can hold this many layers.
68
+ * The output pass multiplies that channel back to a count. Half-float targets hold the same count exactly.
69
+ *
70
+ * @type {number}
71
+ */
72
+ export const SHADER_COMPLEXITY_QUAD_COUNT_SCALE = 16;
44
73
 
45
- /** @type {number} */
74
+ /**
75
+ * Added for each fragment texture sample. Lighting-model costs are returned
76
+ * directly by {@link NodeMaterial#getShaderComplexity}.
77
+ *
78
+ * @type {number}
79
+ */
46
80
  export const SHADER_COMPLEXITY_TEXTURE_COST = 16;
47
81
 
48
- /** @type {number} */
49
- export const SHADER_COMPLEXITY_FEATURE_COST = 30;
50
-
51
- /** @type {number} */
52
- export const SHADER_COMPLEXITY_TRANSMISSION_COST = 60;
53
-
54
82
  /**
55
83
  * Attenuation above this counts as a light that shades the pixel.
56
84
  *
@@ -83,6 +111,26 @@ const SHADER_COMPLEXITY_COLORS = [
83
111
  [ 1.0, 0.9, 0.9 ]
84
112
  ];
85
113
 
114
+ /**
115
+ * Unreal `QuadComplexityColors` from `BaseEngine.ini`.
116
+ * Sampled as a stair: one overlapping fragment is one stop.
117
+ *
118
+ * @type {Array<Array<number>>}
119
+ */
120
+ const QUAD_COMPLEXITY_COLORS = [
121
+ [ 0.0, 0.0, 0.0 ],
122
+ [ 0.0, 0.0, 0.4 ],
123
+ [ 0.0, 0.3, 1.0 ],
124
+ [ 0.0, 0.7, 0.4 ],
125
+ [ 0.0, 1.0, 0.0 ],
126
+ [ 0.8, 0.8, 0.0 ],
127
+ [ 1.0, 0.3, 0.0 ],
128
+ [ 0.7, 0.0, 0.0 ],
129
+ [ 0.5, 0.0, 0.5 ],
130
+ [ 0.7, 0.3, 0.7 ],
131
+ [ 1.0, 0.9, 0.9 ]
132
+ ];
133
+
86
134
  /**
87
135
  * Unreal `LightComplexityColors` from `BaseEngine.ini`.
88
136
  *
@@ -141,6 +189,73 @@ const colorizeLinear = ( cost, colors ) => {
141
189
  */
142
190
  export const colorizeLightComplexity = ( cost ) => colorizeLinear( cost, LIGHT_COMPLEXITY_COLORS );
143
191
 
192
+ /**
193
+ * Unreal's stair sample for quad overdraw (`CS_STAIR`).
194
+ * `cost` is the accumulated fragment count divided by the budget.
195
+ *
196
+ * @param {Node<float>} cost - Overdraw count divided by the budget.
197
+ * @return {Node<vec3>} The ramp color.
198
+ */
199
+ export const colorizeQuadOverdraw = ( cost ) => {
200
+
201
+ const steps = QUAD_COMPLEXITY_COLORS.length - 1;
202
+ const index = round( cost.clamp( 0, 1 ).mul( steps ) );
203
+
204
+ let color = rgb( QUAD_COMPLEXITY_COLORS, 0 );
205
+
206
+ for ( let i = 0; i <= steps; i ++ ) {
207
+
208
+ color = index.equal( float( i ) ).select( rgb( QUAD_COMPLEXITY_COLORS, i ), color );
209
+
210
+ }
211
+
212
+ return color;
213
+
214
+ };
215
+
216
+ /**
217
+ * Views that add a scalar per fragment, then colorize that sum in the output pass.
218
+ *
219
+ * @param {string} view - `renderer.debug.view`.
220
+ * @return {boolean} `true` when the view accumulates.
221
+ */
222
+ export function debugViewAccumulates( view ) {
223
+
224
+ return view === DEBUG_VIEW_SHADER_COMPLEXITY || view === DEBUG_VIEW_QUAD_OVERDRAW || view === DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS;
225
+
226
+ }
227
+
228
+ /**
229
+ * Whether a draw adds its scalar to the accumulating target instead of shading normally.
230
+ * Shadow passes, draws to the screen, and fullscreen quads draw normally. A post-processing
231
+ * quad copies the accumulated scene target, so its own cost must not replace it.
232
+ *
233
+ * @param {Renderer} renderer - The renderer.
234
+ * @param {Material} material - The material being drawn.
235
+ * @param {?Object3D} object - The object being drawn.
236
+ * @return {boolean} `true` when the draw accumulates.
237
+ */
238
+ export function debugDrawAccumulates( renderer, material, object ) {
239
+
240
+ return debugViewAccumulates( renderer.debug.view ) &&
241
+ material.isShadowPassMaterial !== true &&
242
+ renderer.isOutputTarget !== true &&
243
+ ( object === null || object === undefined || object.isQuadMesh !== true );
244
+
245
+ }
246
+
247
+ /**
248
+ * Views that output data or a heatmap, so tone mapping would distort them.
249
+ *
250
+ * @param {string} view - `renderer.debug.view`.
251
+ * @return {boolean} `true` when the view is drawn without tone mapping.
252
+ */
253
+ export function debugViewSkipsToneMapping( view ) {
254
+
255
+ return debugViewAccumulates( view ) || view === DEBUG_VIEW_LIGHTING_COMPLEXITY || view === DEBUG_VIEW_BUFFER;
256
+
257
+ }
258
+
144
259
  /**
145
260
  * Unreal's nonlinear shader-complexity ramp (`ColorizeComplexity`).
146
261
  * The first third of the normalized cost spreads across the first seven stops.
@@ -234,44 +349,83 @@ class ShaderComplexityRatioNode extends Node {
234
349
  }
235
350
 
236
351
  /**
237
- * Static fragment proxy for a material. Texture samples are counted later, during generation.
352
+ * Replaces the shaded color with `vec4(cost / budget, 0, 0, 1)`.
353
+ * The real graph is still generated so texture samples are counted first.
238
354
  *
239
- * @param {NodeMaterial} material - The material being built.
240
- * @return {number} The base cost, excluding texture samples.
355
+ * @param {Node} resultNode - The material's shaded output.
356
+ * @return {Node<vec4>} The complexity ratio in the red channel.
241
357
  */
242
- export function getShaderComplexityBase( material ) {
358
+ export function shaderComplexityOutput( resultNode ) {
243
359
 
244
- let cost = material.lights === true ? SHADER_COMPLEXITY_BASE_LIT : SHADER_COMPLEXITY_BASE_UNLIT;
360
+ // Generate the shaded graph first so texture samples are counted, then
361
+ // replace the color. A bypass of a value node emits a bare expression
362
+ // statement, which WGSL rejects.
363
+ const shaded = vec4( resultNode ).toVar();
245
364
 
246
- if ( material.useClearcoat === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
365
+ const ratio = new ShaderComplexityRatioNode();
247
366
 
248
- if ( material.useSheen === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
367
+ return vec4( ratio, 0, 0, 1 ).add( shaded.mul( 0 ) );
249
368
 
250
- if ( material.useIridescence === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
369
+ }
251
370
 
252
- if ( material.useAnisotropy === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
371
+ /**
372
+ * Same cost as {@link shaderComplexityOutput}, plus one overdraw step in green.
373
+ * The output pass multiplies red by the accumulated green count.
374
+ *
375
+ * @param {Node} resultNode - The material's shaded output.
376
+ * @return {Node<vec4>} Cost ratio in red, one scaled fragment in green.
377
+ */
378
+ export function shaderComplexityAndQuadsOutput( resultNode ) {
253
379
 
254
- if ( material.useTransmission === true ) cost += SHADER_COMPLEXITY_TRANSMISSION_COST;
380
+ const shaded = vec4( resultNode ).toVar();
381
+ const ratio = new ShaderComplexityRatioNode();
382
+ const step = float( 1 / SHADER_COMPLEXITY_QUAD_COUNT_SCALE );
255
383
 
256
- return cost;
384
+ return vec4( ratio, step, 0, 1 ).add( shaded.mul( 0 ) );
257
385
 
258
386
  }
259
387
 
260
388
  /**
261
- * Replaces the shaded color with `vec4(cost / budget, 0, 0, 1)`.
262
- * The real graph is still generated so texture samples are counted first.
389
+ * Reads `1 / quadOverdrawBudget` at code-generation time.
390
+ *
391
+ * @augments Node
392
+ */
393
+ class QuadOverdrawRatioNode extends Node {
394
+
395
+ static get type() {
396
+
397
+ return 'QuadOverdrawRatioNode';
398
+
399
+ }
400
+
401
+ constructor() {
402
+
403
+ super( 'float' );
404
+
405
+ }
406
+
407
+ generate( builder ) {
408
+
409
+ const budget = builder.renderer.debug.quadOverdrawBudget;
410
+ const safeBudget = ( typeof budget === 'number' && budget > 0 ) ? budget : 1;
411
+
412
+ return ( 1 / safeBudget ).toFixed( 6 );
413
+
414
+ }
415
+
416
+ }
417
+
418
+ /**
419
+ * Replaces the shaded color with `vec4(1 / budget, 0, 0, 1)`.
420
+ * The real graph is still generated so clipped fragments are not counted.
263
421
  *
264
422
  * @param {Node} resultNode - The material's shaded output.
265
- * @return {Node<vec4>} The complexity ratio in the red channel.
423
+ * @return {Node<vec4>} One overdraw step in the red channel.
266
424
  */
267
- export function shaderComplexityOutput( resultNode ) {
425
+ export function quadOverdrawOutput( resultNode ) {
268
426
 
269
- // Generate the shaded graph first so texture samples are counted, then
270
- // replace the color. A bypass of a value node emits a bare expression
271
- // statement, which WGSL rejects.
272
427
  const shaded = vec4( resultNode ).toVar();
273
-
274
- const ratio = new ShaderComplexityRatioNode();
428
+ const ratio = new QuadOverdrawRatioNode();
275
429
 
276
430
  return vec4( ratio, 0, 0, 1 ).add( shaded.mul( 0 ) );
277
431
 
@@ -0,0 +1,119 @@
1
+ // WITH_GENESYS
2
+ // Draw call coloration. One stable diffuse color per submitted draw, then lit.
3
+ // !WITH_GENESYS
4
+
5
+ import { Color } from '../../math/Color.js';
6
+ import { diffuseColor, diffuseContribution, metalness, roughness, specularColor, specularColorBlended, specularF90, clearcoat, sheen, iridescence, anisotropy, transmission, retroreflectivity } from '../core/PropertyNode.js';
7
+ import { uniform } from '../core/UniformNode.js';
8
+ import { float, vec3 } from '../tsl/TSLCore.js';
9
+
10
+ /**
11
+ * Draw call coloration. Each submitted draw gets one diffuse color. An instanced mesh is one draw.
12
+ * The color is lit, so lights and shadows still show. Unlit materials stay flat.
13
+ *
14
+ * @type {string}
15
+ */
16
+ export const DEBUG_VIEW_DRAW_CALL = 'drawCall';
17
+
18
+ /**
19
+ * Golden-ratio step. Sequential object ids land on opposite sides of the hue wheel.
20
+ *
21
+ * @type {number}
22
+ */
23
+ const DRAW_CALL_HUE_STEP = 0.618033988749895;
24
+
25
+ /**
26
+ * Saturation of a draw color. High enough that neighboring hues stay apart after lighting.
27
+ *
28
+ * @type {number}
29
+ */
30
+ const DRAW_CALL_SATURATION = 0.85;
31
+
32
+ /**
33
+ * Value of a draw color. Lighting and shadows darken it from here.
34
+ *
35
+ * @type {number}
36
+ */
37
+ const DRAW_CALL_VALUE = 0.92;
38
+
39
+ /**
40
+ * Writes a saturated color for this draw into `color`.
41
+ * Hue walks the wheel by the golden ratio, so consecutive objects do not share a tint.
42
+ * Instances of one mesh share the object id, so they share the color.
43
+ *
44
+ * @param {Color} color - The uniform color, updated in place.
45
+ * @param {Object3D} object - The drawn object.
46
+ * @param {Material} material - The drawn material. A multi-material group is its own draw.
47
+ * @return {Color} `color`.
48
+ */
49
+ function writeDrawCallColor( color, object, material ) {
50
+
51
+ const hue = ( ( object.id + material.id * 64 ) * DRAW_CALL_HUE_STEP ) % 1;
52
+ const s = DRAW_CALL_SATURATION;
53
+ const v = DRAW_CALL_VALUE;
54
+ const sector = hue * 6;
55
+ const i = Math.floor( sector );
56
+ const f = sector - i;
57
+ const p = v * ( 1 - s );
58
+ const q = v * ( 1 - f * s );
59
+ const t = v * ( 1 - ( 1 - f ) * s );
60
+
61
+ switch ( i % 6 ) {
62
+
63
+ case 0: return color.setRGB( v, t, p );
64
+ case 1: return color.setRGB( q, v, p );
65
+ case 2: return color.setRGB( p, v, t );
66
+ case 3: return color.setRGB( p, q, v );
67
+ case 4: return color.setRGB( t, p, v );
68
+ default: return color.setRGB( v, p, q );
69
+
70
+ }
71
+
72
+ }
73
+
74
+ /**
75
+ * Replaces albedo with the draw color after the material has written its channels.
76
+ * Lit materials become a matte dielectric in that color. Unlit materials become flat.
77
+ * The color is an object uniform, so meshes that share a material still differ.
78
+ *
79
+ * @param {NodeBuilder} builder - The current node builder.
80
+ * @param {NodeMaterial} material - The node material being set up. `builder.material` can be the
81
+ * source material of a converted one, such as a glTF `MeshStandardMaterial`.
82
+ */
83
+ export function applyDrawCallDebug( builder, material ) {
84
+
85
+ const colorNode = uniform( new Color() ).onObjectUpdate( ( { object, material: drawnMaterial }, self ) => {
86
+
87
+ if ( object === null || drawnMaterial === null ) return;
88
+
89
+ return writeDrawCallColor( self.value, object, drawnMaterial );
90
+
91
+ } );
92
+
93
+ diffuseColor.rgb.assign( colorNode );
94
+
95
+ if ( material.lights !== true ) return;
96
+
97
+ metalness.assign( float( 0 ) );
98
+ diffuseContribution.assign( colorNode );
99
+ roughness.assign( float( 1 ) );
100
+ specularColor.assign( vec3( 0 ) );
101
+ specularColorBlended.assign( vec3( 0 ) );
102
+ specularF90.assign( float( 0 ) );
103
+
104
+ // Ambient occlusion belonged to the original material.
105
+ builder.context.ambientOcclusion = null;
106
+
107
+ if ( material.useClearcoat === true ) clearcoat.assign( float( 0 ) );
108
+
109
+ if ( material.useSheen === true ) sheen.assign( vec3( 0 ) );
110
+
111
+ if ( material.useIridescence === true ) iridescence.assign( float( 0 ) );
112
+
113
+ if ( material.useAnisotropy === true ) anisotropy.assign( float( 0 ) );
114
+
115
+ if ( material.useTransmission === true ) transmission.assign( float( 0 ) );
116
+
117
+ if ( material.useRetroreflection === true ) retroreflectivity.assign( float( 0 ) );
118
+
119
+ }
@@ -0,0 +1,109 @@
1
+ // WITH_GENESYS
2
+ // Lighting only and detail lighting. Both replace the shaded albedo with a flat gray.
3
+ // !WITH_GENESYS
4
+
5
+ import { diffuseColor, diffuseContribution, metalness, roughness, specularColor, specularColorBlended, specularF90, clearcoat, sheen, iridescence, anisotropy, transmission, retroreflectivity } from '../core/PropertyNode.js';
6
+ import { normalViewGeometry } from '../accessors/Normal.js';
7
+ import { negateOnBackSide } from './FrontFacingNode.js';
8
+ import { float, vec3, Fn } from '../tsl/TSLCore.js';
9
+
10
+ /**
11
+ * Lighting only. Flat gray, no specular, geometry normals. Material detail is dropped.
12
+ *
13
+ * @type {string}
14
+ */
15
+ export const DEBUG_VIEW_LIGHTING_ONLY = 'lightingOnly';
16
+
17
+ /**
18
+ * Detail lighting. Flat gray and a constant specular, with normal maps and roughness kept.
19
+ *
20
+ * @type {string}
21
+ */
22
+ export const DEBUG_VIEW_DETAIL_LIGHTING = 'detailLighting';
23
+
24
+ /**
25
+ * Neutral albedo shared by both views. Matches Unreal's `LightingOnlyBrightness`.
26
+ *
27
+ * @type {number}
28
+ */
29
+ const LIGHTING_ONLY_BRIGHTNESS = 0.3;
30
+
31
+ /**
32
+ * Specular color used by detail lighting. Unreal writes `0.1` into the specular override.
33
+ *
34
+ * @type {number}
35
+ */
36
+ const DETAIL_LIGHTING_SPECULAR = 0.1;
37
+
38
+ /**
39
+ * Geometry normal for lighting only, so normal maps and `normalNode` do not shade the surface.
40
+ *
41
+ * @type {Node<vec3>}
42
+ */
43
+ export const lightingOnlyNormal = /*@__PURE__*/ Fn( ( builder ) => {
44
+
45
+ let node = normalViewGeometry;
46
+
47
+ if ( builder.isFlatShading() !== true ) {
48
+
49
+ node = negateOnBackSide( node );
50
+
51
+ }
52
+
53
+ return node;
54
+
55
+ }, 'vec3' );
56
+
57
+ /**
58
+ * Replaces albedo and specular after the material has written its channels.
59
+ * Lit materials become a dielectric gray. Unlit materials become flat gray.
60
+ *
61
+ * @param {NodeBuilder} builder - The current node builder.
62
+ * @param {NodeMaterial} material - The node material being set up. `builder.material` can be the
63
+ * source material of a converted one, such as a glTF `MeshStandardMaterial`.
64
+ * @param {string} view - `renderer.debug.view`.
65
+ */
66
+ export function applyLightingDebug( builder, material, view ) {
67
+
68
+ const gray = vec3( LIGHTING_ONLY_BRIGHTNESS );
69
+
70
+ diffuseColor.rgb.assign( gray );
71
+
72
+ if ( material.lights !== true ) return;
73
+
74
+ metalness.assign( float( 0 ) );
75
+ diffuseContribution.assign( gray );
76
+
77
+ if ( view === DEBUG_VIEW_LIGHTING_ONLY ) {
78
+
79
+ roughness.assign( float( 1 ) );
80
+ specularColor.assign( vec3( 0 ) );
81
+ specularColorBlended.assign( vec3( 0 ) );
82
+ specularF90.assign( float( 0 ) );
83
+
84
+ // Ambient occlusion was stored for the lighting model. Lighting only has no material.
85
+ builder.context.ambientOcclusion = null;
86
+
87
+ if ( material.useClearcoat === true ) clearcoat.assign( float( 0 ) );
88
+
89
+ if ( material.useSheen === true ) sheen.assign( vec3( 0 ) );
90
+
91
+ if ( material.useIridescence === true ) iridescence.assign( float( 0 ) );
92
+
93
+ if ( material.useAnisotropy === true ) anisotropy.assign( float( 0 ) );
94
+
95
+ if ( material.useTransmission === true ) transmission.assign( float( 0 ) );
96
+
97
+ if ( material.useRetroreflection === true ) retroreflectivity.assign( float( 0 ) );
98
+
99
+ } else {
100
+
101
+ const specular = vec3( DETAIL_LIGHTING_SPECULAR );
102
+
103
+ specularColor.assign( specular );
104
+ specularColorBlended.assign( specular );
105
+ specularF90.assign( float( 1 ) );
106
+
107
+ }
108
+
109
+ }
@@ -7,6 +7,9 @@ import { Mesh } from '../../objects/Mesh.js';
7
7
  import { SphereGeometry } from '../../geometries/SphereGeometry.js';
8
8
  import { BackSide } from '../../constants.js';
9
9
  import { error } from '../../utils.js';
10
+ // WITH_GENESYS
11
+ import { debugViewAccumulates } from '../../nodes/display/ComplexityDebug.js';
12
+ // !WITH_GENESYS
10
13
 
11
14
  const _clearColor = /*@__PURE__*/ new Color4();
12
15
 
@@ -193,7 +196,7 @@ class Background extends DataMap {
193
196
 
194
197
  // WITH_GENESYS
195
198
  // The background is depth-tested off, so additive shader complexity would add it to every pixel.
196
- if ( renderer.debug.view !== 'shaderComplexity' ) {
199
+ if ( debugViewAccumulates( renderer.debug.view ) !== true ) {
197
200
 
198
201
  renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
199
202
 
@@ -224,7 +227,7 @@ class Background extends DataMap {
224
227
  //
225
228
 
226
229
  // WITH_GENESYS
227
- if ( renderer.debug.view === 'shaderComplexity' ) {
230
+ if ( debugViewAccumulates( renderer.debug.view ) ) {
228
231
 
229
232
  _clearColor.set( 0, 0, 0, 1 );
230
233
 
@@ -952,7 +952,7 @@ class RenderObject {
952
952
 
953
953
  // WITH_GENESYS
954
954
  // hash() xors each argument as a number, so a view name has to be hashed first.
955
- cacheKey = hash( cacheKey, hashString( this.renderer.debug.view ), this.renderer.debug.shaderComplexityBudget );
955
+ cacheKey = hash( cacheKey, hashString( this.renderer.debug.view ), hashString( this.renderer.debug.buffer ), this.renderer.debug.shaderComplexityBudget, this.renderer.debug.quadOverdrawBudget );
956
956
  // !WITH_GENESYS
957
957
 
958
958
  return cacheKey;
@@ -4,6 +4,9 @@ import { vec4, renderOutput, context } from '../../nodes/TSL.js';
4
4
  import { NoToneMapping } from '../../constants.js';
5
5
  import QuadMesh from '../../renderers/common/QuadMesh.js';
6
6
  import { warnOnce } from '../../utils.js';
7
+ // WITH_GENESYS
8
+ import { debugViewAccumulates, debugViewSkipsToneMapping } from '../../nodes/display/ComplexityDebug.js';
9
+ // !WITH_GENESYS
7
10
 
8
11
  /**
9
12
  * This module is responsible to manage the rendering pipeline setups in apps.
@@ -139,7 +142,12 @@ class RenderPipeline {
139
142
  const outputColorSpace = renderer.outputColorSpace;
140
143
 
141
144
  renderer.toneMapping = NoToneMapping;
142
- renderer.outputColorSpace = ColorManagement.workingColorSpace;
145
+
146
+ // WITH_GENESYS
147
+ // Accumulating views colorize in the renderer's output pass, which must still encode the color space.
148
+ if ( debugViewAccumulates( renderer.debug.view ) !== true ) renderer.outputColorSpace = ColorManagement.workingColorSpace;
149
+ // !WITH_GENESYS
150
+ // renderer.outputColorSpace = ColorManagement.workingColorSpace;
143
151
 
144
152
  //
145
153
 
@@ -215,19 +223,40 @@ class RenderPipeline {
215
223
  */
216
224
  _update() {
217
225
 
218
- if ( this._toneMapping !== this.renderer.toneMapping ) {
226
+ // WITH_GENESYS
227
+ // Debug views drawn without tone mapping must match the direct path. An accumulating
228
+ // view copies the raw ratio here, and the renderer's output pass colorizes it.
229
+ const view = this.renderer.debug.view;
230
+ const toneMapping = debugViewSkipsToneMapping( view ) ? NoToneMapping : this.renderer.toneMapping;
231
+ const outputColorSpace = debugViewAccumulates( view ) ? ColorManagement.workingColorSpace : this.renderer.outputColorSpace;
232
+
233
+ if ( this._toneMapping !== toneMapping ) {
219
234
 
220
- this._toneMapping = this.renderer.toneMapping;
235
+ this._toneMapping = toneMapping;
221
236
  this.needsUpdate = true;
222
237
 
223
238
  }
224
239
 
225
- if ( this._outputColorSpace !== this.renderer.outputColorSpace ) {
240
+ if ( this._outputColorSpace !== outputColorSpace ) {
226
241
 
227
- this._outputColorSpace = this.renderer.outputColorSpace;
242
+ this._outputColorSpace = outputColorSpace;
228
243
  this.needsUpdate = true;
229
244
 
230
245
  }
246
+ // !WITH_GENESYS
247
+ // if ( this._toneMapping !== this.renderer.toneMapping ) {
248
+ //
249
+ // this._toneMapping = this.renderer.toneMapping;
250
+ // this.needsUpdate = true;
251
+ //
252
+ // }
253
+ //
254
+ // if ( this._outputColorSpace !== this.renderer.outputColorSpace ) {
255
+ //
256
+ // this._outputColorSpace = this.renderer.outputColorSpace;
257
+ // this.needsUpdate = true;
258
+ //
259
+ // }
231
260
 
232
261
  if ( this.needsUpdate === true ) {
233
262
 
@@ -37,7 +37,8 @@ import { float, vec3, vec4, Fn } from '../../nodes/tsl/TSLCore.js';
37
37
  import { detachRendererReference } from '../../nodes/accessors/RendererReferenceNode.js';
38
38
  import { toneMappingExposure } from '../../nodes/display/ToneMappingNode.js';
39
39
  import { ProfilerService } from '../../profiler/ProfilerService.js';
40
- import { DEBUG_VIEW_LIGHTING_COMPLEXITY, DEBUG_VIEW_NONE, DEBUG_VIEW_SHADER_COMPLEXITY, DEFAULT_SHADER_COMPLEXITY_BUDGET } from '../../nodes/display/ComplexityDebug.js';
40
+ import { DEBUG_VIEW_NONE, DEFAULT_QUAD_OVERDRAW_BUDGET, DEFAULT_SHADER_COMPLEXITY_BUDGET, debugViewAccumulates, debugViewSkipsToneMapping } from '../../nodes/display/ComplexityDebug.js';
41
+ import { DEFAULT_BUFFER } from '../../nodes/display/BufferDebug.js';
41
42
  // !WITH_GENESYS
42
43
  import { reference } from '../../nodes/accessors/ReferenceNode.js';
43
44
  import { highpModelNormalViewMatrix, highpModelViewMatrix } from '../../nodes/accessors/ModelNode.js';
@@ -744,8 +745,10 @@ class Renderer {
744
745
  * @property {?Function} onNodeBuilderCreated - A callback function that is executed after a node builder has been created and before it is built.
745
746
  * @property {?Function} onShaderError - A callback function that is executed when a shader error happens. Only supported with WebGL 2 right now.
746
747
  * @property {Function} getShaderAsync - Allows the get the raw shader code for the given scene, camera and 3D object.
747
- * @property {string} view - Debug view. `shaderComplexity` and `lightingComplexity` replace the shaded color with a heatmap.
748
+ * @property {string} view - Debug view. `shaderComplexity`, `lightingComplexity`, `quadOverdraw`, and `shaderComplexityAndQuads` replace the shaded color with a heatmap. `shaderComplexityAndQuads` multiplies the shader cost by the overdraw count. `bufferVisualization` shows one material channel. `lightingOnly` and `detailLighting` light a flat gray surface. `drawCall` lights each submitted draw with its own diffuse color.
749
+ * @property {string} buffer - Channel drawn by `bufferVisualization`: `baseColor`, `worldNormal`, `roughness`, `metallic`, `ambientOcclusion`, or `emissive`.
748
750
  * @property {number} shaderComplexityBudget - Proxy budget that fills the shader-complexity ramp.
751
+ * @property {number} quadOverdrawBudget - Overlapping fragments that fill the quad-overdraw ramp.
749
752
  */
750
753
 
751
754
  /**
@@ -762,7 +765,9 @@ class Renderer {
762
765
  onShaderError: null,
763
766
  // WITH_GENESYS
764
767
  view: DEBUG_VIEW_NONE,
768
+ buffer: DEFAULT_BUFFER,
765
769
  shaderComplexityBudget: DEFAULT_SHADER_COMPLEXITY_BUDGET,
770
+ quadOverdrawBudget: DEFAULT_QUAD_OVERDRAW_BUDGET,
766
771
  // !WITH_GENESYS
767
772
  getShaderAsync: async ( scene, camera, object ) => {
768
773
 
@@ -2699,10 +2704,9 @@ class Renderer {
2699
2704
  const useColorSpace = this.currentColorSpace !== ColorManagement.workingColorSpace;
2700
2705
 
2701
2706
  // WITH_GENESYS
2702
- // Shader complexity writes a scalar into an intermediate target, then the output pass colorizes it.
2703
- const shaderComplexity = this.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY;
2704
-
2705
- return useToneMapping || useColorSpace || shaderComplexity;
2707
+ // Accumulating views write a scalar into an intermediate target, then the output pass colorizes it.
2708
+ // Only screen output needs one. An off-screen target, such as a scene pass, already is the target.
2709
+ return useToneMapping || useColorSpace || ( debugViewAccumulates( this.debug.view ) && this.isOutputTarget );
2706
2710
  // !WITH_GENESYS
2707
2711
  // return useToneMapping || useColorSpace;
2708
2712
 
@@ -2756,9 +2760,7 @@ class Renderer {
2756
2760
  get currentToneMapping() {
2757
2761
 
2758
2762
  // WITH_GENESYS
2759
- const view = this.debug.view;
2760
-
2761
- if ( view === DEBUG_VIEW_SHADER_COMPLEXITY || view === DEBUG_VIEW_LIGHTING_COMPLEXITY ) {
2763
+ if ( debugViewSkipsToneMapping( this.debug.view ) ) {
2762
2764
 
2763
2765
  return NoToneMapping;
2764
2766