Skip to content

Commit 87130e4

Browse files
committed
convert code to use tsl Fn instead of wgslFn
1 parent 84b47fa commit 87130e4

File tree

1 file changed

+67
-48
lines changed

1 file changed

+67
-48
lines changed

examples/jsm/tsl/display/MeshBlendNode.js

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { TempNode, MeshBasicNodeMaterial, RenderTarget, QuadMesh, Vector2 } from 'three/webgpu';
2-
import { nodeObject, vec4, vec3, float, modelPosition, modelWorldMatrix, Fn, wgslFn, NodeUpdateType, texture, screenUV } from 'three/tsl';
2+
import { nodeObject, vec4, vec3, float, modelPosition, modelWorldMatrix, Fn, NodeUpdateType, texture, screenUV, fract, vec2, dot, abs, sqrt, mix, saturate, If, Loop, int } from 'three/tsl';
33

44
//Source: https://www.jacktollenaar.top/mesh-seam-smoothing-blending#h.50wag6hqg9gh
55

@@ -19,22 +19,22 @@ class MeshBlendNode extends TempNode {
1919
this.blendFactor = float( 1.2 );
2020
this.kernelSize = float( 5 );
2121
this.kernelRadius = float( 0.01 * this.blendFactor.value );
22-
this.depthFalloff = float( 0.0001 * this.blendFactor.value );
22+
this.depthFalloff = float( 0.001 * this.blendFactor.value );
2323
this.debugMaterial = new MeshBasicNodeMaterial();
2424
this._quadMesh = new QuadMesh( this.debugMaterial );
2525

2626
}
2727

