-
-
Notifications
You must be signed in to change notification settings - Fork 35.9k
Improved webgpu reflection example #31409
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
1310e4e
00855c6
021c0ed
32aace7
2209746
32febf1
2046bfd
dc72980
b19e320
238cfdd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,9 @@ | |
|
||
import * as THREE from 'three'; | ||
|
||
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'; | ||
import { abs, blendOverlay, color, div, float, Fn, instancedBufferAttribute, materialColor, min, mrt, normalView, normalWorldGeometry, output, pass, positionGeometry, positionLocal, reflector, screenUV, sin, sub, texture, time, uniform, uv, varyingProperty, vec3 } from 'three/tsl'; | ||
import { ao } from 'three/addons/tsl/display/GTAONode.js'; | ||
|
||
import { denoise } from 'three/addons/tsl/display/DenoiseNode.js'; | ||
|
||
import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js'; | ||
|
||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; | ||
|
@@ -44,7 +46,6 @@ | |
|
||
// below uniforms will be animated via TWEEN.js | ||
|
||
const uniformLife = uniform( 0 ); | ||
const uniformEffector1 = uniform( - 0.2 ); | ||
const uniformEffector2 = uniform( - 0.2 ); | ||
|
||
|
@@ -56,19 +57,21 @@ | |
camera.position.set( 4, 2, 4 ); | ||
|
||
scene = new THREE.Scene(); | ||
scene.fog = new THREE.Fog( 0x0487e2, 7, 25 ); | ||
scene.backgroundNode = normalWorldGeometry.y.mix( color( 0x0487e2 ), color( 0x0066ff ) ); | ||
scene.fog = new THREE.Fog( 0x4195a4, 1, 25 ); | ||
scene.backgroundNode = normalWorldGeometry.y.mix( color( 0x4195a4 ), color( 0x0066ff ) ); | ||
camera.lookAt( 0, 1, 0 ); | ||
|
||
const sunLight = new THREE.DirectionalLight( 0xFFE499, 3 ); | ||
sunLight.position.set( 7, 3, 7 ); | ||
const sunLight = new THREE.DirectionalLight( 0xFFE499, 2 ); | ||
sunLight.position.set( 7, 5, 7 ); | ||
sunLight.castShadow = true; | ||
sunLight.shadow.camera.zoom = 1.5; | ||
sunLight.shadow.mapSize.set( 1024, 1024 ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The light shadow needs a small bias since there are self-shadowing artifacts all over the boxes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed with the latest commits. Removing the artifacts made the boxes look a bit pale so I've added a texture to give them more detail. Hope this looks good. |
||
scene.add( sunLight ); | ||
|
||
const waterAmbientLight = new THREE.HemisphereLight( 0x333366, 0x74ccf4, 3 ); | ||
const skyAmbientLight = new THREE.HemisphereLight( 0x74ccf4, 0, 1 ); | ||
const backLight = new THREE.DirectionalLight( 0x0487e2, 0.5 ); | ||
backLight.position.set( 7, - 5, 7 ); | ||
scene.add( backLight ); | ||
|
||
scene.add( sunLight ); | ||
scene.add( skyAmbientLight ); | ||
scene.add( waterAmbientLight ); | ||
|
||
// textures | ||
|
||
|
@@ -86,6 +89,8 @@ | |
// tree | ||
|
||
const treeMesh = createTreeMesh(); | ||
treeMesh.castShadow = true; | ||
treeMesh.receiveShadow = true; | ||
scene.add( treeMesh ); | ||
|
||
// floor | ||
|
@@ -102,7 +107,7 @@ | |
floorMaterial.colorNode = texture( floorColor, floorUV ).add( reflection ); | ||
|
||
const floor = new THREE.Mesh( new THREE.BoxGeometry( 50, .001, 50 ), floorMaterial ); | ||
floor.position.set( 0, 0, 0 ); | ||
floor.receiveShadow = true; | ||
scene.add( floor ); | ||
|
||
// renderer | ||
|
@@ -111,6 +116,9 @@ | |
renderer.setPixelRatio( window.devicePixelRatio ); | ||
renderer.setSize( window.innerWidth, window.innerHeight ); | ||
renderer.setAnimationLoop( animate ); | ||
renderer.shadowMap.enabled = true; | ||
renderer.shadowMap.type = THREE.PCFSoftShadowMap; | ||
renderer.toneMapping = THREE.ACESFilmicToneMapping; | ||
document.body.appendChild( renderer.domElement ); | ||
|
||
stats = new Stats(); | ||
|
@@ -137,18 +145,29 @@ | |
const scenePassColorBlurred = gaussianBlur( scenePassColor ); | ||
scenePassColorBlurred.directionNode = scenePassDepth; | ||
|
||
const vignette = screenUV.distance( .5 ).mul( 1.35 ).clamp().oneMinus(); | ||
const vignette = screenUV.distance( .5 ).mul( 1.25 ).clamp().oneMinus().sub( 0.2 ); | ||
|
||
postProcessing = new THREE.PostProcessing( renderer ); | ||
postProcessing.outputNode = scenePassColorBlurred.mul( vignette ); | ||
postProcessing.outputNode = blendOverlay( scenePassColorBlurred, vignette ); | ||
|
||
// | ||
// tweens | ||
|
||
window.addEventListener( 'resize', onWindowResize ); | ||
const effectTween = new TWEEN.Tween( uniformEffector1 ) | ||
|
||
.to( { value: 1.2 }, 3000 ) | ||
.delay( 800 ) | ||
.repeat( Infinity ) | ||
.easing( TWEEN.Easing.Sinusoidal.InOut ) | ||
.start(); | ||
|
||
const effect2Tween = new TWEEN.Tween( uniformEffector2 ) | ||
|
||
.to( { value: 1.2 }, 3000 ) | ||
.repeat( Infinity ) | ||
.easing( TWEEN.Easing.Sinusoidal.InOut ) | ||
.start(); | ||
|
||
// | ||
|
||
startTweens(); | ||
window.addEventListener( 'resize', onWindowResize ); | ||
|
||
} | ||
|
||
|
@@ -173,52 +192,13 @@ | |
|
||
} | ||
|
||
function startTweens() { | ||
|
||
const lifeTween = new TWEEN.Tween( uniformLife ) | ||
.to( { value: 1 }, 5000 ) | ||
.easing( TWEEN.Easing.Bounce.Out ); | ||
lifeTween.start(); | ||
|
||
const effectTween = new TWEEN.Tween( uniformEffector1 ) | ||
.to( { value: 1.2 }, 2500 ) | ||
.delay( 3000 ) | ||
.easing( TWEEN.Easing.Sinusoidal.InOut ) | ||
.onComplete( function () { | ||
|
||
secondaryTweens(); | ||
|
||
} ); | ||
effectTween.start(); | ||
|
||
} | ||
|
||
function secondaryTweens() { | ||
|
||
uniformEffector1.value = - 0.2; | ||
uniformEffector2.value = - 0.2; | ||
|
||
const effect2Tween = new TWEEN.Tween( uniformEffector2 ) | ||
.to( { value: 1.2 }, 3000 ) | ||
.repeat( Infinity ) | ||
.easing( TWEEN.Easing.Sinusoidal.InOut ); | ||
effect2Tween.start(); | ||
|
||
const effectTween = new TWEEN.Tween( uniformEffector1 ) | ||
.to( { value: 1.2 }, 3000 ) | ||
.delay( 800 ) | ||
.repeat( Infinity ) | ||
.easing( TWEEN.Easing.Sinusoidal.InOut ); | ||
effectTween.start(); | ||
|
||
} | ||
function random () { return ( Math.random() - 0.5 ) * 2.0 }; | ||
|
||
function createTreeMesh() { | ||
|
||
const maxSteps = 6; | ||
const angleLeft = Math.PI / 180 * 30; | ||
const angleRight = Math.PI / 180 * 40; | ||
const lengthMult = 0.88; | ||
const maxSteps = 5; | ||
const angleOffset = Math.PI / 180 * 40; | ||
|
||
const lengthMult = 0.8; | ||
|
||
const positions = []; | ||
const normals = []; | ||
|
@@ -234,6 +214,8 @@ | |
|
||
function createTreePart( angle, x, y, z, length, count ) { | ||
|
||
if ( Math.random() > ( maxSteps / count ) * 0.25 ) return; | ||
|
||
if ( count < maxSteps ) { | ||
|
||
const newLength = length * lengthMult; | ||
|
@@ -246,9 +228,9 @@ | |
if ( size > 25 ) size = 25; | ||
if ( size < 10 ) size = 10; | ||
|
||
size = size / 10; | ||
size = size / 100; | ||
|
||
const subSteps = 60; | ||
const subSteps = 200; | ||
|
||
// below loop generates the instanced data for a tree part | ||
|
||
|
@@ -270,7 +252,7 @@ | |
|
||
positions.push( position.x, position.y, position.z ); | ||
|
||
const scale = 0.25 * Math.random(); | ||
const scale = Math.random(); | ||
|
||
// normal | ||
|
||
|
@@ -279,7 +261,7 @@ | |
|
||
// color | ||
|
||
color.setHSL( 0.55 + Math.random() * 0.05, 1.0, 0.7 + Math.random() * 0.3 ); | ||
color.setHSL( ( count / maxSteps ) * 0.5 + Math.random() * 0.05, 0.75, 0.7 + Math.random() * 0.1 ); | ||
colors.push( color.r, color.g, color.b ); | ||
|
||
// to save vertex buffers, we store the size, time and seed in a single attribute | ||
|
@@ -292,8 +274,12 @@ | |
|
||
} | ||
|
||
createTreePart( angle - angleRight, newX, newY, newZ, newLength, count + 1 ); | ||
createTreePart( angle + angleLeft, newX, newY, newZ, newLength, count + 1 ); | ||
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 ); | ||
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 ); | ||
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 ); | ||
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 ); | ||
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 ); | ||
createTreePart( angle + random(), newX, newY, newZ, newLength + random(), count + 1 ); | ||
|
||
} | ||
|
||
|
@@ -321,8 +307,6 @@ | |
|
||
// TSL | ||
|
||
const vVisbility = varyingProperty( 'float' ); | ||
|
||
const instancePosition = instancedBufferAttribute( attributePosition ); | ||
const instanceNormal = instancedBufferAttribute( attributeNormal ); | ||
const instanceColor = instancedBufferAttribute( attributeColor ); | ||
|
@@ -342,18 +326,13 @@ | |
const dif2 = abs( instanceTime.sub( uniformEffector2 ) ).toConst(); | ||
effect = dif2.lessThanEqual( 0.15 ).select( sub( 0.15, dif2 ).mul( sub( 1.7, instanceTime ).mul( 10 ) ), effect ); | ||
|
||
// life (controls the visibility and initial scale of the cubes) | ||
|
||
const scale = uniformLife.greaterThan( instanceTime ).select( min( 1, div( uniformLife.sub( instanceTime ), 0 ) ) ).oneMinus(); | ||
vVisbility.assign( uniformLife.greaterThan( instanceTime ).select( 1, 0 ) ); | ||
|
||
// accumulate different vertex animations | ||
|
||
let animated = positionLocal.add( instancePosition ).toVar(); | ||
const direction = positionGeometry.normalize().toConst(); | ||
|
||
animated = animated.add( direction.mul( effect.add( instanceSize ) ) ); | ||
animated = animated.sub( direction.mul( scale ) ); | ||
animated = animated.sub( direction.mul( effect ) ); | ||
animated = animated.add( instanceNormal.mul( effect.mul( 1 ) ) ); | ||
animated = animated.add( instanceNormal.mul( abs( sin( time.add( instanceSeed.mul( 2 ) ) ).mul( 1.5 ) ) ) ); | ||
|
||
|
@@ -363,12 +342,24 @@ | |
|
||
material.colorNode = Fn( () => { | ||
|
||
vVisbility.equal( 0 ).discard(); | ||
|
||
return materialColor.mul( instanceColor ); | ||
|
||
} )(); | ||
|
||
material.emissiveNode = Fn( () => { | ||
|
||
const instanceTime = instanceData.y; | ||
|
||
const dif1 = abs( instanceTime.sub( uniformEffector1 ) ).toConst(); | ||
const effect1 = dif1.lessThanEqual( 0.15 ).select( sub( 0.15, dif1 ).mul( sub( 1.7, instanceTime ).mul( 10 ) ), float( 0 ) ); | ||
|
||
const dif2 = abs( instanceTime.sub( uniformEffector2 ) ).toConst(); | ||
const effect2 = dif2.lessThanEqual( 0.15 ).select( sub( 0.15, dif2 ).mul( sub( 1.7, instanceTime ).mul( 10 ) ), effect1 ); | ||
|
||
return vec3( effect1, 0, effect2 ).mul( instanceColor ); | ||
|
||
} )(); | ||
|
||
return mesh; | ||
|
||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.