@gnsx/three 0.186.3 → 0.186.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/three.webgpu.js +610 -17
- package/build/three.webgpu.nodes.js +610 -17
- package/examples/jsm/inspector/tabs/Settings.js +36 -0
- package/package.json +1 -1
- package/src/materials/nodes/NodeMaterial.js +30 -0
- package/src/nodes/core/NodeBuilder.js +74 -0
- package/src/nodes/display/ComplexityDebug.js +278 -0
- package/src/nodes/lighting/LightsNode.js +71 -2
- package/src/nodes/lighting/PointLightNode.js +4 -1
- package/src/nodes/lighting/RectAreaLightNode.js +14 -0
- package/src/nodes/lighting/SpotLightNode.js +10 -2
- package/src/renderers/common/Background.js +17 -1
- package/src/renderers/common/RenderObject.js +5 -0
- package/src/renderers/common/Renderer.js +24 -1
- package/src/renderers/common/nodes/NodeManager.js +22 -7
- package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +8 -0
- package/src/renderers/webgl-fallback/utils/WebGLState.js +19 -3
- package/src/renderers/webgpu/WebGPUBackend.js +14 -2
- package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +16 -0
- package/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +20 -0
- package/build/three.cjs +0 -17
|
@@ -134,10 +134,46 @@ class Settings extends Parameters {
|
|
|
134
134
|
|
|
135
135
|
} ).info( 'Shows how many times each pixel is shaded.' );
|
|
136
136
|
|
|
137
|
+
// WITH_GENESYS
|
|
138
|
+
// Values match Renderer.debug.view. Not stored with inspector settings.
|
|
139
|
+
const debugViewState = { view: 'none' };
|
|
140
|
+
|
|
141
|
+
this._debugViewState = debugViewState;
|
|
142
|
+
this._debugViewControl = modesGroup.add( debugViewState, 'view', {
|
|
143
|
+
'Shaded': 'none',
|
|
144
|
+
'Shader Complexity': 'shaderComplexity',
|
|
145
|
+
'Lighting Complexity': 'lightingComplexity'
|
|
146
|
+
} ).name( 'Debug View' ).onChange( ( view ) => {
|
|
147
|
+
|
|
148
|
+
const renderer = this.inspector.getRenderer();
|
|
149
|
+
|
|
150
|
+
if ( renderer === null || renderer.debug === undefined ) return;
|
|
151
|
+
|
|
152
|
+
renderer.debug.view = view;
|
|
153
|
+
window.dispatchEvent( new Event( 'genesys-debug-view' ) );
|
|
154
|
+
|
|
155
|
+
} ).info( 'Shaded color, a shader-cost heatmap, or a direct-light count. Overdraw draws on top when both are enabled.' );
|
|
156
|
+
// !WITH_GENESYS
|
|
157
|
+
|
|
137
158
|
}
|
|
138
159
|
|
|
139
160
|
init() {
|
|
140
161
|
|
|
162
|
+
// WITH_GENESYS
|
|
163
|
+
const renderer = this.inspector.getRenderer();
|
|
164
|
+
const view = renderer !== null && renderer.debug !== undefined ? renderer.debug.view : null;
|
|
165
|
+
|
|
166
|
+
if ( view === 'none' || view === 'shaderComplexity' || view === 'lightingComplexity' ) {
|
|
167
|
+
|
|
168
|
+
if ( this._debugViewState.view !== view ) {
|
|
169
|
+
|
|
170
|
+
this._debugViewControl.setValue( view );
|
|
171
|
+
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
}
|
|
175
|
+
// !WITH_GENESYS
|
|
176
|
+
|
|
141
177
|
const extensionsGroup = this.createGroup( 'Extensions' );
|
|
142
178
|
|
|
143
179
|
const storageGroup = this.createGroup( 'Storage' );
|
package/package.json
CHANGED
|
@@ -25,6 +25,9 @@ import { modelViewMatrix } from '../../nodes/accessors/ModelNode.js';
|
|
|
25
25
|
import { vertexColor } from '../../nodes/accessors/VertexColorNode.js';
|
|
26
26
|
import { premultiplyAlpha } from '../../nodes/display/PremultiplyAlphaFunctions.js';
|
|
27
27
|
import { subBuild } from '../../nodes/core/SubBuildNode.js';
|
|
28
|
+
// WITH_GENESYS
|
|
29
|
+
import { DEBUG_VIEW_LIGHTING_COMPLEXITY, DEBUG_VIEW_SHADER_COMPLEXITY, getShaderComplexityBase, shaderComplexityOutput } from '../../nodes/display/ComplexityDebug.js';
|
|
30
|
+
// !WITH_GENESYS
|
|
28
31
|
|
|
29
32
|
/**
|
|
30
33
|
* Base class for all node materials.
|
|
@@ -597,6 +600,17 @@ class NodeMaterial extends Material {
|
|
|
597
600
|
|
|
598
601
|
}
|
|
599
602
|
|
|
603
|
+
// WITH_GENESYS
|
|
604
|
+
// The screen output pass colorizes the accumulated ratio. Wrapping that pass
|
|
605
|
+
// would replace the heatmap with this material's own constant cost.
|
|
606
|
+
if ( renderer.debug.view === DEBUG_VIEW_SHADER_COMPLEXITY && this.isShadowPassMaterial !== true && renderer.isOutputTarget !== true ) {
|
|
607
|
+
|
|
608
|
+
builder.shaderComplexityBase = getShaderComplexityBase( this );
|
|
609
|
+
resultNode = shaderComplexityOutput( resultNode );
|
|
610
|
+
|
|
611
|
+
}
|
|
612
|
+
// !WITH_GENESYS
|
|
613
|
+
|
|
600
614
|
builder.stack.outputNode = resultNode;
|
|
601
615
|
|
|
602
616
|
builder.addFlow( 'fragment', builder.removeStack() );
|
|
@@ -1110,6 +1124,22 @@ class NodeMaterial extends Material {
|
|
|
1110
1124
|
|
|
1111
1125
|
}
|
|
1112
1126
|
|
|
1127
|
+
// WITH_GENESYS
|
|
1128
|
+
if ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_COMPLEXITY && this.isShadowPassMaterial !== true ) {
|
|
1129
|
+
|
|
1130
|
+
const hasLightLoop = lightsNode && ( materialLightings.length > 0 || lightsNode.getScope().hasLights );
|
|
1131
|
+
|
|
1132
|
+
if ( hasLightLoop !== true ) {
|
|
1133
|
+
|
|
1134
|
+
outgoingLightNode = vec3( 0 );
|
|
1135
|
+
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
return outgoingLightNode;
|
|
1139
|
+
|
|
1140
|
+
}
|
|
1141
|
+
// !WITH_GENESYS
|
|
1142
|
+
|
|
1113
1143
|
// EMISSIVE
|
|
1114
1144
|
|
|
1115
1145
|
if ( ( emissiveNode && emissiveNode.isNode === true ) || ( material.emissive && material.emissive.isColor === true ) ) {
|
|
@@ -24,6 +24,9 @@ import CubeRenderTarget from '../../renderers/common/CubeRenderTarget.js';
|
|
|
24
24
|
import BindGroup from '../../renderers/common/BindGroup.js';
|
|
25
25
|
|
|
26
26
|
import { REVISION, IntType, UnsignedIntType, LinearFilter, LinearMipmapNearestFilter, NearestMipmapLinearFilter, LinearMipmapLinearFilter, NormalBlending } from '../../constants.js';
|
|
27
|
+
// WITH_GENESYS
|
|
28
|
+
import { SHADER_COMPLEXITY_TEXTURE_COST } from '../display/ComplexityDebug.js';
|
|
29
|
+
// !WITH_GENESYS
|
|
27
30
|
import { RenderTarget } from '../../core/RenderTarget.js';
|
|
28
31
|
import { Color } from '../../math/Color.js';
|
|
29
32
|
import { Vector2 } from '../../math/Vector2.js';
|
|
@@ -268,6 +271,31 @@ class NodeBuilder {
|
|
|
268
271
|
*/
|
|
269
272
|
this.fragmentShader = null;
|
|
270
273
|
|
|
274
|
+
// WITH_GENESYS
|
|
275
|
+
/**
|
|
276
|
+
* Static material-feature cost for the shader-complexity debug view.
|
|
277
|
+
*
|
|
278
|
+
* @type {number}
|
|
279
|
+
* @default 0
|
|
280
|
+
*/
|
|
281
|
+
this.shaderComplexityBase = 0;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Fragment texture samples counted while generating the shader.
|
|
285
|
+
*
|
|
286
|
+
* @type {number}
|
|
287
|
+
* @default 0
|
|
288
|
+
*/
|
|
289
|
+
this.fragmentTextureSamples = 0;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Sample sites already counted, so a node that is generated twice is not double-counted.
|
|
293
|
+
*
|
|
294
|
+
* @type {Set<string>}
|
|
295
|
+
*/
|
|
296
|
+
this.shaderComplexitySampleKeys = new Set();
|
|
297
|
+
// !WITH_GENESYS
|
|
298
|
+
|
|
271
299
|
/**
|
|
272
300
|
* The generated compute shader.
|
|
273
301
|
*
|
|
@@ -1296,6 +1324,46 @@ class NodeBuilder {
|
|
|
1296
1324
|
|
|
1297
1325
|
}
|
|
1298
1326
|
|
|
1327
|
+
// WITH_GENESYS
|
|
1328
|
+
/**
|
|
1329
|
+
* Counts one fragment texture sample toward the shader-complexity proxy.
|
|
1330
|
+
* Depth and cube textures are shadow maps and environment maps, and rect-area
|
|
1331
|
+
* LTC tables belong to the light loop, so they are not material cost.
|
|
1332
|
+
*
|
|
1333
|
+
* @param {?Texture} texture - The texture being sampled.
|
|
1334
|
+
* @param {string} [shaderStage=this.shaderStage] - The stage the sample is emitted for.
|
|
1335
|
+
* @param {string} [key=''] - Stable id for this sample site. Repeated generation of the same site counts once.
|
|
1336
|
+
*/
|
|
1337
|
+
recordFragmentTextureSample( texture, shaderStage = this.shaderStage, key = '' ) {
|
|
1338
|
+
|
|
1339
|
+
if ( shaderStage !== 'fragment' ) return;
|
|
1340
|
+
|
|
1341
|
+
if ( texture && ( texture.isDepthTexture === true || texture.isCubeTexture === true || texture.isRectAreaLTC === true ) ) return;
|
|
1342
|
+
|
|
1343
|
+
if ( key !== '' ) {
|
|
1344
|
+
|
|
1345
|
+
if ( this.shaderComplexitySampleKeys.has( key ) ) return;
|
|
1346
|
+
|
|
1347
|
+
this.shaderComplexitySampleKeys.add( key );
|
|
1348
|
+
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
this.fragmentTextureSamples ++;
|
|
1352
|
+
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/**
|
|
1356
|
+
* Returns the shader-complexity proxy for the material currently being built.
|
|
1357
|
+
*
|
|
1358
|
+
* @return {number} Base feature cost plus weighted fragment texture samples.
|
|
1359
|
+
*/
|
|
1360
|
+
getShaderComplexityCost() {
|
|
1361
|
+
|
|
1362
|
+
return this.shaderComplexityBase + this.fragmentTextureSamples * SHADER_COMPLEXITY_TEXTURE_COST;
|
|
1363
|
+
|
|
1364
|
+
}
|
|
1365
|
+
// !WITH_GENESYS
|
|
1366
|
+
|
|
1299
1367
|
/**
|
|
1300
1368
|
* Generates a texture LOD shader string for the given texture data.
|
|
1301
1369
|
*
|
|
@@ -3119,6 +3187,12 @@ class NodeBuilder {
|
|
|
3119
3187
|
*/
|
|
3120
3188
|
prebuild() {
|
|
3121
3189
|
|
|
3190
|
+
// WITH_GENESYS
|
|
3191
|
+
this.shaderComplexityBase = 0;
|
|
3192
|
+
this.fragmentTextureSamples = 0;
|
|
3193
|
+
this.shaderComplexitySampleKeys.clear();
|
|
3194
|
+
// !WITH_GENESYS
|
|
3195
|
+
|
|
3122
3196
|
const { renderer, material } = this;
|
|
3123
3197
|
|
|
3124
3198
|
// < renderer.contextNode >
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// WITH_GENESYS
|
|
2
|
+
// Shader and lighting complexity debug views. Costs are a material proxy, not ISA instruction counts.
|
|
3
|
+
// !WITH_GENESYS
|
|
4
|
+
|
|
5
|
+
import Node from '../core/Node.js';
|
|
6
|
+
import { float, vec3, vec4, If, Fn } from '../tsl/TSLCore.js';
|
|
7
|
+
import { fract, min, mix } from '../math/MathNode.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Shaded output. No complexity debug view.
|
|
11
|
+
*
|
|
12
|
+
* @type {string}
|
|
13
|
+
*/
|
|
14
|
+
export const DEBUG_VIEW_NONE = 'none';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Material-cost heatmap. Overlapping draws accumulate, then a fullscreen pass applies the ramp.
|
|
18
|
+
*
|
|
19
|
+
* @type {string}
|
|
20
|
+
*/
|
|
21
|
+
export const DEBUG_VIEW_SHADER_COMPLEXITY = 'shaderComplexity';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Direct-light count heatmap. The visible surface wins.
|
|
25
|
+
*
|
|
26
|
+
* @type {string}
|
|
27
|
+
*/
|
|
28
|
+
export const DEBUG_VIEW_LIGHTING_COMPLEXITY = 'lightingComplexity';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 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.
|
|
34
|
+
*
|
|
35
|
+
* @type {number}
|
|
36
|
+
*/
|
|
37
|
+
export const DEFAULT_SHADER_COMPLEXITY_BUDGET = 800;
|
|
38
|
+
|
|
39
|
+
/** @type {number} */
|
|
40
|
+
export const SHADER_COMPLEXITY_BASE_UNLIT = 20;
|
|
41
|
+
|
|
42
|
+
/** @type {number} */
|
|
43
|
+
export const SHADER_COMPLEXITY_BASE_LIT = 40;
|
|
44
|
+
|
|
45
|
+
/** @type {number} */
|
|
46
|
+
export const SHADER_COMPLEXITY_TEXTURE_COST = 16;
|
|
47
|
+
|
|
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
|
+
/**
|
|
55
|
+
* Attenuation above this counts as a light that shades the pixel.
|
|
56
|
+
*
|
|
57
|
+
* @type {number}
|
|
58
|
+
*/
|
|
59
|
+
export const LIGHTING_COMPLEXITY_EPSILON = 0.001;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Added on top of 1 when a received light casts a shadow.
|
|
63
|
+
* Applied even where the shadow map is black.
|
|
64
|
+
*
|
|
65
|
+
* @type {number}
|
|
66
|
+
*/
|
|
67
|
+
export const LIGHTING_COMPLEXITY_SHADOW_WEIGHT = 0.5;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Unreal `ShaderComplexityColors` from `BaseEngine.ini`.
|
|
71
|
+
*
|
|
72
|
+
* @type {Array<Array<number>>}
|
|
73
|
+
*/
|
|
74
|
+
const SHADER_COMPLEXITY_COLORS = [
|
|
75
|
+
[ 0.0, 1.0, 0.127 ],
|
|
76
|
+
[ 0.0, 1.0, 0.0 ],
|
|
77
|
+
[ 0.046, 0.52, 0.0 ],
|
|
78
|
+
[ 0.215, 0.215, 0.0 ],
|
|
79
|
+
[ 0.52, 0.046, 0.0 ],
|
|
80
|
+
[ 0.7, 0.0, 0.0 ],
|
|
81
|
+
[ 1.0, 0.0, 0.0 ],
|
|
82
|
+
[ 1.0, 0.0, 0.5 ],
|
|
83
|
+
[ 1.0, 0.9, 0.9 ]
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Unreal `LightComplexityColors` from `BaseEngine.ini`.
|
|
88
|
+
*
|
|
89
|
+
* @type {Array<Array<number>>}
|
|
90
|
+
*/
|
|
91
|
+
const LIGHT_COMPLEXITY_COLORS = [
|
|
92
|
+
[ 0.0, 0.0, 0.0 ],
|
|
93
|
+
[ 0.0, 0.0, 0.4 ],
|
|
94
|
+
[ 0.0, 0.3, 1.0 ],
|
|
95
|
+
[ 0.0, 0.7, 0.4 ],
|
|
96
|
+
[ 0.0, 1.0, 0.0 ],
|
|
97
|
+
[ 0.8, 0.8, 0.0 ],
|
|
98
|
+
[ 1.0, 0.3, 0.0 ],
|
|
99
|
+
[ 0.7, 0.0, 0.0 ],
|
|
100
|
+
[ 0.5, 0.0, 0.5 ],
|
|
101
|
+
[ 0.7, 0.3, 0.7 ],
|
|
102
|
+
[ 1.0, 0.9, 0.9 ]
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
const rgb = ( colors, index ) => vec3( colors[ index ][ 0 ], colors[ index ][ 1 ], colors[ index ][ 2 ] );
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Linear sample of a fixed RGB ramp. `cost` is in steps, and one step is one light.
|
|
109
|
+
*
|
|
110
|
+
* @param {Node<float>} cost - Complexity in ramp steps.
|
|
111
|
+
* @param {Array<Array<number>>} colors - Ramp stops.
|
|
112
|
+
* @return {Node<vec3>} The ramp color.
|
|
113
|
+
*/
|
|
114
|
+
const colorizeLinear = ( cost, colors ) => {
|
|
115
|
+
|
|
116
|
+
const steps = colors.length - 1;
|
|
117
|
+
const expanded = cost.div( steps ).clamp( 0, 0.999 ).mul( steps );
|
|
118
|
+
const index = expanded.floor();
|
|
119
|
+
const frac = fract( expanded );
|
|
120
|
+
|
|
121
|
+
let color = rgb( colors, 0 );
|
|
122
|
+
|
|
123
|
+
for ( let i = 0; i < steps; i ++ ) {
|
|
124
|
+
|
|
125
|
+
const sample = mix( rgb( colors, i ), rgb( colors, i + 1 ), frac );
|
|
126
|
+
|
|
127
|
+
color = index.greaterThanEqual( float( i ) ).and( index.lessThan( float( i + 1 ) ) ).select( sample, color );
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return color;
|
|
132
|
+
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Maps a direct-light cost through Unreal's light-complexity ramp.
|
|
137
|
+
* Ten lights saturate at white.
|
|
138
|
+
*
|
|
139
|
+
* @param {Node<float>} cost - Accumulated light weight.
|
|
140
|
+
* @return {Node<vec3>} The ramp color.
|
|
141
|
+
*/
|
|
142
|
+
export const colorizeLightComplexity = ( cost ) => colorizeLinear( cost, LIGHT_COMPLEXITY_COLORS );
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Unreal's nonlinear shader-complexity ramp (`ColorizeComplexity`).
|
|
146
|
+
* The first third of the normalized cost spreads across the first seven stops.
|
|
147
|
+
*
|
|
148
|
+
* @param {Node<float>} complexity - Accumulated cost divided by the budget.
|
|
149
|
+
* @return {Node<vec3>} The ramp color.
|
|
150
|
+
*/
|
|
151
|
+
export const colorizeShaderComplexity = /*@__PURE__*/ Fn( ( [ complexity ] ) => {
|
|
152
|
+
|
|
153
|
+
const c = complexity.clamp( 0, 0.999 );
|
|
154
|
+
const expanded = min( float( SHADER_COMPLEXITY_COLORS.length - 2 ), c.mul( 18 ) );
|
|
155
|
+
const color = vec3( 0 ).toVar();
|
|
156
|
+
const frac = fract( expanded );
|
|
157
|
+
|
|
158
|
+
If( expanded.lessThan( 1 ), () => {
|
|
159
|
+
|
|
160
|
+
color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 0 ), rgb( SHADER_COMPLEXITY_COLORS, 1 ), frac ) );
|
|
161
|
+
|
|
162
|
+
} ).ElseIf( expanded.lessThan( 2 ), () => {
|
|
163
|
+
|
|
164
|
+
color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 1 ), rgb( SHADER_COMPLEXITY_COLORS, 2 ), frac ) );
|
|
165
|
+
|
|
166
|
+
} ).ElseIf( expanded.lessThan( 3 ), () => {
|
|
167
|
+
|
|
168
|
+
color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 2 ), rgb( SHADER_COMPLEXITY_COLORS, 3 ), frac ) );
|
|
169
|
+
|
|
170
|
+
} ).ElseIf( expanded.lessThan( 4 ), () => {
|
|
171
|
+
|
|
172
|
+
color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 3 ), rgb( SHADER_COMPLEXITY_COLORS, 4 ), frac ) );
|
|
173
|
+
|
|
174
|
+
} ).ElseIf( expanded.lessThan( 5 ), () => {
|
|
175
|
+
|
|
176
|
+
color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 4 ), rgb( SHADER_COMPLEXITY_COLORS, 5 ), frac ) );
|
|
177
|
+
|
|
178
|
+
} ).ElseIf( expanded.lessThan( 6 ), () => {
|
|
179
|
+
|
|
180
|
+
color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 5 ), rgb( SHADER_COMPLEXITY_COLORS, 6 ), frac ) );
|
|
181
|
+
|
|
182
|
+
} ).Else( () => {
|
|
183
|
+
|
|
184
|
+
// Nine stops, so the upper range uses the `count > 8` scale of 3.
|
|
185
|
+
const upper = c.sub( 0.33333333 ).mul( 3.0 );
|
|
186
|
+
const upperFrac = fract( upper );
|
|
187
|
+
|
|
188
|
+
If( upper.lessThanEqual( 1 ), () => {
|
|
189
|
+
|
|
190
|
+
color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 6 ), rgb( SHADER_COMPLEXITY_COLORS, 7 ), upperFrac ) );
|
|
191
|
+
|
|
192
|
+
} ).Else( () => {
|
|
193
|
+
|
|
194
|
+
color.assign( mix( rgb( SHADER_COMPLEXITY_COLORS, 7 ), rgb( SHADER_COMPLEXITY_COLORS, 8 ), upperFrac ) );
|
|
195
|
+
|
|
196
|
+
} );
|
|
197
|
+
|
|
198
|
+
} );
|
|
199
|
+
|
|
200
|
+
return color;
|
|
201
|
+
|
|
202
|
+
} );
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Reads the builder's shader-complexity proxy at code-generation time,
|
|
206
|
+
* after the real material graph has been generated and texture samples counted.
|
|
207
|
+
*
|
|
208
|
+
* @augments Node
|
|
209
|
+
*/
|
|
210
|
+
class ShaderComplexityRatioNode extends Node {
|
|
211
|
+
|
|
212
|
+
static get type() {
|
|
213
|
+
|
|
214
|
+
return 'ShaderComplexityRatioNode';
|
|
215
|
+
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
constructor() {
|
|
219
|
+
|
|
220
|
+
super( 'float' );
|
|
221
|
+
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
generate( builder ) {
|
|
225
|
+
|
|
226
|
+
const budget = builder.renderer.debug.shaderComplexityBudget;
|
|
227
|
+
const safeBudget = ( typeof budget === 'number' && budget > 0 ) ? budget : 1;
|
|
228
|
+
const ratio = builder.getShaderComplexityCost() / safeBudget;
|
|
229
|
+
|
|
230
|
+
return ratio.toFixed( 6 );
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Static fragment proxy for a material. Texture samples are counted later, during generation.
|
|
238
|
+
*
|
|
239
|
+
* @param {NodeMaterial} material - The material being built.
|
|
240
|
+
* @return {number} The base cost, excluding texture samples.
|
|
241
|
+
*/
|
|
242
|
+
export function getShaderComplexityBase( material ) {
|
|
243
|
+
|
|
244
|
+
let cost = material.lights === true ? SHADER_COMPLEXITY_BASE_LIT : SHADER_COMPLEXITY_BASE_UNLIT;
|
|
245
|
+
|
|
246
|
+
if ( material.useClearcoat === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
|
|
247
|
+
|
|
248
|
+
if ( material.useSheen === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
|
|
249
|
+
|
|
250
|
+
if ( material.useIridescence === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
|
|
251
|
+
|
|
252
|
+
if ( material.useAnisotropy === true ) cost += SHADER_COMPLEXITY_FEATURE_COST;
|
|
253
|
+
|
|
254
|
+
if ( material.useTransmission === true ) cost += SHADER_COMPLEXITY_TRANSMISSION_COST;
|
|
255
|
+
|
|
256
|
+
return cost;
|
|
257
|
+
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
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.
|
|
263
|
+
*
|
|
264
|
+
* @param {Node} resultNode - The material's shaded output.
|
|
265
|
+
* @return {Node<vec4>} The complexity ratio in the red channel.
|
|
266
|
+
*/
|
|
267
|
+
export function shaderComplexityOutput( resultNode ) {
|
|
268
|
+
|
|
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
|
+
const shaded = vec4( resultNode ).toVar();
|
|
273
|
+
|
|
274
|
+
const ratio = new ShaderComplexityRatioNode();
|
|
275
|
+
|
|
276
|
+
return vec4( ratio, 0, 0, 1 ).add( shaded.mul( 0 ) );
|
|
277
|
+
|
|
278
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import Node from '../core/Node.js';
|
|
2
|
-
import { property, vec3 } from '../tsl/TSLBase.js';
|
|
2
|
+
import { float, property, vec3 } from '../tsl/TSLBase.js';
|
|
3
3
|
import { hashArray } from '../core/NodeUtils.js';
|
|
4
4
|
import { warn } from '../../utils.js';
|
|
5
5
|
// WITH_GENESYS
|
|
6
|
+
import { colorizeLightComplexity, DEBUG_VIEW_LIGHTING_COMPLEXITY, LIGHTING_COMPLEXITY_EPSILON, LIGHTING_COMPLEXITY_SHADOW_WEIGHT } from '../display/ComplexityDebug.js';
|
|
7
|
+
// !WITH_GENESYS
|
|
8
|
+
// WITH_GENESYS
|
|
6
9
|
import LightProbeGridNode from './LightProbeGridNode.js';
|
|
7
10
|
// !WITH_GENESYS
|
|
8
11
|
|
|
@@ -27,6 +30,10 @@ const totalSpecular = property( 'vec3', 'totalSpecular' );
|
|
|
27
30
|
*/
|
|
28
31
|
const outgoingLight = property( 'vec3', 'outgoingLight' );
|
|
29
32
|
|
|
33
|
+
// WITH_GENESYS
|
|
34
|
+
const lightingComplexity = property( 'float', 'lightingComplexity' );
|
|
35
|
+
// !WITH_GENESYS
|
|
36
|
+
|
|
30
37
|
/**
|
|
31
38
|
* Sorts an array of lights in ascending order by their IDs.
|
|
32
39
|
*
|
|
@@ -380,6 +387,10 @@ class LightsNode extends Node {
|
|
|
380
387
|
reflectedLight
|
|
381
388
|
}, builder );
|
|
382
389
|
|
|
390
|
+
// WITH_GENESYS
|
|
391
|
+
this._addLightingComplexity( builder, lightNode, lightData );
|
|
392
|
+
// !WITH_GENESYS
|
|
393
|
+
|
|
383
394
|
}
|
|
384
395
|
|
|
385
396
|
/**
|
|
@@ -399,7 +410,39 @@ class LightsNode extends Node {
|
|
|
399
410
|
reflectedLight
|
|
400
411
|
}, builder );
|
|
401
412
|
|
|
413
|
+
// WITH_GENESYS
|
|
414
|
+
this._addLightingComplexity( builder, lightNode, lightData );
|
|
415
|
+
// !WITH_GENESYS
|
|
416
|
+
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// WITH_GENESYS
|
|
420
|
+
/**
|
|
421
|
+
* Adds this direct light's weight to the lighting-complexity accumulator.
|
|
422
|
+
* Point, spot, and rect-area lights count only where attenuation is in range.
|
|
423
|
+
* Directional lights have no falloff, so they always count. Shadow-casting
|
|
424
|
+
* lights add extra weight even where the shadow map is black.
|
|
425
|
+
*
|
|
426
|
+
* @private
|
|
427
|
+
* @param {NodeBuilder} builder - The current node builder.
|
|
428
|
+
* @param {LightingNode} lightNode - The light node being shaded.
|
|
429
|
+
* @param {Object} lightData - Direct light terms, including an optional attenuation node.
|
|
430
|
+
*/
|
|
431
|
+
_addLightingComplexity( builder, lightNode, lightData ) {
|
|
432
|
+
|
|
433
|
+
if ( builder.renderer.debug.view !== DEBUG_VIEW_LIGHTING_COMPLEXITY ) return;
|
|
434
|
+
|
|
435
|
+
if ( builder.material && builder.material.isShadowPassMaterial === true ) return;
|
|
436
|
+
|
|
437
|
+
const attenuation = lightData.attenuation !== undefined ? lightData.attenuation : float( 1 );
|
|
438
|
+
const light = lightNode.light;
|
|
439
|
+
const castsShadow = light !== undefined && light !== null && light.castShadow === true && builder.object.receiveShadow === true;
|
|
440
|
+
const weight = castsShadow ? float( 1 + LIGHTING_COMPLEXITY_SHADOW_WEIGHT ) : float( 1 );
|
|
441
|
+
|
|
442
|
+
lightingComplexity.addAssign( attenuation.greaterThan( LIGHTING_COMPLEXITY_EPSILON ).select( weight, float( 0 ) ) );
|
|
443
|
+
|
|
402
444
|
}
|
|
445
|
+
// !WITH_GENESYS
|
|
403
446
|
|
|
404
447
|
/**
|
|
405
448
|
* Setups the internal lights by building all respective
|
|
@@ -463,6 +506,14 @@ class LightsNode extends Node {
|
|
|
463
506
|
|
|
464
507
|
properties.nodes = stack.nodes;
|
|
465
508
|
|
|
509
|
+
// WITH_GENESYS
|
|
510
|
+
if ( builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_COMPLEXITY && ( builder.material === null || builder.material.isShadowPassMaterial !== true ) ) {
|
|
511
|
+
|
|
512
|
+
lightingComplexity.assign( 0 );
|
|
513
|
+
|
|
514
|
+
}
|
|
515
|
+
// !WITH_GENESYS
|
|
516
|
+
|
|
466
517
|
lightingModel.start( builder );
|
|
467
518
|
|
|
468
519
|
const { backdrop, backdropAlpha } = context;
|
|
@@ -487,10 +538,28 @@ class LightsNode extends Node {
|
|
|
487
538
|
totalDiffuseNode.assign( totalDiffuse );
|
|
488
539
|
totalSpecularNode.assign( directSpecular.add( indirectSpecular ) );
|
|
489
540
|
|
|
490
|
-
|
|
541
|
+
// WITH_GENESYS
|
|
542
|
+
const lightingComplexityView = builder.renderer.debug.view === DEBUG_VIEW_LIGHTING_COMPLEXITY && ( builder.material === null || builder.material.isShadowPassMaterial !== true );
|
|
543
|
+
|
|
544
|
+
if ( lightingComplexityView !== true ) {
|
|
545
|
+
|
|
546
|
+
outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) );
|
|
547
|
+
|
|
548
|
+
}
|
|
549
|
+
// !WITH_GENESYS
|
|
550
|
+
// outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) );
|
|
491
551
|
|
|
492
552
|
lightingModel.finish( builder );
|
|
493
553
|
|
|
554
|
+
// WITH_GENESYS
|
|
555
|
+
// After finish(), so clearcoat and sheen cannot draw over the heatmap.
|
|
556
|
+
if ( lightingComplexityView === true ) {
|
|
557
|
+
|
|
558
|
+
outgoingLightNode.assign( colorizeLightComplexity( lightingComplexity ) );
|
|
559
|
+
|
|
560
|
+
}
|
|
561
|
+
// !WITH_GENESYS
|
|
562
|
+
|
|
494
563
|
outgoingLightNode = outgoingLightNode.bypass( builder.removeStack() );
|
|
495
564
|
|
|
496
565
|
} else {
|
|
@@ -17,7 +17,10 @@ export const directPointLight = ( { color, lightVector, cutoffDistance, decayExp
|
|
|
17
17
|
|
|
18
18
|
const lightColor = color.mul( attenuation );
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
// WITH_GENESYS
|
|
21
|
+
return { lightDirection, lightColor, attenuation };
|
|
22
|
+
// !WITH_GENESYS
|
|
23
|
+
// return { lightDirection, lightColor };
|
|
21
24
|
|
|
22
25
|
};
|
|
23
26
|
|
|
@@ -88,6 +88,20 @@ class RectAreaLightNode extends AnalyticLightNode {
|
|
|
88
88
|
|
|
89
89
|
setupDirectRectArea( builder ) {
|
|
90
90
|
|
|
91
|
+
// WITH_GENESYS
|
|
92
|
+
// LTC lookups are part of the light loop, not the material's shader cost.
|
|
93
|
+
if ( _ltcLib !== null ) {
|
|
94
|
+
|
|
95
|
+
_ltcLib.LTC_FLOAT_1.isRectAreaLTC = true;
|
|
96
|
+
_ltcLib.LTC_FLOAT_2.isRectAreaLTC = true;
|
|
97
|
+
|
|
98
|
+
if ( _ltcLib.LTC_HALF_1 ) _ltcLib.LTC_HALF_1.isRectAreaLTC = true;
|
|
99
|
+
|
|
100
|
+
if ( _ltcLib.LTC_HALF_2 ) _ltcLib.LTC_HALF_2.isRectAreaLTC = true;
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
// !WITH_GENESYS
|
|
104
|
+
|
|
91
105
|
let ltc_1, ltc_2;
|
|
92
106
|
|
|
93
107
|
if ( builder.isAvailable( 'float32Filterable' ) ) {
|
|
@@ -135,7 +135,12 @@ class SpotLightNode extends AnalyticLightNode {
|
|
|
135
135
|
decayExponent: decayExponentNode
|
|
136
136
|
} );
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
// WITH_GENESYS
|
|
139
|
+
const attenuation = spotAttenuation.mul( lightAttenuation );
|
|
140
|
+
|
|
141
|
+
let lightColor = colorNode.mul( attenuation );
|
|
142
|
+
// !WITH_GENESYS
|
|
143
|
+
// let lightColor = colorNode.mul( spotAttenuation ).mul( lightAttenuation );
|
|
139
144
|
|
|
140
145
|
let projected, lightCoord;
|
|
141
146
|
|
|
@@ -159,7 +164,10 @@ class SpotLightNode extends AnalyticLightNode {
|
|
|
159
164
|
|
|
160
165
|
}
|
|
161
166
|
|
|
162
|
-
|
|
167
|
+
// WITH_GENESYS
|
|
168
|
+
return { lightColor, lightDirection, attenuation };
|
|
169
|
+
// !WITH_GENESYS
|
|
170
|
+
// return { lightColor, lightDirection };
|
|
163
171
|
|
|
164
172
|
}
|
|
165
173
|
|
|
@@ -191,7 +191,15 @@ class Background extends DataMap {
|
|
|
191
191
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
|
|
194
|
+
// WITH_GENESYS
|
|
195
|
+
// The background is depth-tested off, so additive shader complexity would add it to every pixel.
|
|
196
|
+
if ( renderer.debug.view !== 'shaderComplexity' ) {
|
|
197
|
+
|
|
198
|
+
renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
|
|
199
|
+
|
|
200
|
+
}
|
|
201
|
+
// !WITH_GENESYS
|
|
202
|
+
// renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
|
|
195
203
|
|
|
196
204
|
} else {
|
|
197
205
|
|
|
@@ -215,6 +223,14 @@ class Background extends DataMap {
|
|
|
215
223
|
|
|
216
224
|
//
|
|
217
225
|
|
|
226
|
+
// WITH_GENESYS
|
|
227
|
+
if ( renderer.debug.view === 'shaderComplexity' ) {
|
|
228
|
+
|
|
229
|
+
_clearColor.set( 0, 0, 0, 1 );
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
// !WITH_GENESYS
|
|
233
|
+
|
|
218
234
|
if ( renderer.autoClear === true || forceClear === true ) {
|
|
219
235
|
|
|
220
236
|
const clearColorValue = renderContext.clearColorValue;
|