Skip to content

Commit

Permalink
WebGPURenderer: Add textureGrad support (#28048)
Browse files Browse the repository at this point in the history
* support texture grad

* cleanup

* add texture vs textureGrad example
  • Loading branch information
RenaudRohlinger committed Apr 2, 2024
1 parent 7a08fc4 commit 75b06a1
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 4 deletions.
3 changes: 2 additions & 1 deletion examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,8 @@
"webgpu_materials_texture_anisotropy",
"webgpu_storage_buffer",
"webgpu_mesh_batch",
"webgpu_instancing_morph"
"webgpu_instancing_morph",
"webgpu_texturegrad"
],
"webaudio": [
"webaudio_orientation",
Expand Down
24 changes: 21 additions & 3 deletions examples/jsm/nodes/accessors/TextureNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class TextureNode extends UniformNode {
this.levelNode = levelNode;
this.compareNode = null;
this.depthNode = null;
this.gradNode = null;

this.sampler = true;
this.updateMatrix = false;
Expand Down Expand Up @@ -155,6 +156,7 @@ class TextureNode extends UniformNode {
properties.uvNode = uvNode;
properties.levelNode = levelNode;
properties.compareNode = this.compareNode;
properties.gradNode = this.gradNode;
properties.depthNode = this.depthNode;

}
Expand All @@ -165,7 +167,7 @@ class TextureNode extends UniformNode {

}

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

const texture = this.value;

Expand All @@ -175,6 +177,10 @@ class TextureNode extends UniformNode {

snippet = builder.generateTextureLevel( texture, textureProperty, uvSnippet, levelSnippet, depthSnippet );

} else if ( gradSnippet ) {

snippet = builder.generateTextureGrad( texture, textureProperty, uvSnippet, gradSnippet, depthSnippet );

} else if ( compareSnippet ) {

snippet = builder.generateTextureCompare( texture, textureProperty, uvSnippet, compareSnippet, depthSnippet );
Expand Down Expand Up @@ -223,18 +229,19 @@ class TextureNode extends UniformNode {

if ( propertyName === undefined ) {

const { uvNode, levelNode, compareNode, depthNode } = properties;
const { uvNode, levelNode, compareNode, depthNode, gradNode } = properties;

const uvSnippet = this.generateUV( builder, uvNode );
const levelSnippet = levelNode ? levelNode.build( builder, 'float' ) : null;
const depthSnippet = depthNode ? depthNode.build( builder, 'int' ) : null;
const compareSnippet = compareNode ? compareNode.build( builder, 'float' ) : null;
const gradSnippet = gradNode ? [ gradNode[ 0 ].build( builder, 'vec2' ), gradNode[ 1 ].build( builder, 'vec2' ) ] : null;

const nodeVar = builder.getVarFromNode( this );

propertyName = builder.getPropertyName( nodeVar );

const snippet = this.generateSnippet( builder, textureProperty, uvSnippet, levelSnippet, depthSnippet, compareSnippet );
const snippet = this.generateSnippet( builder, textureProperty, uvSnippet, levelSnippet, depthSnippet, compareSnippet, gradSnippet );

builder.addLineFlowCode( `${propertyName} = ${snippet}` );

Expand Down Expand Up @@ -324,6 +331,17 @@ class TextureNode extends UniformNode {

}

grad( gradNodeX, gradNodeY ) {

const textureNode = this.clone();
textureNode.gradNode = [ nodeObject( gradNodeX ), nodeObject( gradNodeY ) ];

textureNode.referenceNode = this;

return nodeObject( textureNode );

}

depth( depthNode ) {

const textureNode = this.clone();
Expand Down
6 changes: 6 additions & 0 deletions examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,12 @@ ${ flowData.code }

}

generateTextureGrad( texture, textureProperty, uvSnippet, gradSnippet ) {

return `textureGrad( ${ textureProperty }, ${ uvSnippet }, ${ gradSnippet[ 0 ] }, ${ gradSnippet[ 1 ] } )`;

}

generateTextureCompare( texture, textureProperty, uvSnippet, compareSnippet, depthSnippet, shaderStage = this.shaderStage ) {

if ( shaderStage === 'fragment' ) {
Expand Down
15 changes: 15 additions & 0 deletions examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,21 @@ class WGSLNodeBuilder extends NodeBuilder {

}

generateTextureGrad( texture, textureProperty, uvSnippet, gradSnippet, depthSnippet, shaderStage = this.shaderStage ) {

if ( shaderStage === 'fragment' ) {

// TODO handle i32 or u32 --> uvSnippet, array_index: A, ddx, ddy
return `textureSampleGrad( ${ textureProperty }, ${ textureProperty }_sampler, ${ uvSnippet }, ${ gradSnippet[ 0 ] }, ${ gradSnippet[ 1 ] } )`;

} else {

console.error( `WebGPURenderer: THREE.TextureNode.gradient() does not support ${ shaderStage } shader.` );

}

}

generateTextureCompare( texture, textureProperty, uvSnippet, compareSnippet, depthSnippet, shaderStage = this.shaderStage ) {

if ( shaderStage === 'fragment' ) {
Expand Down
Binary file added examples/screenshots/webgpu_texturegrad.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 154 additions & 0 deletions examples/webgpu_texturegrad.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<html lang="en">
<head>
<title>three.js - WebGPU - Texture Gradient</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>
<br />This example demonstrate texture gradient
<br /> Left canvas is using WebGPU Backend, right canvas is WebGL Backend.
<br /> The bottom half of the texture benefits from the gradient to achieve better blur quality.
</div>

<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 { If, vec4, float, timerLocal, cos, pow, vec2, uv, texture, tslFn, MeshBasicNodeMaterial } from 'three/nodes';

import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';


// WebGPU Backend
init();

// WebGL Backend
init( true );

async function init( forceWebGL = false ) {

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

const scene = new THREE.Scene();

// texture

const material = new MeshBasicNodeMaterial( { color: 0xffffff } );

// load async brick_diffuse
const map = await new THREE.TextureLoader().loadAsync( 'textures/uv_grid_opengl.jpg' );

const elapsedTime = timerLocal();
material.colorNode = tslFn( () => {

const color = vec4( 1. ).toVar();

const vuv = uv().toVar();
const blur = pow( float( 0.0625 ).sub( cos( vuv.x.mul( 20.0 ).add( elapsedTime ) ) ).mul( 0.0625 ), 2.0 );

const grad = vec2( blur ).toVar();

If( vuv.y.greaterThan( 0.5 ), () => {

grad.assign( 0 );

} );

color.assign(
texture( map, vuv.add( vec2( blur, blur ).mul( 0.5 ) ) ).grad( grad, grad ).mul( 0.25 )
.add( texture( map, vuv.add( vec2( blur, blur.negate() ).mul( 0.5 ) ) ).grad( grad, grad ).mul( 0.25 ) )
.add( texture( map, vuv.add( vec2( blur.negate(), blur ).mul( 0.5 ) ) ).grad( grad, grad ).mul( 0.25 ) )
.add( texture( map, vuv.add( vec2( blur.negate(), blur.negate() ).mul( 0.5 ) ) ).grad( grad, grad ).mul( 0.25 ) )
);


If( vuv.y.greaterThan( 0.497 ).and( vuv.y.lessThan( 0.503 ) ), () => {

color.assign( 1 );

} );


return color;

} )();

//

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

const renderer = new WebGPURenderer( { antialias: false, forceWebGL: forceWebGL, trackTimestamp: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth / 2, window.innerHeight );

document.body.appendChild( renderer.domElement );
renderer.domElement.style.position = 'absolute';
renderer.domElement.style.top = '0';
renderer.domElement.style.left = '0';
renderer.domElement.style.width = '50%';
renderer.domElement.style.height = '100%';

if ( forceWebGL ) {

renderer.domElement.style.left = '50%';

scene.background = new THREE.Color( 0x212121 );

} else {

scene.background = new THREE.Color( 0x313131 );

}

//


const animate = async function () {

await renderer.renderAsync( scene, camera );


};

renderer.setAnimationLoop( animate );

window.addEventListener( 'resize', onWindowResize );

function onWindowResize() {

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

const aspect = ( window.innerWidth / 2 ) / window.innerHeight;

const frustumHeight = camera.top - camera.bottom;

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

camera.updateProjectionMatrix();

renderer.render( scene, camera );

}

}

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

0 comments on commit 75b06a1

Please sign in to comment.