From dd3c651d76d8366aadacd9a727f9d9bcb4dbcd2a Mon Sep 17 00:00:00 2001 From: sunag Date: Tue, 14 Jul 2026 20:18:24 -0300 Subject: [PATCH 1/2] TSL: Introduce `OnBeforeRenderPipeline` and `OnAfterRenderPipeline` and update context directly on material (#34025) --- examples/jsm/tsl/display/AnaglyphPassNode.js | 5 +- examples/jsm/tsl/display/BilateralBlurNode.js | 5 +- examples/jsm/tsl/display/BloomNode.js | 19 ++++--- examples/jsm/tsl/display/DepthOfFieldNode.js | 16 ++++-- examples/jsm/tsl/display/FSR1Node.js | 10 ++-- examples/jsm/tsl/display/GTAONode.js | 8 +-- examples/jsm/tsl/display/GaussianBlurNode.js | 5 +- examples/jsm/tsl/display/GodraysNode.js | 5 +- examples/jsm/tsl/display/LensflareNode.js | 5 +- .../tsl/display/ParallaxBarrierPassNode.js | 5 +- .../jsm/tsl/display/RecurrentDenoiseNode.js | 5 +- examples/jsm/tsl/display/SMAANode.js | 13 +++-- examples/jsm/tsl/display/SSAONode.js | 12 +++-- examples/jsm/tsl/display/SSGINode.js | 5 +- examples/jsm/tsl/display/SSRNode.js | 5 +- examples/jsm/tsl/display/SSSNode.js | 5 +- examples/jsm/tsl/display/SharpenNode.js | 7 ++- examples/jsm/tsl/display/TAAUNode.js | 18 ++++--- examples/jsm/tsl/display/TRAANode.js | 15 +++--- .../jsm/tsl/display/TemporalReprojectNode.js | 30 ++++++----- .../webgpu_postprocessing_ssr_denoise.html | 4 +- src/Three.TSL.js | 2 + src/nodes/core/NodeBuilder.js | 11 ++++ src/nodes/lighting/ShadowNode.js | 9 +++- src/nodes/utils/EventNode.js | 54 +++++++++++++++++++ src/nodes/utils/RTTNode.js | 15 ++---- src/renderers/common/RenderPipeline.js | 51 +++++++++--------- 27 files changed, 224 insertions(+), 120 deletions(-) diff --git a/examples/jsm/tsl/display/AnaglyphPassNode.js b/examples/jsm/tsl/display/AnaglyphPassNode.js index 4bb3b0149c03f9..45f9bdf1206206 100644 --- a/examples/jsm/tsl/display/AnaglyphPassNode.js +++ b/examples/jsm/tsl/display/AnaglyphPassNode.js @@ -1,5 +1,5 @@ import { Matrix3, NodeMaterial, Vector3 } from 'three/webgpu'; -import { clamp, Fn, vec4, uv, uniform, max } from 'three/tsl'; +import { clamp, Fn, vec4, uv, uniform, max, context } from 'three/tsl'; import StereoCompositePassNode from './StereoCompositePassNode.js'; import { frameCorners } from '../../utils/CameraUtils.js'; @@ -523,7 +523,8 @@ class AnaglyphPassNode extends StereoCompositePassNode { } ); const material = this._material || ( this._material = new NodeMaterial() ); - material.fragmentNode = anaglyph().context( builder.getSharedContext() ); + material.contextNode = context( builder.getSharedContext() ); + material.fragmentNode = anaglyph(); material.name = 'Anaglyph'; material.needsUpdate = true; diff --git a/examples/jsm/tsl/display/BilateralBlurNode.js b/examples/jsm/tsl/display/BilateralBlurNode.js index c0f356cc15aade..6aa06da6c229d1 100644 --- a/examples/jsm/tsl/display/BilateralBlurNode.js +++ b/examples/jsm/tsl/display/BilateralBlurNode.js @@ -1,5 +1,5 @@ import { RenderTarget, Vector2, NodeMaterial, RendererUtils, QuadMesh, TempNode, NodeUpdateType } from 'three/webgpu'; -import { Fn, float, uv, uniform, convertToTexture, vec2, vec4, passTexture, luminance, abs, exp, max } from 'three/tsl'; +import { Fn, float, uv, uniform, convertToTexture, vec2, vec4, passTexture, luminance, abs, exp, max, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); @@ -302,7 +302,8 @@ class BilateralBlurNode extends TempNode { // const material = this._material || ( this._material = new NodeMaterial() ); - material.fragmentNode = blur().context( builder.getSharedContext() ); + material.contextNode = context( builder.getSharedContext() ); + material.fragmentNode = blur(); material.name = 'Bilateral_blur'; material.needsUpdate = true; diff --git a/examples/jsm/tsl/display/BloomNode.js b/examples/jsm/tsl/display/BloomNode.js index f96b43f02c9628..e7c2bc01dec220 100644 --- a/examples/jsm/tsl/display/BloomNode.js +++ b/examples/jsm/tsl/display/BloomNode.js @@ -1,5 +1,5 @@ import { HalfFloatType, RenderTarget, Vector2, Vector3, TempNode, QuadMesh, NodeMaterial, RendererUtils, NodeUpdateType } from 'three/webgpu'; -import { nodeObject, Fn, float, uv, passTexture, uniform, Loop, texture, luminance, smoothstep, mix, vec4, uniformArray, add, int, array } from 'three/tsl'; +import { nodeObject, Fn, float, uv, passTexture, uniform, Loop, texture, luminance, smoothstep, mix, vec4, uniformArray, add, int, array, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -408,10 +408,13 @@ class BloomNode extends TempNode { */ setup( builder ) { + const sharedContext = context( builder.getSharedContext() ); + // luminosity high pass material this._highPassFilterMaterial = this._highPassFilterMaterial || new NodeMaterial(); - this._highPassFilterMaterial.fragmentNode = this.highPassFn( { input: this.inputNode, threshold: this.threshold, smoothWidth: this.smoothWidth } ).context( builder.getSharedContext() ); + this._highPassFilterMaterial.contextNode = sharedContext; + this._highPassFilterMaterial.fragmentNode = this.highPassFn( { input: this.inputNode, threshold: this.threshold, smoothWidth: this.smoothWidth } ); this._highPassFilterMaterial.name = 'Bloom_highPass'; this._highPassFilterMaterial.needsUpdate = true; @@ -423,7 +426,7 @@ class BloomNode extends TempNode { for ( let i = 0; i < this._nMips; i ++ ) { - this._separableBlurMaterials.push( this._getSeparableBlurMaterial( builder, kernelSizeArray[ i ] ) ); + this._separableBlurMaterials.push( this._getSeparableBlurMaterial( sharedContext, kernelSizeArray[ i ] ) ); } @@ -447,7 +450,8 @@ class BloomNode extends TempNode { } ); this._compositeMaterial = this._compositeMaterial || new NodeMaterial(); - this._compositeMaterial.fragmentNode = compositePass().context( builder.getSharedContext() ); + this._compositeMaterial.contextNode = sharedContext; + this._compositeMaterial.fragmentNode = compositePass(); this._compositeMaterial.name = 'Bloom_comp'; this._compositeMaterial.needsUpdate = true; @@ -492,11 +496,11 @@ class BloomNode extends TempNode { * Create a separable blur material for the given kernel radius. * * @private - * @param {NodeBuilder} builder - The current node builder. + * @param {NodeContext} sharedContext * @param {number} kernelRadius - The kernel radius. * @return {NodeMaterial} */ - _getSeparableBlurMaterial( builder, kernelRadius ) { + _getSeparableBlurMaterial( sharedContext, kernelRadius ) { const coefficients = []; const sigma = kernelRadius / 3; @@ -554,7 +558,8 @@ class BloomNode extends TempNode { } ); const separableBlurMaterial = new NodeMaterial(); - separableBlurMaterial.fragmentNode = separableBlurPass().context( builder.getSharedContext() ); + separableBlurMaterial.contextNode = sharedContext; + separableBlurMaterial.fragmentNode = separableBlurPass(); separableBlurMaterial.name = 'Bloom_separable'; separableBlurMaterial.needsUpdate = true; diff --git a/examples/jsm/tsl/display/DepthOfFieldNode.js b/examples/jsm/tsl/display/DepthOfFieldNode.js index be86d887393663..bdc0811db3ad92 100644 --- a/examples/jsm/tsl/display/DepthOfFieldNode.js +++ b/examples/jsm/tsl/display/DepthOfFieldNode.js @@ -1,5 +1,5 @@ import { TempNode, NodeMaterial, NodeUpdateType, RenderTarget, Vector2, HalfFloatType, RedFormat, QuadMesh, RendererUtils } from 'three/webgpu'; -import { convertToTexture, nodeObject, Fn, uniform, smoothstep, step, texture, max, uniformArray, outputStruct, property, vec4, vec3, uv, Loop, min, mix, float } from 'three/tsl'; +import { convertToTexture, nodeObject, Fn, uniform, smoothstep, step, texture, max, uniformArray, outputStruct, property, vec4, vec3, uv, Loop, min, mix, float, context } from 'three/tsl'; import { gaussianBlur } from './GaussianBlurNode.js'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); @@ -351,6 +351,8 @@ class DepthOfFieldNode extends TempNode { */ setup( builder ) { + const sharedContext = context( builder.getSharedContext() ); + const kernels = this._generateKernels(); // CoC, near and far fields @@ -372,7 +374,8 @@ class DepthOfFieldNode extends TempNode { } ); - this._CoCMaterial.colorNode = CoC().context( builder.getSharedContext() ); + this._CoCMaterial.contextNode = sharedContext; + this._CoCMaterial.colorNode = CoC(); this._CoCMaterial.outputNode = outputNode; this._CoCMaterial.needsUpdate = true; @@ -408,7 +411,8 @@ class DepthOfFieldNode extends TempNode { } ); - this._blur64Material.fragmentNode = blur64().context( builder.getSharedContext() ); + this._blur64Material.contextNode = sharedContext; + this._blur64Material.fragmentNode = blur64(); this._blur64Material.needsUpdate = true; // bokeh 16 blur pass @@ -437,7 +441,8 @@ class DepthOfFieldNode extends TempNode { } ); - this._blur16Material.fragmentNode = blur16().context( builder.getSharedContext() ); + this._blur16Material.contextNode = sharedContext; + this._blur16Material.fragmentNode = blur16(); this._blur16Material.needsUpdate = true; // composite @@ -466,7 +471,8 @@ class DepthOfFieldNode extends TempNode { } ); - this._compositeMaterial.fragmentNode = composite().context( builder.getSharedContext() ); + this._compositeMaterial.contextNode = sharedContext; + this._compositeMaterial.fragmentNode = composite(); this._compositeMaterial.needsUpdate = true; return this._textureNode; diff --git a/examples/jsm/tsl/display/FSR1Node.js b/examples/jsm/tsl/display/FSR1Node.js index c2966621407514..8bbde596509a72 100644 --- a/examples/jsm/tsl/display/FSR1Node.js +++ b/examples/jsm/tsl/display/FSR1Node.js @@ -1,5 +1,5 @@ import { HalfFloatType, RenderTarget, Vector2, NodeMaterial, RendererUtils, QuadMesh, TempNode, NodeUpdateType } from 'three/webgpu'; -import { Fn, float, vec2, vec3, vec4, ivec2, int, uv, floor, fract, abs, max, min, clamp, saturate, sqrt, select, exp2, nodeObject, passTexture, textureSize, textureLoad, convertToTexture } from 'three/tsl'; +import { Fn, float, vec2, vec3, vec4, ivec2, int, uv, floor, fract, abs, max, min, clamp, saturate, sqrt, select, exp2, nodeObject, passTexture, textureSize, textureLoad, convertToTexture, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -423,15 +423,17 @@ class FSR1Node extends TempNode { // - const context = builder.getSharedContext(); + const sharedContext = context( builder.getSharedContext() ); const easuMaterial = this._easuMaterial || ( this._easuMaterial = new NodeMaterial() ); - easuMaterial.fragmentNode = easu().context( context ); + easuMaterial.contextNode = sharedContext; + easuMaterial.fragmentNode = easu(); easuMaterial.name = 'FSR1_EASU'; easuMaterial.needsUpdate = true; const rcasMaterial = this._rcasMaterial || ( this._rcasMaterial = new NodeMaterial() ); - rcasMaterial.fragmentNode = rcas().context( context ); + rcasMaterial.contextNode = sharedContext; + rcasMaterial.fragmentNode = rcas(); rcasMaterial.name = 'FSR1_RCAS'; rcasMaterial.needsUpdate = true; diff --git a/examples/jsm/tsl/display/GTAONode.js b/examples/jsm/tsl/display/GTAONode.js index 64bada8655cbe8..4a09081491b95d 100644 --- a/examples/jsm/tsl/display/GTAONode.js +++ b/examples/jsm/tsl/display/GTAONode.js @@ -1,5 +1,5 @@ import { DataTexture, RenderTarget, RepeatWrapping, Vector2, Vector3, TempNode, QuadMesh, NodeMaterial, RendererUtils, RedFormat } from 'three/webgpu'; -import { reference, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getNormalFromDepth, getViewPosition, getScreenPositionFromClip, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, vec2, vec3, vec4, int, dot, max, min, pow, abs, If, textureSize, sin, cos, PI, texture, passTexture, mat3, normalize, cross, mix, acos, clamp, interleavedGradientNoise, screenCoordinate, rand } from 'three/tsl'; +import { reference, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getNormalFromDepth, getViewPosition, getScreenPositionFromClip, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, vec2, vec3, vec4, int, dot, max, min, pow, abs, If, textureSize, sin, cos, PI, texture, passTexture, mat3, normalize, cross, mix, acos, clamp, interleavedGradientNoise, screenCoordinate, rand, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -350,7 +350,8 @@ class GTAONode extends TempNode { this._currentSamples = this.samples.value; - this._material.fragmentNode = this._ao().context( this._sharedContext ); + this._material.contextNode = context( this._sharedContext ); + this._material.fragmentNode = this._ao(); this._material.needsUpdate = true; } @@ -571,7 +572,8 @@ class GTAONode extends TempNode { this._sharedContext = builder.getSharedContext(); this._currentSamples = this.samples.value; - this._material.fragmentNode = this._ao().context( this._sharedContext ); + this._material.contextNode = context( builder.getSharedContext() ); + this._material.fragmentNode = this._ao(); this._material.needsUpdate = true; // diff --git a/examples/jsm/tsl/display/GaussianBlurNode.js b/examples/jsm/tsl/display/GaussianBlurNode.js index f9a16ab59489b1..b9f13b1c7cf18a 100644 --- a/examples/jsm/tsl/display/GaussianBlurNode.js +++ b/examples/jsm/tsl/display/GaussianBlurNode.js @@ -1,5 +1,5 @@ import { RenderTarget, Vector2, NodeMaterial, RendererUtils, QuadMesh, TempNode, NodeUpdateType } from 'three/webgpu'; -import { Fn, float, uv, uniform, convertToTexture, vec2, vec4, passTexture, premultiplyAlpha, unpremultiplyAlpha } from 'three/tsl'; +import { Fn, float, uv, uniform, convertToTexture, vec2, vec4, passTexture, premultiplyAlpha, unpremultiplyAlpha, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); @@ -287,7 +287,8 @@ class GaussianBlurNode extends TempNode { // const material = this._material || ( this._material = new NodeMaterial() ); - material.fragmentNode = blur().context( builder.getSharedContext() ); + material.contextNode = context( builder.getSharedContext() ); + material.fragmentNode = blur(); material.name = 'Gaussian_blur'; material.needsUpdate = true; diff --git a/examples/jsm/tsl/display/GodraysNode.js b/examples/jsm/tsl/display/GodraysNode.js index 0e265b2261987d..5ae23b5a1e19a1 100644 --- a/examples/jsm/tsl/display/GodraysNode.js +++ b/examples/jsm/tsl/display/GodraysNode.js @@ -1,5 +1,5 @@ import { Frustum, Matrix4, RenderTarget, Vector2, RendererUtils, QuadMesh, TempNode, NodeMaterial, NodeUpdateType, Vector3, Plane } from 'three/webgpu'; -import { cubeTexture, clamp, viewZToPerspectiveDepth, logarithmicDepthToViewZ, float, Loop, max, Fn, passTexture, uv, dot, uniformArray, If, getViewPosition, uniform, vec4, add, interleavedGradientNoise, screenCoordinate, round, mul, uint, mix, exp, vec3, distance, pow, reference, lightPosition, vec2, bool, texture, perspectiveDepthToViewZ, lightShadowMatrix } from 'three/tsl'; +import { cubeTexture, clamp, viewZToPerspectiveDepth, logarithmicDepthToViewZ, float, Loop, max, Fn, passTexture, uv, dot, uniformArray, If, getViewPosition, uniform, vec4, add, interleavedGradientNoise, screenCoordinate, round, mul, uint, mix, exp, vec3, distance, pow, reference, lightPosition, vec2, bool, texture, perspectiveDepthToViewZ, lightShadowMatrix, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -579,7 +579,8 @@ class GodraysNode extends TempNode { } ); - this._material.fragmentNode = godrays().context( builder.getSharedContext() ); + this._material.contextNode = context( builder.getSharedContext() ); + this._material.fragmentNode = godrays(); this._material.needsUpdate = true; return this._textureNode; diff --git a/examples/jsm/tsl/display/LensflareNode.js b/examples/jsm/tsl/display/LensflareNode.js index 05fedec18f2bdc..cda8298b60fa7f 100644 --- a/examples/jsm/tsl/display/LensflareNode.js +++ b/examples/jsm/tsl/display/LensflareNode.js @@ -1,5 +1,5 @@ import { RenderTarget, Vector2, TempNode, NodeUpdateType, QuadMesh, RendererUtils, NodeMaterial } from 'three/webgpu'; -import { convertToTexture, nodeObject, Fn, passTexture, uv, vec2, vec3, vec4, max, float, sub, int, Loop, fract, pow, distance } from 'three/tsl'; +import { convertToTexture, nodeObject, Fn, passTexture, uv, vec2, vec3, vec4, max, float, sub, int, Loop, fract, pow, distance, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -239,7 +239,8 @@ class LensflareNode extends TempNode { } ); - this._material.fragmentNode = lensflare().context( builder.getSharedContext() ); + this._material.contextNode = context( builder.getSharedContext() ); + this._material.fragmentNode = lensflare(); this._material.needsUpdate = true; return this._textureNode; diff --git a/examples/jsm/tsl/display/ParallaxBarrierPassNode.js b/examples/jsm/tsl/display/ParallaxBarrierPassNode.js index ae1baab0956e1f..6962a9485514bd 100644 --- a/examples/jsm/tsl/display/ParallaxBarrierPassNode.js +++ b/examples/jsm/tsl/display/ParallaxBarrierPassNode.js @@ -1,5 +1,5 @@ import { NodeMaterial } from 'three/webgpu'; -import { Fn, vec4, uv, If, mod, screenCoordinate } from 'three/tsl'; +import { Fn, vec4, uv, If, mod, screenCoordinate, context } from 'three/tsl'; import StereoCompositePassNode from './StereoCompositePassNode.js'; /** @@ -66,7 +66,8 @@ class ParallaxBarrierPassNode extends StereoCompositePassNode { } ); const material = this._material || ( this._material = new NodeMaterial() ); - material.fragmentNode = parallaxBarrier().context( builder.getSharedContext() ); + material.contextNode = context( builder.getSharedContext() ); + material.fragmentNode = parallaxBarrier(); material.needsUpdate = true; return super.setup( builder ); diff --git a/examples/jsm/tsl/display/RecurrentDenoiseNode.js b/examples/jsm/tsl/display/RecurrentDenoiseNode.js index cc27339f060f05..fd9c5eaa6a0757 100644 --- a/examples/jsm/tsl/display/RecurrentDenoiseNode.js +++ b/examples/jsm/tsl/display/RecurrentDenoiseNode.js @@ -1,4 +1,4 @@ -import { abs, atan, bool, convertToTexture, cos, cross, Discard, dot, EPSILON, exp, float, Fn, getScreenPosition, getViewPosition, If, int, log, Loop, luminance, mat2, max, mix, nodeObject, NodeUpdateType, normalize, passTexture, PI, property, reflect, sin, smoothstep, sqrt, tan, texture, uniform, unpackRGBToNormal, uv, vec2, vec3, vec4 } from 'three/tsl'; +import { abs, atan, bool, convertToTexture, cos, cross, Discard, dot, EPSILON, exp, float, Fn, getScreenPosition, getViewPosition, If, int, log, Loop, luminance, mat2, max, mix, nodeObject, NodeUpdateType, normalize, passTexture, PI, property, reflect, sin, smoothstep, sqrt, tan, texture, uniform, unpackRGBToNormal, uv, vec2, vec3, vec4, context } from 'three/tsl'; import { HalfFloatType, MathUtils, Matrix4, NodeMaterial, QuadMesh, RendererUtils, RenderTarget, TempNode, Vector2 } from 'three/webgpu'; import { bindAnalyticNoise } from '../utils/RNoise.js'; import { ENV_RAY_LENGTH_THRESHOLD } from '../utils/SpecularHelpers.js'; @@ -880,7 +880,8 @@ class RecurrentDenoiseNode extends TempNode { } ); - this._material.fragmentNode = denoiseFn( uv() ).context( builder.getSharedContext() ); + this._material.contextNode = context( builder.getSharedContext() ); + this._material.fragmentNode = denoiseFn( uv() ); this._material.needsUpdate = true; return this._textureNode; diff --git a/examples/jsm/tsl/display/SMAANode.js b/examples/jsm/tsl/display/SMAANode.js index a1e9e1e83a4cb7..dc464123acce94 100644 --- a/examples/jsm/tsl/display/SMAANode.js +++ b/examples/jsm/tsl/display/SMAANode.js @@ -1,5 +1,5 @@ import { HalfFloatType, LinearFilter, NearestFilter, RenderTarget, Texture, Vector2, QuadMesh, NodeMaterial, TempNode, RendererUtils } from 'three/webgpu'; -import { abs, Fn, NodeUpdateType, uv, uniform, convertToTexture, vec2, vec4, passTexture, max, step, dot, float, texture, If, Loop, int, Break, sqrt, sign, mix } from 'three/tsl'; +import { abs, Fn, NodeUpdateType, uv, uniform, convertToTexture, vec2, vec4, passTexture, max, step, dot, float, texture, If, Loop, int, Break, sqrt, sign, mix, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -651,13 +651,18 @@ class SMAANode extends TempNode { } ); - this._materialEdges.fragmentNode = SMAAEdgeDetection().context( builder.getSharedContext() ); + const sharedContext = context( builder.getSharedContext() ); + + this._materialEdges.contextNode = sharedContext; + this._materialEdges.fragmentNode = SMAAEdgeDetection(); this._materialEdges.needsUpdate = true; - this._materialWeights.fragmentNode = SMAAWeights().context( builder.getSharedContext() ); + this._materialWeights.contextNode = sharedContext; + this._materialWeights.fragmentNode = SMAAWeights(); this._materialWeights.needsUpdate = true; - this._materialBlend.fragmentNode = SMAABlend().context( builder.getSharedContext() ); + this._materialBlend.contextNode = sharedContext; + this._materialBlend.fragmentNode = SMAABlend(); this._materialBlend.needsUpdate = true; return this._textureNode; diff --git a/examples/jsm/tsl/display/SSAONode.js b/examples/jsm/tsl/display/SSAONode.js index ab3d1fc3f415c9..8f2107e5e48eba 100644 --- a/examples/jsm/tsl/display/SSAONode.js +++ b/examples/jsm/tsl/display/SSAONode.js @@ -1,5 +1,5 @@ import { RenderTarget, Vector2, TempNode, QuadMesh, NodeMaterial, RendererUtils, RedFormat } from 'three/webgpu'; -import { reference, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getViewPosition, getScreenPositionFromClip, vogelDiskSample, interleavedGradientNoise, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, vec4, int, dot, max, clamp, length, screenCoordinate, PI2, texture, passTexture } from 'three/tsl'; +import { reference, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getViewPosition, getScreenPositionFromClip, vogelDiskSample, interleavedGradientNoise, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, vec4, int, dot, max, clamp, length, screenCoordinate, PI2, texture, passTexture, context } from 'three/tsl'; import { depthAwareBlur } from './depthAwareBlur.js'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); @@ -385,12 +385,18 @@ class SSAONode extends TempNode { } ); - this._aoMaterial.fragmentNode = ao().context( builder.getSharedContext() ); + const sharedContext = context( builder.getSharedContext() ); + + // compute ambient occlusion + + this._aoMaterial.contextNode = sharedContext; + this._aoMaterial.fragmentNode = ao(); this._aoMaterial.needsUpdate = true; // separable, depth-aware blur ( run horizontally then vertically via `_blurDirection` ) - this._blurMaterial.fragmentNode = depthAwareBlur( this._blurInput, this.depthNode, this._blurDirection, this._camera, this.blurSharpness, this.radius ).context( builder.getSharedContext() ); + this._blurMaterial.contextNode = sharedContext; + this._blurMaterial.fragmentNode = depthAwareBlur( this._blurInput, this.depthNode, this._blurDirection, this._camera, this.blurSharpness, this.radius ); this._blurMaterial.needsUpdate = true; return this._textureNode; diff --git a/examples/jsm/tsl/display/SSGINode.js b/examples/jsm/tsl/display/SSGINode.js index 26a139d1d8632f..930e7860dab125 100644 --- a/examples/jsm/tsl/display/SSGINode.js +++ b/examples/jsm/tsl/display/SSGINode.js @@ -1,5 +1,5 @@ import { RenderTarget, Vector2, TempNode, QuadMesh, NodeMaterial, RendererUtils, MathUtils, RGBFormat, RedFormat, UnsignedInt101111Type, UnsignedByteType } from 'three/webgpu'; -import { clamp, normalize, reference, Fn, NodeUpdateType, uniform, vec4, passTexture, uv, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getViewPosition, screenCoordinate, float, sub, fract, dot, vec2, rand, vec3, Loop, mul, PI, cos, sin, uint, cross, acos, sign, pow, luminance, If, max, abs, Break, sqrt, HALF_PI, div, ceil, shiftRight, convertToTexture, bool, getNormalFromDepth, countOneBits, interleavedGradientNoise, property, outputStruct } from 'three/tsl'; +import { clamp, normalize, reference, Fn, NodeUpdateType, uniform, vec4, passTexture, uv, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getViewPosition, screenCoordinate, float, sub, fract, dot, vec2, rand, vec3, Loop, mul, PI, cos, sin, uint, cross, acos, sign, pow, luminance, If, max, abs, Break, sqrt, HALF_PI, div, ceil, shiftRight, convertToTexture, bool, getNormalFromDepth, countOneBits, interleavedGradientNoise, property, outputStruct, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -648,7 +648,8 @@ class SSGINode extends TempNode { } ); - this._material.colorNode = gi().context( builder.getSharedContext() ); + this._material.contextNode = context( builder.getSharedContext() ); + this._material.colorNode = gi(); this._material.outputNode = outputNode; this._material.needsUpdate = true; diff --git a/examples/jsm/tsl/display/SSRNode.js b/examples/jsm/tsl/display/SSRNode.js index e7905c59434ca3..c270998844e85d 100644 --- a/examples/jsm/tsl/display/SSRNode.js +++ b/examples/jsm/tsl/display/SSRNode.js @@ -1,4 +1,4 @@ -import { Break, Continue, Fn, If, Loop, abs, bool, cross, distance, div, dot, float, getScreenPosition, getViewPosition, int, logarithmicDepthToViewZ, luminance, max, min, mix, mul, nodeObject, normalize, orthographicDepthToViewZ, passTexture, perspectiveDepthToViewZ, reference, reflect, sub, texture, trunc, uniform, uv, vec2, vec3, vec4, viewZToPerspectiveDepth } from 'three/tsl'; +import { Break, Continue, Fn, If, Loop, abs, bool, cross, distance, div, dot, float, getScreenPosition, getViewPosition, int, logarithmicDepthToViewZ, luminance, max, min, mix, mul, nodeObject, normalize, orthographicDepthToViewZ, passTexture, perspectiveDepthToViewZ, reference, reflect, sub, texture, trunc, uniform, uv, vec2, vec3, vec4, viewZToPerspectiveDepth, context } from 'three/tsl'; import { HalfFloatType, LinearFilter, LinearMipmapLinearFilter, Matrix4, NodeMaterial, NodeUpdateType, QuadMesh, RenderTarget, RendererUtils, TempNode, Vector2, Vector3 } from 'three/webgpu'; import { bindAnalyticNoise } from '../utils/RNoise.js'; import { ENV_RAY_LENGTH, getSpecularDominantFactor, ggxReflectionSample } from '../utils/SpecularHelpers.js'; @@ -625,7 +625,8 @@ class SSRNode extends TempNode { if ( this._ssrFn === null ) return; - this._ssrMaterial.fragmentNode = this._ssrFn().context( this._sharedContext ); + this._ssrMaterial.contextNode = context( this._sharedContext ); + this._ssrMaterial.fragmentNode = this._ssrFn(); this._ssrMaterial.needsUpdate = true; } diff --git a/examples/jsm/tsl/display/SSSNode.js b/examples/jsm/tsl/display/SSSNode.js index 5f4c0bd49ab470..290cec59b6b39c 100644 --- a/examples/jsm/tsl/display/SSSNode.js +++ b/examples/jsm/tsl/display/SSSNode.js @@ -1,5 +1,5 @@ import { RedFormat, RenderTarget, Vector2, RendererUtils, QuadMesh, TempNode, NodeMaterial, NodeUpdateType, UnsignedByteType } from 'three/webgpu'; -import { reference, viewZToPerspectiveDepth, logarithmicDepthToViewZ, getScreenPosition, getViewPosition, float, Break, Loop, int, max, abs, If, interleavedGradientNoise, screenCoordinate, Fn, passTexture, uv, uniform, perspectiveDepthToViewZ, orthographicDepthToViewZ, vec2, lightPosition, lightTargetPosition, fract, rand, mix } from 'three/tsl'; +import { reference, viewZToPerspectiveDepth, logarithmicDepthToViewZ, getScreenPosition, getViewPosition, float, Break, Loop, int, max, abs, If, interleavedGradientNoise, screenCoordinate, Fn, passTexture, uv, uniform, perspectiveDepthToViewZ, orthographicDepthToViewZ, vec2, lightPosition, lightTargetPosition, fract, rand, mix, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -454,7 +454,8 @@ class SSSNode extends TempNode { } ); - this._material.fragmentNode = sss().context( builder.getSharedContext() ); + this._material.contextNode = context( builder.getSharedContext() ); + this._material.fragmentNode = sss(); this._material.needsUpdate = true; return this._textureNode; diff --git a/examples/jsm/tsl/display/SharpenNode.js b/examples/jsm/tsl/display/SharpenNode.js index 90535795160ac2..98d3be6ba6e6a8 100644 --- a/examples/jsm/tsl/display/SharpenNode.js +++ b/examples/jsm/tsl/display/SharpenNode.js @@ -1,5 +1,5 @@ import { HalfFloatType, RenderTarget, Vector2, NodeMaterial, RendererUtils, QuadMesh, TempNode, NodeUpdateType } from 'three/webgpu'; -import { Fn, float, vec3, vec4, ivec2, int, uv, floor, abs, max, min, exp2, nodeObject, passTexture, textureSize, textureLoad, convertToTexture } from 'three/tsl'; +import { Fn, float, vec3, vec4, ivec2, int, uv, floor, abs, max, min, exp2, nodeObject, passTexture, textureSize, textureLoad, convertToTexture, context } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -236,10 +236,9 @@ class SharpenNode extends TempNode { // - const context = builder.getSharedContext(); - const material = this._material || ( this._material = new NodeMaterial() ); - material.fragmentNode = rcas().context( context ); + material.contextNode = context( builder.getSharedContext() ); + material.fragmentNode = rcas(); material.name = 'Sharpen_RCAS'; material.needsUpdate = true; diff --git a/examples/jsm/tsl/display/TAAUNode.js b/examples/jsm/tsl/display/TAAUNode.js index cb66854671639b..dab33d083278f2 100644 --- a/examples/jsm/tsl/display/TAAUNode.js +++ b/examples/jsm/tsl/display/TAAUNode.js @@ -1,5 +1,5 @@ import { HalfFloatType, Vector2, RenderTarget, RendererUtils, QuadMesh, NodeMaterial, TempNode, NodeUpdateType, Matrix4, DepthTexture } from 'three/webgpu'; -import { add, exp, float, If, Fn, max, texture, uniform, uv, vec2, vec4, luminance, convertToTexture, passTexture, velocity, getViewPosition, viewZToPerspectiveDepth, struct, ivec2, mix, property, outputStruct } from 'three/tsl'; +import { add, exp, float, If, Fn, max, texture, uniform, uv, vec2, vec4, luminance, convertToTexture, passTexture, velocity, getViewPosition, viewZToPerspectiveDepth, struct, ivec2, mix, property, outputStruct, context, OnBeforeRenderPipeline, OnAfterRenderPipeline } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -501,13 +501,13 @@ class TAAUNode extends TempNode { */ setup( builder ) { - const renderPipeline = builder.context.renderPipeline; + if ( builder.renderPipeline && ! builder.context.renderPipelineState.viewOffsetOwner ) { - if ( renderPipeline ) { + builder.context.renderPipelineState.viewOffsetOwner = this; this._needsPostProcessingSync = true; - renderPipeline.context.onBeforeRenderPipeline = () => { + OnBeforeRenderPipeline( () => { const beautyRenderTarget = ( this.beautyNode.isRTTNode ) ? this.beautyNode.renderTarget : this.beautyNode.passNode.renderTarget; @@ -516,13 +516,13 @@ class TAAUNode extends TempNode { this.setViewOffset( inputWidth, inputHeight ); - }; + } ); - renderPipeline.context.onAfterRenderPipeline = () => { + OnAfterRenderPipeline( () => { this.clearViewOffset(); - }; + } ); } @@ -763,6 +763,9 @@ class TAAUNode extends TempNode { // materials + const sharedContext = context( builder.getSharedContext() ); + + this._resolveMaterial.contextNode = sharedContext; this._resolveMaterial.colorNode = resolve(); this._resolveMaterial.outputNode = outputNode; @@ -775,6 +778,7 @@ class TAAUNode extends TempNode { } )(); + this._seedMaterial.contextNode = sharedContext; this._seedMaterial.outputNode = outputNode; return this._textureNode; diff --git a/examples/jsm/tsl/display/TRAANode.js b/examples/jsm/tsl/display/TRAANode.js index ea2c2a41dd38a8..4ff73005841f53 100644 --- a/examples/jsm/tsl/display/TRAANode.js +++ b/examples/jsm/tsl/display/TRAANode.js @@ -1,5 +1,5 @@ import { HalfFloatType, Vector2, RenderTarget, RendererUtils, QuadMesh, NodeMaterial, TempNode, NodeUpdateType, Matrix4, DepthTexture, FloatType } from 'three/webgpu'; -import { add, float, If, Fn, max, texture, uniform, uv, vec2, vec4, luminance, convertToTexture, passTexture, velocity, getViewPosition, viewZToPerspectiveDepth, struct, ivec2, mix, logarithmicDepthToViewZ, viewZToOrthographicDepth } from 'three/tsl'; +import { add, float, If, Fn, max, texture, uniform, uv, vec2, vec4, luminance, convertToTexture, passTexture, velocity, getViewPosition, viewZToPerspectiveDepth, struct, ivec2, mix, logarithmicDepthToViewZ, viewZToOrthographicDepth, context, OnBeforeRenderPipeline, OnAfterRenderPipeline } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -442,24 +442,24 @@ class TRAANode extends TempNode { */ setup( builder ) { - const renderPipeline = builder.context.renderPipeline; + if ( builder.renderPipeline && ! builder.context.renderPipelineState.viewOffsetOwner ) { - if ( renderPipeline ) { + builder.context.renderPipelineState.viewOffsetOwner = this; this._needsPostProcessingSync = true; - renderPipeline.context.onBeforeRenderPipeline = () => { + OnBeforeRenderPipeline( () => { const size = builder.renderer.getDrawingBufferSize( _size ); this.setViewOffset( size.width, size.height ); - }; + } ); - renderPipeline.context.onAfterRenderPipeline = () => { + OnAfterRenderPipeline( () => { this.clearViewOffset(); - }; + } ); } @@ -709,6 +709,7 @@ class TRAANode extends TempNode { // materials + this._resolveMaterial.contextNode = context( builder.getSharedContext() ); this._resolveMaterial.colorNode = resolve(); return this._textureNode; diff --git a/examples/jsm/tsl/display/TemporalReprojectNode.js b/examples/jsm/tsl/display/TemporalReprojectNode.js index fb5b0050666e59..3284a1335b63fa 100644 --- a/examples/jsm/tsl/display/TemporalReprojectNode.js +++ b/examples/jsm/tsl/display/TemporalReprojectNode.js @@ -1,4 +1,4 @@ -import { EPSILON, Fn, If, abs, convertToTexture, dFdx, dFdy, dot, exp, float, floor, fwidth, getViewPosition, ivec2, luminance, max, min, mix, nodeObject, normalize, passTexture, screenCoordinate, select, smoothstep, sqrt, struct, texture, textureLoad, uniform, unpackRGBToNormal, uv, vec2, vec3, vec4, velocity } from 'three/tsl'; +import { EPSILON, Fn, If, abs, convertToTexture, dFdx, dFdy, dot, exp, float, floor, fwidth, getViewPosition, ivec2, luminance, max, min, mix, nodeObject, normalize, passTexture, screenCoordinate, select, smoothstep, sqrt, struct, texture, textureLoad, uniform, unpackRGBToNormal, uv, vec2, vec3, vec4, velocity, context, OnBeforeRenderPipeline, OnAfterRenderPipeline } from 'three/tsl'; import { DepthTexture, HalfFloatType, Matrix4, NodeMaterial, NodeUpdateType, QuadMesh, RenderTarget, RendererUtils, TempNode, Vector2, Vector3 } from 'three/webgpu'; import { ENV_RAY_LENGTH, ENV_RAY_LENGTH_THRESHOLD } from '../utils/SpecularHelpers.js'; @@ -727,36 +727,39 @@ class TemporalReprojectNode extends TempNode { setup( builder ) { - const renderPipeline = builder.context.renderPipeline; + const sharedContext = context( builder.getSharedContext() ); - if ( renderPipeline ) { + if ( builder.renderPipeline && ! builder.context.renderPipelineState.viewOffsetOwner ) { + + builder.context.renderPipelineState.viewOffsetOwner = this; this._needsPostProcessingSync = true; - renderPipeline.context.onBeforeRenderPipeline = () => { + OnBeforeRenderPipeline( () => { this.setViewOffset(); - }; + } ); - renderPipeline.context.onAfterRenderPipeline = () => { + OnAfterRenderPipeline( () => { this.clearViewOffset(); - }; + } ); } - this._resolveMaterial.fragmentNode = this._buildResolve( builder ); + this._resolveMaterial.contextNode = sharedContext; + this._resolveMaterial.fragmentNode = this._buildResolve(); this._resolveMaterial.needsUpdate = true; - this._buildSeed( builder ); + this._buildSeed( sharedContext ); return this._textureNode; } - _buildSeed( builder ) { + _buildSeed( sharedContext ) { const seed = Fn( () => { @@ -768,12 +771,13 @@ class TemporalReprojectNode extends TempNode { } ); - this._seedMaterial.fragmentNode = seed().context( builder.getSharedContext() ); + this._seedMaterial.contextNode = sharedContext; + this._seedMaterial.fragmentNode = seed(); this._seedMaterial.needsUpdate = true; } - _buildResolve( builder ) { + _buildResolve() { const isSpecular = this.mode === 'specular'; const cameraUniforms = this._cameraUniforms; @@ -944,7 +948,7 @@ class TemporalReprojectNode extends TempNode { } ); - return resolve().context( builder.getSharedContext() ); + return resolve(); } diff --git a/examples/webgpu_postprocessing_ssr_denoise.html b/examples/webgpu_postprocessing_ssr_denoise.html index 8e90b0a0587a4b..13b7a6a9de5658 100644 --- a/examples/webgpu_postprocessing_ssr_denoise.html +++ b/examples/webgpu_postprocessing_ssr_denoise.html @@ -455,7 +455,7 @@ denoiseNode.flickerSuppression.value = params.denoise.flickerSuppression; denoiseNode.adaptiveTrust.value = params.denoise.adaptiveTrust; - } + } } @@ -519,7 +519,7 @@ } - } + } if ( ! isCompareMode( params.output ) ) controls.enabled = true; diff --git a/src/Three.TSL.js b/src/Three.TSL.js index 9544cc14f662e3..dec060e31f3574 100644 --- a/src/Three.TSL.js +++ b/src/Three.TSL.js @@ -407,6 +407,8 @@ export const objectViewPosition = TSL.objectViewPosition; export const objectWorldMatrix = TSL.objectWorldMatrix; export const OnBeforeObjectUpdate = TSL.OnBeforeObjectUpdate; export const OnBeforeMaterialUpdate = TSL.OnBeforeMaterialUpdate; +export const OnBeforeRenderPipeline = TSL.OnBeforeRenderPipeline; +export const OnAfterRenderPipeline = TSL.OnAfterRenderPipeline; export const OnObjectUpdate = TSL.OnObjectUpdate; export const OnMaterialUpdate = TSL.OnMaterialUpdate; export const oneMinus = TSL.oneMinus; diff --git a/src/nodes/core/NodeBuilder.js b/src/nodes/core/NodeBuilder.js index a3a7a6f1dac5ef..d37e05c8f99598 100644 --- a/src/nodes/core/NodeBuilder.js +++ b/src/nodes/core/NodeBuilder.js @@ -891,6 +891,17 @@ class NodeBuilder { } + /** + * A reference to the render pipeline. + * + * @type {RenderPipeline} + */ + get renderPipeline() { + + return this.context.renderPipeline; + + } + /** * Whether the given texture is filtered or not. * diff --git a/src/nodes/lighting/ShadowNode.js b/src/nodes/lighting/ShadowNode.js index b326701e8588d1..cf3081eb645fdc 100644 --- a/src/nodes/lighting/ShadowNode.js +++ b/src/nodes/lighting/ShadowNode.js @@ -22,6 +22,7 @@ import { getShadowMaterial, disposeShadowMaterial, BasicShadowFilter, PCFShadowF import { positionLocal } from '../accessors/Position.js'; import { uniform } from '../core/UniformNode.js'; import { equirectDirection } from '../utils/EquirectUV.js'; +import { context } from '../core/ContextNode.js'; // @@ -492,12 +493,16 @@ class ShadowNode extends ShadowBaseNode { const radius = reference( 'radius', 'float', shadow ).setGroup( renderGroup ); const size = reference( 'mapSize', 'vec2', shadow ).setGroup( renderGroup ); + const sharedContext = context( builder.getSharedContext() ); + let material = this.vsmMaterialVertical || ( this.vsmMaterialVertical = new NodeMaterial() ); - material.fragmentNode = VSMPassVertical( { samples, radius, size, shadowPass: shadowPassVertical, depthLayer: this.depthLayer } ).context( builder.getSharedContext() ); + material.contextNode = sharedContext; + material.fragmentNode = VSMPassVertical( { samples, radius, size, shadowPass: shadowPassVertical, depthLayer: this.depthLayer } ); material.name = 'VSMVertical'; material = this.vsmMaterialHorizontal || ( this.vsmMaterialHorizontal = new NodeMaterial() ); - material.fragmentNode = VSMPassHorizontal( { samples, radius, size, shadowPass: shadowPassHorizontal, depthLayer: this.depthLayer } ).context( builder.getSharedContext() ); + material.contextNode = sharedContext; + material.fragmentNode = VSMPassHorizontal( { samples, radius, size, shadowPass: shadowPassHorizontal, depthLayer: this.depthLayer } ); material.name = 'VSMHorizontal'; } diff --git a/src/nodes/utils/EventNode.js b/src/nodes/utils/EventNode.js index 69b3f3343d2abe..4294d509809287 100644 --- a/src/nodes/utils/EventNode.js +++ b/src/nodes/utils/EventNode.js @@ -55,6 +55,36 @@ class EventNode extends Node { } + setup( builder ) { + + const { eventType, callback } = this; + + if ( eventType === EventNode.BEFORE_RENDER_PIPELINE ) { + + const callbacks = builder.context.onBeforePipelineCallbacks; + + if ( callbacks ) { + + callbacks.push( callback ); + + } + + } else if ( eventType === EventNode.AFTER_RENDER_PIPELINE ) { + + const callbacks = builder.context.onAfterPipelineCallbacks; + + if ( callbacks ) { + + callbacks.push( callback ); + + } + + } + + return super.setup( builder ); + + } + update( frame ) { this.callback( frame ); @@ -75,6 +105,8 @@ EventNode.FRAME = 'frame'; EventNode.BEFORE_OBJECT = 'beforeObject'; EventNode.BEFORE_MATERIAL = 'beforeMaterial'; EventNode.BEFORE_FRAME = 'beforeFrame'; +EventNode.BEFORE_RENDER_PIPELINE = 'beforeRenderPipeline'; +EventNode.AFTER_RENDER_PIPELINE = 'afterRenderPipeline'; export default EventNode; @@ -146,3 +178,25 @@ export const OnBeforeMaterialUpdate = ( callback ) => createEvent( EventNode.BEF * @returns {EventNode} */ export const OnBeforeFrameUpdate = ( callback ) => createEvent( EventNode.BEFORE_FRAME, callback ); + +/** + * Creates an event that triggers a function before the render pipeline is rendered. + * + * The node must be part of a node chain that is used as the output of a `RenderPipeline`, + * otherwise the event is ignored. + * + * @param {Function} callback - The callback function. + * @returns {EventNode} + */ +export const OnBeforeRenderPipeline = ( callback ) => createEvent( EventNode.BEFORE_RENDER_PIPELINE, callback ); + +/** + * Creates an event that triggers a function after the render pipeline is rendered. + * + * The node must be part of a node chain that is used as the output of a `RenderPipeline`, + * otherwise the event is ignored. + * + * @param {Function} callback - The callback function. + * @returns {EventNode} + */ +export const OnAfterRenderPipeline = ( callback ) => createEvent( EventNode.AFTER_RENDER_PIPELINE, callback ); diff --git a/src/nodes/utils/RTTNode.js b/src/nodes/utils/RTTNode.js index deda240d34b1e7..6d9bbe2fa4c2c4 100644 --- a/src/nodes/utils/RTTNode.js +++ b/src/nodes/utils/RTTNode.js @@ -2,6 +2,7 @@ import { nodeObject } from '../tsl/TSLCore.js'; import TextureNode from '../accessors/TextureNode.js'; import { NodeUpdateType } from '../core/constants.js'; import { uv } from '../accessors/UV.js'; +import { context } from '../core/ContextNode.js'; import NodeMaterial from '../../materials/nodes/NodeMaterial.js'; import QuadMesh from '../../renderers/common/QuadMesh.js'; @@ -106,15 +107,6 @@ class RTTNode extends TextureNode { */ this._resolutionScale = 1; - /** - * 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. * @@ -149,7 +141,8 @@ class RTTNode extends TextureNode { setup( builder ) { - this._rttNode = this.node.context( builder.getSharedContext() ); + this._quadMesh.material.contextNode = context( builder.getSharedContext() ); + this._quadMesh.material.fragmentNode = this.node; this._quadMesh.material.name = 'RTT'; this._quadMesh.material.needsUpdate = true; @@ -243,8 +236,6 @@ class RTTNode extends TextureNode { } - - this._quadMesh.material.fragmentNode = this._rttNode; this._quadMesh.name = name; // diff --git a/src/renderers/common/RenderPipeline.js b/src/renderers/common/RenderPipeline.js index ab3085cfdca456..a41c67425e45ed 100644 --- a/src/renderers/common/RenderPipeline.js +++ b/src/renderers/common/RenderPipeline.js @@ -1,6 +1,6 @@ import NodeMaterial from '../../materials/nodes/NodeMaterial.js'; import { ColorManagement } from '../../math/ColorManagement.js'; -import { vec4, renderOutput } from '../../nodes/TSL.js'; +import { vec4, renderOutput, context } from '../../nodes/TSL.js'; import { NoToneMapping } from '../../constants.js'; import QuadMesh from '../../renderers/common/QuadMesh.js'; import { warnOnce } from '../../utils.js'; @@ -29,6 +29,15 @@ class RenderPipeline { */ constructor( renderer, outputNode = vec4( 0, 0, 1, 1 ) ) { + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isRenderPipeline = true; + /** * A reference to the renderer. * @@ -86,13 +95,13 @@ class RenderPipeline { this._quadMesh.name = 'Render Pipeline'; /** - * The context of the render pipeline stack. + * The context data for the render pipeline. * * @private * @type {?Object} * @default null */ - this._context = null; + this._contextData = null; /** * The current tone mapping. @@ -124,7 +133,7 @@ class RenderPipeline { this._update(); - if ( this._context.onBeforeRenderPipeline !== null ) this._context.onBeforeRenderPipeline(); + for ( const callback of this._contextData.onBeforePipelineCallbacks ) callback(); const toneMapping = renderer.toneMapping; const outputColorSpace = renderer.outputColorSpace; @@ -146,19 +155,7 @@ class RenderPipeline { renderer.toneMapping = toneMapping; renderer.outputColorSpace = outputColorSpace; - if ( this._context.onAfterRenderPipeline !== null ) this._context.onAfterRenderPipeline(); - - } - - /** - * Returns the current context of the render pipeline stack. - * - * @readonly - * @type {?Object} - */ - get context() { - - return this._context; + for ( const callback of this._contextData.onAfterPipelineCallbacks ) callback(); } @@ -197,31 +194,31 @@ class RenderPipeline { const toneMapping = this._toneMapping; const outputColorSpace = this._outputColorSpace; - const context = { + const contextData = { renderPipeline: this, - onBeforeRenderPipeline: null, - onAfterRenderPipeline: null + renderPipelineState: { + viewOffsetOwner: null + }, + onBeforePipelineCallbacks: [], + onAfterPipelineCallbacks: [] }; let outputNode = this.outputNode; if ( this.outputColorTransform === true ) { - outputNode = outputNode.context( context ); - outputNode = renderOutput( outputNode, toneMapping, outputColorSpace ); } else { - context.toneMapping = toneMapping; - context.outputColorSpace = outputColorSpace; - - outputNode = outputNode.context( context ); + contextData.toneMapping = toneMapping; + contextData.outputColorSpace = outputColorSpace; } - this._context = context; + this._contextData = contextData; + this._quadMesh.material.contextNode = context( contextData ); this._quadMesh.material.fragmentNode = outputNode; this._quadMesh.material.needsUpdate = true; From d1378082f5b6cfda0c57faa70ce6ead0f087524e Mon Sep 17 00:00:00 2001 From: WestLangley Date: Tue, 14 Jul 2026 19:27:34 -0400 Subject: [PATCH 2/2] Examples: Properly transform normal (#34039) --- examples/webgpu_skinning_instancing_individual.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/webgpu_skinning_instancing_individual.html b/examples/webgpu_skinning_instancing_individual.html index 8917eb486a8cb0..206aad80448fc9 100644 --- a/examples/webgpu_skinning_instancing_individual.html +++ b/examples/webgpu_skinning_instancing_individual.html @@ -38,7 +38,7 @@