Skip to content

Commit

Permalink
WebGPURenderer: PostProcessing - Anamorphic example (#27621)
Browse files Browse the repository at this point in the history
* add threshold

* Add AnamorphicNode

* Add getTextureNode()

* Update Nodes.js

* add anamorphic example

* update

* update types
  • Loading branch information
sunag committed Jan 26, 2024
1 parent 02c8f0e commit 805a72d
Show file tree
Hide file tree
Showing 9 changed files with 334 additions and 5 deletions.
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@
"webgpu_tsl_transpiler",
"webgpu_video_panorama",
"webgpu_postprocessing_afterimage",
"webgpu_postprocessing_anamorphic",
"webgpu_mirror",
"webgpu_multisampled_renderbuffers",
"webgpu_materials_texture_anisotropy"
Expand Down
3 changes: 2 additions & 1 deletion examples/jsm/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export { default as UserDataNode, userData } from './accessors/UserDataNode.js';
// display
export { default as BlendModeNode, burn, dodge, overlay, screen } from './display/BlendModeNode.js';
export { default as BumpMapNode, bumpMap } from './display/BumpMapNode.js';
export { default as ColorAdjustmentNode, saturation, vibrance, hue, lumaCoeffs, luminance } from './display/ColorAdjustmentNode.js';
export { default as ColorAdjustmentNode, saturation, vibrance, hue, lumaCoeffs, luminance, threshold } from './display/ColorAdjustmentNode.js';
export { default as ColorSpaceNode, linearToColorSpace, colorSpaceToLinear, linearTosRGB, sRGBToLinear } from './display/ColorSpaceNode.js';
export { default as FrontFacingNode, frontFacing, faceDirection } from './display/FrontFacingNode.js';
export { default as NormalMapNode, normalMap, TBNViewMatrix } from './display/NormalMapNode.js';
Expand All @@ -120,6 +120,7 @@ export { default as ViewportDepthTextureNode, viewportDepthTexture } from './dis
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 AfterImageNode, afterImage } from './display/AfterImageNode.js';
export { default as AnamorphicNode, anamorphic } from './display/AnamorphicNode.js';

export { default as PassNode, pass, depthPass } from './display/PassNode.js';

Expand Down
18 changes: 16 additions & 2 deletions examples/jsm/nodes/display/AfterImageNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { nodeObject, addNodeElement, tslFn, float, vec4 } from '../shadernode/Sh
import { NodeUpdateType } from '../core/constants.js';
import { uv } from '../accessors/UVNode.js';
import { texture } from '../accessors/TextureNode.js';
import { texturePass } from './PassNode.js';
import { uniform } from '../core/UniformNode.js';
import { RenderTarget } from 'three';
import { sign, max } from '../math/MathNode.js';
Expand All @@ -26,10 +27,18 @@ class AfterImageNode extends TempNode {
this._oldRT = new RenderTarget();
this._oldRT.texture.name = 'AfterImageNode.old';

this._textureNode = texturePass( this, this._compRT.texture );

this.updateBeforeType = NodeUpdateType.RENDER;

}

getTextureNode() {

return this._textureNode;

}

