Skip to content

Commit 9b0d093

Browse files
committed
Examples: Improved WebGPU PMREM examples.
1 parent bd3fe1f commit 9b0d093

File tree

7 files changed

+243
-32
lines changed

7 files changed

+243
-32
lines changed

examples/files.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@
399399
"webgpu_pmrem_cubemap",
400400
"webgpu_pmrem_equirectangular",
401401
"webgpu_pmrem_scene",
402+
"webgpu_pmrem_test",
402403
"webgpu_portal",
403404
"webgpu_postprocessing_3dlut",
404405
"webgpu_postprocessing_afterimage",
39.6 KB
Loading
58.7 KB
Loading
14.4 KB
Loading

examples/webgpu_pmrem_cubemap.html

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,12 @@
2222
<script type="module">
2323

2424
import * as THREE from 'three/webgpu';
25-
import { normalWorldGeometry, uniform, normalView, positionViewDirection, cameraViewMatrix, pmremTexture } from 'three/tsl';
25+
import { normalWorldGeometry, uniform, pmremTexture } from 'three/tsl';
2626

2727
import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js';
2828

2929
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
3030

31-
import { Inspector } from 'three/addons/inspector/Inspector.js';
32-
3331
let camera, scene, renderer;
3432

3533
init();
@@ -40,7 +38,7 @@
4038
document.body.appendChild( container );
4139

4240
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
43-
camera.position.set( - 1.8, 0.6, 2.7 );
41+
camera.position.set( 0, 0, 8 );
4442

4543
scene = new THREE.Scene();
4644

@@ -51,14 +49,10 @@
5149
renderer.setSize( window.innerWidth, window.innerHeight );
5250
renderer.setAnimationLoop( render );
5351
renderer.toneMapping = THREE.ACESFilmicToneMapping;
54-
renderer.inspector = new Inspector();
55-
56-
//await renderer.init();
5752

5853
container.appendChild( renderer.domElement );
5954

6055
const controls = new OrbitControls( camera, renderer.domElement );
61-
controls.addEventListener( 'change', render ); // use if there is no animation loop
6256
controls.minDistance = 2;
6357
controls.maxDistance = 10;
6458
controls.update();
@@ -67,19 +61,28 @@
6761
.setPath( './textures/cube/pisaHDR/' )
6862
.load( [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ], function ( map ) {
6963

70-
const reflectVec = positionViewDirection.negate().reflect( normalView ).transformDirection( cameraViewMatrix );
64+
scene.backgroundNode = pmremTexture( map, normalWorldGeometry, uniform( 0.5 ) );
65+
66+
const geometry = new THREE.SphereGeometry( 0.4, 64, 64 );
67+
68+
for ( let i = 0; i < 6; i ++ ) {
7169

72-
const pmremRoughness = uniform( .5 );
73-
const pmremNode = pmremTexture( map, reflectVec, pmremRoughness );
70+
for ( let j = 0; j < 5; j ++ ) {
7471

75-
scene.backgroundNode = pmremTexture( map, normalWorldGeometry, pmremRoughness );
72+
const material = new THREE.MeshPhysicalNodeMaterial( {
73+
roughness: i / 5,
74+
metalness: j / 4,
75+
envMap: map
76+
} );
7677

77-
scene.add( new THREE.Mesh( new THREE.SphereGeometry( .5, 64, 64 ), new THREE.MeshBasicNodeMaterial( { colorNode: pmremNode } ) ) );
78+
const mesh = new THREE.Mesh( geometry, material );
79+
mesh.position.x = i - 2.5;
80+
mesh.position.y = j - 2;
81+
scene.add( mesh );
7882

79-
// gui
83+
}
8084

81-
const gui = renderer.inspector.createParameters( 'Settings' );
82-
gui.add( pmremRoughness, 'value', 0, 1, 0.001 ).name( 'roughness' ).onChange( () => render() );
85+
}
8386

8487
} );
8588

examples/webgpu_pmrem_equirectangular.html

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,12 @@
2222
<script type="module">
2323

2424
import * as THREE from 'three/webgpu';
25-
import { normalWorldGeometry, uniform, normalView, positionViewDirection, cameraViewMatrix, pmremTexture } from 'three/tsl';
25+
import { normalWorldGeometry, uniform, pmremTexture } from 'three/tsl';
2626

2727
import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
2828

2929
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
3030

31-
import { Inspector } from 'three/addons/inspector/Inspector.js';
32-
3331
let camera, scene, renderer;
3432

