Skip to content

Commit

Permalink
Examples: Add SobelOperatorNode.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mugen87 committed Jun 24, 2024
1 parent 7a99ad6 commit 215e499
Show file tree
Hide file tree
Showing 4 changed files with 249 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@
"webgpu_portal",
"webgpu_postprocessing_afterimage",
"webgpu_postprocessing_anamorphic",
"webgpu_postprocessing_sobel",
"webgpu_reflection",
"webgpu_rtt",
"webgpu_sandbox",
Expand Down
1 change: 1 addition & 0 deletions examples/jsm/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export { default as ViewportDepthNode, viewZToOrthographicDepth, orthographicDep
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 SobelOperatorNode, sobel } from './display/SobelOperatorNode.js';

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

Expand Down
118 changes: 118 additions & 0 deletions examples/jsm/nodes/display/SobelOperatorNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import TempNode from '../core/TempNode.js';
import { uv } from '../accessors/UVNode.js';
import { luminance } from './ColorAdjustmentNode.js';
import { addNodeElement, tslFn, nodeObject, vec2, vec3, vec4, mat3 } from '../shadernode/ShaderNode.js';
import { NodeUpdateType } from '../core/constants.js';
import { uniform } from '../core/UniformNode.js';
import { add } from '../math/OperatorNode.js';
import { Vector2 } from 'three';

class SobelOperatorNode extends TempNode {

constructor( textureNode ) {

super();

this.textureNode = textureNode;

this.updateBeforeType = NodeUpdateType.RENDER;

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

}

updateBefore() {

const map = this.textureNode.value;

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

}

setup() {

const { textureNode } = this;

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

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

const sobel = tslFn( () => {

const texel = this._invSize;

// kernel definition (in glsl matrices are filled in column-major order)

const Gx = mat3( - 1, - 2, - 1, 0, 0, 0, 1, 2, 1 ); // x direction kernel
const Gy = mat3( - 1, 0, 1, - 2, 0, 2, - 1, 0, 1 ); // y direction kernel

// fetch the 3x3 neighbourhood of a fragment

// first column

const tx0y0 = luminance( sampleTexture( uvNode.add( texel.mul( vec2( - 1, - 1 ) ) ) ).xyz );
const tx0y1 = luminance( sampleTexture( uvNode.add( texel.mul( vec2( - 1, 0 ) ) ) ).xyz );
const tx0y2 = luminance( sampleTexture( uvNode.add( texel.mul( vec2( - 1, 1 ) ) ) ).xyz );

// second column

const tx1y0 = luminance( sampleTexture( uvNode.add( texel.mul( vec2( 0, - 1 ) ) ) ).xyz );
const tx1y1 = luminance( sampleTexture( uvNode.add( texel.mul( vec2( 0, 0 ) ) ) ).xyz );
const tx1y2 = luminance( sampleTexture( uvNode.add( texel.mul( vec2( 0, 1 ) ) ) ).xyz );

// third column

const tx2y0 = luminance( sampleTexture( uvNode.add( texel.mul( vec2( 1, - 1 ) ) ) ).xyz );
const tx2y1 = luminance( sampleTexture( uvNode.add( texel.mul( vec2( 1, 0 ) ) ) ).xyz );
const tx2y2 = luminance( sampleTexture( uvNode.add( texel.mul( vec2( 1, 1 ) ) ) ).xyz );

// gradient value in x direction

const valueGx = add(
Gx[ 0 ][ 0 ].mul( tx0y0 ),
Gx[ 1 ][ 0 ].mul( tx1y0 ),
Gx[ 2 ][ 0 ].mul( tx2y0 ),
Gx[ 0 ][ 1 ].mul( tx0y1 ),
Gx[ 1 ][ 1 ].mul( tx1y1 ),
Gx[ 2 ][ 1 ].mul( tx2y1 ),
Gx[ 0 ][ 2 ].mul( tx0y2 ),
Gx[ 1 ][ 2 ].mul( tx1y2 ),
Gx[ 2 ][ 2 ].mul( tx2y2 )
);


// gradient value in y direction

const valueGy = add(
Gy[ 0 ][ 0 ].mul( tx0y0 ),
Gy[ 1 ][ 0 ].mul( tx1y0 ),
Gy[ 2 ][ 0 ].mul( tx2y0 ),
Gy[ 0 ][ 1 ].mul( tx0y1 ),
Gy[ 1 ][ 1 ].mul( tx1y1 ),
Gy[ 2 ][ 1 ].mul( tx2y1 ),
Gy[ 0 ][ 2 ].mul( tx0y2 ),
Gy[ 1 ][ 2 ].mul( tx1y2 ),
Gy[ 2 ][ 2 ].mul( tx2y2 )
);

// magnitute of the total gradient

const G = valueGx.mul( valueGx ).add( valueGy.mul( valueGy ) ).sqrt();

return vec4( vec3( G ), 1 );

} );

const outputNode = sobel();

return outputNode;

}

}

export const sobel = ( node ) => nodeObject( new SobelOperatorNode( nodeObject( node ) ) );

addNodeElement( 'sobel', sobel );

export default SobelOperatorNode;
129 changes: 129 additions & 0 deletions examples/webgpu_postprocessing_sobel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgpu - postprocessing - sobel</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>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/",
"three/nodes": "./jsm/nodes/Nodes.js"
}
}
</script>

<script type="module">

import * as THREE from 'three';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
import PostProcessing from 'three/addons/renderers/common/PostProcessing.js';
import { pass } from 'three/nodes';

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

let camera, scene, renderer;
let postProcessing;

const params = {
enable: true
};

init();

function init() {

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.set( 0, 1, 3 );
camera.lookAt( scene.position );

//

const ambientLight = new THREE.AmbientLight( 0xe7e7e7 );
scene.add( ambientLight );

const pointLight = new THREE.PointLight( 0xffffff, 20 );
camera.add( pointLight );
scene.add( camera );

//

const geometry = new THREE.TorusKnotGeometry( 1, 0.3, 256, 32 );
const material = new THREE.MeshPhongMaterial( { color: 0xffff00 } );

const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

//

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

//

const controls = new OrbitControls( camera, renderer.domElement );
controls.enableZoom = false;


// postprocessing

postProcessing = new PostProcessing( renderer );

const scenePass = pass( scene, camera );
const scenePassColor = scenePass.getTextureNode();

postProcessing.outputNode = scenePassColor.sobel();

//

const gui = new GUI();

gui.add( params, 'enable' );
gui.open();

//

window.addEventListener( 'resize', onWindowResize );

}


function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

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

}

function animate() {

if ( params.enable === true ) {

postProcessing.render();

} else {

renderer.render( scene, camera );

}

}

</script>
</body>
</html>

0 comments on commit 215e499

Please sign in to comment.