@gnsx/three 0.186.4 → 0.186.5

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.
@@ -4,7 +4,7 @@
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
8
 
9
9
  /**
10
10
  * Shaded output. No complexity debug view.
@@ -27,30 +27,57 @@ export const DEBUG_VIEW_SHADER_COMPLEXITY = 'shaderComplexity';
27
27
  */
28
28
  export const DEBUG_VIEW_LIGHTING_COMPLEXITY = 'lightingComplexity';
29
29
 
30
+ /**
31
+ * Per-pixel overdraw heatmap. Each fragment that passes the depth test adds one.
32
+ * This is the portable stand-in for Unreal's 2×2 quad-coverage count.
33
+ *
34
+ * @type {string}
35
+ */
36
+ export const DEBUG_VIEW_QUAD_OVERDRAW = 'quadOverdraw';
37
+
38
+ /**
39
+ * Shader cost multiplied by the number of fragments that shaded the pixel, then the shader-complexity ramp.
40
+ * One opaque surface matches {@link DEBUG_VIEW_SHADER_COMPLEXITY}. Each extra overlapping fragment scales the summed cost.
41
+ * WebGPU cannot count wasted lanes in a 2×2 quad, so the count is fragments that pass the depth test.
42
+ *
43
+ * @type {string}
44
+ */
45
+ export const DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS = 'shaderComplexityAndQuads';
46
+
30
47
  /**
31
48
  * 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.
49
+ * Unlit and basic stay green. Phong and standard move through yellow into red.
50
+ * Transmission plus several texture samples climbs toward white.
34
51
  *
35
52
  * @type {number}
36
53
  */
37
54
  export const DEFAULT_SHADER_COMPLEXITY_BUDGET = 800;
38
55
 
39
- /** @type {number} */
40
- export const SHADER_COMPLEXITY_BASE_UNLIT = 20;
56
+ /**
57
+ * Overlapping fragments that fill the quad-overdraw ramp.
58
+ * Unreal's stair reaches white at ten layers (`1 / 16` stored, then scaled by 1.6).
59
+ *
60
+ * @type {number}
61
+ */
62
+ export const DEFAULT_QUAD_OVERDRAW_BUDGET = 10;
41
63
 
42
- /** @type {number} */
43
- export const SHADER_COMPLEXITY_BASE_LIT = 40;
64
+ /**
65
+ * Storage scale for the overdraw count in {@link DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS}.
66
+ * Each fragment adds `1 / scale`, so an 8-bit target can hold this many layers.
67
+ * The output pass multiplies that channel back to a count. Half-float targets hold the same count exactly.
68
+ *
69
+ * @type {number}
70
+ */
71
+ export const SHADER_COMPLEXITY_QUAD_COUNT_SCALE = 16;
44
72
 
45
- /** @type {number} */
73
+ /**
74
+ * Added for each fragment texture sample. Lighting-model costs are returned
75
+ * directly by {@link NodeMaterial#getShaderComplexity}.
76
+ *
77
+ * @type {number}
78
+ */
46
79
  export const SHADER_COMPLEXITY_TEXTURE_COST = 16;
47
80
 
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
81
  /**
55
82
  * Attenuation above this counts as a light that shades the pixel.
56
83
  *
@@ -83,6 +110,26 @@ const SHADER_COMPLEXITY_COLORS = [
83
110
  [ 1.0, 0.9, 0.9 ]
84
111
  ];
85
112
 
113
+ /**
114
+ * Unreal `QuadComplexityColors` from `BaseEngine.ini`.
115
+ * Sampled as a stair: one overlapping fragment is one stop.
116
+ *
117
+ * @type {Array<Array<number>>}
118
+ */
119
+ const QUAD_COMPLEXITY_COLORS = [
120
+ [ 0.0, 0.0, 0.0 ],
121
+ [ 0.0, 0.0, 0.4 ],
122
+ [ 0.0, 0.3, 1.0 ],
123
+ [ 0.0, 0.7, 0.4 ],
124
+ [ 0.0, 1.0, 0.0 ],
125
+ [ 0.8, 0.8, 0.0 ],
126
+ [ 1.0, 0.3, 0.0 ],
127
+ [ 0.7, 0.0, 0.0 ],
128
+ [ 0.5, 0.0, 0.5 ],
129
+ [ 0.7, 0.3, 0.7 ],
130
+ [ 1.0, 0.9, 0.9 ]
131
+ ];
132
+
86
133
  /**
87
134
  * Unreal `LightComplexityColors` from `BaseEngine.ini`.
88
135
  *
@@ -141,6 +188,42 @@ const colorizeLinear = ( cost, colors ) => {
141
188
  */
