Skip to content

Commit 732bc2a

Browse files
authored
TSL: Add function for BPCEM. (#29773)
* TSL: Add function for computing BPCEMs. * E2E: Add example to exception list. * added on resize ---------
1 parent 83c3f72 commit 732bc2a

File tree

6 files changed

+251
-0
lines changed

6 files changed

+251
-0
lines changed

examples/files.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@
354354
"webgpu_materials_arrays",
355355
"webgpu_materials_basic",
356356
"webgpu_materials_displacementmap",
357+
"webgpu_materials_envmaps_bpcem",
357358
"webgpu_materials_envmaps",
358359
"webgpu_materials_lightmap",
359360
"webgpu_materials_matcap",
25.2 KB
Loading
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>three.js webgpu - materials - bpcem</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 - box projected cube environment mapping (BPCEM)<br/>
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+
26+
<script type="module">
27+
28+
import * as THREE from 'three';
29+
import { bumpMap, float, getParallaxCorrectNormal, pmremTexture, reflectVector, texture, uniform, vec3 } from 'three/tsl';
30+
31+
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
32+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
33+
import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js';
34+
import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTexturesLib.js';
35+
36+
let camera, scene, renderer;
37+
38+
let controls, cubeCamera;
39+
40+
let groundPlane, wallMat;
41+
42+
init();
43+
44+
function init() {
45+
46+
THREE.RectAreaLightNode.setLTC( RectAreaLightTexturesLib.init() );
47+
48+
// scene
49+
50+
scene = new THREE.Scene();
51+
52+
// camera
53+
54+
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
55+
camera.position.set( 0, 200, - 200 );
56+
57+
// cube camera for environment map
58+
59+
const renderTarget = new THREE.WebGLCubeRenderTarget( 512 );
60+
renderTarget.texture.type = THREE.HalfFloatType;
61+
renderTarget.texture.minFilter = THREE.LinearMipmapLinearFilter;
62+
renderTarget.texture.magFilter = THREE.LinearFilter;
63+
renderTarget.texture.generateMipmaps = true;
64+
renderTarget.texture.mapping = THREE.CubeReflectionMapping;
65+
66+
cubeCamera = new THREE.CubeCamera( 1, 1000, renderTarget );
67+
cubeCamera.position.set( 0, - 100, 0 );
68+
69+
// ground floor ( with box projected environment mapping )
70+
71+
const loader = new THREE.TextureLoader();
72+
const rMap = loader.load( 'textures/lava/lavatile.jpg' );
73+
rMap.wrapS = THREE.RepeatWrapping;
74+
rMap.wrapT = THREE.RepeatWrapping;
75+
rMap.repeat.set( 2, 1 );
76+
77+
const roughnessUniform = uniform( 0.25 );
78+
79+
const defaultMat = new THREE.MeshStandardNodeMaterial();
80+
defaultMat.envNode = pmremTexture( renderTarget.texture );
81+
defaultMat.roughnessNode = texture( rMap ).mul( roughnessUniform );
82+
defaultMat.metalnessNode = float( 1 );
83+
84+
const boxProjectedMat = new THREE.MeshStandardNodeMaterial();
85+
boxProjectedMat.envNode = pmremTexture( renderTarget.texture, getParallaxCorrectNormal( reflectVector, vec3( 200, 100, 100 ), vec3( 0, - 50, 0 ) ) );
86+
boxProjectedMat.roughnessNode = texture( rMap ).mul( roughnessUniform );
87+
boxProjectedMat.metalnessNode = float( 1 );
88+
89+
groundPlane = new THREE.Mesh( new THREE.PlaneGeometry( 200, 100, 100 ), boxProjectedMat );
90+
groundPlane.rotateX( - Math.PI / 2 );
91+
groundPlane.position.set( 0, - 49, 0 );
92+
scene.add( groundPlane );
93+
94+
// walls
95+
96+
const diffuseTex = loader.load( 'textures/brick_diffuse.jpg' );
97+
diffuseTex.colorSpace = THREE.SRGBColorSpace;
98+
const bumpTex = loader.load( 'textures/brick_bump.jpg' );
99+
100+
wallMat = new THREE.MeshStandardNodeMaterial();
101+
102+
wallMat.colorNode = texture( diffuseTex );
103+
wallMat.normalNode = bumpMap( texture( bumpTex ), float( 5 ) );
104+
105+
const planeGeo = new THREE.PlaneGeometry( 100, 100 );
106+
107+
const planeBack1 = new THREE.Mesh( planeGeo, wallMat );
108+
planeBack1.position.z = - 50;
109+
planeBack1.position.x = - 50;
110+
scene.add( planeBack1 );
111+
112+
const planeBack2 = new THREE.Mesh( planeGeo, wallMat );
113+
planeBack2.position.z = - 50;
114+
planeBack2.position.x = 50;
115+
scene.add( planeBack2 );
116+
117+
const planeFront1 = new THREE.Mesh( planeGeo, wallMat );
118+
planeFront1.position.z = 50;
119+
planeFront1.position.x = - 50;
120+
planeFront1.rotateY( Math.PI );
121+
scene.add( planeFront1 );
122+
123+
const planeFront2 = new THREE.Mesh( planeGeo, wallMat );
124+
planeFront2.position.z = 50;
125+
planeFront2.position.x = 50;
126+
planeFront2.rotateY( Math.PI );
127+
scene.add( planeFront2 );
128+
129+
const planeRight = new THREE.Mesh( planeGeo, wallMat );
130+
planeRight.position.x = 100;
131+
planeRight.rotateY( - Math.PI / 2 );
132+
scene.add( planeRight );
133+
134+
const planeLeft = new THREE.Mesh( planeGeo, wallMat );
135+
planeLeft.position.x = - 100;
136+
planeLeft.rotateY( Math.PI / 2 );
137+
scene.add( planeLeft );
138+
139+
// area lights
140+
141+
const width = 50;
142+
const height = 50;
143+
const intensity = 5;
144+
145+
const blueRectLight = new THREE.RectAreaLight( 0x9aaeff, intensity, width, height );
146+
blueRectLight.position.set( - 99, 5, 0 );
147+
blueRectLight.lookAt( 0, 5, 0 );
148+
scene.add( blueRectLight );
149+
150+
const blueRectLightHelper = new RectAreaLightHelper( blueRectLight, 0xffffff );
151+
blueRectLight.add( blueRectLightHelper );
152+
153+
const redRectLight = new THREE.RectAreaLight( 0xf3aaaa, intensity, width, height );
154+
redRectLight.position.set( 99, 5, 0 );
155+
redRectLight.lookAt( 0, 5, 0 );
156+
scene.add( redRectLight );
157+
158+
const redRectLightHelper = new RectAreaLightHelper( redRectLight, 0xffffff );
159+
redRectLight.add( redRectLightHelper );
160+
161+
// renderer
162+
163+
renderer = new THREE.WebGPURenderer( { antialias: true } );
164+
renderer.setPixelRatio( window.devicePixelRatio );
165+
renderer.setSize( window.innerWidth, window.innerHeight );
166+
renderer.setAnimationLoop( animate );
167+
document.body.appendChild( renderer.domElement );
168+
169+
window.addEventListener( 'resize', onWindowResize );
170+
171+
// controls
172+
173+
controls = new OrbitControls( camera, renderer.domElement );
174+
controls.target.set( 0, - 10, 0 );
175+
controls.maxDistance = 400;
176+
controls.minDistance = 10;
177+
controls.update();
178+
179+
// gui
180+
181+
const gui = new GUI();
182+
const params = {
183+
'box projected': true
184+
};
185+
gui.add( params, 'box projected' ).onChange( ( value ) => {
186+
187+
groundPlane.material = ( value ) ? boxProjectedMat : defaultMat;
188+
189+
} );
190+
gui.add( roughnessUniform, 'value', 0, 1 ).name( 'roughness' );
191+
192+
}
193+
194+
function onWindowResize() {
195+
196+
camera.aspect = window.innerWidth / window.innerHeight;
197+
camera.updateProjectionMatrix();
198+
199+
renderer.setSize( window.innerWidth, window.innerHeight );
200+
201+
}
202+
203+
function updateCubeMap() {
204+
205+
groundPlane.visible = false;
206+
207+
cubeCamera.position.copy( groundPlane.position );
208+
209+
cubeCamera.update( renderer, scene );
210+
211+
groundPlane.visible = true;
212+
213+
}
214+
215+
function animate() {
216+
217+
updateCubeMap();
218+
219+
renderer.render( scene, camera );
220+
221+
}
222+
223+
</script>
224+
225+
</body>
226+
</html>

src/nodes/TSL.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,6 @@ export { default as V_GGX_SmithCorrelated } from './functions/BSDF/V_GGX_SmithCo
156156
export * from './lighting/LightUtils.js';
157157

158158
export { default as getGeometryRoughness } from './functions/material/getGeometryRoughness.js';
159+
export { default as getParallaxCorrectNormal } from './functions/material/getParallaxCorrectNormal.js';
159160
export { default as getRoughness } from './functions/material/getRoughness.js';
160161
export { default as getShIrradianceAt } from './functions/material/getShIrradianceAt.js';
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { positionWorld } from '../../accessors/Position';
2+
import { float, Fn, min, normalize, sub, vec3 } from '../../tsl/TSLBase.js';
3+
4+
// https://devlog-martinsh.blogspot.com/2011/09/box-projected-cube-environment-mapping.html
5+
6+
const getParallaxCorrectNormal = /*@__PURE__*/ Fn( ( [ normal, cubeSize, cubePos ] ) => {
7+
8+
const nDir = normalize( normal ).toVar( 'nDir' );
9+
const rbmax = sub( float( 0.5 ).mul( cubeSize.sub( cubePos ) ), positionWorld ).div( nDir ).toVar( 'rbmax' );
10+
const rbmin = sub( float( - 0.5 ).mul( cubeSize.sub( cubePos ) ), positionWorld ).div( nDir ).toVar( 'rbmin' );
11+
const rbminmax = vec3().toVar( 'rbminmax' );
12+
rbminmax.x = nDir.x.greaterThan( float( 0 ) ).select( rbmax.x, rbmin.x );
13+
rbminmax.y = nDir.y.greaterThan( float( 0 ) ).select( rbmax.y, rbmin.y );
14+
rbminmax.z = nDir.z.greaterThan( float( 0 ) ).select( rbmax.z, rbmin.z );
15+
16+
const correction = min( min( rbminmax.x, rbminmax.y ), rbminmax.z ).toVar( 'correction' );
17+
const boxIntersection = positionWorld.add( nDir.mul( correction ) ).toVar( 'boxIntersection' );
18+
return boxIntersection.sub( cubePos );
19+
20+
} );
21+
22+
export default getParallaxCorrectNormal;

test/e2e/puppeteer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ const exceptionList = [
158158
'webgpu_tsl_vfx_linkedparticles',
159159
'webgpu_tsl_vfx_tornado',
160160
'webgpu_textures_anisotropy',
161+
'webgpu_materials_envmaps_bpcem',
161162

162163
// WebGPU idleTime and parseTime too low
163164
'webgpu_compute_particles',

0 commit comments

Comments
 (0)