From 78a39035900f995af1561b70436061ad217ec14a Mon Sep 17 00:00:00 2001 From: Mugen87 Date: Thu, 5 Dec 2024 14:16:40 +0100 Subject: [PATCH 1/2] Node: Document more modules. --- src/nodes/core/Node.js | 3 + src/nodes/display/PassNode.js | 2 +- src/nodes/display/ScreenNode.js | 46 ++++++++ src/nodes/display/ViewportDepthNode.js | 38 +++++++ src/nodes/display/ViewportDepthTextureNode.js | 13 +++ .../display/ViewportSharedTextureNode.js | 13 +++ src/nodes/display/ViewportTextureNode.js | 36 ++++++ src/nodes/utils/RTTNode.js | 103 +++++++++++++++++- src/nodes/utils/RotateNode.js | 30 +++++ src/nodes/utils/TriplanarTexturesNode.js | 57 ++++++++++ src/nodes/utils/UVUtils.js | 20 ++++ src/nodes/utils/ViewportUtils.js | 13 +++ 12 files changed, 371 insertions(+), 3 deletions(-) diff --git a/src/nodes/core/Node.js b/src/nodes/core/Node.js index f6799e5fe81e83..f890836b458a82 100644 --- a/src/nodes/core/Node.js +++ b/src/nodes/core/Node.js @@ -522,6 +522,7 @@ class Node extends EventDispatcher { * The {@link Node#updateBeforeType} property defines how often the update is executed. * * @param {NodeFrame} frame - A reference to the current node frame. + * @return {Boolean?} An optional bool that indicates whether the implementation actually performed an update or not (e.g. due to caching). */ updateBefore( /*frame*/ ) { @@ -534,6 +535,7 @@ class Node extends EventDispatcher { * The {@link Node#updateAfterType} property defines how often the update is executed. * * @param {NodeFrame} frame - A reference to the current node frame. + * @return {Boolean?} An optional bool that indicates whether the implementation actually performed an update or not (e.g. due to caching). */ updateAfter( /*frame*/ ) { @@ -546,6 +548,7 @@ class Node extends EventDispatcher { * The {@link Node#updateType} property defines how often the update is executed. * * @param {NodeFrame} frame - A reference to the current node frame. + * @return {Boolean?} An optional bool that indicates whether the implementation actually performed an update or not (e.g. due to caching). */ update( /*frame*/ ) { diff --git a/src/nodes/display/PassNode.js b/src/nodes/display/PassNode.js index 0a1dc6754dfbfb..5893a47d38fbc4 100644 --- a/src/nodes/display/PassNode.js +++ b/src/nodes/display/PassNode.js @@ -326,7 +326,7 @@ class PassNode extends TempNode { this.isPassNode = true; /** - * The `updateBeforeType` is set to `FRAME` since the pass render the + * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node renders the * scene once per frame in its {@link PassNode#updateBefore} method. * * @type {String} diff --git a/src/nodes/display/ScreenNode.js b/src/nodes/display/ScreenNode.js index 9700fa7ec73ac9..0f5921856fde7c 100644 --- a/src/nodes/display/ScreenNode.js +++ b/src/nodes/display/ScreenNode.js @@ -8,6 +8,13 @@ import { Vector4 } from '../../math/Vector4.js'; let screenSizeVec, viewportVec; +/** + * This node provides a collection of screen related metrics. + * Depending on {@link ScreenNode#scope}, the nodes can represent + * resolution or viewport data as well as fragment or uv coordinates. + * + * @augments Node + */ class ScreenNode extends Node { static get type() { @@ -16,16 +23,43 @@ class ScreenNode extends Node { } + /** + * Constructs a new screen node. + * + * @param {('coordinate'|'viewport'|'size'|'uv')} scope - The node's scope. + */ constructor( scope ) { super(); + /** + * The node represents different metric depending on which scope is selected. + * + * - `ScreenNode.COORDINATE`: Window-relative coordinates of the current fragment according to WebGPU standards. + * - `ScreenNode.VIEWPORT`: The current viewport defined as a four-dimnesional vector. + * - `ScreenNode.SIZE`: The dimensions of the current bound framebuffer. + * - `ScreenNode.UV`: Normalized screen coordinates. + * + * @type {('coordinate'|'viewport'|'size'|'uv')} + */ this.scope = scope; + /** + * This flag can be used for type testing. + * + * @type {Boolean} + * @readonly + * @default true + */ this.isViewportNode = true; } + /** + * This method is overwritten since the node type depends on the selected scope. + * + * @return {('vec2'|'vec4')} The node type. + */ getNodeType() { if ( this.scope === ScreenNode.VIEWPORT ) return 'vec4'; @@ -33,6 +67,11 @@ class ScreenNode extends Node { } + /** + * This method is overwritten since the node's update type depends on the selected scope. + * + * @return {NodeUpdateType} The update type. + */ getUpdateType() { let updateType = NodeUpdateType.NONE; @@ -49,6 +88,13 @@ class ScreenNode extends Node { } + /** + * `ScreenNode` implements {@link Node#update} to retrieve viewport and size information + * from the current renderer. + * + * @param {NodeFrame} frame - A reference to the current node frame. + * @return {Boolean?} An optional bool that indicates whether the implementation actually performed an update or not (e.g. due to caching). + */ update( { renderer } ) { const renderTarget = renderer.getRenderTarget(); diff --git a/src/nodes/display/ViewportDepthNode.js b/src/nodes/display/ViewportDepthNode.js index a0211891d309db..30a0c6ab53a564 100644 --- a/src/nodes/display/ViewportDepthNode.js +++ b/src/nodes/display/ViewportDepthNode.js @@ -4,6 +4,13 @@ import { cameraNear, cameraFar } from '../accessors/Camera.js'; import { positionView } from '../accessors/Position.js'; import { viewportDepthTexture } from './ViewportDepthTextureNode.js'; +/** + * This node offers a collection of features in context of the depth logic in the fragment shader. + * Depending on {@link ViewportDepthNode#scope}, it can be used to define a depth value for the current + * fragment or for depth evaluation purposes. + * + * @augments Node + */ class ViewportDepthNode extends Node { static get type() { @@ -12,13 +19,44 @@ class ViewportDepthNode extends Node { } + /** + * Constructs a new viewport depth node. + * + * @param {('depth'|'depthBase'|'linearDepth')} scope - The node's scope. + * @param {Node?} [valueNode=null] - The value node. + */ constructor( scope, valueNode = null ) { super( 'float' ); + /** + * The node behaves differently depending on which scope is selected. + * + * - `ViewportDepthNode.DEPTH_BASE`: Allows to define a value for the current fragment's depth. + * - `ViewportDepthNode.DEPTH`: Represents the depth value for the current fragment (`valueNode` is ignored). + * - `ViewportDepthNode.LINEAR_DEPTH`: Represents the linear (orthographic) depth value of the current fragment. + * If a `valueNode` is set, the scope can be used to convert perspective depth data to linear data. + * + * @type {('depth'|'depthBase'|'linearDepth')} + */ this.scope = scope; + + /** + * Can be used to define a custom depth value. + * The propety is ignored in the `ViewportDepthNode.DEPTH` scope. + * + * @type {Node} + * @default null + */ this.valueNode = valueNode; + /** + * This flag can be used for type testing. + * + * @type {Boolean} + * @readonly + * @default true + */ this.isViewportDepthNode = true; } diff --git a/src/nodes/display/ViewportDepthTextureNode.js b/src/nodes/display/ViewportDepthTextureNode.js index d45a20889f336d..ca3b0d0a596c70 100644 --- a/src/nodes/display/ViewportDepthTextureNode.js +++ b/src/nodes/display/ViewportDepthTextureNode.js @@ -6,6 +6,13 @@ import { DepthTexture } from '../../textures/DepthTexture.js'; let sharedDepthbuffer = null; +/** + * Represents the depth of the current viewport as a texture. This module + * can be used in combination with viewport texture to achieve effects + * that require depth evaluation. + * + * @augments ViewportTextureNode + */ class ViewportDepthTextureNode extends ViewportTextureNode { static get type() { @@ -14,6 +21,12 @@ class ViewportDepthTextureNode extends ViewportTextureNode { } + /** + * Constructs a new viewport shared texture node. + * + * @param {Node} [uvNode=screenUV] - The uv node. + * @param {Node?} [levelNode=null] - The level node. + */ constructor( uvNode = screenUV, levelNode = null ) { if ( sharedDepthbuffer === null ) { diff --git a/src/nodes/display/ViewportSharedTextureNode.js b/src/nodes/display/ViewportSharedTextureNode.js index a6a431753d0f5b..773b0e8f67e742 100644 --- a/src/nodes/display/ViewportSharedTextureNode.js +++ b/src/nodes/display/ViewportSharedTextureNode.js @@ -6,6 +6,13 @@ import { FramebufferTexture } from '../../textures/FramebufferTexture.js'; let _sharedFramebuffer = null; +/** + * `ViewportTextureNode` creates an internal texture for each texture instance. This module + * shares a texture across all instances of `ViewportSharedTextureNode`. It should + * be the first choice when using data of the default/screen framebuffer for performance reasons. + * + * @augments ViewportTextureNode + */ class ViewportSharedTextureNode extends ViewportTextureNode { static get type() { @@ -14,6 +21,12 @@ class ViewportSharedTextureNode extends ViewportTextureNode { } + /** + * Constructs a new viewport shared texture node. + * + * @param {Node} [uvNode=screenUV] - The uv node. + * @param {Node?} [levelNode=null] - The level node. + */ constructor( uvNode = screenUV, levelNode = null ) { if ( _sharedFramebuffer === null ) { diff --git a/src/nodes/display/ViewportTextureNode.js b/src/nodes/display/ViewportTextureNode.js index 2fd052925f75e1..26fbd6bb99e391 100644 --- a/src/nodes/display/ViewportTextureNode.js +++ b/src/nodes/display/ViewportTextureNode.js @@ -9,6 +9,15 @@ import { LinearMipmapLinearFilter } from '../../constants.js'; const _size = /*@__PURE__*/ new Vector2(); +/** + * A special type of texture node which represents the data of the current viewport + * as a texture. The module extracts data from the current bound framebuffer with + * a copy operation so no extra render pass is required to produce the texture data + * (which is good for performance). `ViewportTextureNode` can be used as an input for a + * variety of effects like refractive or transmissive materials. + * + * @augments TextureNode + */ class ViewportTextureNode extends TextureNode { static get type() { @@ -17,6 +26,13 @@ class ViewportTextureNode extends TextureNode { } + /** + * Constructs a new viewport texture node. + * + * @param {Node} [uvNode=screenUV] - The uv node. + * @param {Node?} [levelNode=null] - The level node. + * @param {Texture?} [framebufferTexture=null] - A framebuffer texture holding the viewport data. If not provided, a framebuffer texture is created automatically. + */ constructor( uvNode = screenUV, levelNode = null, framebufferTexture = null ) { if ( framebufferTexture === null ) { @@ -28,10 +44,30 @@ class ViewportTextureNode extends TextureNode { super( framebufferTexture, uvNode, levelNode ); + /** + * Whether to generate mipmaps or not. + * + * @type {Boolean} + * @default false + */ this.generateMipmaps = false; + /** + * This flag can be used for type testing. + * + * @type {Boolean} + * @readonly + * @default true + */ this.isOutputTextureNode = true; + /** + * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node renders the + * scene once per frame in its {@link ViewportTextureNode#updateBefore} method. + * + * @type {String} + * @default 'frame' + */ this.updateBeforeType = NodeUpdateType.FRAME; } diff --git a/src/nodes/utils/RTTNode.js b/src/nodes/utils/RTTNode.js index 57e2c62ce20e15..8ba77872a123c1 100644 --- a/src/nodes/utils/RTTNode.js +++ b/src/nodes/utils/RTTNode.js @@ -11,6 +11,14 @@ import { HalfFloatType } from '../../constants.js'; const _size = /*@__PURE__*/ new Vector2(); +/** + * `RTTNode` takes another node and uses it with a `QuadMesh` to render into a texture (RTT). + * This module is especially relevant in context of post processing where certain nodes require + * texture input for their effects. With the helper function `convertToTexture()` which is based + * on this module, the node system can automatically ensure texture input if required. + * + * @augments Node + */ class RTTNode extends TextureNode { static get type() { @@ -19,30 +27,110 @@ class RTTNode extends TextureNode { } + /** + * Constructs a new RTT node. + * + * @param {Node} node - The node to render a texture with. + * @param {Number?} [width=null] - The width of the internal render target. If not width is applied, the render target is automatically resized. + * @param {Number?} [height=null] - The height of the internal render target. + * @param {Object} [options={type:HalfFloatType}] - The options for the internal render target. + */ constructor( node, width = null, height = null, options = { type: HalfFloatType } ) { const renderTarget = new RenderTarget( width, height, options ); super( renderTarget.texture, uv() ); + /** + * The node to render a texture with. + * + * @type {Node} + */ this.node = node; + + /** + * The width of the internal render target. + * If not width is applied, the render target is automatically resized. + * + * @type {Number?} + * @default null + */ this.width = width; + + /** + * The height of the internal render target. + * + * @type {Number?} + * @default null + */ this.height = height; + /** + * The pixel ratio + * + * @type {Number} + * @default 1 + */ + this.pixelRatio = 1; + + /** + * The render target + * + * @type {RenderTarget} + */ this.renderTarget = renderTarget; + /** + * Whether the texture requires an update or not. + * + * @type {Boolean} + * @default true + */ this.textureNeedsUpdate = true; - this.autoUpdate = true; - this.updateMap = new WeakMap(); + /** + * Whether the texture should automatically be updated or not. + * + * @type {Boolean} + * @default true + */ + this.autoUpdate = true; + /** + * The node which is used with the quad mesh for RTT. + * + * @private + * @type {Node} + * @default null + */ this._rttNode = null; + + /** + * The internal quad mesh for RTT. + * + * @private + * @type {QuadMesh} + */ this._quadMesh = new QuadMesh( new NodeMaterial() ); + /** + * The `updateBeforeType` is set to `NodeUpdateType.RENDER` since the node updates + * the texture once per render in its {@link RTTNode#updateBefore} method. + * + * @type {String} + * @default 'render' + */ this.updateBeforeType = NodeUpdateType.RENDER; } + /** + * Whether the internal render target should automatically be resized or not. + * + * @type {Boolean} + * @readonly + * @default true + */ get autoSize() { return this.width === null; @@ -59,6 +147,12 @@ class RTTNode extends TextureNode { } + /** + * Sets the size of the internal render target + * + * @param {Number} width - The width to set. + * @param {Number} height - The width to set. + */ setSize( width, height ) { this.width = width; @@ -73,6 +167,11 @@ class RTTNode extends TextureNode { } + /** + * Sets the pixel ratio. This will also resize the render target. + * + * @param {Number} pixelRatio - The pixel ratio to set. + */ setPixelRatio( pixelRatio ) { this.pixelRatio = pixelRatio; diff --git a/src/nodes/utils/RotateNode.js b/src/nodes/utils/RotateNode.js index 1a01a26cb81f9a..c5bda223129e14 100644 --- a/src/nodes/utils/RotateNode.js +++ b/src/nodes/utils/RotateNode.js @@ -2,6 +2,11 @@ import TempNode from '../core/TempNode.js'; import { nodeProxy, vec4, mat2, mat4 } from '../tsl/TSLBase.js'; import { cos, sin } from '../math/MathNode.js'; +/** + * Applies a rotation to the given position node. + * + * @augments TempNode + */ class RotateNode extends TempNode { static get type() { @@ -10,15 +15,40 @@ class RotateNode extends TempNode { } + /** + * Constructs a new rorate node. + * + * @param {Node} positionNode - The position node. + * @param {Node} rotationNode - Represents the rotation that is applied to the position node. Depending + * on whether the position data are 2D or 3D, the rotation is expressed a single float value or an Euler value. + */ constructor( positionNode, rotationNode ) { super(); + /** + * The position node. + * + * @type {Node} + */ this.positionNode = positionNode; + + /** + * Represents the rotation that is applied to the position node. + * Depending on whether the position data are 2D or 3D, the rotation is expressed a single float value or an Euler value. + * + * @type {Node} + */ this.rotationNode = rotationNode; } + /** + * The type of the {@link RotateNode#positionNode} defines the node's type. + * + * @param {NodeBuilder} builder - The current node builder. + * @return {String} The node's type. + */ getNodeType( builder ) { return this.positionNode.getNodeType( builder ); diff --git a/src/nodes/utils/TriplanarTexturesNode.js b/src/nodes/utils/TriplanarTexturesNode.js index fa569a04bc9ffa..1ecb08afa4705f 100644 --- a/src/nodes/utils/TriplanarTexturesNode.js +++ b/src/nodes/utils/TriplanarTexturesNode.js @@ -5,6 +5,15 @@ import { positionLocal } from '../accessors/Position.js'; import { texture } from '../accessors/TextureNode.js'; import { nodeProxy, float, vec3 } from '../tsl/TSLBase.js'; +/** + * Can be used for triplanar texture mapping. + * + * ```js + * material.colorNode = triplanarTexture( texture( diffuseMap ) ); + * ``` + * + * @augments Node + */ class TriplanarTexturesNode extends Node { static get type() { @@ -13,17 +22,65 @@ class TriplanarTexturesNode extends Node { } + /** + * Constructs a new triplanar textures node. + * + * @param {Node} textureXNode - First texture node. + * @param {Node?} [textureYNode=null] - Second texture node. When not set, the shader will sample from `textureXNode` instead. + * @param {Node?} [textureZNode=null] - Third texture node. When not set, the shader will sample from `textureXNode` instead. + * @param {Node?} [scaleNode=float(1)] - The scale node. + * @param {Node?} [positionNode=positionLocal] - Vertex positions in local space. + * @param {Node?} [normalNode=normalLocal] - Normals in local space. + */ constructor( textureXNode, textureYNode = null, textureZNode = null, scaleNode = float( 1 ), positionNode = positionLocal, normalNode = normalLocal ) { super( 'vec4' ); + /** + * First texture node. + * + * @type {Node} + */ this.textureXNode = textureXNode; + + /** + * Second texture node. When not set, the shader will sample from `textureXNode` instead. + * + * @type {Node} + * @default null + */ this.textureYNode = textureYNode; + + /** + * Third texture node. When not set, the shader will sample from `textureXNode` instead. + * + * @type {Node} + * @default null + */ this.textureZNode = textureZNode; + /** + * The scale node. + * + * @type {Node} + * @default float(1) + */ this.scaleNode = scaleNode; + /** + * Vertex positions in local space. + * + * @type {Node} + * @default positionLocal + */ this.positionNode = positionNode; + + /** + * Normals in local space. + * + * @type {Node} + * @default normalLocal + */ this.normalNode = normalNode; } diff --git a/src/nodes/utils/UVUtils.js b/src/nodes/utils/UVUtils.js index b74e034a5ac083..3a8e3f48ec3095 100644 --- a/src/nodes/utils/UVUtils.js +++ b/src/nodes/utils/UVUtils.js @@ -1,12 +1,32 @@ import { Fn, vec2 } from '../tsl/TSLBase.js'; import { rotate } from './RotateNode.js'; +/** @module UVUtils **/ + +/** + * Rotates the given uv coordinates around a center point + * + * @method + * @param {vec2} uv - The uv coordinates. + * @param {float} rotation - The rotation defined in radians. + * @param {vec2} center - The center of rotation + * @return {vec2} The rotated uv coordinates. + */ export const rotateUV = /*@__PURE__*/ Fn( ( [ uv, rotation, center = vec2( 0.5 ) ] ) => { return rotate( uv.sub( center ), rotation ).add( center ); } ); +/** + * Applies a spherical warping effect to the given uv coordinats. + * + * @method + * @param {vec2} uv - The uv coordinates. + * @param {float} strength - The stength of the effect. + * @param {vec2} center - The center point + * @return {vec2} The updated uv coordinates. + */ export const spherizeUV = /*@__PURE__*/ Fn( ( [ uv, strength, center = vec2( 0.5 ) ] ) => { const delta = uv.sub( center ); diff --git a/src/nodes/utils/ViewportUtils.js b/src/nodes/utils/ViewportUtils.js index 033192481fec9b..c420f517624e7c 100644 --- a/src/nodes/utils/ViewportUtils.js +++ b/src/nodes/utils/ViewportUtils.js @@ -3,6 +3,19 @@ import { screenUV } from '../display/ScreenNode.js'; import { viewportDepthTexture } from '../display/ViewportDepthTextureNode.js'; import { linearDepth } from '../display/ViewportDepthNode.js'; +/** @module ViewportUtils **/ + +/** + * A special version of a screen uv function that involves a depth comparison + * when computing the final uvs. The function mitigates visual erros when + * using viewport texture nodes for refraction purposes. Without this function + * objects in front of a refractive surface might appear on the refractive surface + * which is incorrect. + * + * @method + * @param {vec2?} uv - Optional uv coordinates. By default `screenUV` is used. + * @return {vec2} The update uv coordinates. + */ export const viewportSafeUV = /*@__PURE__*/ Fn( ( [ uv = null ] ) => { const depth = linearDepth(); From 60c02fe9da681895c06f5d8df7e3322ce6801300 Mon Sep 17 00:00:00 2001 From: Mugen87 Date: Thu, 5 Dec 2024 14:32:51 +0100 Subject: [PATCH 2/2] Fix typo. --- src/nodes/display/ViewportSharedTextureNode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nodes/display/ViewportSharedTextureNode.js b/src/nodes/display/ViewportSharedTextureNode.js index 773b0e8f67e742..880e6f30567663 100644 --- a/src/nodes/display/ViewportSharedTextureNode.js +++ b/src/nodes/display/ViewportSharedTextureNode.js @@ -7,7 +7,7 @@ import { FramebufferTexture } from '../../textures/FramebufferTexture.js'; let _sharedFramebuffer = null; /** - * `ViewportTextureNode` creates an internal texture for each texture instance. This module + * `ViewportTextureNode` creates an internal texture for each node instance. This module * shares a texture across all instances of `ViewportSharedTextureNode`. It should * be the first choice when using data of the default/screen framebuffer for performance reasons. *