Skip to content

Commit cc8051e

Browse files
mrdoobgithub-advanced-security[bot]Mugen87
authored
Improved webgpu reflection example (#31409)
* Improved webgpu reflection example. * Clean up. * Added normalMap to floor. * Updated screenshot. * Potential fix for code scanning alert no. 3582: Unused variable, import, function or class Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Clean up. * Update webgpu_reflection.html Update link. * Update webgpu_reflection.html Fix shadows. * Update webgpu_reflection.html --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Michael Herzog <[email protected]>
1 parent 0d9c7b8 commit cc8051e

File tree

2 files changed

+80
-80
lines changed

2 files changed

+80
-80
lines changed
31.8 KB
Loading

examples/webgpu_reflection.html

Lines changed: 80 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<div id="info">
1212
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu - reflection<br/>
13-
Based on <a href="https://web.archive.org/web/20210101053442/http://oos.moxiecode.com/js_webgl/recursive_tree_cubes/" target="_blank" rel="noopener">Recursive Tree Cubes</a>
13+
Based on <a href="https://oosmoxiecode.com/archive/js_webgl/recursive_tree_cubes/" target="_blank" rel="noopener">Recursive Tree Cubes</a>
1414
by <a href="https://github.com/oosmoxiecode" target="_blank" rel="noopener">oosmoxiecode</a>
1515
</div>
1616

@@ -29,7 +29,7 @@
2929

3030
import * as THREE from 'three';
3131

32-
import { abs, color, div, float, Fn, instancedBufferAttribute, materialColor, min, normalWorldGeometry, pass, positionGeometry, positionLocal, reflector, screenUV, sin, sub, texture, time, uniform, uv, varyingProperty } from 'three/tsl';
32+
import { abs, blendOverlay, color, float, Fn, instancedBufferAttribute, materialColor, normalWorldGeometry, pass, positionGeometry, positionLocal, reflector, screenUV, sin, sub, texture, time, uniform, uv, vec3 } from 'three/tsl';
3333
import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js';
3434

3535
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
@@ -44,48 +44,55 @@
4444

4545
// below uniforms will be animated via TWEEN.js
4646

47-
const uniformLife = uniform( 0 );
4847
const uniformEffector1 = uniform( - 0.2 );
4948
const uniformEffector2 = uniform( - 0.2 );
5049

5150
init();
5251

53-
function init() {
52+
async function init() {
5453

5554
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.25, 30 );
5655
camera.position.set( 4, 2, 4 );
5756

5857
scene = new THREE.Scene();
59-
scene.fog = new THREE.Fog( 0x0487e2, 7, 25 );
60-
scene.backgroundNode = normalWorldGeometry.y.mix( color( 0x0487e2 ), color( 0x0066ff ) );
58+
scene.fog = new THREE.Fog( 0x4195a4, 1, 25 );
59+
scene.backgroundNode = normalWorldGeometry.y.mix( color( 0x4195a4 ), color( 0x0066ff ) );
6160
camera.lookAt( 0, 1, 0 );
6261

63-
const sunLight = new THREE.DirectionalLight( 0xFFE499, 3 );
64-
sunLight.position.set( 7, 3, 7 );
65-
66-
const waterAmbientLight = new THREE.HemisphereLight( 0x333366, 0x74ccf4, 3 );
67-
const skyAmbientLight = new THREE.HemisphereLight( 0x74ccf4, 0, 1 );
68-
62+
const sunLight = new THREE.DirectionalLight( 0xFFE499, 2 );
63+
sunLight.position.set( 7, 5, 7 );
64+
sunLight.castShadow = true;
65+
sunLight.shadow.camera.zoom = 1.5;
66+
sunLight.shadow.mapSize.set( 1024, 1024 );
67+
sunLight.shadow.bias = - 0.0001;
6968
scene.add( sunLight );
70-
scene.add( skyAmbientLight );
71-
scene.add( waterAmbientLight );
69+
70+
const backLight = new THREE.DirectionalLight( 0x0487e2, 0.5 );
71+
backLight.position.set( 7, - 5, 7 );
72+
scene.add( backLight );
7273

7374
// textures
7475

7576
const textureLoader = new THREE.TextureLoader();
7677

77-
const floorColor = textureLoader.load( 'textures/floors/FloorsCheckerboard_S_Diffuse.jpg' );
78+
const floorColor = await textureLoader.loadAsync( 'textures/floors/FloorsCheckerboard_S_Diffuse.jpg' );
7879
floorColor.wrapS = THREE.RepeatWrapping;
7980
floorColor.wrapT = THREE.RepeatWrapping;
8081
floorColor.colorSpace = THREE.SRGBColorSpace;
8182

82-
const floorNormal = textureLoader.load( 'textures/floors/FloorsCheckerboard_S_Normal.jpg' );
83+
const floorNormal = await textureLoader.loadAsync( 'textures/floors/FloorsCheckerboard_S_Normal.jpg' );
8384
floorNormal.wrapS = THREE.RepeatWrapping;
8485
floorNormal.wrapT = THREE.RepeatWrapping;
86+
floorNormal.repeat.set( 15, 15 );
87+
88+
const boxMap = await textureLoader.loadAsync( 'textures/edge3.jpg' );
89+
boxMap.colorSpace = THREE.SRGBColorSpace;
8590

8691
// tree
8792

88-
const treeMesh = createTreeMesh();
93+
const treeMesh = createTreeMesh( boxMap );
94+
treeMesh.castShadow = true;
95+
treeMesh.receiveShadow = true;
8996
scene.add( treeMesh );
9097

9198
// floor
@@ -100,9 +107,11 @@
100107

101108
const floorMaterial = new THREE.MeshPhongNodeMaterial();
102109
floorMaterial.colorNode = texture( floorColor, floorUV ).add( reflection );
110+
floorMaterial.normalMap = floorNormal;
111+
floorMaterial.normalScale.set( 0.2, - 0.2 );
103112

104113
const floor = new THREE.Mesh( new THREE.BoxGeometry( 50, .001, 50 ), floorMaterial );
105-
floor.position.set( 0, 0, 0 );
114+
floor.receiveShadow = true;
106115
scene.add( floor );
107116

108117
// renderer
@@ -111,6 +120,9 @@
111120
renderer.setPixelRatio( window.devicePixelRatio );
112121
renderer.setSize( window.innerWidth, window.innerHeight );
113122
renderer.setAnimationLoop( animate );
123+
renderer.shadowMap.enabled = true;
124+
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
125+
renderer.toneMapping = THREE.ACESFilmicToneMapping;
114126
document.body.appendChild( renderer.domElement );
115127

116128
stats = new Stats();
@@ -137,18 +149,29 @@
137149
const scenePassColorBlurred = gaussianBlur( scenePassColor );
138150
scenePassColorBlurred.directionNode = scenePassDepth;
139151

140-
const vignette = screenUV.distance( .5 ).mul( 1.35 ).clamp().oneMinus();
152+
const vignette = screenUV.distance( .5 ).mul( 1.25 ).clamp().oneMinus().sub( 0.2 );
141153

142154
postProcessing = new THREE.PostProcessing( renderer );
143-
postProcessing.outputNode = scenePassColorBlurred.mul( vignette );
155+
postProcessing.outputNode = blendOverlay( scenePassColorBlurred, vignette );
144156

145-
//
157+
// tweens
146158

147-
window.addEventListener( 'resize', onWindowResize );
159+
new TWEEN.Tween( uniformEffector1 )
160+
.to( { value: 1.2 }, 3000 )
161+
.delay( 800 )
162+
.repeat( Infinity )
163+
.easing( TWEEN.Easing.Sinusoidal.InOut )
164+
.start();
165+
166+
new TWEEN.Tween( uniformEffector2 )
167+
.to( { value: 1.2 }, 3000 )
168+
.repeat( Infinity )
169+
.easing( TWEEN.Easing.Sinusoidal.InOut )
170+
.start();
148171

149172
//
150173

151-
startTweens();
174+
window.addEventListener( 'resize', onWindowResize );
152175

153176
}
154177

@@ -173,52 +196,18 @@
173196

174197
}
175198

