Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGPURenderer: PostProcessing + GaussianBlurNode + QuadMesh #27369

Merged
merged 22 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.isFramebufferTexture === 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
157 changes: 157 additions & 0 deletions examples/jsm/nodes/display/GaussianBlurNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
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;

//

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
Loading