Skip to content

Commit

Permalink
TSL: Introduce node.toTexture() and rtt() (#28773)
Browse files Browse the repository at this point in the history
* TextureNode: Fix analyze reference

* Add RTTNode

* rgbShift: add `.toTexture()` and updated example

* Update GaussianBlurNode.js

* add procedural texture

* Update AnamorphicNode.js

* update imports

* update build for now

* update build

* update names
  • Loading branch information
sunag committed Jul 1, 2024
1 parent 1889513 commit 08d5412
Show file tree
Hide file tree
Showing 11 changed files with 388 additions and 76 deletions.
226 changes: 160 additions & 66 deletions build/three.webgpu.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@
"webgpu_postprocessing_dof",
"webgpu_postprocessing_sobel",
"webgpu_postprocessing",
"webgpu_procedural_texture",
"webgpu_reflection",
"webgpu_refraction",
"webgpu_rtt",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion examples/webgpu_postprocessing.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
const dotScreenPass = scenePassColor.dotScreen();
dotScreenPass.scale.value = 0.3;

const rgbShiftPass = dotScreenPass.getTextureNode().rgbShift();
const rgbShiftPass = dotScreenPass.rgbShift();
rgbShiftPass.amount.value = 0.001;

postProcessing.outputNode = rgbShiftPass;
Expand Down
109 changes: 109 additions & 0 deletions examples/webgpu_procedural_texture.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<html lang="en">
<head>
<title>three.js webgpu - procedural texture</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>

<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu - procedural texture
</div>

<script type="importmap">
{
"imports": {
"three": "../build/three.webgpu.js",
"three/tsl": "../build/three.webgpu.js",
"three/addons/": "./jsm/"
}
}
</script>

<script type="module">

import * as THREE from 'three';
import { checker, uv, uniform } from 'three/tsl';

import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

let camera, scene, renderer;

init();
render();

function init() {

const aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera( - aspect, aspect, 1, - 1, 0, 2 );
camera.position.z = 1;

scene = new THREE.Scene();

// procedural to texture

const uvScale = uniform( 4 );
const blurAmount = uniform( .5 );

const procedural = checker( uv().mul( uvScale ) );
const proceduralToTexture = procedural.toTexture( 512, 512 ); // ( width, height ) <- texture size

const colorNode = proceduralToTexture.gaussianBlur( blurAmount, 10 );

// extra

//proceduralToTexture.autoUpdate = false; // update just once
//proceduralToTexture.textureNeedsUpdate = true; // manually update

// scene

const material = new THREE.MeshBasicNodeMaterial();
material.colorNode = colorNode;

const plane = new THREE.Mesh( new THREE.PlaneGeometry( 1, 1 ), material );
scene.add( plane );

// renderer

renderer = new THREE.WebGPURenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( render );
document.body.appendChild( renderer.domElement );

window.addEventListener( 'resize', onWindowResize );

// gui

const gui = new GUI();
gui.add( uvScale, 'value', 1, 10 ).name( 'uv scale ( before rtt )' );
gui.add( blurAmount, 'value', 0, 2 ).name( 'blur amount ( after rtt )' );
gui.add( proceduralToTexture, 'autoUpdate' ).name( 'auto update' );

}

function onWindowResize() {

renderer.setSize( window.innerWidth, window.innerHeight );

const aspect = window.innerWidth / window.innerHeight;

const frustumHeight = camera.top - camera.bottom;

camera.left = - frustumHeight * aspect / 2;
camera.right = frustumHeight * aspect / 2;

camera.updateProjectionMatrix();

}

function render() {

renderer.renderAsync( scene, camera );

}

</script>
</body>
</html>
1 change: 1 addition & 0 deletions src/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export { default as StorageArrayElementNode } from './utils/StorageArrayElementN
export { default as TimerNode, timerLocal, timerGlobal, timerDelta, frameId } from './utils/TimerNode.js';
export { default as TriplanarTexturesNode, triplanarTextures, triplanarTexture } from './utils/TriplanarTexturesNode.js';
export { default as ReflectorNode, reflector } from './utils/ReflectorNode.js';
export { default as RTTNode, rtt } from './utils/RTTNode.js';

// shadernode
export * from './shadernode/ShaderNode.js';
Expand Down
1 change: 1 addition & 0 deletions src/nodes/accessors/TextureNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class TextureNode extends UniformNode {
setup( builder ) {

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

//

Expand Down
4 changes: 2 additions & 2 deletions src/nodes/display/AnamorphicNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class AnamorphicNode extends TempNode {

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

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

const anamorph = tslFn( () => {

Expand Down Expand Up @@ -142,7 +142,7 @@ class AnamorphicNode extends TempNode {

}

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

addNodeElement( 'anamorphic', anamorphic );

Expand Down
12 changes: 6 additions & 6 deletions src/nodes/display/GaussianBlurNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@ const quadMesh2 = new QuadMesh();

class GaussianBlurNode extends TempNode {

constructor( textureNode, sigma = 2 ) {
constructor( textureNode, directionNode = null, sigma = 2 ) {

super( 'vec4' );

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

this.directionNode = vec2( 1 );

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

Expand Down Expand Up @@ -119,16 +118,17 @@ class GaussianBlurNode extends TempNode {
//

const uvNode = textureNode.uvNode || uv();
const directionNode = vec2( this.directionNode || 1 );

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

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 direction = directionNode.mul( this._passDirection );

const weightSum = float( gaussianCoefficients[ 0 ] ).toVar();
const diffuseSum = vec4( sampleTexture( uvNode ).mul( weightSum ) ).toVar();
Expand Down Expand Up @@ -184,7 +184,7 @@ class GaussianBlurNode extends TempNode {

}

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

addNodeElement( 'gaussianBlur', gaussianBlur );

Expand Down
2 changes: 1 addition & 1 deletion src/nodes/display/RGBShiftNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class RGBShiftNode extends TempNode {

}

export const rgbShift = ( node, amount, angle ) => nodeObject( new RGBShiftNode( nodeObject( node ), amount, angle ) );
export const rgbShift = ( node, amount, angle ) => nodeObject( new RGBShiftNode( nodeObject( node ).toTexture(), amount, angle ) );

addNodeElement( 'rgbShift', rgbShift );

Expand Down
106 changes: 106 additions & 0 deletions src/nodes/utils/RTTNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { nodeObject, addNodeElement } from '../shadernode/ShaderNode.js';
import TextureNode from '../accessors/TextureNode.js';
import { NodeUpdateType } from '../core/constants.js';
import { addNodeClass } from '../core/Node.js';
import NodeMaterial from '../materials/NodeMaterial.js';
import { uv } from '../accessors/UVNode.js';
import QuadMesh from '../../renderers/common/QuadMesh.js';

import { RenderTarget } from '../../core/RenderTarget.js';
import { Vector2 } from '../../math/Vector2.js';
import { HalfFloatType } from '../../constants.js';

const _quadMesh = new QuadMesh( new NodeMaterial() );
const _size = new Vector2();

class RTTNode extends TextureNode {

constructor( node, width = null, height = null, options = { type: HalfFloatType } ) {

const renderTarget = new RenderTarget( width, height, options );

super( renderTarget.texture, uv() );

this.node = node;
this.width = width;
this.height = height;

this.renderTarget = renderTarget;

this.textureNeedsUpdate = true;
this.autoUpdate = true;

this.updateMap = new WeakMap();

this.updateBeforeType = NodeUpdateType.RENDER;

}

get autoSize() {

return this.width === null;

}

setSize( width, height ) {

this.width = width;
this.height = height;

this.renderTarget.setSize( width, height );

this.textureNeedsUpdate = true;

}

updateBefore( { renderer } ) {

if ( this.textureNeedsUpdate === false && this.autoUpdate === false ) return;

this.textureNeedsUpdate = false;

//

if ( this.autoSize === true ) {

const size = renderer.getSize( _size );

this.setSize( size.width, size.height );

}

//

_quadMesh.material.fragmentNode = this.node;

//

const currentRenderTarget = renderer.getRenderTarget();

renderer.setRenderTarget( this.renderTarget );

_quadMesh.render( renderer );

renderer.setRenderTarget( currentRenderTarget );

}

clone() {

const newNode = new TextureNode( this.value, this.uvNode, this.levelNode );
newNode.sampler = this.sampler;
newNode.referenceNode = this;

return newNode;

}

}

export default RTTNode;

export const rtt = ( node, ...params ) => nodeObject( new RTTNode( nodeObject( node ), ...params ) );

addNodeElement( 'toTexture', ( node, ...params ) => node.isTextureNode ? node : rtt( node, ...params ) );

addNodeClass( 'RTTNode', RTTNode );

0 comments on commit 08d5412

Please sign in to comment.