176-
function startTweens() {
177-
178-
const lifeTween = new TWEEN.Tween( uniformLife )
179-
.to( { value: 1 }, 5000 )
180-
.easing( TWEEN.Easing.Bounce.Out );
181-
lifeTween.start();
199+
function random() {
182200

183-
const effectTween = new TWEEN.Tween( uniformEffector1 )
184-
.to( { value: 1.2 }, 2500 )
185-
.delay( 3000 )
186-
.easing( TWEEN.Easing.Sinusoidal.InOut )
187-
.onComplete( function () {
188-
189-
secondaryTweens();
190-
191-
} );
192-
effectTween.start();
201+
return ( Math.random() - 0.5 ) * 2.0;
193202

194203
}
195204

196-
function secondaryTweens() {
197-
198-
uniformEffector1.value = - 0.2;
199-
uniformEffector2.value = - 0.2;
200-
201-
const effect2Tween = new TWEEN.Tween( uniformEffector2 )
202-
.to( { value: 1.2 }, 3000 )
203-
.repeat( Infinity )
204-
.easing( TWEEN.Easing.Sinusoidal.InOut );
205-
effect2Tween.start();
206-
207-
const effectTween = new TWEEN.Tween( uniformEffector1 )
208-
.to( { value: 1.2 }, 3000 )
209-
.delay( 800 )
210-
.repeat( Infinity )
211-
.easing( TWEEN.Easing.Sinusoidal.InOut );
212-
effectTween.start();
213205

214-
}
215206

216-
function createTreeMesh() {
207+
function createTreeMesh( boxMap ) {
217208

218-
const maxSteps = 6;
219-
const angleLeft = Math.PI / 180 * 30;
220-
const angleRight = Math.PI / 180 * 40;
221-
const lengthMult = 0.88;
209+
const maxSteps = 5;
210+
const lengthMult = 0.8;
222211

223212
const positions = [];
224213
const normals = [];
@@ -234,6 +223,8 @@
234223

235224
function createTreePart( angle, x, y, z, length, count ) {
236225

226+
if ( Math.random() > ( maxSteps / count ) * 0.25 ) return;
227+
237228
if ( count < maxSteps ) {
238229

239230
const newLength = length * lengthMult;
@@ -246,9 +237,9 @@
246237
if ( size > 25 ) size = 25;
247238
if ( size < 10 ) size = 10;
248239

249-
size = size / 10;
240+
size = size / 100;
250241

251-
const subSteps = 60;
242+
const subSteps = 200;
252243

253244
// below loop generates the instanced data for a tree part
254245

@@ -270,7 +261,7 @@
270261

271262
positions.push( position.x, position.y, position.z );
272263

273-
const scale = 0.25 * Math.random();
264+
const scale = Math.random();
274265

275266
// normal
276267

@@ -279,7 +270,7 @@
279270

280271
// color
281272

282-
color.setHSL( 0.55 + Math.random() * 0.05, 1.0, 0.7 + Math.random() * 0.3 );
273+
color.setHSL( ( count / maxSteps ) * 0.5 + Math.random() * 0.05, 0.75, 0.6 + Math.random() * 0.1 );
283274
colors.push( color.r, color.g, color.b );
284275

285276
// to save vertex buffers, we store the size, time and seed in a single attribute
@@ -292,8 +283,12 @@
292283

293284
}
294285

295-
createTreePart( angle - angleRight, newX, newY, newZ, newLength, count + 1 );
296-
createTreePart( angle + angleLeft, newX, newY, newZ, newLength, count + 1 );
286+
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 );
287+
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 );
288+
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 );
289+
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 );
290+
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 );
291+
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 );
297292

