Преглед изворни кода

TSL: Add function for BPCEM. (#29773)

* TSL: Add function for computing BPCEMs.

* E2E: Add example to exception list.

* added on resize

---------
Michael Herzog пре 1 година
родитељ
комит
732bc2a5f9

+ 1 - 0
examples/files.json

@@ -354,6 +354,7 @@
 		"webgpu_materials_arrays",
 		"webgpu_materials_arrays",
 		"webgpu_materials_basic",
 		"webgpu_materials_basic",
 		"webgpu_materials_displacementmap",
 		"webgpu_materials_displacementmap",
+		"webgpu_materials_envmaps_bpcem",
 		"webgpu_materials_envmaps",
 		"webgpu_materials_envmaps",
 		"webgpu_materials_lightmap",
 		"webgpu_materials_lightmap",
 		"webgpu_materials_matcap",
 		"webgpu_materials_matcap",

BIN
examples/screenshots/webgpu_materials_envmaps_bpcem.jpg


+ 226 - 0
examples/webgpu_materials_envmaps_bpcem.html

@@ -0,0 +1,226 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgpu - materials - bpcem</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - webgpu - box projected cube environment mapping (BPCEM)<br/>
+		</div>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.webgpu.js",
+					"three/tsl": "../build/three.webgpu.js",
+					"three/addons/": "./jsm/"
+				}
+			}
+		</script>
+
+
+		<script type="module">
+
+			import * as THREE from 'three';
+			import { bumpMap, float, getParallaxCorrectNormal, pmremTexture, reflectVector, texture, uniform, vec3 } from 'three/tsl';
+
+			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
+			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+			import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js';
+			import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTexturesLib.js';
+
+			let camera, scene, renderer;
+
+			let controls, cubeCamera;
+
+			let groundPlane, wallMat;
+
+			init();
+
+			function init() {
+
+				THREE.RectAreaLightNode.setLTC( RectAreaLightTexturesLib.init() );
+
+				// scene
+			
+				scene = new THREE.Scene();
+
+				// camera
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
+				camera.position.set( 0, 200, - 200 );
+
+				// cube camera for environment map
+
+				const renderTarget = new THREE.WebGLCubeRenderTarget( 512 );
+				renderTarget.texture.type = THREE.HalfFloatType;
+				renderTarget.texture.minFilter = THREE.LinearMipmapLinearFilter;
+				renderTarget.texture.magFilter = THREE.LinearFilter;
+				renderTarget.texture.generateMipmaps = true;
+				renderTarget.texture.mapping = THREE.CubeReflectionMapping;
+
+				cubeCamera = new THREE.CubeCamera( 1, 1000, renderTarget );
+				cubeCamera.position.set( 0, - 100, 0 );
+
+				// ground floor ( with box projected environment mapping )
+			
+				const loader = new THREE.TextureLoader();
+				const rMap = loader.load( 'textures/lava/lavatile.jpg' );
+				rMap.wrapS = THREE.RepeatWrapping;
+				rMap.wrapT = THREE.RepeatWrapping;
+				rMap.repeat.set( 2, 1 );
+
+				const roughnessUniform = uniform( 0.25 );
+
+				const defaultMat = new THREE.MeshStandardNodeMaterial();
+				defaultMat.envNode = pmremTexture( renderTarget.texture );
+				defaultMat.roughnessNode = texture( rMap ).mul( roughnessUniform );
+				defaultMat.metalnessNode = float( 1 );
+
+				const boxProjectedMat = new THREE.MeshStandardNodeMaterial();
+				boxProjectedMat.envNode = pmremTexture( renderTarget.texture, getParallaxCorrectNormal( reflectVector, vec3( 200, 100, 100 ), vec3( 0, - 50, 0 ) ) );
+				boxProjectedMat.roughnessNode = texture( rMap ).mul( roughnessUniform );
+				boxProjectedMat.metalnessNode = float( 1 );
+
+				groundPlane = new THREE.Mesh( new THREE.PlaneGeometry( 200, 100, 100 ), boxProjectedMat );
+				groundPlane.rotateX( - Math.PI / 2 );
+				groundPlane.position.set( 0, - 49, 0 );
+				scene.add( groundPlane );
+
+				// walls
+			
+				const diffuseTex = loader.load( 'textures/brick_diffuse.jpg' );
+				diffuseTex.colorSpace = THREE.SRGBColorSpace;
+				const bumpTex = loader.load( 'textures/brick_bump.jpg' );
+
+				wallMat = new THREE.MeshStandardNodeMaterial();
+
+				wallMat.colorNode = texture( diffuseTex );
+				wallMat.normalNode = bumpMap( texture( bumpTex ), float( 5 ) );
+
+				const planeGeo = new THREE.PlaneGeometry( 100, 100 );
+
+				const planeBack1 = new THREE.Mesh( planeGeo, wallMat );
+				planeBack1.position.z = - 50;
+				planeBack1.position.x = - 50;
+				scene.add( planeBack1 );
+
+				const planeBack2 = new THREE.Mesh( planeGeo, wallMat );
+				planeBack2.position.z = - 50;
+				planeBack2.position.x = 50;
+				scene.add( planeBack2 );
+
+				const planeFront1 = new THREE.Mesh( planeGeo, wallMat );
+				planeFront1.position.z = 50;
+				planeFront1.position.x = - 50;
+				planeFront1.rotateY( Math.PI );
+				scene.add( planeFront1 );
+
+				const planeFront2 = new THREE.Mesh( planeGeo, wallMat );
+				planeFront2.position.z = 50;
+				planeFront2.position.x = 50;
+				planeFront2.rotateY( Math.PI );
+				scene.add( planeFront2 );
+
+				const planeRight = new THREE.Mesh( planeGeo, wallMat );
+				planeRight.position.x = 100;
+				planeRight.rotateY( - Math.PI / 2 );
+				scene.add( planeRight );
+
+				const planeLeft = new THREE.Mesh( planeGeo, wallMat );
+				planeLeft.position.x = - 100;
+				planeLeft.rotateY( Math.PI / 2 );
+				scene.add( planeLeft );
+
+				// area lights
+
+				const width = 50;
+				const height = 50;
+				const intensity = 5;
+			
+				const blueRectLight = new THREE.RectAreaLight( 0x9aaeff, intensity, width, height );
+				blueRectLight.position.set( - 99, 5, 0 );
+				blueRectLight.lookAt( 0, 5, 0 );
+				scene.add( blueRectLight );
+
+				const blueRectLightHelper = new RectAreaLightHelper( blueRectLight, 0xffffff );
+				blueRectLight.add( blueRectLightHelper );
+
+				const redRectLight = new THREE.RectAreaLight( 0xf3aaaa, intensity, width, height );
+				redRectLight.position.set( 99, 5, 0 );
+				redRectLight.lookAt( 0, 5, 0 );
+				scene.add( redRectLight );
+
+				const redRectLightHelper = new RectAreaLightHelper( redRectLight, 0xffffff );
+				redRectLight.add( redRectLightHelper );
+
+				// renderer
+
+				renderer = new THREE.WebGPURenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setAnimationLoop( animate );
+				document.body.appendChild( renderer.domElement );
+
+				window.addEventListener( 'resize', onWindowResize );
+
+				// controls
+
+				controls = new OrbitControls( camera, renderer.domElement );
+				controls.target.set( 0, - 10, 0 );
+				controls.maxDistance = 400;
+				controls.minDistance = 10;
+				controls.update();
+
+				// gui
+
+				const gui = new GUI();
+				const params = {
+					'box projected': true
+				};
+				gui.add( params, 'box projected' ).onChange( ( value ) => {
+
+					groundPlane.material = ( value ) ? boxProjectedMat : defaultMat;
+
+				} );
+				gui.add( roughnessUniform, 'value', 0, 1 ).name( 'roughness' );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function updateCubeMap() {
+
+				groundPlane.visible = false;
+
+				cubeCamera.position.copy( groundPlane.position );
+
+				cubeCamera.update( renderer, scene );
+
+				groundPlane.visible = true;
+
+			}
+
+			function animate() {
+
+				updateCubeMap();
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 1 - 0
src/nodes/TSL.js

@@ -156,5 +156,6 @@ export { default as V_GGX_SmithCorrelated } from './functions/BSDF/V_GGX_SmithCo
 export * from './lighting/LightUtils.js';
 export * from './lighting/LightUtils.js';
 
 
 export { default as getGeometryRoughness } from './functions/material/getGeometryRoughness.js';
 export { default as getGeometryRoughness } from './functions/material/getGeometryRoughness.js';
+export { default as getParallaxCorrectNormal } from './functions/material/getParallaxCorrectNormal.js';
 export { default as getRoughness } from './functions/material/getRoughness.js';
 export { default as getRoughness } from './functions/material/getRoughness.js';
 export { default as getShIrradianceAt } from './functions/material/getShIrradianceAt.js';
 export { default as getShIrradianceAt } from './functions/material/getShIrradianceAt.js';

+ 22 - 0
src/nodes/functions/material/getParallaxCorrectNormal.js

@@ -0,0 +1,22 @@
+import { positionWorld } from '../../accessors/Position';
+import { float, Fn, min, normalize, sub, vec3 } from '../../tsl/TSLBase.js';
+
+// https://devlog-martinsh.blogspot.com/2011/09/box-projected-cube-environment-mapping.html
+
+const getParallaxCorrectNormal = /*@__PURE__*/ Fn( ( [ normal, cubeSize, cubePos ] ) => {
+
+	const nDir = normalize( normal ).toVar( 'nDir' );
+	const rbmax = sub( float( 0.5 ).mul( cubeSize.sub( cubePos ) ), positionWorld ).div( nDir ).toVar( 'rbmax' );
+	const rbmin = sub( float( - 0.5 ).mul( cubeSize.sub( cubePos ) ), positionWorld ).div( nDir ).toVar( 'rbmin' );
+	const rbminmax = vec3().toVar( 'rbminmax' );
+	rbminmax.x = nDir.x.greaterThan( float( 0 ) ).select( rbmax.x, rbmin.x );
+	rbminmax.y = nDir.y.greaterThan( float( 0 ) ).select( rbmax.y, rbmin.y );
+	rbminmax.z = nDir.z.greaterThan( float( 0 ) ).select( rbmax.z, rbmin.z );
+
+	const correction = min( min( rbminmax.x, rbminmax.y ), rbminmax.z ).toVar( 'correction' );
+	const boxIntersection = positionWorld.add( nDir.mul( correction ) ).toVar( 'boxIntersection' );
+	return boxIntersection.sub( cubePos );
+
+} );
+
+export default getParallaxCorrectNormal;

+ 1 - 0
test/e2e/puppeteer.js

@@ -158,6 +158,7 @@ const exceptionList = [
 	'webgpu_tsl_vfx_linkedparticles',
 	'webgpu_tsl_vfx_linkedparticles',
 	'webgpu_tsl_vfx_tornado',
 	'webgpu_tsl_vfx_tornado',
 	'webgpu_textures_anisotropy',
 	'webgpu_textures_anisotropy',
+	'webgpu_materials_envmaps_bpcem',
 
 
 	// WebGPU idleTime and parseTime too low
 	// WebGPU idleTime and parseTime too low
 	'webgpu_compute_particles',
 	'webgpu_compute_particles',

粤ICP备19079148号