Skip to content

Commit 460efde

Browse files
authored
WebGPURenderer: Add bloom emissive/selective examples (#28913)
* Add background names * Fix RenderTarget dispose and MRT * add emissive bloom * update * remove support to `array` and added `getNode()` * added bloom selective
1 parent 34889d0 commit 460efde

File tree

6 files changed

+307
-0
lines changed

6 files changed

+307
-0
lines changed

examples/files.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,8 @@
375375
"webgpu_postprocessing_anamorphic",
376376
"webgpu_postprocessing_ao",
377377
"webgpu_postprocessing_bloom",
378+
"webgpu_postprocessing_bloom_emissive",
379+
"webgpu_postprocessing_bloom_selective",
378380
"webgpu_postprocessing_dof",
379381
"webgpu_postprocessing_pixel",
380382
"webgpu_postprocessing_fxaa",
31.3 KB
Loading
20.5 KB
Loading
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>three.js webgpu - bloom emissive</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+
</head>
9+
<body>
10+
11+
<div id="info">
12+
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu - bloom emissive
13+
</div>
14+
15+
<script type="importmap">
16+
{
17+
"imports": {
18+
"three": "../build/three.webgpu.js",
19+
"three/tsl": "../build/three.webgpu.js",
20+
"three/addons/": "./jsm/"
21+
}
22+
}
23+
</script>
24+
25+
<script type="module">
26+
27+
import * as THREE from 'three';
28+
import { pass, mrt, output, emissive } from 'three/tsl';
29+
30+
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
31+
32+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
33+
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
34+
35+
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
36+
37+
let camera, scene, renderer;
38+
let postProcessing;
39+
40+
init();
41+
42+
function init() {
43+
44+
const container = document.createElement( 'div' );
45+
document.body.appendChild( container );
46+
47+
//
48+
49+
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
50+
camera.position.set( - 1.8, 0.6, 2.7 );
51+
52+
scene = new THREE.Scene();
53+
54+
new RGBELoader()
55+
.setPath( 'textures/equirectangular/' )
56+
.load( 'moonless_golf_1k.hdr', function ( texture ) {
57+
58+
texture.mapping = THREE.EquirectangularReflectionMapping;
59+
60+
scene.background = texture;
61+
scene.environment = texture;
62+
63+
// model
64+
65+
const loader = new GLTFLoader().setPath( 'models/gltf/DamagedHelmet/glTF/' );
66+
loader.load( 'DamagedHelmet.gltf', function ( gltf ) {
67+
68+
scene.add( gltf.scene );
69+
70+
} );
71+
72+
} );
73+
74+
//
75+
76+
renderer = new THREE.WebGPURenderer();
77+
renderer.setPixelRatio( window.devicePixelRatio );
78+
renderer.setSize( window.innerWidth, window.innerHeight );
79+
renderer.setAnimationLoop( render );
80+
renderer.toneMapping = THREE.ACESFilmicToneMapping;
81+
container.appendChild( renderer.domElement );
82+
83+
//
84+
85+
const scenePass = pass( scene, camera );
86+
scenePass.setMRT( mrt( {
87+
output,
88+
emissive
89+
} ) );
90+
91+
const outputPass = scenePass.getTextureNode();
92+
const emissivePass = scenePass.getTextureNode( 'emissive' );
93+
94+
const bloomPass = emissivePass.bloom( 2.5, .5 );
95+
96+
postProcessing = new THREE.PostProcessing( renderer );
97+
postProcessing.outputColorTransform = false;
98+
postProcessing.outputNode = outputPass.add( bloomPass ).renderOutput();
99+
100+
//
101+
102+
const controls = new OrbitControls( camera, renderer.domElement );
103+
controls.minDistance = 2;
104+
controls.maxDistance = 10;
105+
controls.target.set( 0, 0, - 0.2 );
106+
107+
window.addEventListener( 'resize', onWindowResize );
108+
109+
//
110+
111+
const gui = new GUI();
112+
113+
const bloomFolder = gui.addFolder( 'bloom' );
114+
bloomFolder.add( bloomPass.strength, 'value', 0.0, 5.0 ).name( 'strength' );
115+
bloomFolder.add( bloomPass.radius, 'value', 0.0, 1.0 ).name( 'radius' );
116+
117+
const toneMappingFolder = gui.addFolder( 'tone mapping' );
118+
toneMappingFolder.add( renderer, 'toneMappingExposure', 0.1, 2 ).name( 'exposure' );
119+
120+
}
121+
122+
function onWindowResize() {
123+
124+
camera.aspect = window.innerWidth / window.innerHeight;
125+
camera.updateProjectionMatrix();
126+
127+
renderer.setSize( window.innerWidth, window.innerHeight );
128+
129+
}
130+
131+
//
132+
133+
function render() {
134+
135+
postProcessing.render();
136+
137+
}
138+
139+
</script>
140+
141+
</body>
142+
</html>
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>three.js webgpu - bloom selective</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+
</head>
9+
<body>
10+
11+
<div id="info">
12+
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu - bloom selective
13+
</div>
14+
15+
<script type="importmap">
16+
{
17+
"imports": {
18+
"three": "../build/three.webgpu.js",
19+
"three/tsl": "../build/three.webgpu.js",
20+
"three/addons/": "./jsm/"
21+
}
22+
}
23+
</script>
24+
25+
<script type="module">
26+
27+
import * as THREE from 'three';
28+
import { pass, mrt, output, float, uniform } from 'three/tsl';
29+
30+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
31+
32+
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
33+
34+
// scene
35+
36+
const scene = new THREE.Scene();
37+
38+
const camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 200 );
39+
camera.position.set( 0, 0, 20 );
40+
camera.lookAt( 0, 0, 0 );
41+
42+
const geometry = new THREE.IcosahedronGeometry( 1, 15 );
43+
44+
for ( let i = 0; i < 50; i ++ ) {
45+
46+
const color = new THREE.Color();
47+
color.setHSL( Math.random(), 0.7, Math.random() * 0.2 + 0.05 );
48+
49+
const bloomIntensity = Math.random() > 0.5 ? 1 : 0;
50+
51+
const material = new THREE.MeshBasicNodeMaterial( { color: color } );
52+
material.mrtNode = mrt( {
53+
bloomIntensity: uniform( bloomIntensity )
54+
} );
55+
56+
const sphere = new THREE.Mesh( geometry, material );
57+
sphere.position.x = Math.random() * 10 - 5;
58+
sphere.position.y = Math.random() * 10 - 5;
59+
sphere.position.z = Math.random() * 10 - 5;
60+
sphere.position.normalize().multiplyScalar( Math.random() * 4.0 + 2.0 );
61+
sphere.scale.setScalar( Math.random() * Math.random() + 0.5 );
62+
scene.add( sphere );
63+
64+
}
65+
66+
// renderer
67+
68+
const renderer = new THREE.WebGPURenderer();
69+
renderer.setPixelRatio( window.devicePixelRatio );
70+
renderer.setSize( window.innerWidth, window.innerHeight );
71+
renderer.setAnimationLoop( animate );
72+
renderer.toneMapping = THREE.NeutralToneMapping;
73+
document.body.appendChild( renderer.domElement );
74+
75+
// post processing
76+
77+
const scenePass = pass( scene, camera );
78+
scenePass.setMRT( mrt( {
79+
output,
80+
bloomIntensity: float( 0 ) // default bloom intensity
81+
} ) );
82+
83+
const outputPass = scenePass.getTextureNode();
84+
const bloomIntensityPass = scenePass.getTextureNode( 'bloomIntensity' );
85+
86+
const bloomPass = outputPass.mul( bloomIntensityPass ).bloom();
87+
88+
const postProcessing = new THREE.PostProcessing( renderer );
89+
postProcessing.outputColorTransform = false;
90+
postProcessing.outputNode = outputPass.add( bloomPass ).renderOutput();
91+
92+
// controls
93+
94+
const controls = new OrbitControls( camera, renderer.domElement );
95+
controls.maxPolarAngle = Math.PI * 0.5;
96+
controls.minDistance = 1;
97+
controls.maxDistance = 100;
98+
99+
// raycaster
100+
101+
const raycaster = new THREE.Raycaster();
102+
const mouse = new THREE.Vector2();
103+
104+
window.addEventListener( 'pointerdown', ( event ) => {
105+
106+
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
107+
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
108+
109+
raycaster.setFromCamera( mouse, camera );
110+
111+
const intersects = raycaster.intersectObjects( scene.children, false );
112+
113+
if ( intersects.length > 0 ) {
114+
115+
const material = intersects[ 0 ].object.material;
116+
117+
const bloomIntensity = material.mrtNode.getNode( 'bloomIntensity' );
118+
bloomIntensity.value = bloomIntensity.value === 0 ? 1 : 0;
119+
120+
}
121+
122+
} );
123+
124+
// gui
125+
126+
const gui = new GUI();
127+
128+
const bloomFolder = gui.addFolder( 'bloom' );
129+
bloomFolder.add( bloomPass.threshold, 'value', 0.0, 1.0 ).name( 'threshold' );
130+
bloomFolder.add( bloomPass.strength, 'value', 0.0, 3 ).name( 'strength' );
131+
bloomFolder.add( bloomPass.radius, 'value', 0.0, 1.0 ).name( 'radius' );
132+
133+
const toneMappingFolder = gui.addFolder( 'tone mapping' );
134+
toneMappingFolder.add( renderer, 'toneMappingExposure', 0.1, 3 ).name( 'exposure' );
135+
136+
// events
137+
138+
window.onresize = function () {
139+
140+
const width = window.innerWidth;
141+
const height = window.innerHeight;
142+
143+
camera.aspect = width / height;
144+
camera.updateProjectionMatrix();
145+
146+
renderer.setSize( width, height );
147+
148+
};
149+
150+
// animate
151+
152+
function animate() {
153+
154+
postProcessing.render();
155+
156+
}
157+
158+
</script>
159+
160+
</body>
161+
162+
</html>

test/e2e/puppeteer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ const exceptionList = [
119119
'webgpu_sandbox',
120120
'webgpu_sprites',
121121
'webgpu_video_panorama',
122+
'webgpu_postprocessing_bloom_emissive',
122123

123124
// Awaiting for WebGPU Backend support in Puppeteer
124125
'webgpu_storage_buffer',

0 commit comments

Comments
 (0)