Skip to content

Commit 3d65226

Browse files
authored
WebGPURenderer: PostProcessing + GaussianBlurNode + QuadMesh (#27369)
* WebGPURenderer: PostProcessing + GaussianBlurNode * update example * revision * revision * Fix RTT & Framebuffer flipY * Fix multi-scene backgroundNode * fixes * new webgpu_portal example * fix title * cleanup * cleanup * adjustments * Added QuadMesh * PostProcessing just for WebGPUBackend for now * portal update * error message * cleanup * using quad texture * Fix flip RTT & DepthNode after QuadMesh * update to QuadMesh * Update webgpu_depth_texture.jpg * update `webgpu_instance_uniform` example
1 parent 64467c0 commit 3d65226

29 files changed

+761
-89
lines changed

examples/files.json

+1
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@
349349
"webgpu_morphtargets_face",
350350
"webgpu_occlusion",
351351
"webgpu_particles",
352+
"webgpu_portal",
352353
"webgpu_rtt",
353354
"webgpu_sandbox",
354355
"webgpu_shadertoy",

examples/jsm/nodes/Nodes.js

+2
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ export { default as ViewportTextureNode, viewportTexture, viewportMipTexture } f
110110
export { default as ViewportSharedTextureNode, viewportSharedTexture } from './display/ViewportSharedTextureNode.js';
111111
export { default as ViewportDepthTextureNode, viewportDepthTexture } from './display/ViewportDepthTextureNode.js';
112112
export { default as ViewportDepthNode, viewZToOrthographicDepth, orthographicDepthToViewZ, viewZToPerspectiveDepth, perspectiveDepthToViewZ, depth, depthTexture, depthPixel } from './display/ViewportDepthNode.js';
113+
export { default as GaussianBlurNode, gaussianBlur } from './display/GaussianBlurNode.js';
114+
export { default as PassNode, pass, depthPass } from './display/PassNode.js';
113115

114116
// code
115117
export { default as ExpressionNode, expression } from './code/ExpressionNode.js';

examples/jsm/nodes/accessors/CubeTextureNode.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,14 @@ class CubeTextureNode extends TextureNode {
2727

2828
setUpdateMatrix( /*updateMatrix*/ ) { } // Ignore .updateMatrix for CubeTextureNode
2929

30-
generateUV( builder, uvNode ) {
30+
setupUV( builder, uvNode ) {
31+
32+
return vec3( uvNode.x.negate(), uvNode.yz );
33+
34+
}
35+
36+
generateUV( builder, cubeUV ) {
3137

32-
const cubeUV = vec3( uvNode.x.negate(), uvNode.yz );
3338
return cubeUV.build( builder, 'vec3' );
3439

3540
}

examples/jsm/nodes/accessors/TextureNode.js

+22-15
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,20 @@ class TextureNode extends UniformNode {
7878

7979
}
8080

81+
setupUV( builder, uvNode ) {
82+
83+
const texture = this.value;
84+
85+
if ( builder.isFlipY() && ( texture.isRenderTargetTexture === true || texture.isFramebufferTexture === true || texture.isDepthTexture === true ) ) {
86+
87+
uvNode = uvNode.setY( uvNode.y.oneMinus() );
88+
89+
}
90+
91+
return uvNode;
92+
93+
}
94+
8195
setup( builder ) {
8296

8397
const properties = builder.getNodeProperties( this );
@@ -100,6 +114,8 @@ class TextureNode extends UniformNode {
100114

101115
}
102116

117+
uvNode = this.setupUV( builder, uvNode );
118+
103119
//
104120

105121
let levelNode = this.levelNode;
@@ -125,6 +141,12 @@ class TextureNode extends UniformNode {
125141

126142
}
127143

144+
generateUV( builder, uvNode ) {
145+
146+
return uvNode.build( builder, this.sampler === true ? 'vec2' : 'ivec2' );
147+
148+
}
149+
128150
generateSnippet( builder, textureProperty, uvSnippet, levelSnippet, depthSnippet, compareSnippet ) {
129151

130152
const texture = this.value;
@@ -153,21 +175,6 @@ class TextureNode extends UniformNode {
153175

154176
}
155177

156-
generateUV( builder, uvNode ) {
157-
158-
const texture = this.value;
159-
160-
if ( ( builder.isFlipY() && ( texture.isFramebufferTexture === true || texture.isDepthTexture === true ) ) ||
161-
( builder.isFlipY() === false && texture.isRenderTargetTexture === true ) ) {
162-
163-
uvNode = uvNode.setY( uvNode.y.fract().oneMinus() );
164-
165-
}
166-
167-
return uvNode.build( builder, this.sampler === true ? 'vec2' : 'ivec2' );
168-
169-
}
170-
171178
generate( builder, output ) {
172179

173180
const properties = builder.getNodeProperties( this );

examples/jsm/nodes/core/AttributeNode.js

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ class AttributeNode extends Node {
1212

1313
}
1414

15+
isGlobal() {
16+
17+
return true;
18+
19+
}
20+
1521
getHash( builder ) {
1622

1723
return this.getAttributeName( builder );

examples/jsm/nodes/core/CacheNode.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ class CacheNode extends Node {
2424
build( builder, ...params ) {
2525

2626
const previousCache = builder.getCache();
27+
const cache = this.cache || builder.globalCache;
2728

28-
builder.setCache( this.cache );
29+
builder.setCache( cache );
2930

3031
const data = this.node.build( builder, ...params );
3132

@@ -40,7 +41,9 @@ class CacheNode extends Node {
4041
export default CacheNode;
4142

4243
export const cache = nodeProxy( CacheNode );
44+
export const globalCache = ( node ) => cache( node, null );
4345

4446
addNodeElement( 'cache', cache );
47+
addNodeElement( 'globalCache', globalCache );
4548

4649
addNodeClass( 'CacheNode', CacheNode );

examples/jsm/nodes/core/NodeBuilder.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -626,9 +626,9 @@ class NodeBuilder {
626626

627627
}
628628

629-
getDataFromNode( node, shaderStage = this.shaderStage ) {
629+
getDataFromNode( node, shaderStage = this.shaderStage, cache = null ) {
630630

631-
const cache = node.isGlobal( this ) ? this.globalCache : this.cache;
631+
cache = cache === null ? ( node.isGlobal( this ) ? this.globalCache : this.cache ) : cache;
632632

633633
let nodeData = cache.getNodeData( node );
634634

@@ -697,7 +697,7 @@ class NodeBuilder {
697697

698698
getUniformFromNode( node, type, shaderStage = this.shaderStage, name = null ) {
699699

700-
const nodeData = this.getDataFromNode( node, shaderStage );
700+
const nodeData = this.getDataFromNode( node, shaderStage, this.globalCache );
701701

702702
let nodeUniform = nodeData.uniform;
703703

examples/jsm/nodes/display/ColorAdjustmentNode.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { addNodeElement, tslFn, nodeProxy, float, vec3 } from '../shadernode/Sha
66

77
const saturationNode = tslFn( ( { color, adjustment } ) => {
88

9-
return adjustment.mix( luminance( color ), color );
9+
return adjustment.mix( luminance( color.rgb ), color.rgb );
1010

1111
} );
1212

@@ -17,7 +17,7 @@ const vibranceNode = tslFn( ( { color, adjustment } ) => {
1717
const mx = color.r.max( color.g.max( color.b ) );
1818
const amt = mx.sub( average ).mul( adjustment ).mul( - 3.0 );
1919

20-
return mix( color, mx, amt );
20+
return mix( color.rgb, mx, amt );
2121

2222
} );
2323

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import TempNode from '../core/TempNode.js';
2+
import { nodeObject, addNodeElement, tslFn, float, vec2, vec3, vec4 } from '../shadernode/ShaderNode.js';
3+
import { NodeUpdateType } from '../core/constants.js';
4+
import { mul } from '../math/OperatorNode.js';
5+
import { uv } from '../accessors/UVNode.js';
6+
import { texture } from '../accessors/TextureNode.js';
7+
import { uniform } from '../core/UniformNode.js';
8+
import { Vector2, RenderTarget } from 'three';
9+
import QuadMesh from '../../objects/QuadMesh.js';
10+
11+
const quadMesh = new QuadMesh();
12+
13+
class GaussianBlurNode extends TempNode {
14+
15+
constructor( textureNode, sigma = 2 ) {
16+
17+
super( textureNode );
18+
19+
this.textureNode = textureNode;
20+
this.sigma = sigma;
21+
22+
this.directionNode = vec2( 1 );
23+
24+
this._invSize = uniform( new Vector2() );
25+
this._passDirection = uniform( new Vector2() );
26+
27+
this._horizontalRT = new RenderTarget();
28+
this._verticalRT = new RenderTarget();
29+
30+
this.updateBeforeType = NodeUpdateType.RENDER;
31+
32+
}
33+
34+
setSize( width, height ) {
35+
36+
this._invSize.value.set( 1 / width, 1 / height );
37+
this._horizontalRT.setSize( width, height );
38+
this._verticalRT.setSize( width, height );
39+
40+
}
41+
42+
updateBefore( frame ) {
43+
44+
const { renderer } = frame;
45+
46+
const textureNode = this.textureNode;
47+
const map = textureNode.value;
48+
49+
const currentRenderTarget = renderer.getRenderTarget();
50+
const currentTexture = textureNode.value;
51+
52+
quadMesh.material = this._material;
53+
54+
this.setSize( map.image.width, map.image.height );
55+
56+
// horizontal
57+
58+
renderer.setRenderTarget( this._horizontalRT );
59+
60+
this._passDirection.value.set( 1, 0 );
61+
62+
quadMesh.render( renderer );
63+
64+
// vertical
65+
66+
textureNode.value = this._horizontalRT.texture;
67+
renderer.setRenderTarget( this._verticalRT );
68+
69+
this._passDirection.value.set( 0, 1 );
70+
71+
quadMesh.render( renderer );
72+
73+
// restore
74+
75+
renderer.setRenderTarget( currentRenderTarget );
76+
textureNode.value = currentTexture;
77+
78+
}
79+
80+
setup( builder ) {
81+
82+
const textureNode = this.textureNode;
83+
84+
if ( textureNode.isTextureNode !== true ) {
85+
86+
console.error( 'GaussianBlurNode requires a TextureNode.' );
87+
88+
return vec4();
89+
90+
}
91+
92+
//
93+
94+
const uvNode = textureNode.uvNode || uv();
95+
96+
const sampleTexture = ( uv ) => textureNode.cache().context( { getUV: () => uv, forceUVContext: true } );
97+
98+
const blur = tslFn( () => {
99+
100+
const kernelSize = 3 + ( 2 * this.sigma );
101+
const gaussianCoefficients = this._getCoefficients( kernelSize );
102+
103+
const invSize = this._invSize;
104+
const direction = vec2( this.directionNode ).mul( this._passDirection );
105+
106+
const weightSum = float( gaussianCoefficients[ 0 ] ).toVar();
107+
const diffuseSum = vec3( sampleTexture( uvNode ).mul( weightSum ) ).toVar();
108+
109+
for ( let i = 1; i < kernelSize; i ++ ) {
110+
111+
const x = float( i );
112+
const w = float( gaussianCoefficients[ i ] );
113+
114+
const uvOffset = vec2( direction.mul( invSize.mul( x ) ) ).toVar();
115+
116+
const sample1 = vec3( sampleTexture( uvNode.add( uvOffset ) ) );
117+
const sample2 = vec3( sampleTexture( uvNode.sub( uvOffset ) ) );
118+
119+
diffuseSum.addAssign( sample1.add( sample2 ).mul( w ) );
120+
weightSum.addAssign( mul( 2.0, w ) );
121+
122+
}
123+
124+
return vec4( diffuseSum.div( weightSum ), 1.0 );
125+
126+
} );
127+
128+
//
129+
130+
const material = this._material || ( this._material = builder.createNodeMaterial( 'MeshBasicNodeMaterial' ) );
131+
material.fragmentNode = blur();
132+
133+
//
134+
135+
const properties = builder.getNodeProperties( this );
136+
properties.textureNode = textureNode;
137+
138+
//
139+
140+
return texture( this._verticalRT.texture );
141+
142+
}
143+
144+
_getCoefficients( kernelRadius ) {
145+
146+
const coefficients = [];
147+
148+
for ( let i = 0; i < kernelRadius; i ++ ) {
149+
150+
coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( kernelRadius * kernelRadius ) ) / kernelRadius );
151+
152+
}
153+
154+
return coefficients;
155+
156+
}
157+
158+
}
159+
160+
export const gaussianBlur = ( node, sigma ) => nodeObject( new GaussianBlurNode( nodeObject( node ), sigma ) );
161+
162+
addNodeElement( 'gaussianBlur', gaussianBlur );
163+
164+
export default GaussianBlurNode;
165+

0 commit comments

Comments
 (0)