@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.
@@ -21640,30 +21640,57 @@ const DEBUG_VIEW_SHADER_COMPLEXITY = 'shaderComplexity';
21640
21640
  */
21641
21641
  const DEBUG_VIEW_LIGHTING_COMPLEXITY = 'lightingComplexity';
21642
21642
 
21643
+ /**
21644
+ * Per-pixel overdraw heatmap. Each fragment that passes the depth test adds one.
21645
+ * This is the portable stand-in for Unreal's 2×2 quad-coverage count.
21646
+ *
21647
+ * @type {string}
21648
+ */
21649
+ const DEBUG_VIEW_QUAD_OVERDRAW = 'quadOverdraw';
21650
+
21651
+ /**
21652
+ * Shader cost multiplied by the number of fragments that shaded the pixel, then the shader-complexity ramp.
21653
+ * One opaque surface matches {@link DEBUG_VIEW_SHADER_COMPLEXITY}. Each extra overlapping fragment scales the summed cost.
21654
+ * WebGPU cannot count wasted lanes in a 2×2 quad, so the count is fragments that pass the depth test.
21655
+ *
21656
+ * @type {string}
21657
+ */
21658
+ const DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS = 'shaderComplexityAndQuads';
21659
+
21643
21660
  /**
21644
21661
  * Proxy budget that fills the shader-complexity ramp.
21645
- * A plain lit material stays in the green stops. Transmission plus several
21646
- * texture samples climbs toward red.
21662
+ * Unlit and basic stay green. Phong and standard move through yellow into red.
21663
+ * Transmission plus several texture samples climbs toward white.
21647
21664
  *
21648
21665
  * @type {number}
21649
21666
  */
21650
21667
  const DEFAULT_SHADER_COMPLEXITY_BUDGET = 800;
21651
21668
 
21652
- /** @type {number} */
21653
- const SHADER_COMPLEXITY_BASE_UNLIT = 20;
21669
+ /**
21670
+ * Overlapping fragments that fill the quad-overdraw ramp.
21671
+ * Unreal's stair reaches white at ten layers (`1 / 16` stored, then scaled by 1.6).
21672
+ *
21673
+ * @type {number}
21674
+ */
21675
+ const DEFAULT_QUAD_OVERDRAW_BUDGET = 10;
21654
21676
 
21655
- /** @type {number} */
21656
- const SHADER_COMPLEXITY_BASE_LIT = 40;
21677
+ /**
21678
+ * Storage scale for the overdraw count in {@link DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS}.
21679
+ * Each fragment adds `1 / scale`, so an 8-bit target can hold this many layers.
21680
+ * The output pass multiplies that channel back to a count. Half-float targets hold the same count exactly.
21681
+ *
21682
+ * @type {number}
21683
+ */
21684
+ const SHADER_COMPLEXITY_QUAD_COUNT_SCALE = 16;
21657
21685
 
21658
- /** @type {number} */
21686
+ /**
21687
+ * Added for each fragment texture sample. Lighting-model costs are returned
21688
+ * directly by {@link NodeMaterial#getShaderComplexity}.
21689
+ *
21690
+ * @type {number}
21691
+ */
21659
21692
  const SHADER_COMPLEXITY_TEXTURE_COST = 16;
21660
21693
 
21661
- /** @type {number} */
21662
- const SHADER_COMPLEXITY_FEATURE_COST = 30;
21663
-
21664
- /** @type {number} */
21665
- const SHADER_COMPLEXITY_TRANSMISSION_COST = 60;
21666
-
21667
21694
  /**
21668
21695
  * Attenuation above this counts as a light that shades the pixel.
21669
21696
  *
@@ -21696,6 +21723,26 @@ const SHADER_COMPLEXITY_COLORS = [
21696
21723
  [ 1.0, 0.9, 0.9 ]
21697
21724
  ];
21698
21725
 
21726
+ /**
21727
+ * Unreal `QuadComplexityColors` from `BaseEngine.ini`.
21728
+ * Sampled as a stair: one overlapping fragment is one stop.
21729
+ *
21730
+ * @type {Array<Array<number>>}
21731
+ */
21732
+ const QUAD_COMPLEXITY_COLORS = [
21733
+ [ 0.0, 0.0, 0.0 ],
21734
+ [ 0.0, 0.0, 0.4 ],
21735
+ [ 0.0, 0.3, 1.0 ],
21736
+ [ 0.0, 0.7, 0.4 ],
21737
+ [ 0.0, 1.0, 0.0 ],
21738
+ [ 0.8, 0.8, 0.0 ],
21739
+ [ 1.0, 0.3, 0.0 ],
21740
+ [ 0.7, 0.0, 0.0 ],
21741
+ [ 0.5, 0.0, 0.5 ],
21742
+ [ 0.7, 0.3, 0.7 ],
21743
+ [ 1.0, 0.9, 0.9 ]
21744
+ ];
21745
+
21699
21746
  /**
21700
21747
  * Unreal `LightComplexityColors` from `BaseEngine.ini`.
21701
21748
  *
@@ -21754,6 +21801,42 @@ const colorizeLinear = ( cost, colors ) => {
21754
21801
  */
21755
21802
  const colorizeLightComplexity = ( cost ) => colorizeLinear( cost, LIGHT_COMPLEXITY_COLORS );
21756
21803
 