142
189
  export const colorizeLightComplexity = ( cost ) => colorizeLinear( cost, LIGHT_COMPLEXITY_COLORS );
143
190
 
191
+ /**
192
+ * Unreal's stair sample for quad overdraw (`CS_STAIR`).
193
+ * `cost` is the accumulated fragment count divided by the budget.
194
+ *
195
+ * @param {Node<float>} cost - Overdraw count divided by the budget.
196
+ * @return {Node<vec3>} The ramp color.
197
+ */
198
+ export const colorizeQuadOverdraw = ( cost ) => {
199
+
200
+ const steps = QUAD_COMPLEXITY_COLORS.length - 1;
201
+ const index = round( cost.clamp( 0, 1 ).mul( steps ) );
202
+
203
+ let color = rgb( QUAD_COMPLEXITY_COLORS, 0 );
204
+
205
+ for ( let i = 0; i <= steps; i ++ ) {
206
+
207
+ color = index.equal( float( i ) ).select( rgb( QUAD_COMPLEXITY_COLORS, i ), color );
208
+
209
+ }
210
+
211
+ return color;
212
+
213
+ };
214
+
215
+ /**
216
+ * Views that add a scalar per fragment, then colorize that sum in the output pass.
217
+ *
218
+ * @param {string} view - `renderer.debug.view`.
219
+ * @return {boolean} `true` when the view accumulates.
220
+ */
221
+ export function debugViewAccumulates( view ) {
222
+
223
+ return view === DEBUG_VIEW_SHADER_COMPLEXITY || view === DEBUG_VIEW_QUAD_OVERDRAW || view === DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS;
224
+
225
+ }
226
+
144
227
  /**
145
228
  * Unreal's nonlinear shader-complexity ramp (`ColorizeComplexity`).
146
229
  * The first third of the normalized cost spreads across the first seven stops.
@@ -234,44 +317,83 @@ class ShaderComplexityRatioNode extends Node {
234
317
  }
235
318
 
236
319
  /**
237
- * Static fragment proxy for a material. Texture samples are counted later, during generation.
320
+ * Replaces the shaded color with `vec4(cost / budget, 0, 0, 1)`.
321
+ * The real graph is still generated so texture samples are counted first.
238
322
  *
239
- * @param {NodeMaterial} material - The material being built.
240
- * @return {number} The base cost, excluding texture samples.
323
+ * @param {Node} resultNode - The material's shaded output.
324
+ * @return {Node<vec4>} The complexity ratio in the red channel.
241
325
  */
