From 2bde4319f155a380acca92d9f9aca13cbe749347 Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 29 Feb 2024 18:11:00 -0300 Subject: [PATCH 01/12] WebGPURenderer: Apply fog before tonemapping and encoding --- examples/jsm/nodes/materials/NodeMaterial.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/jsm/nodes/materials/NodeMaterial.js b/examples/jsm/nodes/materials/NodeMaterial.js index 790ebd7acdf111..844fe493501903 100644 --- a/examples/jsm/nodes/materials/NodeMaterial.js +++ b/examples/jsm/nodes/materials/NodeMaterial.js @@ -380,23 +380,23 @@ class NodeMaterial extends ShaderMaterial { const renderer = builder.renderer; - // TONE MAPPING + // FOG - const toneMappingNode = builder.toneMappingNode; + if ( this.fog === true ) { - if ( this.toneMapped === true && toneMappingNode ) { + const fogNode = builder.fogNode; - outputNode = vec4( toneMappingNode.context( { color: outputNode.rgb } ), outputNode.a ); + if ( fogNode ) outputNode = vec4( fogNode.mixAssign( outputNode.rgb ), outputNode.a ); } - // FOG + // TONE MAPPING - if ( this.fog === true ) { + const toneMappingNode = builder.toneMappingNode; - const fogNode = builder.fogNode; + if ( this.toneMapped === true && toneMappingNode ) { - if ( fogNode ) outputNode = vec4( fogNode.mixAssign( outputNode.rgb ), outputNode.a ); + outputNode = vec4( toneMappingNode.context( { color: outputNode.rgb } ), outputNode.a ); } From 51a9567681012c10dc659ef61f2f8bff67f423d6 Mon Sep 17 00:00:00 2001 From: sunag Date: Sun, 3 Mar 2024 02:39:52 -0300 Subject: [PATCH 02/12] TSL: Add `rendererReference` --- examples/jsm/nodes/Nodes.js | 1 + .../nodes/accessors/RendererReferenceNode.js | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 examples/jsm/nodes/accessors/RendererReferenceNode.js diff --git a/examples/jsm/nodes/Nodes.js b/examples/jsm/nodes/Nodes.js index c6d33ee5a69bf6..373da7861d702f 100644 --- a/examples/jsm/nodes/Nodes.js +++ b/examples/jsm/nodes/Nodes.js @@ -86,6 +86,7 @@ export { default as CubeTextureNode, cubeTexture } from './accessors/CubeTexture export { default as InstanceNode, instance } from './accessors/InstanceNode.js'; export { default as MaterialNode, materialAlphaTest, materialColor, materialShininess, materialEmissive, materialOpacity, materialSpecularColor, materialSpecularStrength, materialReflectivity, materialRoughness, materialMetalness, materialNormal, materialClearcoat, materialClearcoatRoughness, materialClearcoatNormal, materialRotation, materialSheen, materialSheenRoughness, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLineScale, materialLineDashSize, materialLineGapSize, materialLineWidth, materialLineDashOffset, materialPointWidth } from './accessors/MaterialNode.js'; export { default as MaterialReferenceNode, materialReference } from './accessors/MaterialReferenceNode.js'; +export { default as RendererReferenceNode, rendererReference } from './accessors/RendererReferenceNode.js'; export { default as MorphNode, morphReference } from './accessors/MorphNode.js'; export { default as TextureBicubicNode, textureBicubic } from './accessors/TextureBicubicNode.js'; export { default as ModelNode, modelDirection, modelViewMatrix, modelNormalMatrix, modelWorldMatrix, modelPosition, modelViewPosition, modelScale } from './accessors/ModelNode.js'; diff --git a/examples/jsm/nodes/accessors/RendererReferenceNode.js b/examples/jsm/nodes/accessors/RendererReferenceNode.js new file mode 100644 index 00000000000000..147be8b42cb7c2 --- /dev/null +++ b/examples/jsm/nodes/accessors/RendererReferenceNode.js @@ -0,0 +1,29 @@ +import ReferenceNode from './ReferenceNode.js'; +import { addNodeClass } from '../core/Node.js'; +import { nodeObject } from '../shadernode/ShaderNode.js'; + +class RendererReferenceNode extends ReferenceNode { + + constructor( property, inputType, renderer = null ) { + + super( property, inputType, renderer ); + + this.renderer = renderer; + + } + + setReference( state ) { + + this.reference = this.renderer !== null ? this.renderer : state.renderer; + + return this.reference; + + } + +} + +export default RendererReferenceNode; + +export const rendererReference = ( name, type, renderer ) => nodeObject( new RendererReferenceNode( name, type, renderer ) ); + +addNodeClass( 'RendererReferenceNode', RendererReferenceNode ); From fa358a4ade5496e5f9bcdacf265c593138d47948 Mon Sep 17 00:00:00 2001 From: sunag Date: Sun, 3 Mar 2024 02:40:24 -0300 Subject: [PATCH 03/12] PassNode: Add `getViewZNode()` --- examples/jsm/nodes/display/PassNode.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/examples/jsm/nodes/display/PassNode.js b/examples/jsm/nodes/display/PassNode.js index 2e4017ca82922e..cd9e440a4d1422 100644 --- a/examples/jsm/nodes/display/PassNode.js +++ b/examples/jsm/nodes/display/PassNode.js @@ -66,6 +66,7 @@ class PassNode extends TempNode { this._depthTextureNode = nodeObject( new PassTextureNode( this, depthTexture ) ); this._depthNode = null; + this._viewZNode = null; this._cameraNear = uniform( 0 ); this._cameraFar = uniform( 0 ); @@ -91,6 +92,21 @@ class PassNode extends TempNode { } + getViewZNode() { + + if ( this._viewZNode === null ) { + + const cameraNear = this._cameraNear; + const cameraFar = this._cameraFar; + + this._viewZNode = perspectiveDepthToViewZ( this._depthTextureNode, cameraNear, cameraFar ); + + } + + return this._viewZNode; + + } + getDepthNode() { if ( this._depthNode === null ) { @@ -98,7 +114,7 @@ class PassNode extends TempNode { const cameraNear = this._cameraNear; const cameraFar = this._cameraFar; - this._depthNode = viewZToOrthographicDepth( perspectiveDepthToViewZ( this._depthTextureNode, cameraNear, cameraFar ), cameraNear, cameraFar ); + this._depthNode = viewZToOrthographicDepth( this.getViewZNode(), cameraNear, cameraFar ); } From 7669377be2396866cef3e4da304758edfa050c52 Mon Sep 17 00:00:00 2001 From: sunag Date: Sun, 3 Mar 2024 02:42:42 -0300 Subject: [PATCH 04/12] FogNode: Add `.getViewZNode()`, removed `mixAssign()` --- examples/jsm/nodes/fog/FogExp2Node.js | 9 ++++----- examples/jsm/nodes/fog/FogNode.js | 6 +++--- examples/jsm/nodes/fog/FogRangeNode.js | 7 ++++--- examples/jsm/nodes/materials/NodeMaterial.js | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/jsm/nodes/fog/FogExp2Node.js b/examples/jsm/nodes/fog/FogExp2Node.js index 6d3d95edbde5fd..cf344eb15d1cd8 100644 --- a/examples/jsm/nodes/fog/FogExp2Node.js +++ b/examples/jsm/nodes/fog/FogExp2Node.js @@ -1,5 +1,4 @@ import FogNode from './FogNode.js'; -import { positionView } from '../accessors/PositionNode.js'; import { addNodeClass } from '../core/Node.js'; import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js'; @@ -15,12 +14,12 @@ class FogExp2Node extends FogNode { } - setup() { + setup( builder ) { - const depthNode = positionView.z.negate(); - const densityNode = this.densityNode; + const viewZ = this.getViewZNode( builder ); + const density = this.densityNode; - return densityNode.mul( densityNode, depthNode, depthNode ).negate().exp().oneMinus(); + return density.mul( density, viewZ, viewZ ).negate().exp().oneMinus(); } diff --git a/examples/jsm/nodes/fog/FogNode.js b/examples/jsm/nodes/fog/FogNode.js index 8da025a1c17565..55f374f9f36b5c 100644 --- a/examples/jsm/nodes/fog/FogNode.js +++ b/examples/jsm/nodes/fog/FogNode.js @@ -1,5 +1,5 @@ import Node, { addNodeClass } from '../core/Node.js'; -import { mix } from '../math/MathNode.js'; +import { positionView } from '../accessors/PositionNode.js'; import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js'; class FogNode extends Node { @@ -15,9 +15,9 @@ class FogNode extends Node { } - mixAssign( outputNode ) { + getViewZNode( builder ) { - return mix( outputNode, this.colorNode, this ); + return ( builder.context.viewZ || positionView.z ).negate(); } diff --git a/examples/jsm/nodes/fog/FogRangeNode.js b/examples/jsm/nodes/fog/FogRangeNode.js index 448c2e45e10c4a..c7ad6235207422 100644 --- a/examples/jsm/nodes/fog/FogRangeNode.js +++ b/examples/jsm/nodes/fog/FogRangeNode.js @@ -1,6 +1,5 @@ import FogNode from './FogNode.js'; import { smoothstep } from '../math/MathNode.js'; -import { positionView } from '../accessors/PositionNode.js'; import { addNodeClass } from '../core/Node.js'; import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js'; @@ -17,9 +16,11 @@ class FogRangeNode extends FogNode { } - setup() { + setup( builder ) { - return smoothstep( this.nearNode, this.farNode, positionView.z.negate() ); + const viewZ = this.getViewZNode( builder ); + + return smoothstep( this.nearNode, this.farNode, viewZ ); } diff --git a/examples/jsm/nodes/materials/NodeMaterial.js b/examples/jsm/nodes/materials/NodeMaterial.js index 844fe493501903..733bd9a2ac4311 100644 --- a/examples/jsm/nodes/materials/NodeMaterial.js +++ b/examples/jsm/nodes/materials/NodeMaterial.js @@ -386,7 +386,7 @@ class NodeMaterial extends ShaderMaterial { const fogNode = builder.fogNode; - if ( fogNode ) outputNode = vec4( fogNode.mixAssign( outputNode.rgb ), outputNode.a ); + if ( fogNode ) outputNode = vec4( fogNode.mix( outputNode.rgb, fogNode.colorNode ), outputNode.a ); } From d0e4571396e073a59b6af7ec594fdaa942e9635d Mon Sep 17 00:00:00 2001 From: sunag Date: Sun, 3 Mar 2024 02:43:08 -0300 Subject: [PATCH 05/12] TSL: Add `toneMappingExposure` --- examples/jsm/nodes/display/ToneMappingNode.js | 12 ++++++++---- examples/jsm/renderers/common/nodes/Nodes.js | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/jsm/nodes/display/ToneMappingNode.js b/examples/jsm/nodes/display/ToneMappingNode.js index 19332f6013a100..357133a010b7f6 100644 --- a/examples/jsm/nodes/display/ToneMappingNode.js +++ b/examples/jsm/nodes/display/ToneMappingNode.js @@ -1,11 +1,12 @@ import TempNode from '../core/TempNode.js'; import { addNodeClass } from '../core/Node.js'; -import { tslFn, nodeObject, float, mat3, vec3 } from '../shadernode/ShaderNode.js'; - -import { NoToneMapping, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping } from 'three'; +import { addNodeElement, tslFn, nodeObject, float, mat3, vec3 } from '../shadernode/ShaderNode.js'; +import { rendererReference } from '../accessors/RendererReferenceNode.js'; import { clamp, log2, max, pow } from '../math/MathNode.js'; import { mul } from '../math/OperatorNode.js'; +import { NoToneMapping, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping } from 'three'; + // exposure only const LinearToneMappingNode = tslFn( ( { color, exposure } ) => { @@ -127,7 +128,7 @@ const toneMappingLib = { class ToneMappingNode extends TempNode { - constructor( toneMapping = NoToneMapping, exposureNode = float( 1 ), colorNode = null ) { + constructor( toneMapping = NoToneMapping, exposureNode = toneMappingExposure, colorNode = null ) { super( 'vec3' ); @@ -180,5 +181,8 @@ class ToneMappingNode extends TempNode { export default ToneMappingNode; export const toneMapping = ( mapping, exposure, color ) => nodeObject( new ToneMappingNode( mapping, nodeObject( exposure ), nodeObject( color ) ) ); +export const toneMappingExposure = rendererReference( 'toneMappingExposure', 'float' ); + +addNodeElement( 'toneMapping', ( color, mapping, exposure ) => toneMapping( mapping, exposure, color ) ); addNodeClass( 'ToneMappingNode', ToneMappingNode ); diff --git a/examples/jsm/renderers/common/nodes/Nodes.js b/examples/jsm/renderers/common/nodes/Nodes.js index bccb34a6bbc7a1..e7b33ddd958549 100644 --- a/examples/jsm/renderers/common/nodes/Nodes.js +++ b/examples/jsm/renderers/common/nodes/Nodes.js @@ -275,7 +275,7 @@ class Nodes extends DataMap { if ( rendererData.toneMapping !== rendererToneMapping ) { - const rendererToneMappingNode = rendererData.rendererToneMappingNode || toneMapping( rendererToneMapping, reference( 'toneMappingExposure', 'float', renderer ) ); + const rendererToneMappingNode = rendererData.rendererToneMappingNode || toneMapping( rendererToneMapping ); rendererToneMappingNode.toneMapping = rendererToneMapping; rendererData.rendererToneMappingNode = rendererToneMappingNode; From 0d7d848818368ebeac53fba705bd44626f060772 Mon Sep 17 00:00:00 2001 From: sunag Date: Sun, 3 Mar 2024 02:43:25 -0300 Subject: [PATCH 06/12] PostProcessing: Add `.renderAsync()` --- examples/jsm/renderers/common/PostProcessing.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/jsm/renderers/common/PostProcessing.js b/examples/jsm/renderers/common/PostProcessing.js index 2598362708dfa5..71ea7afbc4cc94 100644 --- a/examples/jsm/renderers/common/PostProcessing.js +++ b/examples/jsm/renderers/common/PostProcessing.js @@ -12,11 +12,19 @@ class PostProcessing { } - async render() { + renderAsync() { quadMesh.material.fragmentNode = this.outputNode; - await quadMesh.render( this.renderer ); + return quadMesh.renderAsync( this.renderer ); + + } + + render() { + + quadMesh.material.fragmentNode = this.outputNode; + + quadMesh.render( this.renderer ); } From 7fb4f28c8ff591521816a0cb34201ecf0c45a50f Mon Sep 17 00:00:00 2001 From: sunag Date: Sun, 3 Mar 2024 02:43:36 -0300 Subject: [PATCH 07/12] Add `webgpu_custom_fog_background` example --- examples/files.json | 1 + .../webgpu_custom_fog_background.jpg | Bin 0 -> 3415 bytes examples/webgpu_custom_fog_background.html | 152 ++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 examples/screenshots/webgpu_custom_fog_background.jpg create mode 100644 examples/webgpu_custom_fog_background.html diff --git a/examples/files.json b/examples/files.json index 8e99bc1ae21d0e..af51d22bc59cfb 100644 --- a/examples/files.json +++ b/examples/files.json @@ -333,6 +333,7 @@ "webgpu_cubemap_dynamic", "webgpu_cubemap_mix", "webgpu_custom_fog", + "webgpu_custom_fog_background", "webgpu_depth_texture", "webgpu_equirectangular", "webgpu_instance_mesh", diff --git a/examples/screenshots/webgpu_custom_fog_background.jpg b/examples/screenshots/webgpu_custom_fog_background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..658eec1c3a74485db3cfd153f04bf9fee4a30fa1 GIT binary patch literal 3415 zcmex=iF;N$`UAd82aiwDGkXk%h!W@hDLXJZFTlSKSKz#z!M@QZN*Gov5_lOQ9r zAmjfdjEjJ7WCc47=uik?WMXDvWn%|Afm;CRY-VPlV_AVN1*)tC$}zAAvI;30I#U-U>$dGXcJ4ZK_{h;?$4{I*b?NeztJkjIxOwa0qsLF4K70P+<*SdMK7aZ8?fZ|P zzZe;qA>IL!82$lzoRJ9>=IkK_0Zp(e6?1osf!KmTtr@Gvt1BaB&)!Jgq~-~Q{(KX^14 q7BDq%F^p29K`@#MMl-@_Suk20j@AgHwc%)OI9eMH!`jgP|4jgHh|UiH literal 0 HcmV?d00001 diff --git a/examples/webgpu_custom_fog_background.html b/examples/webgpu_custom_fog_background.html new file mode 100644 index 00000000000000..53ea8a9046a1bb --- /dev/null +++ b/examples/webgpu_custom_fog_background.html @@ -0,0 +1,152 @@ + + + + three.js webgpu - GLTFloader + + + + + + + +
+ three.js - GLTFLoader
+ Battle Damaged Sci-fi Helmet by + theblueturtle_
+ Royal Esplanade by HDRI Haven +
+ + + + + + + From eaecbd2cbe7f12f283c448d8e9ebab58f550fbee Mon Sep 17 00:00:00 2001 From: sunag Date: Sun, 3 Mar 2024 02:49:21 -0300 Subject: [PATCH 08/12] cleanup --- examples/webgpu_custom_fog_background.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/webgpu_custom_fog_background.html b/examples/webgpu_custom_fog_background.html index 53ea8a9046a1bb..0f38279451d3d6 100644 --- a/examples/webgpu_custom_fog_background.html +++ b/examples/webgpu_custom_fog_background.html @@ -70,7 +70,7 @@ // post processing - // render scene pass + // render scene pass ( the same of css ) const scenePass = pass( scene, camera ); const scenePassViewZ = scenePass.getViewZNode(); @@ -83,7 +83,7 @@ // tone mapping scene pass const scenePassTM = scenePass.toneMapping( THREE.ACESFilmicToneMapping ); - // compose fog from fog factor and background color + // mix fog from fog factor and background color const compose = fogFactor.mix( scenePassTM, backgroundColor ); postProcessing = new PostProcessing( renderer ); From 35e49c1512e0d1570ed8f886729cb5e03ee484b7 Mon Sep 17 00:00:00 2001 From: sunag Date: Sun, 3 Mar 2024 02:51:48 -0300 Subject: [PATCH 09/12] cleanup --- examples/webgpu_custom_fog_background.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/webgpu_custom_fog_background.html b/examples/webgpu_custom_fog_background.html index 0f38279451d3d6..17520caea1ff0f 100644 --- a/examples/webgpu_custom_fog_background.html +++ b/examples/webgpu_custom_fog_background.html @@ -1,7 +1,7 @@ - three.js webgpu - GLTFloader + three.js webgpu - custom fog background @@ -16,7 +16,7 @@
- three.js - GLTFLoader
+ three.js - webgpu custom fog background
Battle Damaged Sci-fi Helmet by theblueturtle_
Royal Esplanade by HDRI Haven From a17496361fa05b4970456b7b6cae23633b547e34 Mon Sep 17 00:00:00 2001 From: sunag Date: Sun, 3 Mar 2024 02:56:03 -0300 Subject: [PATCH 10/12] improve description --- examples/webgpu_custom_fog_background.html | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/webgpu_custom_fog_background.html b/examples/webgpu_custom_fog_background.html index 17520caea1ff0f..c4ae1c19afe9aa 100644 --- a/examples/webgpu_custom_fog_background.html +++ b/examples/webgpu_custom_fog_background.html @@ -78,6 +78,7 @@ const backgroundColor = color( 0x0066ff ); // get fog factor from scene pass context + // equivalent to: scene.fog = new THREE.Fog( 0x0066ff, 2.5, 4 ); const fogFactor = rangeFog( null, 2.5, 4 ).context( { viewZ: scenePassViewZ } ); // tone mapping scene pass From 1e7f19fc7c8504a887fee7e58b21d603b4c91403 Mon Sep 17 00:00:00 2001 From: sunag Date: Mon, 4 Mar 2024 21:43:54 -0300 Subject: [PATCH 11/12] update example --- .../webgpu_custom_fog_background.jpg | Bin 3415 -> 29816 bytes examples/webgpu_custom_fog_background.html | 13 +++++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/examples/screenshots/webgpu_custom_fog_background.jpg b/examples/screenshots/webgpu_custom_fog_background.jpg index 658eec1c3a74485db3cfd153f04bf9fee4a30fa1..3e48bedbba189fa4eb302e33e26e1e6a938f9163 100644 GIT binary patch literal 29816 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 NsQ<+~Q~x{lKL9$RoWcMA delta 51 fcmezIg7Lc4hUd(iCoqLG;wL6gVB%r6|9=wz5@-_x diff --git a/examples/webgpu_custom_fog_background.html b/examples/webgpu_custom_fog_background.html index c4ae1c19afe9aa..be63ec9ad53b8f 100644 --- a/examples/webgpu_custom_fog_background.html +++ b/examples/webgpu_custom_fog_background.html @@ -50,7 +50,6 @@ let postProcessing; init(); - render(); function init() { @@ -78,8 +77,8 @@ const backgroundColor = color( 0x0066ff ); // get fog factor from scene pass context - // equivalent to: scene.fog = new THREE.Fog( 0x0066ff, 2.5, 4 ); - const fogFactor = rangeFog( null, 2.5, 4 ).context( { viewZ: scenePassViewZ } ); + // equivalent to: scene.fog = new THREE.Fog( 0x0066ff, 2.7, 4 ); + const fogFactor = rangeFog( null, 2.7, 4 ).context( { viewZ: scenePassViewZ } ); // tone mapping scene pass const scenePassTM = scenePass.toneMapping( THREE.ACESFilmicToneMapping ); @@ -100,8 +99,6 @@ scene.environment = texture; - render(); - // model const loader = new GLTFLoader().setPath( 'models/gltf/DamagedHelmet/glTF/' ); @@ -118,11 +115,11 @@ // const controls = new OrbitControls( camera, renderer.domElement ); - controls.addEventListener( 'change', render ); // use if there is no animation loop controls.minDistance = 2; - controls.maxDistance = 10; - controls.target.set( 0, 0, - 0.2 ); + controls.maxDistance = 5; + controls.target.set( 0, - 0.1, - 0.2 ); controls.update(); + controls.addEventListener( 'change', render ); // use if there is no animation loop window.addEventListener( 'resize', onWindowResize ); From f9af5f0e911c899245a847172649e3d2caa474aa Mon Sep 17 00:00:00 2001 From: sunag Date: Wed, 13 Mar 2024 13:26:56 -0300 Subject: [PATCH 12/12] FogNode: Use getViewZ() in context instead of viewZ as Node --- examples/jsm/nodes/fog/FogNode.js | 12 +++++++++++- examples/webgpu_custom_fog_background.html | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/jsm/nodes/fog/FogNode.js b/examples/jsm/nodes/fog/FogNode.js index 55f374f9f36b5c..4e18d5c7631a85 100644 --- a/examples/jsm/nodes/fog/FogNode.js +++ b/examples/jsm/nodes/fog/FogNode.js @@ -17,7 +17,17 @@ class FogNode extends Node { getViewZNode( builder ) { - return ( builder.context.viewZ || positionView.z ).negate(); + let viewZ; + + const getViewZ = builder.context.getViewZ; + + if ( getViewZ !== undefined ) { + + viewZ = getViewZ( this ); + + } + + return ( viewZ || positionView.z ).negate(); } diff --git a/examples/webgpu_custom_fog_background.html b/examples/webgpu_custom_fog_background.html index be63ec9ad53b8f..01c51b3c9fae1a 100644 --- a/examples/webgpu_custom_fog_background.html +++ b/examples/webgpu_custom_fog_background.html @@ -78,7 +78,7 @@ // get fog factor from scene pass context // equivalent to: scene.fog = new THREE.Fog( 0x0066ff, 2.7, 4 ); - const fogFactor = rangeFog( null, 2.7, 4 ).context( { viewZ: scenePassViewZ } ); + const fogFactor = rangeFog( null, 2.7, 4 ).context( { getViewZ: () => scenePassViewZ } ); // tone mapping scene pass const scenePassTM = scenePass.toneMapping( THREE.ACESFilmicToneMapping );