浏览代码

Merge pull request #17790 from nipmarsh/lightprobe_cubecamera

Compute lightprobe from a CubeRenderTarget
Mr.doob 5 年之前
父节点
当前提交
757bcd0051

+ 1 - 0
examples/files.js

@@ -61,6 +61,7 @@ var files = {
 		"webgl_layers",
 		"webgl_layers",
 		"webgl_lensflares",
 		"webgl_lensflares",
 		"webgl_lightprobe",
 		"webgl_lightprobe",
+		"webgl_lightprobe_cubecamera",
 		"webgl_lights_hemisphere",
 		"webgl_lights_hemisphere",
 		"webgl_lights_physical",
 		"webgl_lights_physical",
 		"webgl_lights_pointlights",
 		"webgl_lights_pointlights",

+ 123 - 1
examples/js/lights/LightProbeGenerator.js

@@ -50,7 +50,7 @@ THREE.LightProbeGenerator = {
 				color.setRGB( data[ i ] / 255, data[ i + 1 ] / 255, data[ i + 2 ] / 255 );
 				color.setRGB( data[ i ] / 255, data[ i + 1 ] / 255, data[ i + 2 ] / 255 );
 
 
 				// convert to linear color space
 				// convert to linear color space
-				color.copySRGBToLinear( color );
+				convertColorToLinear( color, cubeTexture.encoding );
 
 
 				// pixel coordinate on unit cube
 				// pixel coordinate on unit cube
 
 
@@ -116,6 +116,128 @@ THREE.LightProbeGenerator = {
 
 
 		return new THREE.LightProbe( sh );
 		return new THREE.LightProbe( sh );
 
 
+	},
+
+	fromRenderTargetCube: function ( renderer, renderTargetCube ) {
+		
+		// The renderTarget must be set to RGBA in order to make readRenderTargetPixels works
+		var norm, lengthSq, weight, totalWeight = 0;
+
+		var coord = new THREE.Vector3();
+
+		var dir = new THREE.Vector3();
+
+		var color = new THREE.Color();
+
+		var shBasis = [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
+
+		var sh = new THREE.SphericalHarmonics3();
+		var shCoefficients = sh.coefficients;
+
+		for ( var faceIndex = 0; faceIndex < 6; faceIndex ++ ) {
+
+			var imageWidth = renderTargetCube.width; // assumed to be square
+			var data = new Uint8Array( imageWidth * imageWidth * 4 );
+			renderer.readRenderTargetPixels( renderTargetCube, 0, 0, imageWidth, imageWidth, data, faceIndex );
+
+			var pixelSize = 2 / imageWidth;
+
+			for ( var i = 0, il = data.length; i < il; i += 4 ) { // RGBA assumed
+
+				// pixel color
+				color.setRGB( data[ i ] / 255, data[ i + 1 ] / 255, data[ i + 2 ] / 255 );
+
+				// convert to linear color space
+				convertColorToLinear( color, renderTargetCube.texture.encoding );
+
+				// pixel coordinate on unit cube
+
+				var pixelIndex = i / 4;
+
+				var col = - 1 + ( pixelIndex % imageWidth + 0.5 ) * pixelSize;
+
+				var row = 1 - ( Math.floor( pixelIndex / imageWidth ) + 0.5 ) * pixelSize;
+
+				switch ( faceIndex ) {
+
+					case 0: coord.set( 1, row, - col ); break;
+
+					case 1: coord.set( - 1, row, col ); break;
+
+					case 2: coord.set( col, 1, - row ); break;
+
+					case 3: coord.set( col, - 1, row ); break;
+
+					case 4: coord.set( col, row, 1 ); break;
+
+					case 5: coord.set( - col, row, - 1 ); break;
+
+				}
+
+				// weight assigned to this pixel
+
+				lengthSq = coord.lengthSq();
+
+				weight = 4 / ( Math.sqrt( lengthSq ) * lengthSq );
+
+				totalWeight += weight;
+
+				// direction vector to this pixel
+				dir.copy( coord ).normalize();
+
+				// evaluate SH basis functions in direction dir
+				THREE.SphericalHarmonics3.getBasisAt( dir, shBasis );
+
+				// accummuulate
+				for ( var j = 0; j < 9; j ++ ) {
+
+					shCoefficients[ j ].x += shBasis[ j ] * color.r * weight;
+					shCoefficients[ j ].y += shBasis[ j ] * color.g * weight;
+					shCoefficients[ j ].z += shBasis[ j ] * color.b * weight;
+
+				}
+
+			}
+
+		}
+
+		// normalize
+		norm = ( 4 * Math.PI ) / totalWeight;
+
+		for ( var j = 0; j < 9; j ++ ) {
+
+			shCoefficients[ j ].x *= norm;
+			shCoefficients[ j ].y *= norm;
+			shCoefficients[ j ].z *= norm;
+
+		}
+
+		return new THREE.LightProbe( sh );
+
+	}
+
+};
+
+var convertColorToLinear = function ( color, encoding ) {
+
+	switch ( encoding ) {
+
+		case THREE.sRGBEncoding:
+
+			color.convertSRGBToLinear();
+			break;
+
+		case THREE.LinearEncoding:
+
+			break;
+
+		default:
+
+			console.warn( 'WARNING: LightProbeGenerator convertColorToLinear() encountered an unsupported encoding.' );
+			break;
+
 	}
 	}
 
 
+	return color;
+
 };
 };