242
- export function getShaderComplexityBase( material ) {
326
+ export function shaderComplexityOutput( resultNode ) {
243
327
 
244
- let cost = material.lights === true ? SHADER_COMPLEXITY_BASE_LIT : SHADER_COMPLEXITY_BASE_UNLIT;
328
+ // Generate the shaded graph first so texture samples are counted, then
329
+ // replace the color. A bypass of a value node emits a bare expression
330
+ // statement, which WGSL rejects.
331
+ const shaded = vec4( resultNode ).toVar();
245
332
 
246
- if ( material.useClearcoat === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
333
+ const ratio = new ShaderComplexityRatioNode();
247
334
 
248
- if ( material.useSheen === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
335
+ return vec4( ratio, 0, 0, 1 ).add( shaded.mul( 0 ) );
249
336
 
250
- if ( material.useIridescence === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
337
+ }
251
338
 
252
- if ( material.useAnisotropy === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
339
+ /**
340
+ * Same cost as {@link shaderComplexityOutput}, plus one overdraw step in green.
341
+ * The output pass multiplies red by the accumulated green count.
342
+ *
343
+ * @param {Node} resultNode - The material's shaded output.
344
+ * @return {Node<vec4>} Cost ratio in red, one scaled fragment in green.
345
+ */
346
+ export function shaderComplexityAndQuadsOutput( resultNode ) {
253
347
 
254
- if ( material.useTransmission === true ) cost += SHADER_COMPLEXITY_TRANSMISSION_COST;
348
+ const shaded = vec4( resultNode ).toVar();
349
+ const ratio = new ShaderComplexityRatioNode();
350
+ const step = float( 1 / SHADER_COMPLEXITY_QUAD_COUNT_SCALE );
255
351
 
256
- return cost;
352
+ return vec4( ratio, step, 0, 1 ).add( shaded.mul( 0 ) );
257
353
 
258
354
  }
259
355
 
260
356
  /**
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.
357
+ * Reads `1 / quadOverdrawBudget` at code-generation time.
358
+ *
359
+ * @augments Node
360
+ */
361
+ class QuadOverdrawRatioNode extends Node {
362
+
363
+ static get type() {
364
+
365
+ return 'QuadOverdrawRatioNode';
366
+
367
+ }
368
+
369
+ constructor() {
370
+
371
+ super( 'float' );
372
+
373
+ }
374
+
375
+ generate( builder ) {
376
+
377
+ const budget = builder.renderer.debug.quadOverdrawBudget;
378
+ const safeBudget = ( typeof budget === 'number' && budget > 0 ) ? budget : 1;
379
+
380
+ return ( 1 / safeBudget ).toFixed( 6 );
381
+
382
+ }
383
+
384
+ }
385
+
386
+ /**
387
+ * Replaces the shaded color with `vec4(1 / budget, 0, 0, 1)`.
388
+ * The real graph is still generated so clipped fragments are not counted.
263
389
  *
264
390
  * @param {Node} resultNode - The material's shaded output.
265
- * @return {Node<vec4>} The complexity ratio in the red channel.
391
+ * @return {Node<vec4>} One overdraw step in the red channel.
266
392
  */
267
- export function shaderComplexityOutput( resultNode ) {
393
+ export function quadOverdrawOutput( resultNode ) {
268
394
 
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
395
  const shaded = vec4( resultNode ).toVar();
273
-
274
- const ratio = new ShaderComplexityRatioNode();
396
+ const ratio = new QuadOverdrawRatioNode();
275
397
 
276
398
  return vec4( ratio, 0, 0, 1 ).add( shaded.mul( 0 ) );
277
399
 
@@ -0,0 +1,118 @@
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
+ */
81
+ export function applyDrawCallDebug( builder ) {
82
+
83
+ const material = builder.material;
84
+ const colorNode = uniform( new Color() ).onObjectUpdate( ( { object, material: drawnMaterial }, self ) => {
85
+
86
+ if ( object === null || drawnMaterial === null ) return;
87
+
88
+ return writeDrawCallColor( self.value, object, drawnMaterial );
89
+
90
+ } );
91
+
92
+ diffuseColor.rgb.assign( colorNode );
93
+
94
+ if ( material.lights !== true ) return;
95
+
96
+ metalness.assign( float( 0 ) );
97
+ diffuseContribution.assign( colorNode );
98
+ roughness.assign( float( 1 ) );
99
+ specularColor.assign( vec3( 0 ) );
100
+ specularColorBlended.assign( vec3( 0 ) );
101
+ specularF90.assign( float( 0 ) );
102
+
103
+ // Ambient occlusion belonged to the original material.
104
+ builder.context.ambientOcclusion = null;
105
+
106
+ if ( material.useClearcoat === true ) clearcoat.assign( float( 0 ) );
107
+
108
+ if ( material.useSheen === true ) sheen.assign( vec3( 0 ) );
109
+
110
+ if ( material.useIridescence === true ) iridescence.assign( float( 0 ) );
111
+
112
+ if ( material.useAnisotropy === true ) anisotropy.assign( float( 0 ) );
113
+
114
+ if ( material.useTransmission === true ) transmission.assign( float( 0 ) );
115
+
116
+ if ( material.useRetroreflection === true ) retroreflectivity.assign( float( 0 ) );
117
+
118
+ }
@@ -0,0 +1,108 @@
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 {string} view - `renderer.debug.view`.
63
+ */
64
+ export function applyLightingDebug( builder, view ) {
65
+
66
+ const material = builder.material;
67
+ const gray = vec3( LIGHTING_ONLY_BRIGHTNESS );
68
+
69
+ diffuseColor.rgb.assign( gray );
70
+
71
+ if ( material.lights !== true ) return;
72
+
73
+ metalness.assign( float( 0 ) );
74
+ diffuseContribution.assign( gray );
75
+
76
+ if ( view === DEBUG_VIEW_LIGHTING_ONLY ) {
77
+
78
+ roughness.assign( float( 1 ) );
79
+ specularColor.assign( vec3( 0 ) );
80
+ specularColorBlended.assign( vec3( 0 ) );
81
+ specularF90.assign( float( 0 ) );
82
+
83
+ // Ambient occlusion was stored for the lighting model. Lighting only has no material.
84
+ builder.context.ambientOcclusion = null;
85
+
86
+ if ( material.useClearcoat === true ) clearcoat.assign( float( 0 ) );
87
+
88
+ if ( material.useSheen === true ) sheen.assign( vec3( 0 ) );
89
+
90
+ if ( material.useIridescence === true ) iridescence.assign( float( 0 ) );
91
+
92
+ if ( material.useAnisotropy === true ) anisotropy.assign( float( 0 ) );
93
+
94
+ if ( material.useTransmission === true ) transmission.assign( float( 0 ) );
95
+
96
+ if ( material.useRetroreflection === true ) retroreflectivity.assign( float( 0 ) );
97
+
98
+ } else {
99
+
100
+ const specular = vec3( DETAIL_LIGHTING_SPECULAR );
101
+
102
+ specularColor.assign( specular );
103
+ specularColorBlended.assign( specular );
104
+ specularF90.assign( float( 1 ) );
105
+
106
+ }
107
+
108
+ }
@@ -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;
@@ -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_LIGHTING_COMPLEXITY, DEBUG_VIEW_NONE, DEBUG_VIEW_QUAD_OVERDRAW, DEBUG_VIEW_SHADER_COMPLEXITY, DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS, DEFAULT_QUAD_OVERDRAW_BUDGET, DEFAULT_SHADER_COMPLEXITY_BUDGET, debugViewAccumulates } from '../../nodes/display/ComplexityDebug.js';
41
+ import { DEBUG_VIEW_BUFFER, 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,8 @@ 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
+ return useToneMapping || useColorSpace || debugViewAccumulates( this.debug.view );
2706
2709
  // !WITH_GENESYS
2707
2710
  // return useToneMapping || useColorSpace;
2708
2711
 
@@ -2758,7 +2761,7 @@ class Renderer {
2758
2761
  // WITH_GENESYS
2759
2762
  const view = this.debug.view;
2760
2763
 
2761
- if ( view === DEBUG_VIEW_SHADER_COMPLEXITY || view === DEBUG_VIEW_LIGHTING_COMPLEXITY ) {
2764
+ if ( view === DEBUG_VIEW_SHADER_COMPLEXITY || view === DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS || view === DEBUG_VIEW_LIGHTING_COMPLEXITY || view === DEBUG_VIEW_QUAD_OVERDRAW || view === DEBUG_VIEW_BUFFER ) {
2762
2765
 
2763
2766
  return NoToneMapping;
2764
2767
 
@@ -10,7 +10,7 @@ import { builtin } from '../../../nodes/accessors/BuiltinNode.js';
10
10
  import { CubeUVReflectionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping, NoToneMapping } from '../../../constants.js';
11
11
  // WITH_GENESYS
12
12
  import { vec4 } from '../../../nodes/tsl/TSLCore.js';
13
- import { colorizeShaderComplexity, DEBUG_VIEW_SHADER_COMPLEXITY } from '../../../nodes/display/ComplexityDebug.js';
13
+ import { colorizeQuadOverdraw, colorizeShaderComplexity, DEBUG_VIEW_QUAD_OVERDRAW, DEBUG_VIEW_SHADER_COMPLEXITY, DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS, SHADER_COMPLEXITY_QUAD_COUNT_SCALE } from '../../../nodes/display/ComplexityDebug.js';
14
14
  // !WITH_GENESYS
15
15
  import { hashArray } from '../../../nodes/core/NodeUtils.js';
16
16
  import { error } from '../../../utils.js';
@@ -1035,9 +1035,19 @@ class NodeManager extends DataMap {
1035
1035
  }
1036
1036
 
1037
1037
  // WITH_GENESYS
1038
- if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY ) {
1038
+ if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY || renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS ) {
1039
1039
 
1040
- return vec4( colorizeShaderComplexity( sampled.r ), 1 ).renderOutput( NoToneMapping, renderer.currentColorSpace );
1040
+ const complexity = renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS
1041
+ ? sampled.r.mul( sampled.g ).mul( SHADER_COMPLEXITY_QUAD_COUNT_SCALE )
1042
+ : sampled.r;
1043
+
1044
+ return vec4( colorizeShaderComplexity( complexity ), 1 ).renderOutput( NoToneMapping, renderer.currentColorSpace );
1045
+
1046
+ }
1047
+
1048
+ if ( renderer.debug.view === DEBUG_VIEW_QUAD_OVERDRAW ) {
1049
+
1050
+ return vec4( colorizeQuadOverdraw( sampled.r ), 1 ).renderOutput( NoToneMapping, renderer.currentColorSpace );
1041
1051
 
1042
1052
  }
1043
1053
  // !WITH_GENESYS
@@ -11,6 +11,9 @@ import {
11
11
  import { Color } from '../../../math/Color.js';
12
12
  import { Vector4 } from '../../../math/Vector4.js';
13
13
  import { error, ReversedDepthFuncs, warnOnce } from '../../../utils.js';
14
+ // WITH_GENESYS
15
+ import { debugViewAccumulates } from '../../../nodes/display/ComplexityDebug.js';
16
+ // !WITH_GENESYS
14
17
 
15
18
  let equationToGL, factorToGL;
16
19
 
@@ -936,7 +939,7 @@ class WebGLState {
936
939
 
937
940
  // WITH_GENESYS
938
941
  // WebGL has no cached render pipeline. One+One is applied on each draw instead.
939
- if ( this.backend.renderer.debug.view === 'shaderComplexity' && material.isShadowPassMaterial !== true && this.backend.renderer.isOutputTarget !== true ) {
942
+ if ( debugViewAccumulates( this.backend.renderer.debug.view ) && material.isShadowPassMaterial !== true && this.backend.renderer.isOutputTarget !== true ) {
940
943
 
941
944
  this.setBlending( AdditiveBlending, AddEquation, OneFactor, OneFactor, AddEquation, OneFactor, OneFactor, material.blendColor, material.blendAlpha, true );
942
945
 
@@ -15,6 +15,9 @@ import {
15
15
  } from '../../../constants.js';
16
16
 
17
17
  import { error, ReversedDepthFuncs, warn, warnOnce } from '../../../utils.js';
18
+ // WITH_GENESYS
19
+ import { debugViewAccumulates } from '../../../nodes/display/ComplexityDebug.js';
20
+ // !WITH_GENESYS
18
21
 
19
22
  import GPUComputePipelineDescriptor from '../descriptors/GPUComputePipelineDescriptor.js';
20
23
  import GPUPipelineLayoutDescriptor from '../descriptors/GPUPipelineLayoutDescriptor.js';
@@ -108,7 +111,7 @@ class WebGPUPipelineUtils {
108
111
 
109
112
  // WITH_GENESYS
110
113
  // Accumulate shader cost with One+One. Opaque materials normally disable blending.
111
- if ( backend.renderer.debug.view === 'shaderComplexity' && material.isShadowPassMaterial !== true && backend.renderer.isOutputTarget !== true ) {
114
+ if ( debugViewAccumulates( backend.renderer.debug.view ) && material.isShadowPassMaterial !== true && backend.renderer.isOutputTarget !== true ) {
112
115
 
113
116
  materialBlending = {
114
117
  color: {