Skip to content

Commit

Permalink
WebGPURenderer: PostProcessing + GaussianBlurNode + QuadMesh (mrdoob#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
sunag authored and AdaRoseCannon committed Jan 15, 2024
1 parent 1043754 commit b2c5c6b
Show file tree
Hide file tree
Showing 29 changed files with 761 additions and 89 deletions.
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@
"webgpu_morphtargets_face",
"webgpu_occlusion",
"webgpu_particles",
"webgpu_portal",
"webgpu_rtt",
"webgpu_sandbox",
"webgpu_shadertoy",
Expand Down
2 changes: 2 additions & 0 deletions examples/jsm/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export { default as ViewportTextureNode, viewportTexture, viewportMipTexture } f
export { default as ViewportSharedTextureNode, viewportSharedTexture } from './display/ViewportSharedTextureNode.js';
export { default as ViewportDepthTextureNode, viewportDepthTexture } from './display/ViewportDepthTextureNode.js';
export { default as ViewportDepthNode, viewZToOrthographicDepth, orthographicDepthToViewZ, viewZToPerspectiveDepth, perspectiveDepthToViewZ, depth, depthTexture, depthPixel } from './display/ViewportDepthNode.js';
export { default as GaussianBlurNode, gaussianBlur } from './display/GaussianBlurNode.js';
export { default as PassNode, pass, depthPass } from './display/PassNode.js';

// code
export { default as ExpressionNode, expression } from './code/ExpressionNode.js';
Expand Down
9 changes: 7 additions & 2 deletions examples/jsm/nodes/accessors/CubeTextureNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,14 @@ class CubeTextureNode extends TextureNode {

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

generateUV( builder, uvNode ) {
setupUV( builder, uvNode ) {

return vec3( uvNode.x.negate(), uvNode.yz );

}

generateUV( builder, cubeUV ) {

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

}
Expand Down
37 changes: 22 additions & 15 deletions examples/jsm/nodes/accessors/TextureNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ class TextureNode extends UniformNode {

}

setupUV( builder, uvNode ) {

const texture = this.value;

if ( builder.isFlipY() && ( texture.isRenderTargetTexture === true || texture.isFramebufferTexture === true || texture.isDepthTexture === true ) ) {

uvNode = uvNode.setY( uvNode.y.oneMinus() );

}

return uvNode;

}

setup( builder ) {

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

}

uvNode = this.setupUV( builder, uvNode );

//

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

}

generateUV( builder, uvNode ) {

return uvNode.build( builder, this.sampler === true ? 'vec2' : 'ivec2' );

}

generateSnippet( builder, textureProperty, uvSnippet, levelSnippet, depthSnippet, compareSnippet ) {

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

}

generateUV( builder, uvNode ) {

const texture = this.value;

if ( ( builder.isFlipY() && ( texture.isFramebufferTexture === true || texture.isDepthTexture === true ) ) ||
( builder.isFlipY() === false && texture.isRenderTargetTexture === true ) ) {

uvNode = uvNode.setY( uvNode.y.fract().oneMinus() );

}

return uvNode.build( builder, this.sampler === true ? 'vec2' : 'ivec2' );

}

generate( builder, output ) {

const properties = builder.getNodeProperties( this );
Expand Down
6 changes: 6 additions & 0 deletions examples/jsm/nodes/core/AttributeNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ class AttributeNode extends Node {

}

isGlobal() {

return true;

}

getHash( builder ) {

return this.getAttributeName( builder );
Expand Down
5 changes: 4 additions & 1 deletion examples/jsm/nodes/core/CacheNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ class CacheNode extends Node {
build( builder, ...params ) {

const previousCache = builder.getCache();
const cache = this.cache || builder.globalCache;

builder.setCache( this.cache );
builder.setCache( cache );

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

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

export const cache = nodeProxy( CacheNode );
export const globalCache = ( node ) => cache( node, null );

addNodeElement( 'cache', cache );
addNodeElement( 'globalCache', globalCache );

addNodeClass( 'CacheNode', CacheNode );
6 changes: 3 additions & 3 deletions examples/jsm/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -626,9 +626,9 @@ class NodeBuilder {

}

getDataFromNode( node, shaderStage = this.shaderStage ) {
getDataFromNode( node, shaderStage = this.shaderStage, cache = null ) {

const cache = node.isGlobal( this ) ? this.globalCache : this.cache;
cache = cache === null ? ( node.isGlobal( this ) ? this.globalCache : this.cache ) : cache;

let nodeData = cache.getNodeData( node );

Expand Down Expand Up @@ -697,7 +697,7 @@ class NodeBuilder {

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

const nodeData = this.getDataFromNode( node, shaderStage );
const nodeData = this.getDataFromNode( node, shaderStage, this.globalCache );

let nodeUniform = nodeData.uniform;

Expand Down
4 changes: 2 additions & 2 deletions examples/jsm/nodes/display/ColorAdjustmentNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { addNodeElement, tslFn, nodeProxy, float, vec3 } from '../shadernode/Sha

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

return adjustment.mix( luminance( color ), color );
return adjustment.mix( luminance( color.rgb ), color.rgb );

} );

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

return mix( color, mx, amt );
return mix( color.rgb, mx, amt );

} );

Expand Down
165 changes: 165 additions & 0 deletions examples/jsm/nodes/display/GaussianBlurNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import TempNode from '../core/TempNode.js';
import { nodeObject, addNodeElement, tslFn, float, vec2, vec3, vec4 } from '../shadernode/ShaderNode.js';
import { NodeUpdateType } from '../core/constants.js';
import { mul } from '../math/OperatorNode.js';
import { uv } from '../accessors/UVNode.js';
import { texture } from '../accessors/TextureNode.js';
import { uniform } from '../core/UniformNode.js';
import { Vector2, RenderTarget } from 'three';
import QuadMesh from '../../objects/QuadMesh.js';

const quadMesh = new QuadMesh();

class GaussianBlurNode extends TempNode {

constructor( textureNode, sigma = 2 ) {

super( textureNode );

this.textureNode = textureNode;
this.sigma = sigma;

this.directionNode = vec2( 1 );

this._invSize = uniform( new Vector2() );
this._passDirection = uniform( new Vector2() );

this._horizontalRT = new RenderTarget();
this._verticalRT = new RenderTarget();

this.updateBeforeType = NodeUpdateType.RENDER;

}

setSize( width, height ) {

this._invSize.value.set( 1 / width, 1 / height );
this._horizontalRT.setSize( width, height );
this._verticalRT.setSize( width, height );

}

updateBefore( frame ) {

const { renderer } = frame;

const textureNode = this.textureNode;
const map = textureNode.value;

const currentRenderTarget = renderer.getRenderTarget();
const currentTexture = textureNode.value;

quadMesh.material = this._material;

this.setSize( map.image.width, map.image.height );

// horizontal

renderer.setRenderTarget( this._horizontalRT );

this._passDirection.value.set( 1, 0 );

quadMesh.render( renderer );

// vertical

textureNode.value = this._horizontalRT.texture;
renderer.setRenderTarget( this._verticalRT );

this._passDirection.value.set( 0, 1 );

quadMesh.render( renderer );

// restore

renderer.setRenderTarget( currentRenderTarget );
textureNode.value = currentTexture;

}

setup( builder ) {

const textureNode = this.textureNode;

if ( textureNode.isTextureNode !== true ) {

console.error( 'GaussianBlurNode requires a TextureNode.' );

return vec4();

}

//

const uvNode = textureNode.uvNode || uv();

const sampleTexture = ( uv ) => textureNode.cache().context( { getUV: () => uv, forceUVContext: true } );

const blur = tslFn( () => {

const kernelSize = 3 + ( 2 * this.sigma );
const gaussianCoefficients = this._getCoefficients( kernelSize );

const invSize = this._invSize;
const direction = vec2( this.directionNode ).mul( this._passDirection );

const weightSum = float( gaussianCoefficients[ 0 ] ).toVar();
const diffuseSum = vec3( sampleTexture( uvNode ).mul( weightSum ) ).toVar();

for ( let i = 1; i < kernelSize; i ++ ) {

const x = float( i );
const w = float( gaussianCoefficients[ i ] );

const uvOffset = vec2( direction.mul( invSize.mul( x ) ) ).toVar();

const sample1 = vec3( sampleTexture( uvNode.add( uvOffset ) ) );
const sample2 = vec3( sampleTexture( uvNode.sub( uvOffset ) ) );

diffuseSum.addAssign( sample1.add( sample2 ).mul( w ) );
weightSum.addAssign( mul( 2.0, w ) );

}

return vec4( diffuseSum.div( weightSum ), 1.0 );

} );

//

const material = this._material || ( this._material = builder.createNodeMaterial( 'MeshBasicNodeMaterial' ) );
material.fragmentNode = blur();

//

const properties = builder.getNodeProperties( this );
properties.textureNode = textureNode;

//

return texture( this._verticalRT.texture );

}

_getCoefficients( kernelRadius ) {

const coefficients = [];

for ( let i = 0; i < kernelRadius; i ++ ) {

coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( kernelRadius * kernelRadius ) ) / kernelRadius );

}

return coefficients;

}

}

export const gaussianBlur = ( node, sigma ) => nodeObject( new GaussianBlurNode( nodeObject( node ), sigma ) );

addNodeElement( 'gaussianBlur', gaussianBlur );

export default GaussianBlurNode;

Loading

0 comments on commit b2c5c6b

Please sign in to comment.