3533
init();
@@ -40,7 +38,7 @@
4038
document.body.appendChild( container );
4139

4240
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
43-
camera.position.set( - 1.8, 0.6, 2.7 );
41+
camera.position.set( 0, 0, 8 );
4442

4543
scene = new THREE.Scene();
4644

@@ -51,14 +49,10 @@
5149
renderer.setSize( window.innerWidth, window.innerHeight );
5250
renderer.setAnimationLoop( render );
5351
renderer.toneMapping = THREE.ACESFilmicToneMapping;
54-
renderer.inspector = new Inspector();
55-
56-
//await renderer.init();
5752

5853
container.appendChild( renderer.domElement );
5954

6055
const controls = new OrbitControls( camera, renderer.domElement );
61-
controls.addEventListener( 'change', render ); // use if there is no animation loop
6256
controls.minDistance = 2;
6357
controls.maxDistance = 10;
6458
controls.update();
@@ -69,19 +63,28 @@
6963

7064
map.mapping = THREE.EquirectangularReflectionMapping;
7165

72-
const reflectVec = positionViewDirection.negate().reflect( normalView ).transformDirection( cameraViewMatrix );
66+
scene.backgroundNode = pmremTexture( map, normalWorldGeometry, uniform( 0.5 ) );
67+
68+
const geometry = new THREE.SphereGeometry( 0.4, 64, 64 );
69+
70+
for ( let i = 0; i < 6; i ++ ) {
7371

74-
const pmremRoughness = uniform( .5 );
75-
const pmremNode = pmremTexture( map, reflectVec, pmremRoughness );
72+
for ( let j = 0; j < 5; j ++ ) {
7673

77-
scene.backgroundNode = pmremTexture( map, normalWorldGeometry, pmremRoughness );
74+
const material = new THREE.MeshPhysicalNodeMaterial( {
75+
roughness: i / 5,
76+
metalness: j / 4,
77+
envMap: map
78+
} );
7879

79-
scene.add( new THREE.Mesh( new THREE.SphereGeometry( .5, 64, 64 ), new THREE.MeshBasicNodeMaterial( { colorNode: pmremNode } ) ) );
80+
const mesh = new THREE.Mesh( geometry, material );
81+
mesh.position.x = i - 2.5;
82+
mesh.position.y = j - 2;
83+
scene.add( mesh );
8084

81-
// gui
85+
}
8286

83-
const gui = renderer.inspector.createParameters( 'Settings' );
84-
gui.add( pmremRoughness, 'value', 0, 1, 0.001 ).name( 'roughness' ).onChange( () => render() );
87+
}
8588

8689
} );
8790

