Skip to content

Commit e2830ac

Browse files
authored
Examples: Add webgpu_postprocessing_dof_basic. (#31445)
* Examples: Add `webgpu_postprocessing_dof_basic`. * Example: Animate focus point transition. * Examples: Clean up.
1 parent 4a276d2 commit e2830ac

File tree

4 files changed

+202
-0
lines changed

4 files changed

+202
-0
lines changed

examples/files.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@
400400
"webgpu_postprocessing_bloom_selective",
401401
"webgpu_postprocessing_difference",
402402
"webgpu_postprocessing_dof",
403+
"webgpu_postprocessing_dof_basic",
403404
"webgpu_postprocessing_pixel",
404405
"webgpu_postprocessing_fxaa",
405406
"webgpu_postprocessing_lensflare",

examples/models/gltf/bath_day.glb

792 KB
Binary file not shown.
88.2 KB
Loading
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>three.js webgpu - postprocessing dof - basic</title>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
7+
<link type="text/css" rel="stylesheet" href="main.css">
8+
<style>
9+
body {
10+
touch-action: none;
11+
color: #000000;
12+
}
13+
a {
14+
color: #2983ff;
15+
}
16+
</style>
17+
</head>
18+
<body>
19+
20+
<div id="info">
21+
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - DOF - basic</a><br />
22+
<a href="https://skfb.ly/opNFG" target="_blank" rel="noopener">Bath day</a> by
23+
<a href="https://sketchfab.com/stanst" target="_blank" rel="noopener">Stan.St</a> is licensed under <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noopener">Creative Commons Attribution</a>.<br />
24+
Click on a position in the scene to focus it.<br />
25+
</div>
26+
27+
<script type="importmap">
28+
{
29+
"imports": {
30+
"three": "../build/three.webgpu.js",
31+
"three/webgpu": "../build/three.webgpu.js",
32+
"three/tsl": "../build/three.tsl.js",
33+
"three/addons/": "./jsm/"
34+
}
35+
}
36+
</script>
37+
38+
<script type="module">
39+
40+
import * as THREE from 'three';
41+
import { mix, pass, renderOutput, smoothstep, uniform, vec3 } from 'three/tsl';
42+
import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js';
43+
import { fxaa } from 'three/addons/tsl/display/FXAANode.js';
44+
45+
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
46+
import { UltraHDRLoader } from 'three/addons/loaders/UltraHDRLoader.js';
47+
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
48+
49+
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
50+
import TWEEN from 'three/addons/libs/tween.module.js';
51+
52+
let camera, scene, timer, renderer, model, mixer, raycaster, postProcessing;
53+
54+
const pointerCoords = new THREE.Vector2();
55+
const focusPoint = new THREE.Vector3( 1, 1.75, - 0.4 );
56+
const focusPointView = uniform( vec3() );
57+
58+
init();
59+
60+
async function init() {
61+
62+
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 );
63+
camera.position.set( - 5, 4, 5 );
64+
camera.lookAt( 0, 1.5, 0 );
65+
66+
scene = new THREE.Scene();
67+
scene.background = new THREE.Color( 0x90D5FF );
68+
69+
raycaster = new THREE.Raycaster();
70+
71+
timer = new THREE.Timer();
72+
73+
const dracoLoader = new DRACOLoader();
74+
dracoLoader.setDecoderPath( 'jsm/libs/draco/gltf/' );
75+
76+
const loader = new GLTFLoader();
77+
loader.setDRACOLoader( dracoLoader );
78+
const gltf = await loader.loadAsync( 'models/gltf/bath_day.glb' );
79+
80+
model = gltf.scene;
81+
scene.add( model );
82+
83+
mixer = new THREE.AnimationMixer( model );
84+
const action = mixer.clipAction( gltf.animations[ 0 ] );
85+
action.play();
86+
87+
//
88+
89+
const hdrLoader = new UltraHDRLoader();
90+
hdrLoader.setDataType( THREE.HalfFloatType );
91+
const envMap = await hdrLoader.loadAsync( 'textures/equirectangular/spruit_sunrise_2k.hdr.jpg' );
92+
envMap.mapping = THREE.EquirectangularReflectionMapping;
93+
scene.environmentRotation.y = Math.PI * 0.5;
94+
scene.environment = envMap;
95+
96+
// renderer
97+
98+
renderer = new THREE.WebGPURenderer();
99+
renderer.setPixelRatio( window.devicePixelRatio );
100+
renderer.setSize( window.innerWidth, window.innerHeight );
101+
renderer.setAnimationLoop( animate );
102+
renderer.toneMapping = THREE.NeutralToneMapping;
103+
document.body.appendChild( renderer.domElement );
104+
105+
// post processing
106+
107+
postProcessing = new THREE.PostProcessing( renderer );
108+
postProcessing.outputColorTransform = false;
109+
110+
// DOF uniforms
111+
112+
const maxBlur = uniform( 2 ); // maximum amount of blur
113+
const minDistance = uniform( 1 ); // all positions at or below minDistance will be completely in focus.
114+
const maxDistance = uniform( 3 ); // all positions at or beyond maxDistance will be completely out of focus.
115+
116+
// beauty and blur/out-of-focus pass
117+
118+
const scenePass = pass( scene, camera );
119+
const scenePassViewZ = scenePass.getViewZNode();
120+
const scenePassBlurred = gaussianBlur( scenePass, maxBlur );
121+
122+
// simple DOF from https://lettier.github.io/3d-game-shaders-for-beginners/depth-of-field.html
123+
124+
const blur = smoothstep( minDistance, maxDistance, scenePassViewZ.sub( focusPointView.z ).abs() );
125+
const dofPass = mix( scenePass, scenePassBlurred, blur );
126+
127+
const outputPass = renderOutput( dofPass );
128+
const fxaaPass = fxaa( outputPass );
129+
130+
postProcessing.outputNode = fxaaPass;
131+
132+
// GUI
133+
134+
const gui = new GUI();
135+
gui.add( minDistance, 'value', 0, 3 ).name( 'min distance' );
136+
gui.add( maxDistance, 'value', 0, 5 ).name( 'max distance' );
137+
gui.add( maxBlur, 'value', 0, 5 ).name( 'max blur' );
138+
139+
//
140+
141+
renderer.domElement.addEventListener( 'pointerdown', onPointerDown );
142+
143+
window.addEventListener( 'resize', onWindowResize );
144+
145+
}
146+
147+
function onPointerDown( event ) {
148+
149+
pointerCoords.set(
150+
( event.clientX / window.innerWidth ) * 2 - 1,
151+
- ( event.clientY / window.innerHeight ) * 2 + 1
152+
);
153+
154+
raycaster.setFromCamera( pointerCoords, camera );
155+
156+
const intersects = raycaster.intersectObject( model );
157+
158+
if ( intersects.length > 0 ) {
159+
160+
TWEEN.removeAll();
161+
162+
new TWEEN.Tween( focusPoint )
163+
.to( intersects[ 0 ].point, 500 )
164+
.easing( TWEEN.Easing.Cubic.InOut )
165+
.start();
166+
167+
}
168+
169+
}
170+
171+
function onWindowResize() {
172+
173+
camera.aspect = window.innerWidth / window.innerHeight;
174+
camera.updateProjectionMatrix();
175+
176+
renderer.setSize( window.innerWidth, window.innerHeight );
177+
178+
}
179+
180+
function animate() {
181+
182+
TWEEN.update();
183+
184+
timer.update();
185+
186+
mixer.update( timer.getDelta() );
187+
188+
// since the focus point is expressed in view space, it must be updated on every
189+
// camera change. for simplicity, do this every frame.
190+
191+
camera.updateMatrixWorld();
192+
focusPointView.value.copy( focusPoint ).applyMatrix4( camera.matrixWorldInverse );
193+
194+
postProcessing.render();
195+
196+
197+
}
198+
199+
</script>
200+
</body>
201+
</html>

0 commit comments

Comments
 (0)