298293
}
299294

@@ -306,7 +301,7 @@
306301
createTreePart( angle, 0, 0, 0, 16, 0 );
307302

308303
const geometry = new THREE.BoxGeometry();
309-
const material = new THREE.MeshStandardNodeMaterial();
304+
const material = new THREE.MeshStandardNodeMaterial( { map: boxMap } );
310305
const mesh = new THREE.Mesh( geometry, material );
311306
mesh.scale.setScalar( 0.05 );
312307
mesh.count = instanceCount;
@@ -321,13 +316,11 @@
321316

322317
// TSL
323318

324-
const vVisibility = varyingProperty( 'float' );
325-
326319
const instancePosition = instancedBufferAttribute( attributePosition );
327320
const instanceNormal = instancedBufferAttribute( attributeNormal );
328321
const instanceColor = instancedBufferAttribute( attributeColor );
329322
const instanceData = instancedBufferAttribute( attributeData );
330-
323+
331324
material.positionNode = Fn( () => {
332325

333326
const instanceSize = instanceData.x;
@@ -342,18 +335,13 @@
342335
const dif2 = abs( instanceTime.sub( uniformEffector2 ) ).toConst();
343336
effect = dif2.lessThanEqual( 0.15 ).select( sub( 0.15, dif2 ).mul( sub( 1.7, instanceTime ).mul( 10 ) ), effect );
344337

345-
// life (controls the visibility and initial scale of the cubes)
346-
347-
const scale = uniformLife.greaterThan( instanceTime ).select( min( 1, div( uniformLife.sub( instanceTime ), 0 ) ) ).oneMinus();
348-
vVisibility.assign( uniformLife.greaterThan( instanceTime ).select( 1, 0 ) );
349-
350338
// accumulate different vertex animations
351339

352340
let animated = positionLocal.add( instancePosition ).toVar();
353341
const direction = positionGeometry.normalize().toConst();
354342

355343
animated = animated.add( direction.mul( effect.add( instanceSize ) ) );
356-
animated = animated.sub( direction.mul( scale ) );
344+
animated = animated.sub( direction.mul( effect ) );
357345
animated = animated.add( instanceNormal.mul( effect.mul( 1 ) ) );
358346
animated = animated.add( instanceNormal.mul( abs( sin( time.add( instanceSeed.mul( 2 ) ) ).mul( 1.5 ) ) ) );
359347

@@ -363,12 +351,24 @@
363351

364352
material.colorNode = Fn( () => {
365353

366-
vVisibility.equal( 0 ).discard();
367-
368354
return materialColor.mul( instanceColor );
369355

370356
} )();
371357

358+
material.emissiveNode = Fn( () => {
359+
360+
const instanceTime = instanceData.y;
361+
362+
const dif1 = abs( instanceTime.sub( uniformEffector1 ) ).toConst();
363+
const effect1 = dif1.lessThanEqual( 0.15 ).select( sub( 0.15, dif1 ).mul( sub( 1.7, instanceTime ).mul( 10 ) ), float( 0 ) );
364+
365+
const dif2 = abs( instanceTime.sub( uniformEffector2 ) ).toConst();
366+
const effect2 = dif2.lessThanEqual( 0.15 ).select( sub( 0.15, dif2 ).mul( sub( 1.7, instanceTime ).mul( 10 ) ), effect1 );
367+
368+
return vec3( effect1, 0, effect2 ).mul( instanceColor );
369+
370+
} )();
371+
372372
return mesh;
373373

374374
}

0 commit comments

Comments
 (0)