examples/webgpu_pmrem_test.html

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>three.js webgpu - PMREM directional light test</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="container">
12+
<div id="info">
13+
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu -
14+
PMREM test by <a href="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/elalish" target="_blank" rel="noopener">Emmett Lalish</a>
15+
<br>
16+
<br>1: white metal. 2: white dielectric. 3: black dielectric.
17+
<br>PMREM on: HDR with a single bright pixel. PMREM off: DirectionalLight.
18+
<br>The difference between these renders indicates the error in the PMREM approximations.
19+
</div>
20+
</div>
21+
22+
<script type="importmap">
23+
{
24+
"imports": {
25+
"three": "../build/three.webgpu.js",
26+
"three/tsl": "../build/three.webgpu.js",
27+
"three/addons/": "./jsm/"
28+
}
29+
}
30+
</script>
31+
32+
<script type="module">
33+
34+
import * as THREE from 'three';
35+
36+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
37+
import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
38+
39+
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
40+
41+
import WebGPU from 'three/addons/capabilities/WebGPU.js';
42+
43+
let scene, camera, controls, renderer;
44+
45+
async function init() {
46+
47+
const width = window.innerWidth;
48+
const height = window.innerHeight;
49+
const aspect = width / height;
50+
51+
// renderer
52+
53+
renderer = new THREE.WebGPURenderer( { antialias: true } );
54+
renderer.setPixelRatio( window.devicePixelRatio );
55+
renderer.setSize( width, height );
56+
57+
// tonemapping
58+
renderer.toneMapping = THREE.ACESFilmicToneMapping;
59+
renderer.toneMappingExposure = 1;
60+
61+
document.body.appendChild( renderer.domElement );
62+
63+
window.addEventListener( 'resize', onWindowResize );
64+
65+
// scene
66+
67+
scene = new THREE.Scene();
68+
69+
// camera
70+
71+
camera = new THREE.PerspectiveCamera( 40, aspect, 1, 30 );
72+
updateCamera();
73+
camera.position.set( 0, 0, 16 );
74+
75+
// controls
76+
77+
controls = new OrbitControls( camera, renderer.domElement );
78+
controls.addEventListener( 'change', render ); // use if there is no animation loop
79+
controls.minDistance = 4;
80+
controls.maxDistance = 20;
81+
82+
// light
83+
84+
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0 ); // set intensity to 0 to start
85+
const x = 597;
86+
const y = 213;
87+
const theta = ( x + 0.5 ) * Math.PI / 512;
88+
const phi = ( y + 0.5 ) * Math.PI / 512;
89+
90+
directionalLight.position.setFromSphericalCoords( 100, - phi, Math.PI / 2 - theta );
91+
92+
scene.add( directionalLight );
93+
// scene.add( new THREE.DirectionalLightHelper( directionalLight ) );
94+
95+
// The spot1Lux HDR environment map is expressed in nits (lux / sr). The directional light has units of lux,
96+
// so to match a 1 lux light, we set a single pixel with a value equal to 1 divided by the solid
97+
// angle of the pixel in steradians. This image is 1024 x 512,
98+
// so the value is 1 / ( sin( phi ) * ( pi / 512 ) ^ 2 ) = 27,490 nits.
99+
100+
const gui = new GUI();
101+
gui.add( { enabled: true }, 'enabled' )
102+
.name( 'PMREM' )
103+
.onChange( value => {
104+
105+
directionalLight.intensity = value ? 0 : 1;
106+
107+
scene.traverse( function ( child ) {
108+
109+
if ( child.isMesh ) {
110+
111+
child.material.envMapIntensity = 1 - directionalLight.intensity;
112+
113+
}
114+
115+
} );
116+
117+
render();
118+
119+
} );
120+
121+
}
122+
123+
async function createObjects() {
124+
125+
let radianceMap = null;
126+
new HDRLoader()
127+
// .setDataType( THREE.FloatType )
128+
.setPath( 'textures/equirectangular/' )
129+
.load( 'spot1Lux.hdr', function ( texture ) {
130+
131+
radianceMap = pmremGenerator.fromEquirectangular( texture ).texture;
132+
pmremGenerator.dispose();
133+
134+
scene.background = radianceMap;
135+
136+
const geometry = new THREE.SphereGeometry( 0.4, 32, 32 );
137+
138+
for ( let x = 0; x <= 10; x ++ ) {
139+
140+
for ( let y = 0; y <= 2; y ++ ) {
141+
142+
const material = new THREE.MeshPhysicalNodeMaterial( {
143+
roughness: x / 10,
144+
metalness: y < 1 ? 1 : 0,
145+
color: y < 2 ? 0xffffff : 0x000000,
146+
envMap: radianceMap,
147+
envMapIntensity: 1
148+
} );
149+
150+
const mesh = new THREE.Mesh( geometry, material );
151+
mesh.position.x = x - 5;
152+
mesh.position.y = 1 - y;
153+
scene.add( mesh );
154+
155+
}
156+
157+
}
158+
159+
render();
160+
161+
} );
162+
163+
const pmremGenerator = new THREE.PMREMGenerator( renderer );
164+
pmremGenerator.compileEquirectangularShader();
165+
166+
}
167+
168+
function onWindowResize() {
169+
170+
const width = window.innerWidth;
171+
const height = window.innerHeight;
172+
173+
camera.aspect = width / height;
174+
updateCamera();
175+
176+
renderer.setSize( width, height );
177+
178+
render();
179+
180+
}
181+
182+
function updateCamera() {
183+
184+
const horizontalFoV = 40;
185+
const verticalFoV = 2 * Math.atan( Math.tan( horizontalFoV / 2 * Math.PI / 180 ) / camera.aspect ) * 180 / Math.PI;
186+
camera.fov = verticalFoV;
187+
camera.updateProjectionMatrix();
188+
189+
}
190+
191+
function render() {
192+
193+
renderer.render( scene, camera );
194+
195+
}
196+
197+
Promise.resolve()
198+
.then( init )
199+
.then( createObjects )
200+
.then( render );
201+
202+
</script>
203+
</body>
204+
</html>

0 commit comments

Comments
 (0)