21804
+ /**
21805
+ * Unreal's stair sample for quad overdraw (`CS_STAIR`).
21806
+ * `cost` is the accumulated fragment count divided by the budget.
21807
+ *
21808
+ * @param {Node<float>} cost - Overdraw count divided by the budget.
21809
+ * @return {Node<vec3>} The ramp color.
21810
+ */
21811
+ const colorizeQuadOverdraw = ( cost ) => {
21812
+
21813
+ const steps = QUAD_COMPLEXITY_COLORS.length - 1;
21814
+ const index = round( cost.clamp( 0, 1 ).mul( steps ) );
21815
+
21816
+ let color = rgb( QUAD_COMPLEXITY_COLORS, 0 );
21817
+
21818
+ for ( let i = 0; i <= steps; i ++ ) {
21819
+
21820
+ color = index.equal( float( i ) ).select( rgb( QUAD_COMPLEXITY_COLORS, i ), color );
21821
+
21822
+ }
21823
+
21824
+ return color;
21825
+
21826
+ };
21827
+
21828
+ /**
21829
+ * Views that add a scalar per fragment, then colorize that sum in the output pass.
21830
+ *
21831
+ * @param {string} view - `renderer.debug.view`.
21832
+ * @return {boolean} `true` when the view accumulates.
21833
+ */
21834
+ function debugViewAccumulates( view ) {
21835
+
21836
+ return view === DEBUG_VIEW_SHADER_COMPLEXITY || view === DEBUG_VIEW_QUAD_OVERDRAW || view === DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS;
21837
+
21838
+ }
21839
+
21757
21840
  /**
21758
21841
  * Unreal's nonlinear shader-complexity ramp (`ColorizeComplexity`).
21759
21842
  * The first third of the normalized cost spreads across the first seven stops.
@@ -21847,49 +21930,449 @@ class ShaderComplexityRatioNode extends Node {
21847
21930
  }
21848
21931
 
21849
21932
  /**
21850
- * Static fragment proxy for a material. Texture samples are counted later, during generation.
21933
+ * Replaces the shaded color with `vec4(cost / budget, 0, 0, 1)`.
21934
+ * The real graph is still generated so texture samples are counted first.
21851
21935
  *
21852
- * @param {NodeMaterial} material - The material being built.
21853
- * @return {number} The base cost, excluding texture samples.
21936
+ * @param {Node} resultNode - The material's shaded output.
21937
+ * @return {Node<vec4>} The complexity ratio in the red channel.
21854
21938
  */