+ 6 - 3
examples/jsm/lights/LightProbeGenerator.d.ts

@@ -1,10 +1,13 @@
 import {
 import {
-	CubeTexture,
-	LightProbe
+  CubeTexture,
+  LightProbe,
+  WebGLRenderer,
+  WebGLRenderTargetCube,
 } from '../../../src/Three';
 } from '../../../src/Three';
 
 
 export namespace LightProbeGenerator {
 export namespace LightProbeGenerator {
 
 
-	export function fromCubeTexture( cubeTexture: CubeTexture ): LightProbe;
+  export function fromCubeTexture(cubeTexture: CubeTexture): LightProbe;
+  export function fromRenderTargetCube(renderer: WebGLRenderer, renderTargetCube: WebGLRenderTargetCube): LightProbe;
 
 
 }
 }

+ 126 - 2
examples/jsm/lights/LightProbeGenerator.js

@@ -5,8 +5,10 @@
 import {
 import {
 	Color,
 	Color,
 	LightProbe,
 	LightProbe,
+	LinearEncoding,
 	SphericalHarmonics3,
 	SphericalHarmonics3,
-	Vector3
+	Vector3,
+	sRGBEncoding
 } from "../../../build/three.module.js";
 } from "../../../build/three.module.js";
 
 
 var LightProbeGenerator = {
 var LightProbeGenerator = {
@@ -57,7 +59,7 @@ var LightProbeGenerator = {
 				color.setRGB( data[ i ] / 255, data[ i + 1 ] / 255, data[ i + 2 ] / 255 );
 				color.setRGB( data[ i ] / 255, data[ i + 1 ] / 255, data[ i + 2 ] / 255 );
 
 
 				// convert to linear color space
 				// convert to linear color space
-				color.copySRGBToLinear( color );
+				convertColorToLinear( color, cubeTexture.encoding );
 
 
 				// pixel coordinate on unit cube
 				// pixel coordinate on unit cube
 
 
@@ -123,8 +125,130 @@ var LightProbeGenerator = {
 
 
 		return new LightProbe( sh );
 		return new LightProbe( sh );
 
 
+	},
+
+	fromRenderTargetCube: function ( renderer, renderTargetCube ) {
+		
+		// The renderTarget must be set to RGBA in order to make readRenderTargetPixels works
+		var norm, lengthSq, weight, totalWeight = 0;
+
+		var coord = new Vector3();
+
+		var dir = new Vector3();
+
+		var color = new Color();
+
+		var shBasis = [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
+
+		var sh = new SphericalHarmonics3();
+		var shCoefficients = sh.coefficients;
+
+		for ( var faceIndex = 0; faceIndex < 6; faceIndex ++ ) {
+
+			var imageWidth = renderTargetCube.width; // assumed to be square
+			var data = new Uint8Array( imageWidth * imageWidth * 4 );
+			renderer.readRenderTargetPixels( renderTargetCube, 0, 0, imageWidth, imageWidth, data, faceIndex );
+
+			var pixelSize = 2 / imageWidth;
+
+			for ( var i = 0, il = data.length; i < il; i += 4 ) { // RGBA assumed
+
+				// pixel color
+				color.setRGB( data[ i ] / 255, data[ i + 1 ] / 255, data[ i + 2 ] / 255 );
+
+				// convert to linear color space
+				convertColorToLinear( color, renderTargetCube.texture.encoding );
+
+				// pixel coordinate on unit cube
+
+				var pixelIndex = i / 4;
+
+				var col = - 1 + ( pixelIndex % imageWidth + 0.5 ) * pixelSize;
+
+				var row = 1 - ( Math.floor( pixelIndex / imageWidth ) + 0.5 ) * pixelSize;
+
+				switch ( faceIndex ) {
+
+					case 0: coord.set( 1, row, - col ); break;
+
+					case 1: coord.set( - 1, row, col ); break;
+
+					case 2: coord.set( col, 1, - row ); break;
+
+					case 3: coord.set( col, - 1, row ); break;
+
+					case 4: coord.set( col, row, 1 ); break;
+
+					case 5: coord.set( - col, row, - 1 ); break;
+
+				}
+
+				// weight assigned to this pixel
+
+				lengthSq = coord.lengthSq();
+
+				weight = 4 / ( Math.sqrt( lengthSq ) * lengthSq );
+
+				totalWeight += weight;
+
+				// direction vector to this pixel
+				dir.copy( coord ).normalize();
+
+				// evaluate SH basis functions in direction dir
+				SphericalHarmonics3.getBasisAt( dir, shBasis );
+
+				// accummuulate
+				for ( var j = 0; j < 9; j ++ ) {
+
+					shCoefficients[ j ].x += shBasis[ j ] * color.r * weight;
+					shCoefficients[ j ].y += shBasis[ j ] * color.g * weight;
+					shCoefficients[ j ].z += shBasis[ j ] * color.b * weight;
+
+				}
+
+			}
+
+		}
+
+		// normalize
+		norm = ( 4 * Math.PI ) / totalWeight;
+
+		for ( var j = 0; j < 9; j ++ ) {
+
+			shCoefficients[ j ].x *= norm;
+			shCoefficients[ j ].y *= norm;
+			shCoefficients[ j ].z *= norm;
+
+		}
+
+		return new LightProbe( sh );
+
 	}
 	}
 
 
 };
 };
 
 
+var convertColorToLinear = function ( color, encoding ) {
+
+	switch ( encoding ) {
+
+		case sRGBEncoding:
+
+			color.convertSRGBToLinear();
+			break;
+
+		case LinearEncoding:
+		
+			break;
+
+		default:
+
+			console.warn( 'WARNING: LightProbeGenerator convertColorToLinear() encountered an unsupported encoding.' );
+			break;
+
+	}
+
+	return color;
+
+};
+
 export { LightProbeGenerator };
 export { LightProbeGenerator };

+ 119 - 0
examples/webgl_lightprobe_cubecamera.html

@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - 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="http://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - light probe from cubeCamera
+		</div>
+
+		<script type="module">
+
+			import * as THREE from '../build/three.module.js';
+
+			import { GUI } from './jsm/libs/dat.gui.module.js';
+
+			import { OrbitControls } from './jsm/controls/OrbitControls.js';
+			import { LightProbeGenerator } from './jsm/lights/LightProbeGenerator.js';
+
+			var renderer, scene, camera, cubeCamera;
+
+			var lightProbe;
+
+			init();
+
+			function init() {
+
+				// renderer
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				// gamma
+				renderer.gammaOutput = true;
+				renderer.gammaFactor = 2.2; // approximate sRGB
+
+				// scene
+				scene = new THREE.Scene();
+
+				// camera
+				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.set( 0, 0, 30 );
+
+				cubeCamera = new THREE.CubeCamera( 1, 1000, 256, { format: THREE.RGBAFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } );
+
+				//Since gamma is applied during rendering, the cubeCamera renderTarget texture encoding must be sRGBEncoding
+				cubeCamera.renderTarget.texture.encoding = THREE.sRGBEncoding;
+
+				// controls
+				var 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
+				var genCubeUrls = function ( prefix, postfix ) {
+
+					return [
+						prefix + 'px' + postfix, prefix + 'nx' + postfix,
+						prefix + 'py' + postfix, prefix + 'ny' + postfix,
+						prefix + 'pz' + postfix, prefix + 'nz' + postfix
+					];
+
+				};
+
+				var urls = genCubeUrls( 'textures/cube/pisa/', '.png' );
+
+				new THREE.CubeTextureLoader().load( urls, function ( cubeTexture ) {
+
+					cubeTexture.encoding = THREE.sRGBEncoding;
+
+					scene.background = cubeTexture;
+
+					cubeCamera.update( renderer, scene );
+
+					lightProbe.copy( LightProbeGenerator.fromRenderTargetCube( renderer, cubeCamera.renderTarget ) );
+
+					scene.add( new THREE.LightProbeHelper(lightProbe, 5) );
+
+					render();
+
+				} );
+
+				// listener
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			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>