Browse Source

LightProbeGenerator: adapt for WebGPU compatibility (#29335)

* adapt for webGPU

* improve backend detection

* rework

* rework

* exclude from tests

---------

Co-authored-by: aardgoose <angus.sawyer@email.com>
aardgoose 1 year ago
parent
commit
bac758d7f8

+ 1 - 0
examples/files.json

@@ -331,6 +331,7 @@
 		"webgpu_instance_uniform",
 		"webgpu_instancing_morph",
 		"webgpu_lightprobe",
+		"webgpu_lightprobe_cubecamera",
 		"webgpu_lights_custom",
 		"webgpu_lights_ies_spotlight",
 		"webgpu_lights_phong",

+ 24 - 10
examples/jsm/lights/LightProbeGenerator.js

@@ -7,7 +7,8 @@ import {
 	SRGBColorSpace,
 	NoColorSpace,
 	HalfFloatType,
-	DataUtils
+	DataUtils,
+	WebGLCoordinateSystem
 } from 'three';
 
 class LightProbeGenerator {
@@ -126,7 +127,9 @@ class LightProbeGenerator {
 
 	}
 
-	static fromCubeRenderTarget( renderer, cubeRenderTarget ) {
+	static async fromCubeRenderTarget( renderer, cubeRenderTarget ) {
+
+		const flip = renderer.coordinateSystem === WebGLCoordinateSystem ? -1 : 1;
 
 		// The renderTarget must be set to RGBA in order to make readRenderTargetPixels works
 		let totalWeight = 0;
@@ -143,12 +146,11 @@ class LightProbeGenerator {
 		const shCoefficients = sh.coefficients;
 
 		const dataType = cubeRenderTarget.texture.type;
+		const imageWidth = cubeRenderTarget.width; // assumed to be square
 
-		for ( let faceIndex = 0; faceIndex < 6; faceIndex ++ ) {
-
-			const imageWidth = cubeRenderTarget.width; // assumed to be square
+		let data;
 
-			let data;
+		if ( renderer.isWebGLRenderer ) {
 
 			if ( dataType === HalfFloatType ) {
 
@@ -162,7 +164,19 @@ class LightProbeGenerator {
 
 			}
 
-			renderer.readRenderTargetPixels( cubeRenderTarget, 0, 0, imageWidth, imageWidth, data, faceIndex );
+		}
+
+		for ( let faceIndex = 0; faceIndex < 6; faceIndex ++ ) {
+
+			if ( renderer.isWebGLRenderer ) {
+
+				await renderer.readRenderTargetPixelsAsync( cubeRenderTarget, 0, 0, imageWidth, imageWidth, data, faceIndex );
+
+			} else {
+
+				data = await renderer.readRenderTargetPixelsAsync( cubeRenderTarget, 0, 0, imageWidth, imageWidth, 0, faceIndex );
+
+			}
 
 			const pixelSize = 2 / imageWidth;
 
@@ -194,15 +208,15 @@ class LightProbeGenerator {
 
 				const pixelIndex = i / 4;
 
-				const col = - 1 + ( pixelIndex % imageWidth + 0.5 ) * pixelSize;
+				const col = ( 1 - ( pixelIndex % imageWidth + 0.5 ) * pixelSize ) * flip;
 
 				const row = 1 - ( Math.floor( pixelIndex / imageWidth ) + 0.5 ) * pixelSize;
 
 				switch ( faceIndex ) {
 
-					case 0: coord.set( 1, row, - col ); break;
+					case 0: coord.set( - 1 * flip, row, col * flip ); break;
 
-					case 1: coord.set( - 1, row, col ); break;
+					case 1: coord.set( 1 * flip, row, - col * flip ); break;
 
 					case 2: coord.set( col, 1, - row ); break;
 

BIN
examples/screenshots/webgpu_lightprobe_cubecamera.jpg


+ 4 - 2
examples/webgl_lightprobe_cubecamera.html

@@ -78,13 +78,15 @@
 
 				const urls = genCubeUrls( 'textures/cube/pisa/', '.png' );
 
-				new THREE.CubeTextureLoader().load( urls, function ( cubeTexture ) {
+				new THREE.CubeTextureLoader().load( urls, async function ( cubeTexture ) {
 
 					scene.background = cubeTexture;
 
 					cubeCamera.update( renderer, scene );
 
-					lightProbe.copy( LightProbeGenerator.fromCubeRenderTarget( renderer, cubeRenderTarget ) );
+					const probe = await LightProbeGenerator.fromCubeRenderTarget( renderer, cubeRenderTarget );
+
+					lightProbe.copy( probe );
 
 					scene.add( new LightProbeHelper( lightProbe, 5 ) );
 

+ 125 - 0
examples/webgpu_lightprobe_cubecamera.html

@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgpu - light probe from cubeCamera</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 - light probe from cubeCamera
+		</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 { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+			import { LightProbeHelper } from 'three/addons/helpers/LightProbeHelperGPU.js';
+			import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js';
+
+			let renderer, scene, camera, cubeCamera;
+
+			let lightProbe;
+
+			init();
+
+			function init() {
+
+				// renderer
+				renderer = new THREE.WebGPURenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				// scene
+				scene = new THREE.Scene();
+
+				// camera
+				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.set( 0, 0, 30 );
+
+				const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 256 );
+
+				cubeCamera = new THREE.CubeCamera( 1, 1000, cubeRenderTarget );
+
+				// controls
+				const controls = new OrbitControls( camera, renderer.domElement );
+				controls.addEventListener( 'change', render );
+				controls.minDistance = 10;
+				controls.maxDistance = 50;
+				controls.enablePan = false;
+
+				// probe
+				lightProbe = new THREE.LightProbe();
+				scene.add( lightProbe );
+
+				// envmap
+				const genCubeUrls = function ( prefix, postfix ) {
+
+					return [
+						prefix + 'px' + postfix, prefix + 'nx' + postfix,
+						prefix + 'py' + postfix, prefix + 'ny' + postfix,
+						prefix + 'pz' + postfix, prefix + 'nz' + postfix
+					];
+
+				};
+
+				const urls = genCubeUrls( 'textures/cube/pisa/', '.png' );
+
+				new THREE.CubeTextureLoader().load( urls, async function ( cubeTexture ) {
+
+					scene.background = cubeTexture;
+
+					await renderer.init();
+
+					cubeCamera.update( renderer, scene );
+
+					const probe = await LightProbeGenerator.fromCubeRenderTarget( renderer, cubeRenderTarget );
+
+					lightProbe.copy( probe );
+
+					scene.add( new LightProbeHelper( lightProbe, 5 ) );
+
+					render();
+
+				} );
+
+				// listener
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			function onWindowResize() {
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				render();
+
+			}
+
+			function render() {
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 1 - 0
test/e2e/puppeteer.js

@@ -128,6 +128,7 @@ const exceptionList = [
 	// WebGPURenderer: Unknown problem
 	'webgpu_camera_logarithmicdepthbuffer',
 	'webgpu_clipping',
+	'webgpu_lightprobe_cubecamera',
 	'webgpu_loader_materialx',
 	'webgpu_materials_video',
 	'webgpu_materialx_noise',

粤ICP备19079148号