21855
- function getShaderComplexityBase( material ) {
21939
+ function shaderComplexityOutput( resultNode ) {
21856
21940
 
21857
- let cost = material.lights === true ? SHADER_COMPLEXITY_BASE_LIT : SHADER_COMPLEXITY_BASE_UNLIT;
21941
+ // Generate the shaded graph first so texture samples are counted, then
21942
+ // replace the color. A bypass of a value node emits a bare expression
21943
+ // statement, which WGSL rejects.
21944
+ const shaded = vec4( resultNode ).toVar();
21858
21945
 
21859
- if ( material.useClearcoat === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
21946
+ const ratio = new ShaderComplexityRatioNode();
21860
21947
 
21861
- if ( material.useSheen === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
21948
+ return vec4( ratio, 0, 0, 1 ).add( shaded.mul( 0 ) );
21862
21949
 
21863
- if ( material.useIridescence === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
21950
+ }
21864
21951
 
21865
- if ( material.useAnisotropy === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
21952
+ /**
21953
+ * Same cost as {@link shaderComplexityOutput}, plus one overdraw step in green.
21954
+ * The output pass multiplies red by the accumulated green count.
21955
+ *
21956
+ * @param {Node} resultNode - The material's shaded output.
21957
+ * @return {Node<vec4>} Cost ratio in red, one scaled fragment in green.
21958
+ */
21959
+ function shaderComplexityAndQuadsOutput( resultNode ) {
21866
21960
 
21867
- if ( material.useTransmission === true ) cost += SHADER_COMPLEXITY_TRANSMISSION_COST;
21961
+ const shaded = vec4( resultNode ).toVar();
21962
+ const ratio = new ShaderComplexityRatioNode();
21963
+ const step = float( 1 / SHADER_COMPLEXITY_QUAD_COUNT_SCALE );
21868
21964
 
21869
- return cost;
21965
+ return vec4( ratio, step, 0, 1 ).add( shaded.mul( 0 ) );
21870
21966
 
21871
21967
  }
21872
21968
 
21873
21969
  /**
21874
- * Replaces the shaded color with `vec4(cost / budget, 0, 0, 1)`.
21875
- * The real graph is still generated so texture samples are counted first.
21970
+ * Reads `1 / quadOverdrawBudget` at code-generation time.
21971
+ *
21972
+ * @augments Node
21973
+ */
21974
+ class QuadOverdrawRatioNode extends Node {
21975
+
21976
+ static get type() {
21977
+
21978
+ return 'QuadOverdrawRatioNode';
21979
+
21980
+ }
21981
+
21982
+ constructor() {
21983
+
21984
+ super( 'float' );
21985
+
21986
+ }
21987
+
21988
+ generate( builder ) {
21989
+
21990
+ const budget = builder.renderer.debug.quadOverdrawBudget;
21991
+ const safeBudget = ( typeof budget === 'number' && budget > 0 ) ? budget : 1;
21992
+
21993
+ return ( 1 / safeBudget ).toFixed( 6 );
21994
+
21995
+ }
21996
+
21997
+ }
21998
+
21999
+ /**
22000
+ * Replaces the shaded color with `vec4(1 / budget, 0, 0, 1)`.
22001
+ * The real graph is still generated so clipped fragments are not counted.
21876
22002
  *
21877
22003
  * @param {Node} resultNode - The material's shaded output.
21878
- * @return {Node<vec4>} The complexity ratio in the red channel.
22004
+ * @return {Node<vec4>} One overdraw step in the red channel.
21879
22005
  */
21880
- function shaderComplexityOutput( resultNode ) {
22006
+ function quadOverdrawOutput( resultNode ) {
21881
22007
 
21882
- // Generate the shaded graph first so texture samples are counted, then
21883
- // replace the color. A bypass of a value node emits a bare expression
21884
- // statement, which WGSL rejects.
21885
22008
  const shaded = vec4( resultNode ).toVar();
21886
-
21887
- const ratio = new ShaderComplexityRatioNode();
22009
+ const ratio = new QuadOverdrawRatioNode();
21888
22010
 
21889
22011
  return vec4( ratio, 0, 0, 1 ).add( shaded.mul( 0 ) );
21890
22012
 
21891
22013
  }
21892
22014
 
22015
+ // WITH_GENESYS
22016
+ // Buffer visualization. Shows one material channel in place of the shaded color.
22017
+ // !WITH_GENESYS
22018
+
22019
+
22020
+ /**
22021
+ * Buffer visualization. The visible surface wins, and one material channel replaces the shaded color.
22022
+ *
22023
+ * @type {string}
22024
+ */
22025
+ const DEBUG_VIEW_BUFFER = 'bufferVisualization';
22026
+
22027
+ /**
22028
+ * Albedo before lighting, including maps and vertex colors.
22029
+ *
22030
+ * @type {string}
22031
+ */
22032
+ const BUFFER_BASE_COLOR = 'baseColor';
22033
+
22034
+ /**
22035
+ * Shading normal in world space, packed to RGB with `n * 0.5 + 0.5`.
22036
+ *
22037
+ * @type {string}
22038
+ */
22039
+ const BUFFER_WORLD_NORMAL = 'worldNormal';
22040
+
22041
+ /**
22042
+ * Scalar roughness used by the lighting model. Black is smooth.
22043
+ *
22044
+ * @type {string}
22045
+ */
22046
+ const BUFFER_ROUGHNESS = 'roughness';
22047
+
22048
+ /**
22049
+ * Scalar metalness. White is metal. Unreal calls this buffer Metallic.
22050
+ *
22051
+ * @type {string}
22052
+ */
22053
+ const BUFFER_METALLIC = 'metallic';
22054
+
22055
+ /**
22056
+ * Material ambient occlusion. White means no occlusion map.
22057
+ *
22058
+ * @type {string}
22059
+ */
22060
+ const BUFFER_AMBIENT_OCCLUSION = 'ambientOcclusion';
22061
+
22062
+ /**
22063
+ * Emissive color before it is added to the lit result.
22064
+ *
22065
+ * @type {string}
22066
+ */
22067
+ const BUFFER_EMISSIVE = 'emissive';
22068
+
22069
+ /**
22070
+ * Channel drawn when buffer visualization is first enabled.
22071
+ *
22072
+ * @type {string}
22073
+ */
22074
+ const DEFAULT_BUFFER = BUFFER_BASE_COLOR;
22075
+
22076
+ /**
22077
+ * Writes a defined value for channels a material may never assign.
22078
+ * Standard and physical materials overwrite roughness and metalness in `setupVariants`,
22079
+ * and emissive is overwritten when the material has one.
22080
+ *
22081
+ * @param {string} buffer - `renderer.debug.buffer`.
22082
+ */
22083
+ function assignBufferDefaults( buffer ) {
22084
+
22085
+ if ( buffer === BUFFER_ROUGHNESS ) {
22086
+
22087
+ roughness.assign( float( 0 ) );
22088
+
22089
+ } else if ( buffer === BUFFER_METALLIC ) {
22090
+
22091
+ metalness.assign( float( 0 ) );
22092
+
22093
+ } else if ( buffer === BUFFER_EMISSIVE ) {
22094
+
22095
+ emissive.assign( vec3( 0 ) );
22096
+
22097
+ }
22098
+
22099
+ }
22100
+
22101
+ /**
22102
+ * The color for one buffer channel. Scalar channels are copied into RGB.
22103
+ *
22104
+ * @param {string} buffer - `renderer.debug.buffer`.
22105
+ * @return {Node<vec3>} The channel color.
22106
+ */
22107
+ function bufferVisualizationColor( buffer ) {
22108
+
22109
+ if ( buffer === BUFFER_WORLD_NORMAL ) {
22110
+
22111
+ return normalWorld.mul( 0.5 ).add( 0.5 );
22112
+
22113
+ }
22114
+
22115
+ if ( buffer === BUFFER_ROUGHNESS ) {
22116
+
22117
+ return vec3( roughness );
22118
+
22119
+ }
22120
+
22121
+ if ( buffer === BUFFER_METALLIC ) {
22122
+
22123
+ return vec3( metalness );
22124
+
22125
+ }
22126
+
22127
+ if ( buffer === BUFFER_AMBIENT_OCCLUSION ) {
22128
+
22129
+ return vec3( ambientOcclusion );
22130
+
22131
+ }
22132
+
22133
+ if ( buffer === BUFFER_EMISSIVE ) {
22134
+
22135
+ return emissive;
22136
+
22137
+ }
22138
+
22139
+ return diffuseColor.rgb;
22140
+
22141
+ }
22142
+
22143
+ /**
22144
+ * Replaces the shaded color with the selected buffer. Alpha stays the material's coverage
22145
+ * so masked and transparent surfaces keep their shape.
22146
+ *
22147
+ * @param {string} buffer - `renderer.debug.buffer`.
22148
+ * @return {Node<vec4>} The buffer color.
22149
+ */
22150
+ function bufferVisualizationOutput( buffer ) {
22151
+
22152
+ return vec4( bufferVisualizationColor( buffer ), diffuseColor.a );
22153
+
22154
+ }
22155
+
22156
+ // WITH_GENESYS
22157
+ // Lighting only and detail lighting. Both replace the shaded albedo with a flat gray.
22158
+ // !WITH_GENESYS
22159
+
22160
+
22161
+ /**
22162
+ * Lighting only. Flat gray, no specular, geometry normals. Material detail is dropped.
22163
+ *
22164
+ * @type {string}
22165
+ */
22166
+ const DEBUG_VIEW_LIGHTING_ONLY = 'lightingOnly';
22167
+
22168
+ /**
22169
+ * Detail lighting. Flat gray and a constant specular, with normal maps and roughness kept.
22170
+ *
22171
+ * @type {string}
22172
+ */
22173
+ const DEBUG_VIEW_DETAIL_LIGHTING = 'detailLighting';
22174
+
22175
+ /**
22176
+ * Neutral albedo shared by both views. Matches Unreal's `LightingOnlyBrightness`.
22177
+ *
22178
+ * @type {number}
22179
+ */
22180
+ const LIGHTING_ONLY_BRIGHTNESS = 0.3;
22181
+
22182
+ /**
22183
+ * Specular color used by detail lighting. Unreal writes `0.1` into the specular override.
22184
+ *
22185
+ * @type {number}
22186
+ */
22187
+ const DETAIL_LIGHTING_SPECULAR = 0.1;
22188
+
22189
+ /**
22190
+ * Geometry normal for lighting only, so normal maps and `normalNode` do not shade the surface.
22191
+ *
22192
+ * @type {Node<vec3>}
22193
+ */
22194
+ const lightingOnlyNormal = /*@__PURE__*/ Fn( ( builder ) => {
22195
+
22196
+ let node = normalViewGeometry;
22197
+
22198
+ if ( builder.isFlatShading() !== true ) {
22199
+
22200
+ node = negateOnBackSide( node );
22201
+
22202
+ }
22203
+
22204
+ return node;
22205
+
22206
+ }, 'vec3' );
22207
+
22208
+ /**
22209
+ * Replaces albedo and specular after the material has written its channels.
22210
+ * Lit materials become a dielectric gray. Unlit materials become flat gray.
22211
+ *
22212
+ * @param {NodeBuilder} builder - The current node builder.
22213
+ * @param {string} view - `renderer.debug.view`.
22214
+ */
22215
+ function applyLightingDebug( builder, view ) {
22216
+
22217
+ const material = builder.material;
22218
+ const gray = vec3( LIGHTING_ONLY_BRIGHTNESS );
22219
+
22220
+ diffuseColor.rgb.assign( gray );
22221
+
22222
+ if ( material.lights !== true ) return;
22223
+
22224
+ metalness.assign( float( 0 ) );
22225
+ diffuseContribution.assign( gray );
22226
+
22227
+ if ( view === DEBUG_VIEW_LIGHTING_ONLY ) {
22228
+
22229
+ roughness.assign( float( 1 ) );
22230
+ specularColor.assign( vec3( 0 ) );
22231
+ specularColorBlended.assign( vec3( 0 ) );
22232
+ specularF90.assign( float( 0 ) );
22233
+
22234
+ // Ambient occlusion was stored for the lighting model. Lighting only has no material.
22235
+ builder.context.ambientOcclusion = null;
22236
+
22237
+ if ( material.useClearcoat === true ) clearcoat.assign( float( 0 ) );
22238
+
22239
+ if ( material.useSheen === true ) sheen.assign( vec3( 0 ) );
22240
+
22241
+ if ( material.useIridescence === true ) iridescence.assign( float( 0 ) );
22242
+
22243
+ if ( material.useAnisotropy === true ) anisotropy.assign( float( 0 ) );
22244
+
22245
+ if ( material.useTransmission === true ) transmission.assign( float( 0 ) );
22246
+
22247
+ if ( material.useRetroreflection === true ) retroreflectivity.assign( float( 0 ) );
22248
+
22249
+ } else {
22250
+
22251
+ const specular = vec3( DETAIL_LIGHTING_SPECULAR );
22252
+
22253
+ specularColor.assign( specular );
22254
+ specularColorBlended.assign( specular );
22255
+ specularF90.assign( float( 1 ) );
22256
+
22257
+ }
22258
+
22259
+ }
22260
+
22261
+ // WITH_GENESYS
22262
+ // Draw call coloration. One stable diffuse color per submitted draw, then lit.
22263
+ // !WITH_GENESYS
22264
+
22265
+
22266
+ /**
22267
+ * Draw call coloration. Each submitted draw gets one diffuse color. An instanced mesh is one draw.
22268
+ * The color is lit, so lights and shadows still show. Unlit materials stay flat.
22269
+ *
22270
+ * @type {string}
22271
+ */
22272
+ const DEBUG_VIEW_DRAW_CALL = 'drawCall';
22273
+
22274
+ /**
22275
+ * Golden-ratio step. Sequential object ids land on opposite sides of the hue wheel.
22276
+ *
22277
+ * @type {number}
22278
+ */
22279
+ const DRAW_CALL_HUE_STEP = 0.618033988749895;
22280
+
22281
+ /**
22282
+ * Saturation of a draw color. High enough that neighboring hues stay apart after lighting.
22283
+ *
22284
+ * @type {number}
22285
+ */
22286
+ const DRAW_CALL_SATURATION = 0.85;
22287
+
22288
+ /**
22289
+ * Value of a draw color. Lighting and shadows darken it from here.
22290
+ *
22291
+ * @type {number}
22292
+ */
22293
+ const DRAW_CALL_VALUE = 0.92;
22294
+
22295
+ /**
22296
+ * Writes a saturated color for this draw into `color`.
22297
+ * Hue walks the wheel by the golden ratio, so consecutive objects do not share a tint.
22298
+ * Instances of one mesh share the object id, so they share the color.
22299
+ *
22300
+ * @param {Color} color - The uniform color, updated in place.
22301
+ * @param {Object3D} object - The drawn object.
22302
+ * @param {Material} material - The drawn material. A multi-material group is its own draw.
22303
+ * @return {Color} `color`.
22304
+ */
22305
+ function writeDrawCallColor( color, object, material ) {
22306
+
22307
+ const hue = ( ( object.id + material.id * 64 ) * DRAW_CALL_HUE_STEP ) % 1;
22308
+ const s = DRAW_CALL_SATURATION;
22309
+ const v = DRAW_CALL_VALUE;
22310
+ const sector = hue * 6;
22311
+ const i = Math.floor( sector );
22312
+ const f = sector - i;
22313
+ const p = v * ( 1 - s );
22314
+ const q = v * ( 1 - f * s );
22315
+ const t = v * ( 1 - ( 1 - f ) * s );
22316
+
22317
+ switch ( i % 6 ) {
22318
+
22319
+ case 0: return color.setRGB( v, t, p );
22320
+ case 1: return color.setRGB( q, v, p );
22321
+ case 2: return color.setRGB( p, v, t );
22322
+ case 3: return color.setRGB( p, q, v );
22323
+ case 4: return color.setRGB( t, p, v );
22324
+ default: return color.setRGB( v, p, q );
22325
+
22326
+ }
22327
+
22328
+ }
22329
+
22330
+ /**
22331
+ * Replaces albedo with the draw color after the material has written its channels.
22332
+ * Lit materials become a matte dielectric in that color. Unlit materials become flat.
22333
+ * The color is an object uniform, so meshes that share a material still differ.
22334
+ *
22335
+ * @param {NodeBuilder} builder - The current node builder.
22336
+ */
22337
+ function applyDrawCallDebug( builder ) {
22338
+
22339
+ const material = builder.material;
22340
+ const colorNode = uniform( new Color() ).onObjectUpdate( ( { object, material: drawnMaterial }, self ) => {
22341
+
22342
+ if ( object === null || drawnMaterial === null ) return;
22343
+
22344
+ return writeDrawCallColor( self.value, object, drawnMaterial );
22345
+
22346
+ } );
22347
+
22348
+ diffuseColor.rgb.assign( colorNode );
22349
+
22350
+ if ( material.lights !== true ) return;
22351
+
22352
+ metalness.assign( float( 0 ) );
22353
+ diffuseContribution.assign( colorNode );
22354
+ roughness.assign( float( 1 ) );
22355
+ specularColor.assign( vec3( 0 ) );
22356
+ specularColorBlended.assign( vec3( 0 ) );
22357
+ specularF90.assign( float( 0 ) );
22358
+
22359
+ // Ambient occlusion belonged to the original material.
22360
+ builder.context.ambientOcclusion = null;
22361
+
22362
+ if ( material.useClearcoat === true ) clearcoat.assign( float( 0 ) );
22363
+
22364
+ if ( material.useSheen === true ) sheen.assign( vec3( 0 ) );
22365
+
22366
+ if ( material.useIridescence === true ) iridescence.assign( float( 0 ) );
22367
+
22368
+ if ( material.useAnisotropy === true ) anisotropy.assign( float( 0 ) );
22369
+
22370
+ if ( material.useTransmission === true ) transmission.assign( float( 0 ) );
22371
+
22372
+ if ( material.useRetroreflection === true ) retroreflectivity.assign( float( 0 ) );
22373
+
22374
+ }
22375
+
21893
22376
  // !WITH_GENESYS
21894
22377
 
21895
22378
  /**
@@ -22390,10 +22873,33 @@ class NodeMaterial extends Material {
22390
22873
 
22391
22874
  if ( this.fragmentNode === null ) {
22392
22875
 
22876
+ // WITH_GENESYS
22877
+ // Fallback for materials that never write the channel. Lit materials overwrite these.
22878
+ if ( renderer.debug.view === DEBUG_VIEW_BUFFER && this.isShadowPassMaterial !== true ) {
22879
+
22880
+ assignBufferDefaults( renderer.debug.buffer );
22881
+
22882
+ }
22883
+ // !WITH_GENESYS
22884
+
22393
22885
  this.setupDiffuseColor( builder );
22394
22886
  this.setupAmbientOcclusion( builder );
22395
22887
  this.setupVariants( builder );
22396
22888
 
22889
+ // WITH_GENESYS
22890
+ // After the material writes its channels, so roughness and normals already exist
22891
+ // for detail lighting and can be replaced for lighting only.
22892
+ if ( ( renderer.debug.view === DEBUG_VIEW_LIGHTING_ONLY || renderer.debug.view === DEBUG_VIEW_DETAIL_LIGHTING ) && this.isShadowPassMaterial !== true ) {
22893
+
22894
+ applyLightingDebug( builder, renderer.debug.view );
22895
+
22896
+ } else if ( renderer.debug.view === DEBUG_VIEW_DRAW_CALL && this.isShadowPassMaterial !== true ) {
22897
+
22898
+ applyDrawCallDebug( builder );
22899
+
22900
+ }
22901
+ // !WITH_GENESYS
22902
+
22397
22903
  const outgoingLightNode = this.setupLighting( builder );
22398
22904
 
22399
22905
  if ( clippingNode !== null ) builder.stack.addToStack( clippingNode );
@@ -22468,9 +22974,24 @@ class NodeMaterial extends Material {
22468
22974
  // would replace the heatmap with this material's own constant cost.
22469
22975
  if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY && this.isShadowPassMaterial !== true && renderer.isOutputTarget !== true ) {
22470
22976
 
22471
- builder.shaderComplexityBase = getShaderComplexityBase( this );
22977
+ builder.shaderComplexityBase = this.getShaderComplexity();
22472
22978
  resultNode = shaderComplexityOutput( resultNode );
22473
22979
 
22980
+ } else if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS && this.isShadowPassMaterial !== true && renderer.isOutputTarget !== true ) {
22981
+
22982
+ builder.shaderComplexityBase = this.getShaderComplexity();
22983
+ resultNode = shaderComplexityAndQuadsOutput( resultNode );
22984
+
22985
+ } else if ( renderer.debug.view === DEBUG_VIEW_QUAD_OVERDRAW && this.isShadowPassMaterial !== true && renderer.isOutputTarget !== true ) {
22986
+
22987
+ resultNode = quadOverdrawOutput( resultNode );
22988
+
22989
+ } else if ( renderer.debug.view === DEBUG_VIEW_BUFFER && this.isShadowPassMaterial !== true && this.fragmentNode === null ) {
22990
+
22991
+ // The fullscreen output pass sets fragmentNode. Scene materials do not, including
22992
+ // when the scene is drawn straight to the canvas.
22993
+ resultNode = bufferVisualizationOutput( renderer.debug.buffer );
22994
+
22474
22995
  }
22475
22996
  // !WITH_GENESYS
22476
22997
 
@@ -22484,6 +23005,21 @@ class NodeMaterial extends Material {
22484
23005
 
22485
23006
  }
22486
23007
 
23008
+ // WITH_GENESYS
23009
+ /**
23010
+ * Static shader-complexity proxy for this material's lighting model.
23011
+ * Fragment texture samples are counted later, while the shader is generated.
23012
+ * Derived materials override this instead of registering a type flag.
23013
+ *
23014
+ * @return {number} The base cost, excluding texture samples.
23015
+ */
23016
+ getShaderComplexity() {
23017
+
23018
+ return this.lights === true ? 40 : 20;
23019
+
23020
+ }
23021
+ // !WITH_GENESYS
23022
+
22487
23023
  /**
22488
23024
  * Setups the clipping node.
22489
23025
  *
@@ -22808,9 +23344,18 @@ class NodeMaterial extends Material {
22808
23344
  /**
22809
23345
  * Setups the normal node from the material.
22810
23346
  *
23347
+ * @param {NodeBuilder} builder - The current node builder.
22811
23348
  * @return {Node<vec3>} The normal node.
22812
23349
  */
22813
- setupNormal() {
23350
+ setupNormal( builder ) {
23351
+
23352
+ // WITH_GENESYS
23353
+ if ( ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_ONLY || builder.renderer.debug.view === DEBUG_VIEW_DRAW_CALL ) && this.isShadowPassMaterial !== true && this.fragmentNode === null ) {
23354
+
23355
+ return lightingOnlyNormal();
23356
+
23357
+ }
23358
+ // !WITH_GENESYS
22814
23359
 
22815
23360
  return this.normalNode ? vec3( this.normalNode ) : materialNormal;
22816
23361
 
@@ -23007,6 +23552,16 @@ class NodeMaterial extends Material {
23007
23552
 
23008
23553
  if ( ( emissiveNode && emissiveNode.isNode === true ) || ( material.emissive && material.emissive.isColor === true ) ) {
23009
23554
 
23555
+ // WITH_GENESYS
23556
+ // Lighting only and draw call substitute a plain lit material, so emissive is dropped.
23557
+ // Detail lighting keeps it.
23558
+ if ( ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_ONLY || builder.renderer.debug.view === DEBUG_VIEW_DRAW_CALL ) && this.isShadowPassMaterial !== true ) {
23559
+
23560
+ return outgoingLightNode;
23561
+
23562
+ }
23563
+ // !WITH_GENESYS
23564
+
23010
23565
  emissive.assign( vec3( emissiveNode ? emissiveNode : materialEmissive ) );
23011
23566
 
23012
23567
  outgoingLightNode = outgoingLightNode.add( emissive );
@@ -24881,6 +25436,19 @@ class MeshBasicNodeMaterial extends NodeMaterial {
24881
25436
 
24882
25437
  }
24883
25438
 
25439
+ // WITH_GENESYS
25440
+ /**
25441
+ * Shader-complexity proxy for {@link BasicLightingModel}.
25442
+ *
25443
+ * @return {number} The base cost, excluding texture samples.
25444
+ */
25445
+ getShaderComplexity() {
25446
+
25447
+ return 80;
25448
+
25449
+ }
25450
+ // !WITH_GENESYS
25451
+
24884
25452
  /**
24885
25453
  * Setups the lighting model.
24886
25454
  *
@@ -25064,6 +25632,19 @@ class MeshLambertNodeMaterial extends NodeMaterial {
25064
25632
 
25065
25633
  }
25066
25634
 
25635
+ // WITH_GENESYS
25636
+ /**
25637
+ * Shader-complexity proxy for Lambert. Specular is forced off.
25638
+ *
25639
+ * @return {number} The base cost, excluding texture samples.
25640
+ */
25641
+ getShaderComplexity() {
25642
+
25643
+ return 140;
25644
+
25645
+ }
25646
+ // !WITH_GENESYS
25647
+
25067
25648
  /**
25068
25649
  * Setups the lighting model.
25069
25650
  *
@@ -25165,6 +25746,19 @@ class MeshPhongNodeMaterial extends NodeMaterial {
25165
25746
 
25166
25747
  }
25167
25748
 
25749
+ // WITH_GENESYS
25750
+ /**
25751
+ * Shader-complexity proxy for Blinn-Phong.
25752
+ *
25753
+ * @return {number} The base cost, excluding texture samples.
25754
+ */
25755
+ getShaderComplexity() {
25756
+
25757
+ return 200;
25758
+
25759
+ }
25760
+ // !WITH_GENESYS
25761
+
25168
25762
  /**
25169
25763
  * Setups the lighting model.
25170
25764
  *
@@ -28653,6 +29247,19 @@ class MeshStandardNodeMaterial extends NodeMaterial {
28653
29247
 
28654
29248
  }
28655
29249
 
29250
+ // WITH_GENESYS
29251
+ /**
29252
+ * Shader-complexity proxy for the Cook-Torrance GGX model.
29253
+ *
29254
+ * @return {number} The base cost, excluding texture samples.
29255
+ */
29256
+ getShaderComplexity() {
29257
+
29258
+ return 240;
29259
+
29260
+ }
29261
+ // !WITH_GENESYS
29262
+
28656
29263
  /**
28657
29264
  * Setups the lighting model.
28658
29265
  *
@@ -29082,6 +29689,31 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial {
29082
29689
 
29083
29690
  }
29084
29691
 
29692
+ // WITH_GENESYS
29693
+ /**
29694
+ * GGX base from {@link MeshStandardNodeMaterial}, plus each enabled physical feature.
29695
+ *
29696
+ * @return {number} The base cost, excluding texture samples.
29697
+ */
29698
+ getShaderComplexity() {
29699
+
29700
+ let cost = super.getShaderComplexity();
29701
+
29702
+ if ( this.useClearcoat === true ) cost += 30;
29703
+
29704
+ if ( this.useSheen === true ) cost += 30;
29705
+
29706
+ if ( this.useIridescence === true ) cost += 30;
29707
+
29708
+ if ( this.useAnisotropy === true ) cost += 30;
29709
+
29710
+ if ( this.useTransmission === true ) cost += 60;
29711
+
29712
+ return cost;
29713
+
29714
+ }
29715
+ // !WITH_GENESYS
29716
+
29085
29717
  /**
29086
29718
  * Setups the lighting model.
29087
29719
  *
@@ -29366,6 +29998,21 @@ class MeshSSSNodeMaterial extends MeshPhysicalNodeMaterial {
29366
29998
 
29367
29999
  }
29368
30000
 
30001
+ // WITH_GENESYS
30002
+ /**
30003
+ * Physical complexity, plus the subsurface term when it is evaluated.
30004
+ *
30005
+ * @return {number} The base cost, excluding texture samples.
30006
+ */
30007
+ getShaderComplexity() {
30008
+
30009
+ const cost = super.getShaderComplexity();
30010
+
30011
+ return this.useSSS === true ? cost + 40 : cost;
30012
+
30013
+ }
30014
+ // !WITH_GENESYS
30015
+
29369
30016
  /**
29370
30017
  * Setups the lighting model.
29371
30018
  *
@@ -29487,6 +30134,19 @@ class MeshToonNodeMaterial extends NodeMaterial {
29487
30134
 
29488
30135
  }
29489
30136
 
30137
+ // WITH_GENESYS
30138
+ /**
30139
+ * Shader-complexity proxy for toon banding.
30140
+ *
30141
+ * @return {number} The base cost, excluding texture samples.
30142
+ */
30143
+ getShaderComplexity() {
30144
+
30145
+ return 170;
30146
+
30147
+ }
30148
+ // !WITH_GENESYS
30149
+
29490
30150
  /**
29491
30151
  * Setups the lighting model.
29492
30152
  *
@@ -29558,6 +30218,19 @@ class MeshMatcapNodeMaterial extends NodeMaterial {
29558
30218
 
29559
30219
  }
29560
30220
 
30221
+ // WITH_GENESYS
30222
+ /**
30223
+ * Shader-complexity proxy for matcap UV shading.
30224
+ *
30225
+ * @return {number} The base cost, excluding texture samples.
30226
+ */
30227
+ getShaderComplexity() {
30228
+
30229
+ return 50;
30230
+
30231
+ }
30232
+ // !WITH_GENESYS
30233
+
29561
30234
  /**
29562
30235
  * Setups the matcap specific node variables.
29563
30236
  *
@@ -30233,6 +30906,19 @@ class ShadowNodeMaterial extends NodeMaterial {
30233
30906
 
30234
30907
  }
30235
30908
 
30909
+ // WITH_GENESYS
30910
+ /**
30911
+ * Shader-complexity proxy for the shadow mask.
30912
+ *
30913
+ * @return {number} The base cost, excluding texture samples.
30914
+ */
30915
+ getShaderComplexity() {
30916
+
30917
+ return 110;
30918
+
30919
+ }
30920
+ // !WITH_GENESYS
30921
+
30236
30922
  /**
30237
30923
  * Setups the lighting model.
30238
30924
  *
@@ -30519,6 +31205,19 @@ class VolumeNodeMaterial extends NodeMaterial {
30519
31205
 
30520
31206
  }
30521
31207
 
31208
+ // WITH_GENESYS
31209
+ /**
31210
+ * Shader-complexity proxy for volumetric ray integration.
31211
+ *
31212
+ * @return {number} The base cost, excluding texture samples.
31213
+ */
31214
+ getShaderComplexity() {
31215
+
31216
+ return 280;
31217
+
31218
+ }
31219
+ // !WITH_GENESYS
31220
+
30522
31221
  setupLightingModel() {
30523
31222
 
30524
31223
  return new VolumetricLightingModel();
@@ -31767,7 +32466,7 @@ class RenderObject {
31767
32466
 
31768
32467
  // WITH_GENESYS
31769
32468
  // hash() xors each argument as a number, so a view name has to be hashed first.
31770
- cacheKey = hash$1( cacheKey, hashString( this.renderer.debug.view ), this.renderer.debug.shaderComplexityBudget );
32469
+ cacheKey = hash$1( cacheKey, hashString( this.renderer.debug.view ), hashString( this.renderer.debug.buffer ), this.renderer.debug.shaderComplexityBudget, this.renderer.debug.quadOverdrawBudget );
31771
32470
  // !WITH_GENESYS
31772
32471
 
31773
32472
  return cacheKey;
@@ -52221,6 +52920,8 @@ var TSL = /*#__PURE__*/Object.freeze({
52221
52920
  xor: xor
52222
52921
  });
52223
52922
 
52923
+ // !WITH_GENESYS
52924
+
52224
52925
  const _clearColor = /*@__PURE__*/ new Color4();
52225
52926
 
52226
52927
  /**
@@ -52406,7 +53107,7 @@ class Background extends DataMap {
52406
53107
 
52407
53108
  // WITH_GENESYS
52408
53109
  // The background is depth-tested off, so additive shader complexity would add it to every pixel.
52409
- if ( renderer.debug.view !== 'shaderComplexity' ) {
53110
+ if ( debugViewAccumulates( renderer.debug.view ) !== true ) {
52410
53111
 
52411
53112
  renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
52412
53113
 
@@ -52437,7 +53138,7 @@ class Background extends DataMap {
52437
53138
  //
52438
53139
 
52439
53140
  // WITH_GENESYS
52440
- if ( renderer.debug.view === 'shaderComplexity' ) {
53141
+ if ( debugViewAccumulates( renderer.debug.view ) ) {
52441
53142
 
52442
53143
  _clearColor.set( 0, 0, 0, 1 );
52443
53144
 
@@ -59727,9 +60428,19 @@ class NodeManager extends DataMap {
59727
60428
  }
59728
60429
 
59729
60430
  // WITH_GENESYS
59730
- if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY ) {
60431
+ if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY || renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS ) {
60432
+
60433
+ const complexity = renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY_AND_QUADS
60434
+ ? sampled.r.mul( sampled.g ).mul( SHADER_COMPLEXITY_QUAD_COUNT_SCALE )
60435
+ : sampled.r;
59731
60436
 
59732
- return vec4( colorizeShaderComplexity( sampled.r ), 1 ).renderOutput( NoToneMapping, renderer.currentColorSpace );
60437
+ return vec4( colorizeShaderComplexity( complexity ), 1 ).renderOutput( NoToneMapping, renderer.currentColorSpace );
60438
+
60439
+ }
60440
+
60441
+ if ( renderer.debug.view === DEBUG_VIEW_QUAD_OVERDRAW ) {
60442
+
60443
+ return vec4( colorizeQuadOverdraw( sampled.r ), 1 ).renderOutput( NoToneMapping, renderer.currentColorSpace );
59733
60444
 
59734
60445
  }
59735
60446
  // !WITH_GENESYS
@@ -63692,8 +64403,10 @@ class Renderer {
63692
64403
  * @property {?Function} onNodeBuilderCreated - A callback function that is executed after a node builder has been created and before it is built.
63693
64404
  * @property {?Function} onShaderError - A callback function that is executed when a shader error happens. Only supported with WebGL 2 right now.
63694
64405
  * @property {Function} getShaderAsync - Allows the get the raw shader code for the given scene, camera and 3D object.
63695
- * @property {string} view - Debug view. `shaderComplexity` and `lightingComplexity` replace the shaded color with a heatmap.
64406
+ * @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.
64407
+ * @property {string} buffer - Channel drawn by `bufferVisualization`: `baseColor`, `worldNormal`, `roughness`, `metallic`, `ambientOcclusion`, or `emissive`.
63696
64408
  * @property {number} shaderComplexityBudget - Proxy budget that fills the shader-complexity ramp.
64409
+ * @property {number} quadOverdrawBudget - Overlapping fragments that fill the quad-overdraw ramp.
63697
64410
  */
63698
64411
 
63699
64412
  /**
@@ -63710,7 +64423,9 @@ class Renderer {
63710
64423
  onShaderError: null,
63711
64424
  // WITH_GENESYS
63712
64425
  view: DEBUG_VIEW_NONE,
64426
+ buffer: DEFAULT_BUFFER,
63713
64427
  shaderComplexityBudget: DEFAULT_SHADER_COMPLEXITY_BUDGET,
64428
+ quadOverdrawBudget: DEFAULT_QUAD_OVERDRAW_BUDGET,
63714
64429
  // !WITH_GENESYS
63715
64430
  getShaderAsync: async ( scene, camera, object ) => {
63716
64431
 
@@ -65647,10 +66362,8 @@ class Renderer {
65647
66362
  const useColorSpace = this.currentColorSpace !== ColorManagement.workingColorSpace;
65648
66363
 
65649
66364
  // WITH_GENESYS
65650
- // Shader complexity writes a scalar into an intermediate target, then the output pass colorizes it.
65651
- const shaderComplexity = this.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY;
65652
-
65653
- return useToneMapping || useColorSpace || shaderComplexity;
66365
+ // Accumulating views write a scalar into an intermediate target, then the output pass colorizes it.
66366
+ return useToneMapping || useColorSpace || debugViewAccumulates( this.debug.view );
65654
66367
  // !WITH_GENESYS
65655
66368
  // return useToneMapping || useColorSpace;
65656
66369
 
@@ -65706,7 +66419,7 @@ class Renderer {
65706
66419
  // WITH_GENESYS
65707
66420
  const view = this.debug.view;
65708
66421
 
65709
- if ( view === DEBUG_VIEW_SHADER_COMPLEXITY || view === DEBUG_VIEW_LIGHTING_COMPLEXITY ) {
66422
+ 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 ) {
65710
66423
 
65711
66424
  return NoToneMapping;
65712
66425
 
@@ -71510,6 +72223,8 @@ class WebGLAttributeUtils {
71510
72223
 
71511
72224
  }
71512
72225
 
72226
+ // !WITH_GENESYS
72227
+
71513
72228
  let equationToGL, factorToGL;
71514
72229
 
71515
72230
  /**
@@ -72434,7 +73149,7 @@ class WebGLState {
72434
73149
 
72435
73150
  // WITH_GENESYS
72436
73151
  // WebGL has no cached render pipeline. One+One is applied on each draw instead.
72437
- if ( this.backend.renderer.debug.view === 'shaderComplexity' && material.isShadowPassMaterial !== true && this.backend.renderer.isOutputTarget !== true ) {
73152
+ if ( debugViewAccumulates( this.backend.renderer.debug.view ) && material.isShadowPassMaterial !== true && this.backend.renderer.isOutputTarget !== true ) {
72438
73153
 
72439
73154
  this.setBlending( AdditiveBlending, AddEquation, OneFactor, OneFactor, AddEquation, OneFactor, OneFactor, material.blendColor, material.blendAlpha, true );
72440
73155
 
@@ -87139,7 +87854,7 @@ class WebGPUPipelineUtils {
87139
87854
 
87140
87855
  // WITH_GENESYS
87141
87856
  // Accumulate shader cost with One+One. Opaque materials normally disable blending.
87142
- if ( backend.renderer.debug.view === 'shaderComplexity' && material.isShadowPassMaterial !== true && backend.renderer.isOutputTarget !== true ) {
87857
+ if ( debugViewAccumulates( backend.renderer.debug.view ) && material.isShadowPassMaterial !== true && backend.renderer.isOutputTarget !== true ) {
87143
87858
 
87144
87859
  materialBlending = {
87145
87860
  color: {