setSize( width, height ) {

this._compRT.setSize( width, height );
Expand All @@ -42,6 +51,12 @@ class AfterImageNode extends TempNode {
const { renderer } = frame;

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

const textureType = map.type;

this._compRT.texture.type = textureType;
this._oldRT.texture.type = textureType;

const currentRenderTarget = renderer.getRenderTarget();
const currentTexture = textureNode.value;
Expand All @@ -58,7 +73,6 @@ class AfterImageNode extends TempNode {
this._compRT = temp;

// set size before swapping fails
const map = currentTexture;
this.setSize( map.image.width, map.image.height );

renderer.setRenderTarget( currentRenderTarget );
Expand Down Expand Up @@ -120,7 +134,7 @@ class AfterImageNode extends TempNode {

//

return texture( this._compRT.texture );
return this._textureNode;

}

Expand Down
148 changes: 148 additions & 0 deletions examples/jsm/nodes/display/AnamorphicNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import TempNode from '../core/TempNode.js';
import { nodeObject, addNodeElement, tslFn, float, vec2, vec3, vec4 } from '../shadernode/ShaderNode.js';
import { loop } from '../utils/LoopNode.js';
import { uniform } from '../core/UniformNode.js';
import { NodeUpdateType } from '../core/constants.js';
import { threshold } from './ColorAdjustmentNode.js';
import { uv } from '../accessors/UVNode.js';
import { texturePass } from './PassNode.js';
import { Vector2, RenderTarget } from 'three';
import QuadMesh from '../../objects/QuadMesh.js';

const quadMesh = new QuadMesh();

class AnamorphicNode extends TempNode {

constructor( textureNode, tresholdNode, scaleNode, samples ) {

super( 'vec4' );

this.textureNode = textureNode;
this.tresholdNode = tresholdNode;
this.scaleNode = scaleNode;
this.colorNode = vec3( 0.1, 0.0, 1.0 );
this.samples = samples;
this.resolution = new Vector2( 1, 1 );

this._renderTarget = new RenderTarget();
this._renderTarget.texture.name = 'anamorphic';

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

this._textureNode = texturePass( this, this._renderTarget.texture );

this.updateBeforeType = NodeUpdateType.RENDER;

}

getTextureNode() {

return this._textureNode;

}

setSize( width, height ) {

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

width = Math.max( Math.round( width * this.resolution.x ), 1 );
height = Math.max( Math.round( height * this.resolution.y ), 1 );

this._renderTarget.setSize( width, height );

}

updateBefore( frame ) {

const { renderer } = frame;

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

this._renderTarget.texture.type = map.type;

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

quadMesh.material = this._material;

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

// render

renderer.setRenderTarget( this._renderTarget );

quadMesh.render( renderer );

// restore

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

}

setup( builder ) {

const textureNode = this.textureNode;

if ( textureNode.isTextureNode !== true ) {

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

return vec4();

}

//

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

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

const anamorph = tslFn( () => {

const samples = this.samples;
const halfSamples = Math.floor( samples / 2 );

const total = vec3( 0 ).toVar();

loop( { start: - halfSamples, end: halfSamples }, ( { i } ) => {

const softness = float( i ).abs().div( halfSamples ).oneMinus();

const uv = vec2( uvNode.x.add( this._invSize.x.mul( i ).mul( this.scaleNode ) ), uvNode.y );
const color = sampleTexture( uv );
const pass = threshold( color, this.tresholdNode ).mul( softness );

total.addAssign( pass );

} );

return total.mul( this.colorNode );

} );

//

const material = this._material || ( this._material = builder.createNodeMaterial() );
material.fragmentNode = anamorph();

//

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

//

return this._textureNode;

}

}

export const anamorphic = ( node, threshold = .9, scale = 3, samples = 32 ) => nodeObject( new AnamorphicNode( nodeObject( node ), nodeObject( threshold ), nodeObject( scale ), samples ) );

addNodeElement( 'anamorphic', anamorphic );

export default AnamorphicNode;

3 changes: 3 additions & 0 deletions examples/jsm/nodes/display/ColorAdjustmentNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,11 @@ export const hue = nodeProxy( ColorAdjustmentNode, ColorAdjustmentNode.HUE );
export const lumaCoeffs = vec3( 0.2125, 0.7154, 0.0721 );
export const luminance = ( color, luma = lumaCoeffs ) => dot( color, luma );

export const threshold = ( color, threshold ) => mix( vec3( 0.0 ), color, luminance( color ).sub( threshold ).max( 0 ) );

addNodeElement( 'saturation', saturation );
addNodeElement( 'vibrance', vibrance );
addNodeElement( 'hue', hue );
addNodeElement( 'threshold', threshold );

addNodeClass( 'ColorAdjustmentNode', ColorAdjustmentNode );
17 changes: 15 additions & 2 deletions examples/jsm/nodes/display/GaussianBlurNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { nodeObject, addNodeElement, tslFn, float, vec2, vec4 } from '../shadern
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 { texturePass } from './PassNode.js';
import { uniform } from '../core/UniformNode.js';
import { Vector2, RenderTarget } from 'three';
import QuadMesh from '../../objects/QuadMesh.js';
Expand Down Expand Up @@ -33,6 +33,8 @@ class GaussianBlurNode extends TempNode {
this._verticalRT = new RenderTarget();
this._verticalRT.texture.name = 'GaussianBlurNode.vertical';

this._textureNode = texturePass( this, this._verticalRT.texture );

this.updateBeforeType = NodeUpdateType.RENDER;

this.resolution = new Vector2( 1, 1 );
Expand Down Expand Up @@ -65,6 +67,11 @@ class GaussianBlurNode extends TempNode {

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

const textureType = map.type;

this._horizontalRT.texture.type = textureType;
this._verticalRT.texture.type = textureType;

// horizontal

renderer.setRenderTarget( this._horizontalRT );
Expand All @@ -89,6 +96,12 @@ class GaussianBlurNode extends TempNode {

}

getTextureNode() {

return this._textureNode;

}

setup( builder ) {

const textureNode = this.textureNode;
Expand Down Expand Up @@ -149,7 +162,7 @@ class GaussianBlurNode extends TempNode {

//

return texture( this._verticalRT.texture );
return this._textureNode;

}

Expand Down
1 change: 1 addition & 0 deletions examples/jsm/nodes/display/PassNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ PassNode.DEPTH = 'depth';
export default PassNode;

export const pass = ( scene, camera ) => nodeObject( new PassNode( PassNode.COLOR, scene, camera ) );
export const texturePass = ( pass, texture ) => nodeObject( new PassTextureNode( pass, texture ) );
export const depthPass = ( scene, camera ) => nodeObject( new PassNode( PassNode.DEPTH, scene, camera ) );

addNodeClass( 'PassNode', PassNode );
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 805a72d

Please sign in to comment.