From b77ac19611b267dbdfdb5627ce530d5feace4baa Mon Sep 17 00:00:00 2001 From: Christian Helgeson <62450112+cmhhelgeson@users.noreply.github.com> Date: Fri, 22 Aug 2025 12:44:44 -0700 Subject: [PATCH 1/4] test --- examples/jsm/materials/ProceduralWood.js | 439 +++++++++++++++++++++++ examples/webgpu_tsl_wood.html | 192 ++++++++++ 2 files changed, 631 insertions(+) create mode 100644 examples/jsm/materials/ProceduralWood.js create mode 100644 examples/webgpu_tsl_wood.html diff --git a/examples/jsm/materials/ProceduralWood.js b/examples/jsm/materials/ProceduralWood.js new file mode 100644 index 00000000000000..3245e50b692d40 --- /dev/null +++ b/examples/jsm/materials/ProceduralWood.js @@ -0,0 +1,439 @@ +import * as THREE from 'three'; +import * as TSL from 'three/tsl'; + +// some helpers below are ported from Blender +const mapRange = TSL.wgslFn( ` + fn map_range(x: f32, fromMin: f32, fromMax: f32, toMin: f32, toMax: f32, clmp: bool) -> f32 + { + let factor = (x - fromMin) / (fromMax - fromMin); + var result = toMin + factor * (toMax - toMin); + + if (clmp && toMin < toMax) + { + result = clamp(result, toMin, toMax); + } + else if (clmp && toMin > toMax) + { + result = clamp(result, toMax, toMin); + } + + return result; + } +` ); + +const voronoi3d = TSL.wgslFn( ` + fn voronoi3d(x: vec3, smoothness: f32, randomness: f32) -> f32 + { + let p = floor(x); + let f = fract(x); + + var res = 0.0; + var totalWeight = 0.0; + + for (var k = -1; k <= 1; k++) + { + for (var j = -1; j <= 1; j++) + { + for (var i = -1; i <= 1; i++) + { + let b = vec3(f32(i), f32(j), f32(k)); + let hashOffset = hash3d(p + b) * randomness; + let r = b - f + hashOffset; + let d = length(r); + + let weight = exp(-d * d / max(smoothness * smoothness, 0.001)); + res += d * weight; + totalWeight += weight; + } + } + } + + if (totalWeight > 0.0) + { + res /= totalWeight; + } + + return smoothstep(0.0, 1.0, res); + } + + fn hash3d(p: vec3) -> vec3 + { + var p3 = fract(p * vec3(0.1031, 0.1030, 0.0973)); + p3 += dot(p3, p3.yzx + 33.33); + return fract((p3.xxy + p3.yzz) * p3.zyx); + } +` ); + +const softLightMix = TSL.wgslFn( ` + fn node_mix_soft(t: f32, col1: vec3, col2: vec3) -> vec3 + { + let tm = 1.0 - t; + + let one = vec3(1.0); + let scr = one - (one - col2) * (one - col1); + + return tm * col1 + t * ((one - col1) * col2 * col1 + col1 * scr); + } +` ); + +const noiseFbm = TSL.Fn( ( [ p, detail, roughness, lacunarity, useNormalize ] ) => { + + const fscale = TSL.float( 1.0 ).toVar(); + const amp = TSL.float( 1.0 ).toVar(); + const maxamp = TSL.float( 0.0 ).toVar(); + const sum = TSL.float( 0.0 ).toVar(); + + const iterations = detail.floor(); + + TSL.Loop( iterations, () => { + + const t = TSL.mx_noise_float( p.mul( fscale ) ); + sum.addAssign( t.mul( amp ) ); + maxamp.addAssign( amp ); + amp.mulAssign( roughness ); + fscale.mulAssign( lacunarity ); + + } ); + + const rmd = detail.sub( iterations ); + const hasRemainder = rmd.greaterThan( 0.001 ); + + return TSL.select( + hasRemainder, + TSL.select( + useNormalize.equal( 1 ), + ( () => { + + const t = TSL.mx_noise_float( p.mul( fscale ) ); + const sum2 = sum.add( t.mul( amp ) ); + const maxamp2 = maxamp.add( amp ); + const normalizedSum = sum.div( maxamp ).mul( 0.5 ).add( 0.5 ); + const normalizedSum2 = sum2.div( maxamp2 ).mul( 0.5 ).add( 0.5 ); + return TSL.mix( normalizedSum, normalizedSum2, rmd ); + + } )(), + ( () => { + + const t = TSL.mx_noise_float( p.mul( fscale ) ); + const sum2 = sum.add( t.mul( amp ) ); + return TSL.mix( sum, sum2, rmd ); + + } )() + ), + TSL.select( + useNormalize.equal( 1 ), + sum.div( maxamp ).mul( 0.5 ).add( 0.5 ), + sum + ) + ); + +} ); + +const noiseFbm3d = TSL.Fn( ( [ p, detail, roughness, lacunarity, useNormalize ] ) => { + + const fscale = TSL.float( 1.0 ).toVar(); + const amp = TSL.float( 1.0 ).toVar(); + const maxamp = TSL.float( 0.0 ).toVar(); + const sum = TSL.vec3( 0.0 ).toVar(); + + const iterations = detail.floor(); + + TSL.Loop( iterations, () => { + + const t = TSL.mx_noise_vec3( p.mul( fscale ) ); + sum.addAssign( t.mul( amp ) ); + maxamp.addAssign( amp ); + amp.mulAssign( roughness ); + fscale.mulAssign( lacunarity ); + + } ); + + const rmd = detail.sub( iterations ); + const hasRemainder = rmd.greaterThan( 0.001 ); + + return TSL.select( + hasRemainder, + TSL.select( + useNormalize.equal( 1 ), + ( () => { + + const t = TSL.mx_noise_vec3( p.mul( fscale ) ); + const sum2 = sum.add( t.mul( amp ) ); + const maxamp2 = maxamp.add( amp ); + const normalizedSum = sum.div( maxamp ).mul( 0.5 ).add( 0.5 ); + const normalizedSum2 = sum2.div( maxamp2 ).mul( 0.5 ).add( 0.5 ); + return TSL.mix( normalizedSum, normalizedSum2, rmd ); + + } )(), + ( () => { + + const t = TSL.mx_noise_vec3( p.mul( fscale ) ); + const sum2 = sum.add( t.mul( amp ) ); + return TSL.mix( sum, sum2, rmd ); + + } )() + ), + TSL.select( + useNormalize.equal( 1 ), + sum.div( maxamp ).mul( 0.5 ).add( 0.5 ), + sum + ) + ); + +} ); + +const woodCenter = TSL.Fn( ( [ p, centerSize ] ) => { + + const pxyCenter = p.mul( TSL.vec3( 1, 1, 0 ) ).length(); + const center = mapRange( pxyCenter, 0, 1, 0, centerSize, true ); + + return center; + +} ); + +const spaceWarp = TSL.Fn( ( [ p, warpStrength, xyScale, zScale ] ) => { + + const combinedXyz = TSL.vec3( xyScale, xyScale, zScale ).mul( p ); + const noise = noiseFbm3d( combinedXyz.mul( 1.6 * 1.5 ), TSL.float( 1 ), TSL.float( 0.5 ), TSL.float( 2 ), TSL.int( 1 ) ).sub( 0.5 ).mul( warpStrength ); + const pXy = p.mul( TSL.vec3( 1, 1, 0 ) ); + const normalizedXy = pXy.normalize(); + const warp = noise.mul( normalizedXy ).add( pXy ); + + return warp; + +} ); + +const woodRings = TSL.Fn( ( [ w, ringCount, ringBias, ringSizeVariance, ringVarianceScale, barkThickness ] ) => { + + const rings = noiseFbm( w.mul( ringVarianceScale ), TSL.float( 1 ), TSL.float( 0.5 ), TSL.float( 2 ), TSL.int( 1 ) ).mul( ringSizeVariance ).add( w ).mul( ringCount ).fract().mul( barkThickness ); + + return TSL.min( mapRange( rings, 0, ringBias, 0, 1, TSL.bool( true ) ), mapRange( rings, ringBias, 1, 1, 0, TSL.bool( true ) ) ); + +} ); + +const woodDetail = TSL.Fn( ( [ warp, p, y, splotchScale ] ) => { + + const radialCoords = TSL.clamp( TSL.atan( warp.y, warp.x ).div( TSL.PI2 ).add( 0.5 ), 0, 1 ).mul( TSL.PI2.mul( 3 ) ); + const combinedXyz = TSL.vec3( radialCoords.sin(), y, radialCoords.cos().mul( p.z ) ); + const scaled = TSL.vec3( 0.1, 1.19, 0.05 ).mul( combinedXyz ); + + return noiseFbm( scaled.mul( splotchScale ), TSL.float( 1 ), TSL.float( 0.5 ), TSL.float( 2 ), TSL.bool( true ) ); + +} ); + +const cellStructure = TSL.Fn( ( [ p, cellScale, cellSize ] ) => { + + const warp = spaceWarp( p.mul( cellScale.div( 50 ) ), cellScale.div( 1000 ), 0.1, 1.77 ); + const cells = voronoi3d( warp.xy.mul( 75 ), 0.5, 1 ); + + return mapRange( cells, cellSize, cellSize.add( 0.21 ), 0, 1, TSL.bool( true ) ); + +} ); + +const wood = TSL.Fn( ( [ + p, + centerSize, + largeWarpScale, + largeGrainStretch, + smallWarpStrength, + smallWarpScale, + fineWarpStrength, + fineWarpScale, + ringCount, + ringBias, + ringSizeVariance, + ringVarianceScale, + barkThickness, + splotchScale, + splotchIntensity, + cellScale, + cellSize, + darkGrainColor, + lightGrainColor +] ) => { + + const center = woodCenter( p, centerSize ); + const mainWarp = spaceWarp( spaceWarp( p, center, largeWarpScale, largeGrainStretch ), smallWarpStrength, smallWarpScale, 0.17 ); + const detailWarp = spaceWarp( mainWarp, fineWarpStrength, fineWarpScale, 0.17 ); + const rings = woodRings( detailWarp.length(), ringCount, ringBias, ringSizeVariance, ringVarianceScale, barkThickness ); + const detail = woodDetail( detailWarp, p, detailWarp.length(), splotchScale ); + const cells = cellStructure( mainWarp, cellScale, cellSize ); + const baseColor = TSL.mix( darkGrainColor, lightGrainColor, rings ); + + return softLightMix( splotchIntensity, softLightMix( 0.407, baseColor, cells ), detail ); + +} ); + +const woodParams = { + teak: { + originOffset: { x: - 0.4, y: 0, z: 0 }, + centerSize: 1.11, largeWarpScale: 0.32, largeGrainStretch: 0.24, smallWarpStrength: 0.059, + smallWarpScale: 2, fineWarpStrength: 0.006, fineWarpScale: 32.8, ringCount: 34, + ringBias: 0.59, ringSizeVariance: 0.16, ringVarianceScale: 1.4, barkThickness: 0.61, + splotchScale: 0.2, splotchIntensity: 0.541, cellScale: 910, cellSize: 0.1, + darkGrainColor: '#0c0504', lightGrainColor: '#533319' + }, + walnut: { + originOffset: { x: - 0.4, y: 0, z: 0 }, + centerSize: 1.07, largeWarpScale: 0.42, largeGrainStretch: 0.34, smallWarpStrength: 0.016, + smallWarpScale: 10.3, fineWarpStrength: 0.028, fineWarpScale: 12.7, ringCount: 32, + ringBias: 0.08, ringSizeVariance: 0.03, ringVarianceScale: 5.5, barkThickness: 0.98, + splotchScale: 1.84, splotchIntensity: 0.97, cellScale: 710, cellSize: 0.31, + darkGrainColor: '#311e13', lightGrainColor: '#523424' + }, + white_oak: { + originOffset: { x: - 0.4, y: 0, z: 0 }, + centerSize: 1.23, largeWarpScale: 0.21, largeGrainStretch: 0.21, smallWarpStrength: 0.034, + smallWarpScale: 2.44, fineWarpStrength: 0.01, fineWarpScale: 14.3, ringCount: 34, + ringBias: 0.82, ringSizeVariance: 0.16, ringVarianceScale: 1.4, barkThickness: 0.7, + splotchScale: 0.2, splotchIntensity: 0.541, cellScale: 800, cellSize: 0.28, + darkGrainColor: '#8b4c21', lightGrainColor: '#c57e43' + }, + pine: { + originOffset: { x: - 0.4, y: 0, z: - 0.2 }, + centerSize: 1.23, largeWarpScale: 0.21, largeGrainStretch: 0.18, smallWarpStrength: 0.041, + smallWarpScale: 2.44, fineWarpStrength: 0.006, fineWarpScale: 23.2, ringCount: 24, + ringBias: 0.1, ringSizeVariance: 0.07, ringVarianceScale: 5, barkThickness: 0.35, + splotchScale: 0.51, splotchIntensity: 3.32, cellScale: 1480, cellSize: 0.07, + darkGrainColor: '#c58355', lightGrainColor: '#d19d61' + }, + poplar: { + originOffset: { x: - 0.4, y: 0, z: 0.2 }, + centerSize: 1.43, largeWarpScale: 0.33, largeGrainStretch: 0.18, smallWarpStrength: 0.04, + smallWarpScale: 4.3, fineWarpStrength: 0.004, fineWarpScale: 33.6, ringCount: 37, + ringBias: 0.07, ringSizeVariance: 0.03, ringVarianceScale: 3.8, barkThickness: 0.3, + splotchScale: 1.92, splotchIntensity: 0.71, cellScale: 830, cellSize: 0.04, + darkGrainColor: '#716347', lightGrainColor: '#998966' + }, + maple: { + originOffset: { x: - 0.4, y: 0.3, z: - 0.2 }, + centerSize: 1.4, largeWarpScale: 0.38, largeGrainStretch: 0.25, smallWarpStrength: 0.067, + smallWarpScale: 2.5, fineWarpStrength: 0.005, fineWarpScale: 33.6, ringCount: 35, + ringBias: 0.1, ringSizeVariance: 0.07, ringVarianceScale: 4.6, barkThickness: 0.61, + splotchScale: 0.46, splotchIntensity: 1.49, cellScale: 800, cellSize: 0.03, + darkGrainColor: '#b08969', lightGrainColor: '#bc9d7d' + }, + red_oak: { + originOffset: { x: - 0.4, y: 0, z: 0.4 }, + centerSize: 1.21, largeWarpScale: 0.24, largeGrainStretch: 0.25, smallWarpStrength: 0.044, + smallWarpScale: 2.54, fineWarpStrength: 0.01, fineWarpScale: 14.5, ringCount: 34, + ringBias: 0.92, ringSizeVariance: 0.03, ringVarianceScale: 5.6, barkThickness: 1.01, + splotchScale: 0.28, splotchIntensity: 3.48, cellScale: 800, cellSize: 0.25, + darkGrainColor: '#af613b', lightGrainColor: '#e0a27a' + }, + cherry: { + originOffset: { x: - 0.4, y: 0.3, z: 0 }, + centerSize: 1.33, largeWarpScale: 0.11, largeGrainStretch: 0.33, smallWarpStrength: 0.024, + smallWarpScale: 2.48, fineWarpStrength: 0.01, fineWarpScale: 15.3, ringCount: 36, + ringBias: 0.02, ringSizeVariance: 0.04, ringVarianceScale: 6.5, barkThickness: 0.09, + splotchScale: 1.27, splotchIntensity: 1.24, cellScale: 1530, cellSize: 0.15, + darkGrainColor: '#913f27', lightGrainColor: '#b45837' + }, + cedar: { + originOffset: { x: - 0.4, y: 0.1, z: 0.1 }, + centerSize: 1.11, largeWarpScale: 0.39, largeGrainStretch: 0.12, smallWarpStrength: 0.061, + smallWarpScale: 1.9, fineWarpStrength: 0.006, fineWarpScale: 4.8, ringCount: 25, + ringBias: 0.01, ringSizeVariance: 0.07, ringVarianceScale: 6.7, barkThickness: 0.1, + splotchScale: 0.61, splotchIntensity: 2.54, cellScale: 630, cellSize: 0.19, + darkGrainColor: '#9a5b49', lightGrainColor: '#ae745e' + }, + mahogany: { + originOffset: { x: - 0.4, y: 0.2, z: 0 }, + centerSize: 1.25, largeWarpScale: 0.26, largeGrainStretch: 0.29, smallWarpStrength: 0.044, + smallWarpScale: 2.54, fineWarpStrength: 0.01, fineWarpScale: 15.3, ringCount: 38, + ringBias: 0.01, ringSizeVariance: 0.33, ringVarianceScale: 1.2, barkThickness: 0.07, + splotchScale: 0.77, splotchIntensity: 1.39, cellScale: 1400, cellSize: 0.23, + darkGrainColor: '#501d12', lightGrainColor: '#6d3722' + } +}; + +export const WoodGenuses = [ 'teak', 'walnut', 'white_oak', 'pine', 'poplar', 'maple', 'red_oak', 'cherry', 'cedar', 'mahogany' ]; +export const Finishes = [ 'raw', 'matte', 'semigloss', 'gloss' ]; + +export function GetWoodPreset( genus, finish ) { + + const params = woodParams[ genus ]; + + let clearcoat, clearcoatRoughness, clearcoatDarken; + + switch ( finish ) { + + case 'gloss': + clearcoatDarken = 0.2; clearcoatRoughness = 0.1; clearcoat = 1; + break; + + case 'semigloss': + clearcoatDarken = 0.4; clearcoatRoughness = 0.4; clearcoat = 1; + break; + + case 'matte': + clearcoatDarken = 0.6; clearcoatRoughness = 1; clearcoat = 1; + break; + + case 'raw': + default: + clearcoatDarken = 1; clearcoatRoughness = 0; clearcoat = 0; + + } + + return { ...params, genus, finish, clearcoat, clearcoatRoughness, clearcoatDarken }; + +} + +export function GenerateWoodMaterial( params ) { + + const material = new THREE.MeshPhysicalNodeMaterial(); + + const uniforms = {}; + + uniforms.centerSize = TSL.uniform( params.centerSize ); + uniforms.largeWarpScale = TSL.uniform( params.largeWarpScale ); + uniforms.largeGrainStretch = TSL.uniform( params.largeGrainStretch ); + uniforms.smallWarpStrength = TSL.uniform( params.smallWarpStrength ); + uniforms.smallWarpScale = TSL.uniform( params.smallWarpScale ); + uniforms.fineWarpStrength = TSL.uniform( params.fineWarpStrength ); + uniforms.fineWarpScale = TSL.uniform( params.fineWarpScale ); + uniforms.ringCount = TSL.uniform( params.ringCount ); + uniforms.ringBias = TSL.uniform( params.ringBias ); + uniforms.ringSizeVariance = TSL.uniform( params.ringSizeVariance ); + uniforms.ringVarianceScale = TSL.uniform( params.ringVarianceScale ); + uniforms.barkThickness = TSL.uniform( params.barkThickness ); + uniforms.splotchScale = TSL.uniform( params.splotchScale ); + uniforms.splotchIntensity = TSL.uniform( params.splotchIntensity ); + uniforms.cellScale = TSL.uniform( params.cellScale ); + uniforms.cellSize = TSL.uniform( params.cellSize ); + uniforms.darkGrainColor = TSL.uniform( new THREE.Color( params.darkGrainColor ) ); + uniforms.lightGrainColor = TSL.uniform( new THREE.Color( params.lightGrainColor ) ); + + // remember uniforms for real-time updates + material.uniforms = uniforms; + + material.colorNode = wood( + TSL.positionLocal.add( TSL.vec3( params.originOffset.x, params.originOffset.y, params.originOffset.z ) ), + uniforms.centerSize, + uniforms.largeWarpScale, + uniforms.largeGrainStretch, + uniforms.smallWarpStrength, + uniforms.smallWarpScale, + uniforms.fineWarpStrength, + uniforms.fineWarpScale, + uniforms.ringCount, + uniforms.ringBias, + uniforms.ringSizeVariance, + uniforms.ringVarianceScale, + uniforms.barkThickness, + uniforms.splotchScale, + uniforms.splotchIntensity, + uniforms.cellScale, + uniforms.cellSize, + uniforms.darkGrainColor, + uniforms.lightGrainColor + ).mul( params.clearcoatDarken ); + + //material.customProgramCacheKey = () => params.genus + params.finish; + material.clearcoatNode = params.clearcoat; + material.clearcoatRoughness = params.clearcoatRoughness; + + return material; + +} diff --git a/examples/webgpu_tsl_wood.html b/examples/webgpu_tsl_wood.html new file mode 100644 index 00000000000000..fd4d8b668c3d0f --- /dev/null +++ b/examples/webgpu_tsl_wood.html @@ -0,0 +1,192 @@ + + + + + Three.js WebGPU - Procedural Wood Materials + + + + + + + +
+ three.js webgpu - tsl procedural wood materials
+ by Logan Seeley, based on Lance Phan's Blender tutorial +
+ + + + + + \ No newline at end of file From a62e6efbc68c4b2f9679706877b8f153508475c2 Mon Sep 17 00:00:00 2001 From: Christian Helgeson <62450112+cmhhelgeson@users.noreply.github.com> Date: Fri, 22 Aug 2025 12:45:02 -0700 Subject: [PATCH 2/4] Revert "test" This reverts commit b77ac19611b267dbdfdb5627ce530d5feace4baa. --- examples/jsm/materials/ProceduralWood.js | 439 ----------------------- examples/webgpu_tsl_wood.html | 192 ---------- 2 files changed, 631 deletions(-) delete mode 100644 examples/jsm/materials/ProceduralWood.js delete mode 100644 examples/webgpu_tsl_wood.html diff --git a/examples/jsm/materials/ProceduralWood.js b/examples/jsm/materials/ProceduralWood.js deleted file mode 100644 index 3245e50b692d40..00000000000000 --- a/examples/jsm/materials/ProceduralWood.js +++ /dev/null @@ -1,439 +0,0 @@ -import * as THREE from 'three'; -import * as TSL from 'three/tsl'; - -// some helpers below are ported from Blender -const mapRange = TSL.wgslFn( ` - fn map_range(x: f32, fromMin: f32, fromMax: f32, toMin: f32, toMax: f32, clmp: bool) -> f32 - { - let factor = (x - fromMin) / (fromMax - fromMin); - var result = toMin + factor * (toMax - toMin); - - if (clmp && toMin < toMax) - { - result = clamp(result, toMin, toMax); - } - else if (clmp && toMin > toMax) - { - result = clamp(result, toMax, toMin); - } - - return result; - } -` ); - -const voronoi3d = TSL.wgslFn( ` - fn voronoi3d(x: vec3, smoothness: f32, randomness: f32) -> f32 - { - let p = floor(x); - let f = fract(x); - - var res = 0.0; - var totalWeight = 0.0; - - for (var k = -1; k <= 1; k++) - { - for (var j = -1; j <= 1; j++) - { - for (var i = -1; i <= 1; i++) - { - let b = vec3(f32(i), f32(j), f32(k)); - let hashOffset = hash3d(p + b) * randomness; - let r = b - f + hashOffset; - let d = length(r); - - let weight = exp(-d * d / max(smoothness * smoothness, 0.001)); - res += d * weight; - totalWeight += weight; - } - } - } - - if (totalWeight > 0.0) - { - res /= totalWeight; - } - - return smoothstep(0.0, 1.0, res); - } - - fn hash3d(p: vec3) -> vec3 - { - var p3 = fract(p * vec3(0.1031, 0.1030, 0.0973)); - p3 += dot(p3, p3.yzx + 33.33); - return fract((p3.xxy + p3.yzz) * p3.zyx); - } -` ); - -const softLightMix = TSL.wgslFn( ` - fn node_mix_soft(t: f32, col1: vec3, col2: vec3) -> vec3 - { - let tm = 1.0 - t; - - let one = vec3(1.0); - let scr = one - (one - col2) * (one - col1); - - return tm * col1 + t * ((one - col1) * col2 * col1 + col1 * scr); - } -` ); - -const noiseFbm = TSL.Fn( ( [ p, detail, roughness, lacunarity, useNormalize ] ) => { - - const fscale = TSL.float( 1.0 ).toVar(); - const amp = TSL.float( 1.0 ).toVar(); - const maxamp = TSL.float( 0.0 ).toVar(); - const sum = TSL.float( 0.0 ).toVar(); - - const iterations = detail.floor(); - - TSL.Loop( iterations, () => { - - const t = TSL.mx_noise_float( p.mul( fscale ) ); - sum.addAssign( t.mul( amp ) ); - maxamp.addAssign( amp ); - amp.mulAssign( roughness ); - fscale.mulAssign( lacunarity ); - - } ); - - const rmd = detail.sub( iterations ); - const hasRemainder = rmd.greaterThan( 0.001 ); - - return TSL.select( - hasRemainder, - TSL.select( - useNormalize.equal( 1 ), - ( () => { - - const t = TSL.mx_noise_float( p.mul( fscale ) ); - const sum2 = sum.add( t.mul( amp ) ); - const maxamp2 = maxamp.add( amp ); - const normalizedSum = sum.div( maxamp ).mul( 0.5 ).add( 0.5 ); - const normalizedSum2 = sum2.div( maxamp2 ).mul( 0.5 ).add( 0.5 ); - return TSL.mix( normalizedSum, normalizedSum2, rmd ); - - } )(), - ( () => { - - const t = TSL.mx_noise_float( p.mul( fscale ) ); - const sum2 = sum.add( t.mul( amp ) ); - return TSL.mix( sum, sum2, rmd ); - - } )() - ), - TSL.select( - useNormalize.equal( 1 ), - sum.div( maxamp ).mul( 0.5 ).add( 0.5 ), - sum - ) - ); - -} ); - -const noiseFbm3d = TSL.Fn( ( [ p, detail, roughness, lacunarity, useNormalize ] ) => { - - const fscale = TSL.float( 1.0 ).toVar(); - const amp = TSL.float( 1.0 ).toVar(); - const maxamp = TSL.float( 0.0 ).toVar(); - const sum = TSL.vec3( 0.0 ).toVar(); - - const iterations = detail.floor(); - - TSL.Loop( iterations, () => { - - const t = TSL.mx_noise_vec3( p.mul( fscale ) ); - sum.addAssign( t.mul( amp ) ); - maxamp.addAssign( amp ); - amp.mulAssign( roughness ); - fscale.mulAssign( lacunarity ); - - } ); - - const rmd = detail.sub( iterations ); - const hasRemainder = rmd.greaterThan( 0.001 ); - - return TSL.select( - hasRemainder, - TSL.select( - useNormalize.equal( 1 ), - ( () => { - - const t = TSL.mx_noise_vec3( p.mul( fscale ) ); - const sum2 = sum.add( t.mul( amp ) ); - const maxamp2 = maxamp.add( amp ); - const normalizedSum = sum.div( maxamp ).mul( 0.5 ).add( 0.5 ); - const normalizedSum2 = sum2.div( maxamp2 ).mul( 0.5 ).add( 0.5 ); - return TSL.mix( normalizedSum, normalizedSum2, rmd ); - - } )(), - ( () => { - - const t = TSL.mx_noise_vec3( p.mul( fscale ) ); - const sum2 = sum.add( t.mul( amp ) ); - return TSL.mix( sum, sum2, rmd ); - - } )() - ), - TSL.select( - useNormalize.equal( 1 ), - sum.div( maxamp ).mul( 0.5 ).add( 0.5 ), - sum - ) - ); - -} ); - -const woodCenter = TSL.Fn( ( [ p, centerSize ] ) => { - - const pxyCenter = p.mul( TSL.vec3( 1, 1, 0 ) ).length(); - const center = mapRange( pxyCenter, 0, 1, 0, centerSize, true ); - - return center; - -} ); - -const spaceWarp = TSL.Fn( ( [ p, warpStrength, xyScale, zScale ] ) => { - - const combinedXyz = TSL.vec3( xyScale, xyScale, zScale ).mul( p ); - const noise = noiseFbm3d( combinedXyz.mul( 1.6 * 1.5 ), TSL.float( 1 ), TSL.float( 0.5 ), TSL.float( 2 ), TSL.int( 1 ) ).sub( 0.5 ).mul( warpStrength ); - const pXy = p.mul( TSL.vec3( 1, 1, 0 ) ); - const normalizedXy = pXy.normalize(); - const warp = noise.mul( normalizedXy ).add( pXy ); - - return warp; - -} ); - -const woodRings = TSL.Fn( ( [ w, ringCount, ringBias, ringSizeVariance, ringVarianceScale, barkThickness ] ) => { - - const rings = noiseFbm( w.mul( ringVarianceScale ), TSL.float( 1 ), TSL.float( 0.5 ), TSL.float( 2 ), TSL.int( 1 ) ).mul( ringSizeVariance ).add( w ).mul( ringCount ).fract().mul( barkThickness ); - - return TSL.min( mapRange( rings, 0, ringBias, 0, 1, TSL.bool( true ) ), mapRange( rings, ringBias, 1, 1, 0, TSL.bool( true ) ) ); - -} ); - -const woodDetail = TSL.Fn( ( [ warp, p, y, splotchScale ] ) => { - - const radialCoords = TSL.clamp( TSL.atan( warp.y, warp.x ).div( TSL.PI2 ).add( 0.5 ), 0, 1 ).mul( TSL.PI2.mul( 3 ) ); - const combinedXyz = TSL.vec3( radialCoords.sin(), y, radialCoords.cos().mul( p.z ) ); - const scaled = TSL.vec3( 0.1, 1.19, 0.05 ).mul( combinedXyz ); - - return noiseFbm( scaled.mul( splotchScale ), TSL.float( 1 ), TSL.float( 0.5 ), TSL.float( 2 ), TSL.bool( true ) ); - -} ); - -const cellStructure = TSL.Fn( ( [ p, cellScale, cellSize ] ) => { - - const warp = spaceWarp( p.mul( cellScale.div( 50 ) ), cellScale.div( 1000 ), 0.1, 1.77 ); - const cells = voronoi3d( warp.xy.mul( 75 ), 0.5, 1 ); - - return mapRange( cells, cellSize, cellSize.add( 0.21 ), 0, 1, TSL.bool( true ) ); - -} ); - -const wood = TSL.Fn( ( [ - p, - centerSize, - largeWarpScale, - largeGrainStretch, - smallWarpStrength, - smallWarpScale, - fineWarpStrength, - fineWarpScale, - ringCount, - ringBias, - ringSizeVariance, - ringVarianceScale, - barkThickness, - splotchScale, - splotchIntensity, - cellScale, - cellSize, - darkGrainColor, - lightGrainColor -] ) => { - - const center = woodCenter( p, centerSize ); - const mainWarp = spaceWarp( spaceWarp( p, center, largeWarpScale, largeGrainStretch ), smallWarpStrength, smallWarpScale, 0.17 ); - const detailWarp = spaceWarp( mainWarp, fineWarpStrength, fineWarpScale, 0.17 ); - const rings = woodRings( detailWarp.length(), ringCount, ringBias, ringSizeVariance, ringVarianceScale, barkThickness ); - const detail = woodDetail( detailWarp, p, detailWarp.length(), splotchScale ); - const cells = cellStructure( mainWarp, cellScale, cellSize ); - const baseColor = TSL.mix( darkGrainColor, lightGrainColor, rings ); - - return softLightMix( splotchIntensity, softLightMix( 0.407, baseColor, cells ), detail ); - -} ); - -const woodParams = { - teak: { - originOffset: { x: - 0.4, y: 0, z: 0 }, - centerSize: 1.11, largeWarpScale: 0.32, largeGrainStretch: 0.24, smallWarpStrength: 0.059, - smallWarpScale: 2, fineWarpStrength: 0.006, fineWarpScale: 32.8, ringCount: 34, - ringBias: 0.59, ringSizeVariance: 0.16, ringVarianceScale: 1.4, barkThickness: 0.61, - splotchScale: 0.2, splotchIntensity: 0.541, cellScale: 910, cellSize: 0.1, - darkGrainColor: '#0c0504', lightGrainColor: '#533319' - }, - walnut: { - originOffset: { x: - 0.4, y: 0, z: 0 }, - centerSize: 1.07, largeWarpScale: 0.42, largeGrainStretch: 0.34, smallWarpStrength: 0.016, - smallWarpScale: 10.3, fineWarpStrength: 0.028, fineWarpScale: 12.7, ringCount: 32, - ringBias: 0.08, ringSizeVariance: 0.03, ringVarianceScale: 5.5, barkThickness: 0.98, - splotchScale: 1.84, splotchIntensity: 0.97, cellScale: 710, cellSize: 0.31, - darkGrainColor: '#311e13', lightGrainColor: '#523424' - }, - white_oak: { - originOffset: { x: - 0.4, y: 0, z: 0 }, - centerSize: 1.23, largeWarpScale: 0.21, largeGrainStretch: 0.21, smallWarpStrength: 0.034, - smallWarpScale: 2.44, fineWarpStrength: 0.01, fineWarpScale: 14.3, ringCount: 34, - ringBias: 0.82, ringSizeVariance: 0.16, ringVarianceScale: 1.4, barkThickness: 0.7, - splotchScale: 0.2, splotchIntensity: 0.541, cellScale: 800, cellSize: 0.28, - darkGrainColor: '#8b4c21', lightGrainColor: '#c57e43' - }, - pine: { - originOffset: { x: - 0.4, y: 0, z: - 0.2 }, - centerSize: 1.23, largeWarpScale: 0.21, largeGrainStretch: 0.18, smallWarpStrength: 0.041, - smallWarpScale: 2.44, fineWarpStrength: 0.006, fineWarpScale: 23.2, ringCount: 24, - ringBias: 0.1, ringSizeVariance: 0.07, ringVarianceScale: 5, barkThickness: 0.35, - splotchScale: 0.51, splotchIntensity: 3.32, cellScale: 1480, cellSize: 0.07, - darkGrainColor: '#c58355', lightGrainColor: '#d19d61' - }, - poplar: { - originOffset: { x: - 0.4, y: 0, z: 0.2 }, - centerSize: 1.43, largeWarpScale: 0.33, largeGrainStretch: 0.18, smallWarpStrength: 0.04, - smallWarpScale: 4.3, fineWarpStrength: 0.004, fineWarpScale: 33.6, ringCount: 37, - ringBias: 0.07, ringSizeVariance: 0.03, ringVarianceScale: 3.8, barkThickness: 0.3, - splotchScale: 1.92, splotchIntensity: 0.71, cellScale: 830, cellSize: 0.04, - darkGrainColor: '#716347', lightGrainColor: '#998966' - }, - maple: { - originOffset: { x: - 0.4, y: 0.3, z: - 0.2 }, - centerSize: 1.4, largeWarpScale: 0.38, largeGrainStretch: 0.25, smallWarpStrength: 0.067, - smallWarpScale: 2.5, fineWarpStrength: 0.005, fineWarpScale: 33.6, ringCount: 35, - ringBias: 0.1, ringSizeVariance: 0.07, ringVarianceScale: 4.6, barkThickness: 0.61, - splotchScale: 0.46, splotchIntensity: 1.49, cellScale: 800, cellSize: 0.03, - darkGrainColor: '#b08969', lightGrainColor: '#bc9d7d' - }, - red_oak: { - originOffset: { x: - 0.4, y: 0, z: 0.4 }, - centerSize: 1.21, largeWarpScale: 0.24, largeGrainStretch: 0.25, smallWarpStrength: 0.044, - smallWarpScale: 2.54, fineWarpStrength: 0.01, fineWarpScale: 14.5, ringCount: 34, - ringBias: 0.92, ringSizeVariance: 0.03, ringVarianceScale: 5.6, barkThickness: 1.01, - splotchScale: 0.28, splotchIntensity: 3.48, cellScale: 800, cellSize: 0.25, - darkGrainColor: '#af613b', lightGrainColor: '#e0a27a' - }, - cherry: { - originOffset: { x: - 0.4, y: 0.3, z: 0 }, - centerSize: 1.33, largeWarpScale: 0.11, largeGrainStretch: 0.33, smallWarpStrength: 0.024, - smallWarpScale: 2.48, fineWarpStrength: 0.01, fineWarpScale: 15.3, ringCount: 36, - ringBias: 0.02, ringSizeVariance: 0.04, ringVarianceScale: 6.5, barkThickness: 0.09, - splotchScale: 1.27, splotchIntensity: 1.24, cellScale: 1530, cellSize: 0.15, - darkGrainColor: '#913f27', lightGrainColor: '#b45837' - }, - cedar: { - originOffset: { x: - 0.4, y: 0.1, z: 0.1 }, - centerSize: 1.11, largeWarpScale: 0.39, largeGrainStretch: 0.12, smallWarpStrength: 0.061, - smallWarpScale: 1.9, fineWarpStrength: 0.006, fineWarpScale: 4.8, ringCount: 25, - ringBias: 0.01, ringSizeVariance: 0.07, ringVarianceScale: 6.7, barkThickness: 0.1, - splotchScale: 0.61, splotchIntensity: 2.54, cellScale: 630, cellSize: 0.19, - darkGrainColor: '#9a5b49', lightGrainColor: '#ae745e' - }, - mahogany: { - originOffset: { x: - 0.4, y: 0.2, z: 0 }, - centerSize: 1.25, largeWarpScale: 0.26, largeGrainStretch: 0.29, smallWarpStrength: 0.044, - smallWarpScale: 2.54, fineWarpStrength: 0.01, fineWarpScale: 15.3, ringCount: 38, - ringBias: 0.01, ringSizeVariance: 0.33, ringVarianceScale: 1.2, barkThickness: 0.07, - splotchScale: 0.77, splotchIntensity: 1.39, cellScale: 1400, cellSize: 0.23, - darkGrainColor: '#501d12', lightGrainColor: '#6d3722' - } -}; - -export const WoodGenuses = [ 'teak', 'walnut', 'white_oak', 'pine', 'poplar', 'maple', 'red_oak', 'cherry', 'cedar', 'mahogany' ]; -export const Finishes = [ 'raw', 'matte', 'semigloss', 'gloss' ]; - -export function GetWoodPreset( genus, finish ) { - - const params = woodParams[ genus ]; - - let clearcoat, clearcoatRoughness, clearcoatDarken; - - switch ( finish ) { - - case 'gloss': - clearcoatDarken = 0.2; clearcoatRoughness = 0.1; clearcoat = 1; - break; - - case 'semigloss': - clearcoatDarken = 0.4; clearcoatRoughness = 0.4; clearcoat = 1; - break; - - case 'matte': - clearcoatDarken = 0.6; clearcoatRoughness = 1; clearcoat = 1; - break; - - case 'raw': - default: - clearcoatDarken = 1; clearcoatRoughness = 0; clearcoat = 0; - - } - - return { ...params, genus, finish, clearcoat, clearcoatRoughness, clearcoatDarken }; - -} - -export function GenerateWoodMaterial( params ) { - - const material = new THREE.MeshPhysicalNodeMaterial(); - - const uniforms = {}; - - uniforms.centerSize = TSL.uniform( params.centerSize ); - uniforms.largeWarpScale = TSL.uniform( params.largeWarpScale ); - uniforms.largeGrainStretch = TSL.uniform( params.largeGrainStretch ); - uniforms.smallWarpStrength = TSL.uniform( params.smallWarpStrength ); - uniforms.smallWarpScale = TSL.uniform( params.smallWarpScale ); - uniforms.fineWarpStrength = TSL.uniform( params.fineWarpStrength ); - uniforms.fineWarpScale = TSL.uniform( params.fineWarpScale ); - uniforms.ringCount = TSL.uniform( params.ringCount ); - uniforms.ringBias = TSL.uniform( params.ringBias ); - uniforms.ringSizeVariance = TSL.uniform( params.ringSizeVariance ); - uniforms.ringVarianceScale = TSL.uniform( params.ringVarianceScale ); - uniforms.barkThickness = TSL.uniform( params.barkThickness ); - uniforms.splotchScale = TSL.uniform( params.splotchScale ); - uniforms.splotchIntensity = TSL.uniform( params.splotchIntensity ); - uniforms.cellScale = TSL.uniform( params.cellScale ); - uniforms.cellSize = TSL.uniform( params.cellSize ); - uniforms.darkGrainColor = TSL.uniform( new THREE.Color( params.darkGrainColor ) ); - uniforms.lightGrainColor = TSL.uniform( new THREE.Color( params.lightGrainColor ) ); - - // remember uniforms for real-time updates - material.uniforms = uniforms; - - material.colorNode = wood( - TSL.positionLocal.add( TSL.vec3( params.originOffset.x, params.originOffset.y, params.originOffset.z ) ), - uniforms.centerSize, - uniforms.largeWarpScale, - uniforms.largeGrainStretch, - uniforms.smallWarpStrength, - uniforms.smallWarpScale, - uniforms.fineWarpStrength, - uniforms.fineWarpScale, - uniforms.ringCount, - uniforms.ringBias, - uniforms.ringSizeVariance, - uniforms.ringVarianceScale, - uniforms.barkThickness, - uniforms.splotchScale, - uniforms.splotchIntensity, - uniforms.cellScale, - uniforms.cellSize, - uniforms.darkGrainColor, - uniforms.lightGrainColor - ).mul( params.clearcoatDarken ); - - //material.customProgramCacheKey = () => params.genus + params.finish; - material.clearcoatNode = params.clearcoat; - material.clearcoatRoughness = params.clearcoatRoughness; - - return material; - -} diff --git a/examples/webgpu_tsl_wood.html b/examples/webgpu_tsl_wood.html deleted file mode 100644 index fd4d8b668c3d0f..00000000000000 --- a/examples/webgpu_tsl_wood.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - Three.js WebGPU - Procedural Wood Materials - - - - - - - -
- three.js webgpu - tsl procedural wood materials
- by Logan Seeley, based on Lance Phan's Blender tutorial -
- - - - - - \ No newline at end of file From c4ba166165ac0e361d3ada2ceb51bc043abaf26d Mon Sep 17 00:00:00 2001 From: Christian Helgeson <62450112+cmhhelgeson@users.noreply.github.com> Date: Sat, 23 Aug 2025 13:27:44 -0700 Subject: [PATCH 3/4] add examples --- examples/tags.json | 2 + examples/webgpu_clipping_hardware.html | 185 +++++++++++++++++++++ examples/webgpu_clipping_intersection.html | 166 ++++++++++++++++++ 3 files changed, 353 insertions(+) create mode 100644 examples/webgpu_clipping_hardware.html create mode 100644 examples/webgpu_clipping_intersection.html diff --git a/examples/tags.json b/examples/tags.json index a598a395061dd1..dc570a7501e5d6 100644 --- a/examples/tags.json +++ b/examples/tags.json @@ -119,6 +119,8 @@ "webxr_xr_ballshooter": [ "physics" ], "webgpu_clearcoat": [ "anisotropy" ], "webgpu_clipping": [ "solid" ], + "webgpu_clipping_hardware": ["solid"], + "webgpu_clipping_intersection": ["solid"], "webgpu_compute_audio": [ "gpgpu" ], "webgpu_compute_birds": [ "gpgpu" ], "webgpu_compute_geometry": [ "gpgpu" ], diff --git a/examples/webgpu_clipping_hardware.html b/examples/webgpu_clipping_hardware.html new file mode 100644 index 00000000000000..3576ccd34bd691 --- /dev/null +++ b/examples/webgpu_clipping_hardware.html @@ -0,0 +1,185 @@ + + + + three.js WebGL 2 - clip cull distance + + + + + + +
+ three.js - vertex shader clipping via + WEBGL_clip_cull_distance + or clip-distances + +
+ + + + + + \ No newline at end of file diff --git a/examples/webgpu_clipping_intersection.html b/examples/webgpu_clipping_intersection.html new file mode 100644 index 00000000000000..9ae8aaa6d518b5 --- /dev/null +++ b/examples/webgpu_clipping_intersection.html @@ -0,0 +1,166 @@ + + + + three.js webgl - clipIntersection + + + + + + + + + + + From 5e5e5021e019e2af2943c5ed94d7e7c27528839f Mon Sep 17 00:00:00 2001 From: Christian Helgeson <62450112+cmhhelgeson@users.noreply.github.com> Date: Mon, 25 Aug 2025 14:11:17 -0700 Subject: [PATCH 4/4] webgpu_clipping_advanceD --- examples/files.json | 3 + .../screenshots/webgpu_clipping_advanced.jpg | Bin 0 -> 30999 bytes .../screenshots/webgpu_clipping_hardware.jpg | Bin 0 -> 15278 bytes .../webgpu_clipping_intersection.jpg | Bin 0 -> 21973 bytes examples/webgpu_clipping_advanced.html | 438 ++++++++++++++++++ 5 files changed, 441 insertions(+) create mode 100644 examples/screenshots/webgpu_clipping_advanced.jpg create mode 100644 examples/screenshots/webgpu_clipping_hardware.jpg create mode 100644 examples/screenshots/webgpu_clipping_intersection.jpg create mode 100644 examples/webgpu_clipping_advanced.html diff --git a/examples/files.json b/examples/files.json index 287f2130bdd0f3..8b87189ae72dae 100644 --- a/examples/files.json +++ b/examples/files.json @@ -304,6 +304,9 @@ "webgpu_centroid_sampling", "webgpu_clearcoat", "webgpu_clipping", + "webgpu_clipping_advanced", + "webgpu_clipping_hardware", + "webgpu_clipping_intersection", "webgpu_compute_audio", "webgpu_compute_birds", "webgpu_compute_cloth", diff --git a/examples/screenshots/webgpu_clipping_advanced.jpg b/examples/screenshots/webgpu_clipping_advanced.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d108b19da7d58f243d0b18f02f7256ec9ff3e9d2 GIT binary patch literal 30999 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

8QI8ks7iC9zxSf-`5EtZZiF{5 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 literal 0 HcmV?d00001 diff --git a/examples/screenshots/webgpu_clipping_intersection.jpg b/examples/screenshots/webgpu_clipping_intersection.jpg new file mode 100644 index 0000000000000000000000000000000000000000..292521d4a5e2e2c28a912c9796ec9f9c5c6f4b3d GIT binary patch literal 21973 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

lvInLEOn&!AWd=4bo~$}=mwC1tmSdBN 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 literal 0 HcmV?d00001 diff --git a/examples/webgpu_clipping_advanced.html b/examples/webgpu_clipping_advanced.html new file mode 100644 index 00000000000000..16b49445f0b80b --- /dev/null +++ b/examples/webgpu_clipping_advanced.html @@ -0,0 +1,438 @@ + + + + three.js webgl - clipping planes - advanced + + + + + + + + + + + \ No newline at end of file