2828
setup() {
2929

30-
const CustomHash = wgslFn( `
31-
fn Hash(p: vec3f) -> f32 {
30+
const CustomHash = Fn( ( [ p ] ) => {
31+
32+
var lp = fract( p.mul( 0.3183099 ).add( 0.1 ) );
33+
lp = lp.mul( 17.0 );
34+
return fract( lp.x.mul( lp.y ).mul( lp.z ).mul( lp.x.add( lp.y ).add( lp.z ) ) );
35+
36+
} );
3237

33-
var lp = (fract(p * 0.3183099 + 0.1));
34-
lp *= 17.0;
35-
return fract(lp.x * lp.y * lp.z * (lp.x + lp.y + lp.z));
36-
}
37-
` );
3838
this.hashShader = Fn( () => {
3939

4040
const p = vec3( modelWorldMatrix.mul( vec3( modelPosition ) ) ).toVar();
@@ -47,45 +47,64 @@ class MeshBlendNode extends TempNode {
4747
const uv = screenUV;
4848
const FinalOutputNode = Fn( ()=>{
4949

50-
const outputPassFunc1 = wgslFn( `
51-
fn OutputPassFunc1(sceneDepth: vec4<f32>, tex: texture_2d<f32>, sampler: sampler, uv: vec2f, kernelSize: f32, kernelRadius: f32, depthFalloff: f32) -> vec4<f32> {
52-
var seamLocation = vec2<f32>(0., 0.);
53-
var minDist = f32(9999999.);
54-
55-
let objectIDColor = textureSample(tex, sampler, uv);
56-
57-
for(var x: f32 = -kernelSize; x <= kernelSize; x += 1.) {
58-
for(var y: f32 = -kernelSize; y <= kernelSize; y += 1.) {
59-
let offset = vec2<f32>(x, y) * kernelRadius * sceneDepth.r / kernelSize;
60-
let SampleUV = uv + offset;
61-
let sampledObjectIDColor = textureSample(tex, sampler, SampleUV);
62-
if(sampledObjectIDColor.x != objectIDColor.x) {
63-
let dist = dot(offset, offset);
64-
if(dist < minDist) {
65-
minDist = dist;
66-
seamLocation = offset;
67-
}
68-
}
69-
}
70-
}
71-
72-
return vec4<f32>(seamLocation.x, seamLocation.y, minDist, 1.);
73-
}
74-
` );
75-
76-
const finalPass = wgslFn( `
77-
fn FinalPass(sceneColor: vec4f, mirroredColor: vec4f, seamLocation: vec2f, kernelRadius: f32, sceneDepth: vec4f, otherDepth: vec4f, depthFalloff: f32, minDist: f32) -> vec4f {
78-
79-
let depthDiff = abs(otherDepth.r - sceneDepth.r);
80-
81-
let maxSearchDistance = kernelRadius / sceneDepth.r;
82-
let weight = saturate(0.5 - sqrt(minDist) / maxSearchDistance);
83-
let depthWeight = saturate(1. -depthDiff / depthFalloff * kernelRadius);
84-
var finalWeight = weight * depthWeight;
85-
86-
return mix(sceneColor, mirroredColor, finalWeight);
87-
}
88-
` );
50+
// sampling helpers (capture outside Fn so they can be used with varying UV offsets)
51+
const sampleSceneDepth = ( v ) => texture( this.sceneDepthNode, v );
52+
const sampleRT = ( v ) => texture( this.renderTarget.textures[ 0 ], v );
53+
const sampleSceneOutput = ( v ) => texture( this.sceneOutputNode, v );
54+
55+
const outputPassFunc1 = Fn( ( [ sceneDepthNode, rtNode, sceneOutNode, uvNode, kernelSizeNode, kernelRadiusNode, depthFalloffNode ] ) => {
56+
57+
const sceneDepthVar = sceneDepthNode.toVar();
58+
59+
// kernelSizeNode is expected to be a numeric node with a .value available at build time
60+
const kSize = kernelSizeNode.value || 0;
61+
62+
const seamLocation = vec2( 0., 0. ).toVar();
63+
var minDist = float( 9999999. ).toVar();
64+
65+
const objectIDColor = sampleRT( uvNode ).toVar();
66+
67+
// Use TSL Loop so the iteration becomes shader-side loops
68+
const k = int( kSize );
69+
Loop( { start: k.negate(), end: k, type: 'int', condition: '<=', name: 'x' }, ( { x } ) => {
70+
71+
Loop( { start: k.negate(), end: k, type: 'int', condition: '<=', name: 'y' }, ( { y } ) => {
72+
73+
const offset = vec2( x.toFloat(), y.toFloat() ).mul( kernelRadiusNode.mul( sceneDepthVar.r.mul( 0.3 ) ).div( float( kSize ) ) ).toVar();
74+
const SampleUV = uvNode.add( offset ).toVar();
75+
const sampledObjectIDColor = sampleRT( SampleUV ).toVar();
76+
If( sampledObjectIDColor.x.notEqual( objectIDColor.x ), () => {
77+
78+
const dist = dot( offset, offset );
79+
If( dist.lessThan( minDist ), () => {
80+
81+
minDist.assign( dist );
82+
seamLocation.assign( offset );
83+
84+
} );
85+
86+
} );
87+
88+
} );
89+
90+
} );
91+
92+
return vec4( seamLocation.x, seamLocation.y, minDist, 1. );
93+
94+
} );
95+
96+
const finalPass = Fn( ( [ sceneColor, mirroredColor, seamLocation, kernelRadiusNode, sceneDepth, otherDepth, depthFalloffNode, minDist ] ) => {
97+
98+
const depthDiff = abs( otherDepth.r.sub( sceneDepth.r ) );
99+
100+
const maxSearchDistance = kernelRadiusNode.div( sceneDepth.r );
101+
const weight = saturate( float( 0.5 ).sub( sqrt( minDist ).div( maxSearchDistance ) ) );
102+
const depthWeight = saturate( float( 1. ).sub( depthDiff.div( depthFalloffNode.mul( kernelRadiusNode ) ) ) );
103+
const finalWeight = weight.mul( depthWeight );
104+
105+
return mix( sceneColor, mirroredColor, finalWeight );
106+
107+
} );
89108

90109
const pass1 = outputPassFunc1(
91110
texture( this.sceneDepthNode, uv ),

0 commit comments

Comments
 (0)