From 0eae2928693d40c1bde4cc20dde75ada985f8cbf Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Tue, 10 Dec 2024 13:29:41 +0100 Subject: [PATCH] Nodes: Document more modules. (#30087) --- src/nodes/fog/Fog.js | 4 +- src/nodes/lighting/AnalyticLightNode.js | 3 +- src/nodes/lighting/LightsNode.js | 113 ++++++++++++- src/nodes/lighting/ShadowBaseNode.js | 50 ++++++ src/nodes/lighting/ShadowNode.js | 205 +++++++++++++++++++++++- src/nodes/math/TriNoise3D.js | 2 - 6 files changed, 369 insertions(+), 8 deletions(-) diff --git a/src/nodes/fog/Fog.js b/src/nodes/fog/Fog.js index ab83a9225e02e5..3cd015cdbce9d2 100644 --- a/src/nodes/fog/Fog.js +++ b/src/nodes/fog/Fog.js @@ -2,6 +2,8 @@ import { positionView } from '../accessors/Position.js'; import { smoothstep } from '../math/MathNode.js'; import { Fn, vec4 } from '../tsl/TSLBase.js'; +/** @module Fog **/ + /** * Returns a node that represents the `z` coordinate in view space * for the current fragment. It's a different representation of the @@ -75,7 +77,7 @@ export const fog = Fn( ( [ color, factor ] ) => { export function rangeFog( color, near, far ) { // @deprecated, r171 - console.warn( 'THREE.TSL: "rangeFog( color, near, far )" is deprecated. Use "fog( color, rangeFog( near, far ) )" instead.' ); + console.warn( 'THREE.TSL: "rangeFog( color, near, far )" is deprecated. Use "fog( color, rangeFogFactor( near, far ) )" instead.' ); return fog( color, rangeFogFactor( near, far ) ); } diff --git a/src/nodes/lighting/AnalyticLightNode.js b/src/nodes/lighting/AnalyticLightNode.js index e94053d9bea011..21233582d689ca 100644 --- a/src/nodes/lighting/AnalyticLightNode.js +++ b/src/nodes/lighting/AnalyticLightNode.js @@ -126,7 +126,8 @@ class AnalyticLightNode extends LightingNode { /** * Setups the shadow for this light. This method is only executed if the light - * cast shadows and the current build object receives shadows. + * cast shadows and the current build object receives shadows. It incorporates + * shadows into the lighting computation. * * @param {NodeBuilder} builder - The current node builder. */ diff --git a/src/nodes/lighting/LightsNode.js b/src/nodes/lighting/LightsNode.js index ae7ff18ebc84f0..7e601de752809f 100644 --- a/src/nodes/lighting/LightsNode.js +++ b/src/nodes/lighting/LightsNode.js @@ -2,6 +2,8 @@ import Node from '../core/Node.js'; import { nodeObject, vec3 } from '../tsl/TSLBase.js'; import { hashArray } from '../core/NodeUtils.js'; +/** @module LightsNode **/ + const sortLights = ( lights ) => { return lights.sort( ( a, b ) => a.id - b.id ); @@ -26,6 +28,13 @@ const getLightNodeById = ( id, lightNodes ) => { const _lightsNodeRef = /*@__PURE__*/ new WeakMap(); +/** + * This node represents the scene's lighting and manages the lighting model's life cycle + * for the current build 3D object. It is responsible for computing the total outgoing + * light in a given lighting context. + * + * @augments Node + */ class LightsNode extends Node { static get type() { @@ -34,29 +43,76 @@ class LightsNode extends Node { } + /** + * Constructs a new lights node. + */ constructor() { super( 'vec3' ); + /** + * A node representing the total diffuse light. + * + * @type {Node} + */ this.totalDiffuseNode = vec3().toVar( 'totalDiffuse' ); + + /** + * A node representing the total specular light. + * + * @type {Node} + */ this.totalSpecularNode = vec3().toVar( 'totalSpecular' ); + /** + * A node representing the outgoing light. + * + * @type {Node} + */ this.outgoingLightNode = vec3().toVar( 'outgoingLight' ); + /** + * An array representing the lights in the scene. + * + * @private + * @type {Array} + */ this._lights = []; + /** + * For each light in the scene, this node will create a + * corresponding light node. + * + * @private + * @type {Array?} + * @default null + */ this._lightNodes = null; + + /** + * A hash for identifying the current light nodes setup. + * + * @private + * @type {String?} + * @default null + */ this._lightNodesHash = null; + /** + * `LightsNode` sets this property to `true` by default. + * + * @type {Boolean} + * @default true + */ this.global = true; } /** - * Overwrites the default `customCacheKey()` implementation by including the + * Overwrites the default {@link Node#customCacheKey} implementation by including the * light IDs into the cache key. * - * @return {Number} The hash. + * @return {Number} The custom cache key. */ customCacheKey() { @@ -72,6 +128,12 @@ class LightsNode extends Node { } + /** + * Computes a hash value for identifying the current light nodes setup. + * + * @param {NodeBuilder} builder - A reference to the current node builder. + * @return {String} The computed hash. + */ getHash( builder ) { if ( this._lightNodesHash === null ) { @@ -106,6 +168,12 @@ class LightsNode extends Node { } + /** + * Creates lighting nodes for each scene light. This makes it possible to further + * process lights in the node system. + * + * @param {NodeBuilder} builder - A reference to the current node builder. + */ setupLightsNode( builder ) { const lightNodes = []; @@ -133,6 +201,8 @@ class LightsNode extends Node { if ( lightNode === null ) { + // find the corresponding node type for a given light + const lightNodeClass = nodeLibrary.getLightNodeClass( light.constructor ); if ( lightNodeClass === null ) { @@ -167,6 +237,13 @@ class LightsNode extends Node { } + /** + * Setups the internal lights by building all respective + * light nodes. + * + * @param {NodeBuilder} builder - A reference to the current node builder. + * @param {Array} lightNodes - An array of lighting nodes. + */ setupLights( builder, lightNodes ) { for ( const lightNode of lightNodes ) { @@ -177,6 +254,14 @@ class LightsNode extends Node { } + /** + * The implementation makes sure that for each light in the scene + * there is a corresponding light node. By building the light nodes + * and evaluating the lighting model the outgoing light is computed. + * + * @param {NodeBuilder} builder - A reference to the current node builder. + * @return {Node} A node representing the outgoing light. + */ setup( builder ) { if ( this._lightNodes === null ) this.setupLightsNode( builder ); @@ -253,6 +338,12 @@ class LightsNode extends Node { } + /** + * Configures this node with an array of lights. + * + * @param {Array} lights - An array of lights. + * @return {LightsNode} A reference to this node. + */ setLights( lights ) { this._lights = lights; @@ -264,12 +355,22 @@ class LightsNode extends Node { } + /** + * Returns an array of the scene's lights. + * + * @return {Array} The scene's lights. + */ getLights() { return this._lights; } + /** + * Whether the scene has lights or not. + * + * @type {Boolean} + */ get hasLights() { return this._lights.length > 0; @@ -280,4 +381,12 @@ class LightsNode extends Node { export default LightsNode; +/** + * Factory method for creating an instance of `LightsNode` and configuring + * it with the given array of lights. + * + * @method + * @param {Array} lights - An array of lights. + * @return {LightsNode} The created lights node. + */ export const lights = ( lights = [] ) => nodeObject( new LightsNode() ).setLights( lights ); diff --git a/src/nodes/lighting/ShadowBaseNode.js b/src/nodes/lighting/ShadowBaseNode.js index 965f9b002e6650..ccb5c64776f551 100644 --- a/src/nodes/lighting/ShadowBaseNode.js +++ b/src/nodes/lighting/ShadowBaseNode.js @@ -3,6 +3,17 @@ import { NodeUpdateType } from '../core/constants.js'; import { vec3 } from '../tsl/TSLBase.js'; import { positionWorld } from '../accessors/Position.js'; +/** @module ShadowBaseNode **/ + +/** + * Base class for all shadow nodes. + * + * Shadow nodes encapsulate shadow related logic and are always coupled to lighting nodes. + * Lighting nodes might share the same shadow node type or use specific ones depending on + * their requirements. + * + * @augments Node + */ class ShadowBaseNode extends Node { static get type() { @@ -11,17 +22,46 @@ class ShadowBaseNode extends Node { } + /** + * Constructs a new shadow base node. + * + * @param {Light} light - The shadow casting light. + */ constructor( light ) { super(); + /** + * The shadow casting light. + * + * @type {Light} + */ this.light = light; + + /** + * Overwritten since shadows are updated by default per render. + * + * @type {String} + * @default 'render' + */ this.updateBeforeType = NodeUpdateType.RENDER; + /** + * This flag can be used for type testing. + * + * @type {Boolean} + * @readonly + * @default true + */ this.isShadowBaseNode = true; } + /** + * Setups the shadow position node which is by default the predefined TSL node object `shadowWorldPosition`. + * + * @param {(NodeBuilder|{material})} object - A configuration object that must at least hold a material reference. + */ setupShadowPosition( { material } ) { // Use assign inside an Fn() @@ -30,6 +70,11 @@ class ShadowBaseNode extends Node { } + /** + * Can be called when the shadow isn't required anymore. That can happen when + * a lighting node stops casting shadows by setting {@link Object3D#castShadow} + * to `false`. + */ dispose() { this.updateBeforeType = NodeUpdateType.NONE; @@ -38,6 +83,11 @@ class ShadowBaseNode extends Node { } +/** + * Represents the vertex position in world space during the shadow pass. + * + * @type {Node} + */ export const shadowWorldPosition = /*@__PURE__*/ vec3().toVar( 'shadowWorldPosition' ); export default ShadowBaseNode; diff --git a/src/nodes/lighting/ShadowNode.js b/src/nodes/lighting/ShadowNode.js index b1ea226e431047..37eff093d2ca42 100644 --- a/src/nodes/lighting/ShadowNode.js +++ b/src/nodes/lighting/ShadowNode.js @@ -63,12 +63,34 @@ const getShadowMaterial = ( light ) => { }; +/** @module ShadowNode **/ + +/** + * A shadow filtering function performing basic filtering. This is in fact an unfiltered version of the shadow map + * with a binary `[0,1]` result. + * + * @method + * @param {Object} inputs - The input parameter object. + * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. + * @param {Node} inputs.shadowCoord - The shadow coordinates. + * @return {Node} The filtering result. + */ export const BasicShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord } ) => { return texture( depthTexture, shadowCoord.xy ).compare( shadowCoord.z ); } ); +/** + * A shadow filtering function performing PCF filtering. + * + * @method + * @param {Object} inputs - The input parameter object. + * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. + * @param {Node} inputs.shadowCoord - The shadow coordinates. + * @param {LightShadow} inputs.shadow - The light shadow. + * @return {Node} The filtering result. + */ export const PCFShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord, shadow } ) => { const depthCompare = ( uv, compare ) => texture( depthTexture, uv ).compare( compare ); @@ -108,6 +130,16 @@ export const PCFShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord, } ); +/** + * A shadow filtering function performing PCF soft filtering. + * + * @method + * @param {Object} inputs - The input parameter object. + * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. + * @param {Node} inputs.shadowCoord - The shadow coordinates. + * @param {LightShadow} inputs.shadow - The light shadow. + * @return {Node} The filtering result. + */ export const PCFSoftShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord, shadow } ) => { const depthCompare = ( uv, compare ) => texture( depthTexture, uv ).compare( compare ); @@ -164,8 +196,15 @@ export const PCFSoftShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoo } ); -// VSM - +/** + * A shadow filtering function performing VSM filtering. + * + * @method + * @param {Object} inputs - The input parameter object. + * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. + * @param {Node} inputs.shadowCoord - The shadow coordinates. + * @return {Node} The filtering result. + */ export const VSMShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord } ) => { const occlusion = float( 1 ).toVar(); @@ -188,6 +227,17 @@ export const VSMShadowFilter = /*@__PURE__*/ Fn( ( { depthTexture, shadowCoord } } ); +/** + * Represents the shader code for the first VSM render pass. + * + * @method + * @param {Object} inputs - The input parameter object. + * @param {Node} inputs.samples - The number of samples + * @param {Node} inputs.radius - The radius. + * @param {Node} inputs.size - The size. + * @param {TextureNode} inputs.shadowPass - A reference to the render target's depth data. + * @return {Node} The VSM output. + */ const VSMPassVertical = /*@__PURE__*/ Fn( ( { samples, radius, size, shadowPass } ) => { const mean = float( 0 ).toVar(); @@ -214,6 +264,17 @@ const VSMPassVertical = /*@__PURE__*/ Fn( ( { samples, radius, size, shadowPass } ); +/** + * Represents the shader code for the second VSM render pass. + * + * @method + * @param {Object} inputs - The input parameter object. + * @param {Node} inputs.samples - The number of samples + * @param {Node} inputs.radius - The radius. + * @param {Node} inputs.size - The size. + * @param {TextureNode} inputs.shadowPass - The result of the first VSM render pass. + * @return {Node} The VSM output. + */ const VSMPassHorizontal = /*@__PURE__*/ Fn( ( { samples, radius, size, shadowPass } ) => { const mean = float( 0 ).toVar(); @@ -246,6 +307,11 @@ const _shadowFilterLib = [ BasicShadowFilter, PCFShadowFilter, PCFSoftShadowFilt const _quadMesh = /*@__PURE__*/ new QuadMesh(); +/** + * Represents the default shadow implementation for lighting nodes. + * + * @augments ShadowBaseNode + */ class ShadowNode extends ShadowBaseNode { static get type() { @@ -254,26 +320,101 @@ class ShadowNode extends ShadowBaseNode { } + /** + * Constructs a new shadow node. + * + * @param {Light} light - The shadow casting light. + * @param {LightShadow?} [shadow=null] - An optional light shadow. + */ constructor( light, shadow = null ) { super( light ); + /** + * The light shadow which defines the properties light's + * shadow. + * + * @type {LightShadow?} + * @default null + */ this.shadow = shadow || light.shadow; + /** + * A reference to the shadow map which is a render target. + * + * @type {RenderTarget?} + * @default null + */ this.shadowMap = null; + /** + * Only relevant for VSM shadows. Render target for the + * first VSM render pass. + * + * @type {RenderTarget?} + * @default null + */ this.vsmShadowMapVertical = null; + + /** + * Only relevant for VSM shadows. Render target for the + * second VSM render pass. + * + * @type {RenderTarget?} + * @default null + */ this.vsmShadowMapHorizontal = null; + /** + * Only relevant for VSM shadows. Node material which + * is used to render the first VSM pass. + * + * @type {NodeMaterial?} + * @default null + */ this.vsmMaterialVertical = null; + + /** + * Only relevant for VSM shadows. Node material which + * is used to render the second VSM pass. + * + * @type {NodeMaterial?} + * @default null + */ this.vsmMaterialHorizontal = null; + /** + * A reference to the output node which defines the + * final result of this shadow node. + * + * @type {Node?} + * @private + * @default null + */ this._node = null; + /** + * This flag can be used for type testing. + * + * @type {Boolean} + * @readonly + * @default true + */ this.isShadowNode = true; } + /** + * Setups the shadow filtering. + * + * @param {NodeBuilder} builder - A reference to the current node builder. + * @param {Object} inputs - A configuration object that defines the shadow filtering. + * @param {Function} inputs.filterFn - This function defines the filtering type of the shadow map e.g. PCF. + * @param {DepthTexture} inputs.depthTexture - A reference to the shadow map's texture data. + * @param {Node} inputs.shadowCoord - Shadow coordinates which are used to sample from the shadow map. + * @param {LightShadow} inputs.shadow - The light shadow. + * @return {Node} The result node of the shadow filtering. + */ setupShadowFilter( builder, { filterFn, depthTexture, shadowCoord, shadow } ) { const frustumTest = shadowCoord.x.greaterThanEqual( 0 ) @@ -288,6 +429,13 @@ class ShadowNode extends ShadowBaseNode { } + /** + * Setups the shadow coordinates. + * + * @param {NodeBuilder} builder - A reference to the current node builder. + * @param {Node} shadowPosition - A node representing the shadow position. + * @return {Node} The shadow coordinates. + */ setupShadowCoord( builder, shadowPosition ) { const { shadow } = this; @@ -335,12 +483,24 @@ class ShadowNode extends ShadowBaseNode { } + /** + * Returns the shadow filtering function for the given shadow type. + * + * @param {Number} type - The shadow type. + * @return {Function} The filtering function. + */ getShadowFilterFn( type ) { return _shadowFilterLib[ type ]; } + /** + * Setups the shadow output node. + * + * @param {NodeBuilder} builder - A reference to the current node builder. + * @return {Node} The shadow output node. + */ setupShadow( builder ) { const { renderer } = builder; @@ -415,6 +575,13 @@ class ShadowNode extends ShadowBaseNode { } + /** + * The implementation performs the setup of the output node. An output is only + * produces if shadow mapping is globally enabled in the renderer. + * + * @param {NodeBuilder} builder - A reference to the current node builder. + * @return {ShaderCallNodeInternal} The output node. + */ setup( builder ) { if ( builder.renderer.shadowMap.enabled === false ) return; @@ -449,6 +616,14 @@ class ShadowNode extends ShadowBaseNode { } + /** + * Renders the shadow. The logic of this function could be included + * into {@link ShadowNode#updateShadow} however more specialized shadow + * nodes might require a custom shadow map rendering. By having a + * dedicated method, it's easier to overwrite the default beavior. + * + * @param {NodeFrme} frame - A reference to the current node frame. + */ renderShadow( frame ) { const { shadow, shadowMap, light } = this; @@ -462,6 +637,11 @@ class ShadowNode extends ShadowBaseNode { } + /** + * Updates the shadow. + * + * @param {NodeFrme} frame - A reference to the current node frame. + */ updateShadow( frame ) { const { shadowMap, light, shadow } = this; @@ -520,6 +700,11 @@ class ShadowNode extends ShadowBaseNode { } + /** + * For VSM additional render passes are required. + * + * @param {Renderer} renderer - A reference to the current renderer. + */ vsmPass( renderer ) { const { shadow } = this; @@ -537,6 +722,9 @@ class ShadowNode extends ShadowBaseNode { } + /** + * Frees the internal resources of this shadow node. + */ dispose() { this.shadowMap.dispose(); @@ -566,6 +754,11 @@ class ShadowNode extends ShadowBaseNode { } + /** + * The implementation performs the update of the shadow map if necessary. + * + * @param {NodeFrme} frame - A reference to the current node frame. + */ updateBefore( frame ) { const { shadow } = this; @@ -590,4 +783,12 @@ class ShadowNode extends ShadowBaseNode { export default ShadowNode; +/** + * Factory method for creating an instance of `ShadowNode`. + * + * @method + * @param {Light} light - The shadow casting light. + * @param {LightShadow} shadow - The light shadow. + * @return {ShadowNode} The created shadow node. + */ export const shadow = ( light, shadow ) => nodeObject( new ShadowNode( light, shadow ) ); diff --git a/src/nodes/math/TriNoise3D.js b/src/nodes/math/TriNoise3D.js index 7e3ceee7d56c54..3b648703750b7c 100644 --- a/src/nodes/math/TriNoise3D.js +++ b/src/nodes/math/TriNoise3D.js @@ -29,8 +29,6 @@ const tri3 = /*@__PURE__*/ Fn( ( [ p ] ) => { ] } ); -/** @module Hash **/ - /** * Generates a noise value from the given position, speed and time parameters. *