소스 검색

Merge branch 'dev' of https://github.com/mrdoob/three.js into dev

linbingquan 6 년 전
부모
커밋
510468f6f9
47개의 변경된 파일478개의 추가작업 그리고 129개의 파일을 삭제
  1. 0 0
      build/three.js
  2. 28 28
      build/three.min.js
  3. 0 0
      build/three.module.js
  4. 1 0
      examples/files.js
  5. 50 8
      examples/js/controls/FirstPersonControls.js
  6. 11 0
      examples/js/controls/OrbitControls.js
  7. 10 5
      examples/js/exporters/ColladaExporter.js
  8. 4 4
      examples/js/exporters/GLTFExporter.js
  9. 1 1
      examples/js/loaders/ColladaLoader.js
  10. 5 4
      examples/js/loaders/GLTFLoader.js
  11. 2 2
      examples/js/postprocessing/AdaptiveToneMappingPass.js
  12. 1 1
      examples/js/postprocessing/BloomPass.js
  13. 1 1
      examples/js/postprocessing/BokehPass.js
  14. 1 1
      examples/js/postprocessing/ClearPass.js
  15. 1 1
      examples/js/postprocessing/CubeTexturePass.js
  16. 1 1
      examples/js/postprocessing/DotScreenPass.js
  17. 16 4
      examples/js/postprocessing/EffectComposer.js
  18. 2 2
      examples/js/postprocessing/FilmPass.js
  19. 1 1
      examples/js/postprocessing/GlitchPass.js
  20. 1 1
      examples/js/postprocessing/HalftonePass.js
  21. 2 2
      examples/js/postprocessing/MaskPass.js
  22. 1 1
      examples/js/postprocessing/OutlinePass.js
  23. 1 1
      examples/js/postprocessing/RenderPass.js
  24. 1 1
      examples/js/postprocessing/SAOPass.js
  25. 1 1
      examples/js/postprocessing/SMAAPass.js
  26. 1 1
      examples/js/postprocessing/SSAOPass.js
  27. 1 1
      examples/js/postprocessing/ShaderPass.js
  28. 3 3
      examples/js/postprocessing/TAARenderPass.js
  29. 1 1
      examples/js/postprocessing/TexturePass.js
  30. 1 1
      examples/js/postprocessing/UnrealBloomPass.js
  31. 179 0
      examples/webgl_furnace_test.html
  32. 3 0
      examples/webgl_nearestneighbour.html
  33. 8 4
      examples/webgl_postprocessing_fxaa.html
  34. 8 12
      examples/webgl_shadowmap.html
  35. 8 12
      examples/webgl_shadowmap_performance.html
  36. 4 8
      src/audio/Audio.js
  37. 1 0
      src/core/BufferGeometry.d.ts
  38. 2 0
      src/core/Object3D.d.ts
  39. 5 0
      src/core/Object3D.js
  40. 9 2
      src/loaders/ObjectLoader.js
  41. 2 0
      src/materials/MeshPhysicalMaterial.js
  42. 1 0
      src/math/Color.d.ts
  43. 9 0
      src/math/Vector2.d.ts
  44. 17 1
      src/math/Vector3.d.ts
  45. 40 7
      src/renderers/shaders/ShaderChunk/bsdfs.glsl.js
  46. 1 1
      src/renderers/shaders/ShaderChunk/lights_fragment_end.glsl.js
  47. 31 4
      src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
build/three.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 28 - 28
build/three.min.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
build/three.module.js


+ 1 - 0
examples/files.js

@@ -19,6 +19,7 @@ var files = {
 		"webgl_effects_peppersghost",
 		"webgl_effects_stereo",
 		"webgl_framebuffer_texture",
+		"webgl_furnace_test",
 		"webgl_geometries",
 		"webgl_geometries_parametric",
 		"webgl_geometry_colors",

+ 50 - 8
examples/js/controls/FirstPersonControls.js

@@ -34,9 +34,6 @@ THREE.FirstPersonControls = function ( object, domElement ) {
 	this.mouseX = 0;
 	this.mouseY = 0;
 
-	this.lat = 0;
-	this.lon = 0;
-
 	this.moveForward = false;
 	this.moveBackward = false;
 	this.moveLeft = false;
@@ -47,6 +44,17 @@ THREE.FirstPersonControls = function ( object, domElement ) {
 	this.viewHalfX = 0;
 	this.viewHalfY = 0;
 
+	// private variables
+
+	var lat = 0;
+	var lon = 0;
+
+	var lookDirection = new THREE.Vector3();
+	var spherical = new THREE.Spherical();
+	var target = new THREE.Vector3();
+
+	//
+
 	if ( this.domElement !== document ) {
 
 		this.domElement.setAttribute( 'tabindex', - 1 );
@@ -181,6 +189,26 @@ THREE.FirstPersonControls = function ( object, domElement ) {
 
 	};
 
+	this.lookAt = function ( x, y, z ) {
+
+		if ( x.isVector3 ) {
+
+			target.copy( x );
+
+		} else {
+
+			target.set( x, y, z );
+
+		}
+
+		this.object.lookAt( target );
+
+		setOrientation( this );
+
+		return this;
+
+	};
+
 	this.update = function () {
 
 		var targetPosition = new THREE.Vector3();
@@ -229,13 +257,13 @@ THREE.FirstPersonControls = function ( object, domElement ) {
 
 			}
 
-			this.lon -= this.mouseX * actualLookSpeed;
-			if ( this.lookVertical ) this.lat -= this.mouseY * actualLookSpeed * verticalLookRatio;
+			lon -= this.mouseX * actualLookSpeed;
+			if ( this.lookVertical ) lat -= this.mouseY * actualLookSpeed * verticalLookRatio;
 
-			this.lat = Math.max( - 85, Math.min( 85, this.lat ) );
+			lat = Math.max( - 85, Math.min( 85, lat ) );
 
-			var phi = THREE.Math.degToRad( 90 - this.lat );
-			var theta = THREE.Math.degToRad( this.lon );
+			var phi = THREE.Math.degToRad( 90 - lat );
+			var theta = THREE.Math.degToRad( lon );
 
 			if ( this.constrainVertical ) {
 
@@ -295,6 +323,20 @@ THREE.FirstPersonControls = function ( object, domElement ) {
 
 	}
 
+	function setOrientation( controls ) {
+
+		var quaternion = controls.object.quaternion;
+
+		lookDirection.set( 0, 0, - 1 ).applyQuaternion( quaternion );
+		spherical.setFromVector3( lookDirection );
+
+		lat = 90 - THREE.Math.radToDeg( spherical.phi );
+		lon = THREE.Math.radToDeg( spherical.theta );
+
+	}
+
 	this.handleResize();
 
+	setOrientation( this );
+
 };

+ 11 - 0
examples/js/controls/OrbitControls.js

@@ -539,6 +539,10 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 		//console.log( 'handleKeyDown' );
 
+		// prevent the browser from scrolling on cursor up/down
+
+		event.preventDefault();
+
 		switch ( event.keyCode ) {
 
 			case scope.keys.UP:
@@ -673,8 +677,15 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 		if ( scope.enabled === false ) return;
 
+		// Prevent the browser from scrolling.
+
 		event.preventDefault();
 
+		// Manually set the focus since calling preventDefault above
+		// prevents the browser from setting it automatically.
+
+		scope.domElement.focus ? scope.domElement.focus() : window.focus();
+
 		switch ( event.button ) {
 
 			case scope.mouseButtons.LEFT:

+ 10 - 5
examples/js/exporters/ColladaExporter.js

@@ -413,7 +413,7 @@ THREE.ColladaExporter.prototype = {
 
 					(
 						type !== 'constant' ?
-						'<diffuse>' +
+							'<diffuse>' +
 
 						(
 							m.map ?
@@ -421,12 +421,12 @@ THREE.ColladaExporter.prototype = {
 								`<color sid="diffuse">${ diffuse.r } ${ diffuse.g } ${ diffuse.b } 1</color>`
 						) +
 						'</diffuse>'
-						: ''
+							: ''
 					) +
 
 					(
 						type === 'phong' ?
-						`<specular><color sid="specular">${ specular.r } ${ specular.g } ${ specular.b } 1</color></specular>` +
+							`<specular><color sid="specular">${ specular.r } ${ specular.g } ${ specular.b } 1</color></specular>` +
 
 						'<shininess>' +
 
@@ -437,7 +437,7 @@ THREE.ColladaExporter.prototype = {
 						) +
 
 						'</shininess>'
-						: ''
+							: ''
 					) +
 
 					`<reflective><color>${ diffuse.r } ${ diffuse.g } ${ diffuse.b } 1</color></reflective>` +
@@ -525,10 +525,15 @@ THREE.ColladaExporter.prototype = {
 				// the materials.
 				var mat = o.material || new THREE.MeshBasicMaterial();
 				var materials = Array.isArray( mat ) ? mat : [ mat ];
+
 				if ( geometry.groups.length > materials.length ) {
+
 					matidsArray = new Array( geometry.groups.length );
+
 				} else {
-					matidsArray = new Array( materials.length )
+
+					matidsArray = new Array( materials.length );
+
 				}
 				matids = matidsArray.fill()
 					.map( ( v, i ) => processMaterial( materials[ i % materials.length ] ) );

+ 4 - 4
examples/js/exporters/GLTFExporter.js

@@ -365,7 +365,7 @@ THREE.GLTFExporter.prototype = {
 		 */
 		function applyTextureTransform( mapDef, texture ) {
 
-			var didTransform = false
+			var didTransform = false;
 			var transformDef = {};
 
 			if ( texture.offset.x !== 0 || texture.offset.y !== 0 ) {
@@ -981,7 +981,7 @@ THREE.GLTFExporter.prototype = {
 			// occlusionTexture
 			if ( material.aoMap ) {
 
-				var occlusionMapDef = { 
+				var occlusionMapDef = {
 					index: processTexture( material.aoMap ),
 					texCoord: 1
 				};
@@ -1611,7 +1611,7 @@ THREE.GLTFExporter.prototype = {
 				lightDef.type = 'spot';
 				if ( light.distance > 0 ) lightDef.range = light.distance;
 				lightDef.spot = {};
-				lightDef.spot.innerConeAngle = ( light.penumbra - 1.0 ) * light.angle * -1.0;
+				lightDef.spot.innerConeAngle = ( light.penumbra - 1.0 ) * light.angle * - 1.0;
 				lightDef.spot.outerConeAngle = light.angle;
 
 			}
@@ -1627,7 +1627,7 @@ THREE.GLTFExporter.prototype = {
 					&& ( light.target.parent !== light
 					 || light.target.position.x !== 0
 					 || light.target.position.y !== 0
-					 || light.target.position.z !== -1 ) ) {
+					 || light.target.position.z !== - 1 ) ) {
 
 				console.warn( 'THREE.GLTFExporter: Light direction may be lost. For best results, '
 					+ 'make light.target a child of the light with position 0,0,-1.' );

+ 1 - 1
examples/js/loaders/ColladaLoader.js

@@ -2825,7 +2825,7 @@ THREE.ColladaLoader.prototype = {
 						break;
 
 					case 'mass':
-						data.mass = parseFloats( child.textContent )[0];
+						data.mass = parseFloats( child.textContent )[ 0 ];
 						break;
 
 				}

+ 5 - 4
examples/js/loaders/GLTFLoader.js

@@ -338,7 +338,7 @@ THREE.GLTFLoader = ( function () {
 
 			case 'directional':
 				lightNode = new THREE.DirectionalLight( color );
-				lightNode.target.position.set( 0, 0, -1 );
+				lightNode.target.position.set( 0, 0, - 1 );
 				lightNode.add( lightNode.target );
 				break;
 
@@ -356,7 +356,7 @@ THREE.GLTFLoader = ( function () {
 				lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0;
 				lightNode.angle = lightDef.spot.outerConeAngle;
 				lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle;
-				lightNode.target.position.set( 0, 0, -1 );
+				lightNode.target.position.set( 0, 0, - 1 );
 				lightNode.add( lightNode.target );
 				break;
 
@@ -968,6 +968,7 @@ THREE.GLTFLoader = ( function () {
 					uniforms.refractionRatio.value = material.refractionRatio;
 
 					uniforms.maxMipLevel.value = renderer.properties.get( material.envMap ).__maxMipLevel;
+
 				}
 
 				uniforms.specular.value.copy( material.specular );
@@ -1852,7 +1853,7 @@ THREE.GLTFLoader = ( function () {
 
 				case 'light':
 					dependency = this.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].loadLight( index );
-					break
+					break;
 
 				default:
 					throw new Error( 'Unknown type: ' + type );
@@ -3133,7 +3134,7 @@ THREE.GLTFLoader = ( function () {
 
 		var nodeDef = json.nodes[ nodeIndex ];
 
-		return ( function() {
+		return ( function () {
 
 			// .isBone isn't in glTF spec. See .markDefs
 			if ( nodeDef.isBone === true ) {

+ 2 - 2
examples/js/postprocessing/AdaptiveToneMappingPass.js

@@ -130,7 +130,7 @@ THREE.AdaptiveToneMappingPass.prototype = Object.assign( Object.create( THREE.Pa
 
 	constructor: THREE.AdaptiveToneMappingPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		if ( this.needsInit ) {
 
@@ -153,7 +153,7 @@ THREE.AdaptiveToneMappingPass.prototype = Object.assign( Object.create( THREE.Pa
 			//Use the new luminance values, the previous luminance and the frame delta to
 			//adapt the luminance over time.
 			this.quad.material = this.materialAdaptiveLum;
-			this.materialAdaptiveLum.uniforms.delta.value = delta;
+			this.materialAdaptiveLum.uniforms.delta.value = deltaTime;
 			this.materialAdaptiveLum.uniforms.lastLum.value = this.previousLuminanceRT.texture;
 			this.materialAdaptiveLum.uniforms.currentLum.value = this.currentLuminanceRT.texture;
 			renderer.render( this.scene, this.camera, this.luminanceRT );

+ 1 - 1
examples/js/postprocessing/BloomPass.js

@@ -80,7 +80,7 @@ THREE.BloomPass.prototype = Object.assign( Object.create( THREE.Pass.prototype )
 
 	constructor: THREE.BloomPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		if ( maskActive ) renderer.context.disable( renderer.context.STENCIL_TEST );
 

+ 1 - 1
examples/js/postprocessing/BokehPass.js

@@ -82,7 +82,7 @@ THREE.BokehPass.prototype = Object.assign( Object.create( THREE.Pass.prototype )
 
 	constructor: THREE.BokehPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		this.quad2.material = this.materialBokeh;
 

+ 1 - 1
examples/js/postprocessing/ClearPass.js

@@ -17,7 +17,7 @@ THREE.ClearPass.prototype = Object.assign( Object.create( THREE.Pass.prototype )
 
 	constructor: THREE.ClearPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		var oldClearColor, oldClearAlpha;
 

+ 1 - 1
examples/js/postprocessing/CubeTexturePass.js

@@ -36,7 +36,7 @@ THREE.CubeTexturePass.prototype = Object.assign( Object.create( THREE.Pass.proto
 
 	constructor: THREE.CubeTexturePass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		var oldAutoClear = renderer.autoClear;
 		renderer.autoClear = false;

+ 1 - 1
examples/js/postprocessing/DotScreenPass.js

@@ -38,7 +38,7 @@ THREE.DotScreenPass.prototype = Object.assign( Object.create( THREE.Pass.prototy
 
 	constructor: THREE.DotScreenPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		this.uniforms[ "tDiffuse" ].value = readBuffer.texture;
 		this.uniforms[ "tSize" ].value.set( readBuffer.width, readBuffer.height );

+ 16 - 4
examples/js/postprocessing/EffectComposer.js

@@ -46,6 +46,8 @@ THREE.EffectComposer = function ( renderer, renderTarget ) {
 
 	this.copyPass = new THREE.ShaderPass( THREE.CopyShader );
 
+	this._previousFrameTime = Date.now();
+
 };
 
 Object.assign( THREE.EffectComposer.prototype, {
@@ -73,7 +75,17 @@ Object.assign( THREE.EffectComposer.prototype, {
 
 	},
 
-	render: function ( delta ) {
+	render: function ( deltaTime ) {
+
+		// deltaTime value is in seconds
+
+		if ( deltaTime === undefined ) {
+
+			deltaTime = ( Date.now() - this._previousFrameTime ) * 0.001;
+
+		}
+
+		this._previousFrameTime = Date.now();
 
 		var maskActive = false;
 
@@ -85,7 +97,7 @@ Object.assign( THREE.EffectComposer.prototype, {
 
 			if ( pass.enabled === false ) continue;
 
-			pass.render( this.renderer, this.writeBuffer, this.readBuffer, delta, maskActive );
+			pass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime, maskActive );
 
 			if ( pass.needsSwap ) {
 
@@ -95,7 +107,7 @@ Object.assign( THREE.EffectComposer.prototype, {
 
 					context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
 
-					this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, delta );
+					this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, deltaTime );
 
 					context.stencilFunc( context.EQUAL, 1, 0xffffffff );
 
@@ -180,7 +192,7 @@ Object.assign( THREE.Pass.prototype, {
 
 	setSize: function ( width, height ) {},
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		console.error( 'THREE.Pass: .render() must be implemented in derived pass.' );
 

+ 2 - 2
examples/js/postprocessing/FilmPass.js

@@ -39,10 +39,10 @@ THREE.FilmPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ),
 
 	constructor: THREE.FilmPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		this.uniforms[ "tDiffuse" ].value = readBuffer.texture;
-		this.uniforms[ "time" ].value += delta;
+		this.uniforms[ "time" ].value += deltaTime;
 
 		this.quad.material = this.material;
 

+ 1 - 1
examples/js/postprocessing/GlitchPass.js

@@ -40,7 +40,7 @@ THREE.GlitchPass.prototype = Object.assign( Object.create( THREE.Pass.prototype
 
 	constructor: THREE.GlitchPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		this.uniforms[ "tDiffuse" ].value = readBuffer.texture;
 		this.uniforms[ 'seed' ].value = Math.random();//default seeding

+ 1 - 1
examples/js/postprocessing/HalftonePass.js

@@ -48,7 +48,7 @@ THREE.HalftonePass = function ( width, height, params ) {
 
 	constructor: THREE.HalftonePass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
  		this.material.uniforms[ "tDiffuse" ].value = readBuffer.texture;
  		this.quad.material = this.material;

+ 2 - 2
examples/js/postprocessing/MaskPass.js

@@ -20,7 +20,7 @@ THREE.MaskPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ),
 
 	constructor: THREE.MaskPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		var context = renderer.context;
 		var state = renderer.state;
@@ -88,7 +88,7 @@ THREE.ClearMaskPass.prototype = Object.create( THREE.Pass.prototype );
 
 Object.assign( THREE.ClearMaskPass.prototype, {
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		renderer.state.buffers.stencil.setTest( false );
 

+ 1 - 1
examples/js/postprocessing/OutlinePass.js

@@ -255,7 +255,7 @@ THREE.OutlinePass.prototype = Object.assign( Object.create( THREE.Pass.prototype
 
 	},
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		if ( this.selectedObjects.length > 0 ) {
 

+ 1 - 1
examples/js/postprocessing/RenderPass.js

@@ -24,7 +24,7 @@ THREE.RenderPass.prototype = Object.assign( Object.create( THREE.Pass.prototype
 
 	constructor: THREE.RenderPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		var oldAutoClear = renderer.autoClear;
 		renderer.autoClear = false;

+ 1 - 1
examples/js/postprocessing/SAOPass.js

@@ -178,7 +178,7 @@ THREE.SAOPass.OUTPUT = {
 THREE.SAOPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ), {
 	constructor: THREE.SAOPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		// Rendering readBuffer first when rendering to screen
 		if ( this.renderToScreen ) {

+ 1 - 1
examples/js/postprocessing/SMAAPass.js

@@ -120,7 +120,7 @@ THREE.SMAAPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ),
 
 	constructor: THREE.SMAAPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		// pass 1
 

+ 1 - 1
examples/js/postprocessing/SSAOPass.js

@@ -173,7 +173,7 @@ THREE.SSAOPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ),
 
 	},
 
-	render: function ( renderer, writeBuffer /*, readBuffer, delta, maskActive */ ) {
+	render: function ( renderer, writeBuffer /*, readBuffer, deltaTime, maskActive */ ) {
 
 		// render beauty and depth
 

+ 1 - 1
examples/js/postprocessing/ShaderPass.js

@@ -42,7 +42,7 @@ THREE.ShaderPass.prototype = Object.assign( Object.create( THREE.Pass.prototype
 
 	constructor: THREE.ShaderPass,
 
-	render: function( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		if ( this.uniforms[ this.textureID ] ) {
 

+ 3 - 3
examples/js/postprocessing/TAARenderPass.js

@@ -33,11 +33,11 @@ THREE.TAARenderPass.prototype = Object.assign( Object.create( THREE.SSAARenderPa
 
 	constructor: THREE.TAARenderPass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime ) {
 
 		if ( ! this.accumulate ) {
 
-			THREE.SSAARenderPass.prototype.render.call( this, renderer, writeBuffer, readBuffer, delta );
+			THREE.SSAARenderPass.prototype.render.call( this, renderer, writeBuffer, readBuffer, deltaTime );
 
 			this.accumulateIndex = - 1;
 			return;
@@ -62,7 +62,7 @@ THREE.TAARenderPass.prototype = Object.assign( Object.create( THREE.SSAARenderPa
 
 		if ( this.accumulate && this.accumulateIndex === - 1 ) {
 
-			THREE.SSAARenderPass.prototype.render.call( this, renderer, this.holdRenderTarget, readBuffer, delta );
+			THREE.SSAARenderPass.prototype.render.call( this, renderer, this.holdRenderTarget, readBuffer, deltaTime );
 
 			this.accumulateIndex = 0;
 

+ 1 - 1
examples/js/postprocessing/TexturePass.js

@@ -41,7 +41,7 @@ THREE.TexturePass.prototype = Object.assign( Object.create( THREE.Pass.prototype
 
 	constructor: THREE.TexturePass,
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		var oldAutoClear = renderer.autoClear;
 		renderer.autoClear = false;

+ 1 - 1
examples/js/postprocessing/UnrealBloomPass.js

@@ -185,7 +185,7 @@ THREE.UnrealBloomPass.prototype = Object.assign( Object.create( THREE.Pass.proto
 
 	},
 
-	render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
+	render: function ( renderer, writeBuffer, readBuffer, deltaTime, maskActive ) {
 
 		this.oldClearColor.copy( renderer.getClearColor() );
 		this.oldClearAlpha = renderer.getClearAlpha();

+ 179 - 0
examples/webgl_furnace_test.html

@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js white furnace test</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				background:#777;
+				padding:0;
+				margin:0;
+				font-weight: bold;
+				overflow:hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px;
+				min-width: 60%;
+				margin: 0 20%;
+				color: #ffffff;
+				padding: 15px;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+				background-color: black;
+				color: white;
+			}
+
+			a {
+				color: #ffffff;
+			}
+		</style>
+
+		<script src="../build/three.js"></script>
+		<script src="js/WebGL.js"></script>
+		<script src="js/loaders/EquirectangularToCubeGenerator.js"></script>
+		<script src="js/pmrem/PMREMGenerator.js"></script>
+		<script src="js/pmrem/PMREMCubeUVPacker.js"></script>
+
+	</head>
+	<body>
+
+		<div id="container">
+			<div id="info">
+				<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> -
+				<a href="https://google.github.io/filament/Filament.md.html#toc4.7.2" target="_blank" rel="noopener">White Furnace</a> energy conservation test.<br />
+				There are 11 fully metal spheres with full white specular color and increasing roughness values rendered here. A metal object, no matter how rough, fully reflects all energy. If uniformly lit, the spheres should be indistinguishable from the environment. If spheres are visible, then some energy has been lost or gained after reflection.<br />
+				<a href="https://jsantell.com/" target="_blank" rel="noopener">by Jordan Santell</a><br/><br/></div>
+		</div>
+
+		<script>
+
+if ( WEBGL.isWebGLAvailable() === false ) {
+
+	document.body.appendChild( WEBGL.getWebGLErrorMessage() );
+
+}
+
+var scene, camera, renderer, envMap, radianceMap, gui;
+var right = 6;
+var config = {
+	preserveEnergy: true,
+};
+
+function init() {
+
+	var width = window.innerWidth;
+	var height = window.innerHeight;
+	var aspect = width / height;
+
+	// renderer
+
+	renderer = new THREE.WebGLRenderer( { antialias: true } );
+	renderer.setSize( width, height );
+	renderer.setPixelRatio( window.devicePixelRatio );
+	document.body.appendChild( renderer.domElement );
+
+	window.addEventListener( 'resize', onResize, false );
+
+	// scene
+
+	scene = new THREE.Scene();
+
+	// camera
+
+	camera = new THREE.OrthographicCamera( - right, right, right / aspect, - right / aspect, 1, 30 );
+	camera.position.set( 0, 0, 9 );
+
+}
+
+function createObjects() {
+
+	var geo = new THREE.SphereBufferGeometry( 0.4, 32, 32 );
+	var count = 10;
+	for ( var x = 0; x <= count; x ++ ) {
+
+		var mesh = new THREE.Mesh( geo, new THREE.MeshPhysicalMaterial( {
+			roughness: x / count,
+			metalness: 1,
+			color: 0xffffff,
+			envMap: radianceMap,
+			envMapIntensity: 1,
+			reflectivity: 1,
+		} ) );
+		mesh.position.x = x - ( Math.floor( count / 2 ) );
+		scene.add( mesh );
+
+	}
+
+}
+
+function createEnvironment() {
+
+	var color = new THREE.Color( 0xcccccc );
+	var sky = new THREE.Mesh( new THREE.SphereBufferGeometry( 1, 32, 32 ), new THREE.MeshBasicMaterial( {
+		color: color,
+		side: THREE.DoubleSide,
+	} ) );
+	sky.scale.setScalar( 100 );
+
+	var envScene = new THREE.Scene();
+	envScene.add( sky );
+	envScene.background = color;
+	var cubeCamera = new THREE.CubeCamera( 1, 100, 256, 256 );
+	cubeCamera.update( renderer, envScene );
+
+	envMap = cubeCamera.renderTarget.texture;
+
+	scene.background = color;
+
+}
+
+function getRadiance() {
+
+	return new Promise( function ( resolve, reject ) {
+
+		var pmremGenerator = new THREE.PMREMGenerator( envMap );
+		pmremGenerator.update( renderer );
+		var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
+		pmremCubeUVPacker.update( renderer );
+		var cubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
+
+		pmremGenerator.dispose();
+		pmremCubeUVPacker.dispose();
+		radianceMap = cubeRenderTarget.texture;
+		resolve();
+
+	} );
+
+}
+
+function onResize() {
+
+	var aspect = window.innerWidth / window.innerHeight;
+	camera.top = right / aspect;
+	camera.bottom = - camera.top;
+	camera.updateProjectionMatrix();
+	renderer.setSize( window.innerWidth, window.innerHeight );
+	render();
+
+}
+
+function render() {
+
+	renderer.render( scene, camera );
+
+}
+
+Promise.resolve()
+	.then( init )
+	.then( createEnvironment )
+	.then( getRadiance )
+	.then( createObjects )
+	.then( render );
+
+		</script>
+	</body>
+</html>

+ 3 - 0
examples/webgl_nearestneighbour.html

@@ -90,6 +90,9 @@
 				controls.movementSpeed = 100;
 				controls.lookSpeed = 0.1;
 
+				controls.lookAt( 500, 500, 500 );
+
+
 				// add a skybox background
 				var cubeTextureLoader = new THREE.CubeTextureLoader();
 

+ 8 - 4
examples/webgl_postprocessing_fxaa.html

@@ -138,8 +138,10 @@
 				fxaaPass = new THREE.ShaderPass( THREE.FXAAShader );
 				fxaaPass.renderToScreen = true;
 
-				fxaaPass.material.uniforms[ "resolution" ].value.x = 1 / window.innerWidth;
-				fxaaPass.material.uniforms[ "resolution" ].value.y = 1 / window.innerHeight;
+				var pixelRatio = renderer.getPixelRatio();
+
+				fxaaPass.material.uniforms[ 'resolution' ].value.x = 1 / ( window.innerWidth * pixelRatio );
+				fxaaPass.material.uniforms[ 'resolution' ].value.y = 1 / ( window.innerHeight * pixelRatio );
 
 				composer1 = new THREE.EffectComposer( renderer );
 				composer1.addPass( renderPass );
@@ -169,8 +171,10 @@
 				composer1.setSize( window.innerWidth, window.innerHeight );
 				composer2.setSize( window.innerWidth, window.innerHeight );
 
-				fxaaPass.material.uniforms[ 'resolution' ].value.x = 1 / window.innerWidth;
-				fxaaPass.material.uniforms[ 'resolution' ].value.y = 1 / window.innerHeight;
+				var pixelRatio = renderer.getPixelRatio();
+
+				fxaaPass.material.uniforms[ 'resolution' ].value.x = 1 / ( window.innerWidth * pixelRatio );
+				fxaaPass.material.uniforms[ 'resolution' ].value.y = 1 / ( window.innerHeight * pixelRatio );
 
 			}
 

+ 8 - 12
examples/webgl_shadowmap.html

@@ -85,29 +85,25 @@
 				container = document.createElement( 'div' );
 				document.body.appendChild( container );
 
-				// SCENE CAMERA
+				// CAMERA
 
 				camera = new THREE.PerspectiveCamera( 23, SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR );
 				camera.position.set( 700, 50, 1900 );
 
+				// SCENE
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0x59472b );
+				scene.fog = new THREE.Fog( 0x59472b, 1000, FAR );
+
 				controls = new THREE.FirstPersonControls( camera );
 
 				controls.lookSpeed = 0.0125;
 				controls.movementSpeed = 500;
 				controls.noFly = false;
 				controls.lookVertical = true;
-				controls.constrainVertical = true;
-				controls.verticalMin = 1.5;
-				controls.verticalMax = 2.0;
 
-				controls.lon = 250;
-				controls.lat = 30;
-
-				// SCENE
-
-				scene = new THREE.Scene();
-				scene.background = new THREE.Color( 0x59472b );
-				scene.fog = new THREE.Fog( 0x59472b, 1000, FAR );
+				controls.lookAt( scene.position );
 
 				// LIGHTS
 

+ 8 - 12
examples/webgl_shadowmap_performance.html

@@ -81,29 +81,25 @@
 				container = document.createElement( 'div' );
 				document.body.appendChild( container );
 
-				// SCENE CAMERA
+				// CAMERA
 
 				camera = new THREE.PerspectiveCamera( 23, SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR );
 				camera.position.set( 700, 50, 1900 );
 
+				// SCENE
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0x59472b );
+				scene.fog = new THREE.Fog( 0x59472b, 1000, FAR );
+
 				controls = new THREE.FirstPersonControls( camera );
 
 				controls.lookSpeed = 0.0125;
 				controls.movementSpeed = 500;
 				controls.noFly = false;
 				controls.lookVertical = true;
-				controls.constrainVertical = true;
-				controls.verticalMin = 1.5;
-				controls.verticalMax = 2.0;
 
-				controls.lon = 250;
-				controls.lat = 30;
-
-				// SCENE
-
-				scene = new THREE.Scene();
-				scene.background = new THREE.Color( 0x59472b );
-				scene.fog = new THREE.Fog( 0x59472b, 1000, FAR );
+				controls.lookAt( scene.position );
 
 				// LIGHTS
 

+ 4 - 8
src/audio/Audio.js

@@ -95,10 +95,8 @@ Audio.prototype = Object.assign( Object.create( Object3D.prototype ), {
 		var source = this.context.createBufferSource();
 
 		source.buffer = this.buffer;
-		this.setDetune( this.detune );
 		source.loop = this.loop;
 		source.onended = this.onEnded.bind( this );
-		source.playbackRate.setValueAtTime( this.playbackRate, this.startTime );
 		this.startTime = this.context.currentTime;
 		source.start( this.startTime, this.offset );
 
@@ -106,6 +104,9 @@ Audio.prototype = Object.assign( Object.create( Object3D.prototype ), {
 
 		this.source = source;
 
+		this.setDetune( this.detune );
+		this.setPlaybackRate( this.playbackRate );
+
 		return this.connect();
 
 	},
@@ -228,12 +229,7 @@ Audio.prototype = Object.assign( Object.create( Object3D.prototype ), {
 
 		this.detune = value;
 
-		if ( this.source.detune === undefined ) {
-
-			console.warn( 'THREE.Audio: AudioBufferSourceNode.detune not supported by the browser.' );
-			return;
-
-		}
+		if ( this.source.detune === undefined ) return; // only set detune when available
 
 		if ( this.isPlaying === true ) {
 

+ 1 - 0
src/core/BufferGeometry.d.ts

@@ -44,6 +44,7 @@ export class BufferGeometry extends EventDispatcher {
   boundingBox: Box3;
   boundingSphere: Sphere;
   drawRange: { start: number; count: number };
+  userData: {[key: string]: any};
 
   getIndex(): BufferAttribute;
   setIndex(index: BufferAttribute | number[]): void;

+ 2 - 0
src/core/Object3D.d.ts

@@ -308,6 +308,8 @@ export class Object3D extends EventDispatcher {
    */
   updateMatrixWorld(force: boolean): void;
 
+  updateWorldMatrix(updateParents: boolean, updateChildren: boolean): void;
+
   toJSON(meta?: {
     geometries: any;
     materials: any;

+ 5 - 0
src/core/Object3D.js

@@ -6,6 +6,7 @@ import { Euler } from '../math/Euler.js';
 import { Layers } from './Layers.js';
 import { Matrix3 } from '../math/Matrix3.js';
 import { _Math } from '../math/Math.js';
+import { TrianglesDrawMode } from '../constants.js';
 
 /**
  * @author mrdoob / http://mrdoob.com/
@@ -718,6 +719,10 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false;
 
+		// object specific properties
+
+		if ( this.isMesh && this.drawMode !== TrianglesDrawMode ) object.drawMode = this.drawMode;
+
 		//
 
 		function serialize( library, element ) {

+ 9 - 2
src/loaders/ObjectLoader.js

@@ -497,8 +497,13 @@ Object.assign( ObjectLoader.prototype, {
 
 				} else {
 
-					materials[ data.uuid ] = loader.parse( data );
-					cache[ data.uuid ] = materials[ data.uuid ];
+					if ( cache[ data.uuid ] === undefined ) {
+
+						cache[ data.uuid ] = loader.parse( data );
+
+					}
+
+					materials[ data.uuid ] = cache[ data.uuid ];
 
 				}
 
@@ -845,6 +850,8 @@ Object.assign( ObjectLoader.prototype, {
 
 				}
 
+				if ( data.drawMode !== undefined ) object.setDrawMode( data.drawMode );
+
 				break;
 
 			case 'LOD':

+ 2 - 0
src/materials/MeshPhysicalMaterial.js

@@ -5,6 +5,8 @@ import { MeshStandardMaterial } from './MeshStandardMaterial.js';
  *
  * parameters = {
  *  reflectivity: <float>
+ *  clearCoat: <float>
+ *  clearCoatRoughness: <float>
  * }
  */
 

+ 1 - 0
src/math/Color.d.ts

@@ -125,4 +125,5 @@ export class Color {
   equals(color: Color): boolean;
   fromArray(rgb: number[], offset?: number): this;
   toArray(array?: number[], offset?: number): number[];
+  toArray(xyz: ArrayLike<number>, offset?: number): ArrayLike<number>;
 }

+ 9 - 0
src/math/Vector2.d.ts

@@ -401,9 +401,18 @@ export class Vector2 implements Vector {
    * Returns an array [x, y], or copies x and y into the provided array.
    * @param array (optional) array to store the vector to. If this is not provided, a new array will be created.
    * @param offset (optional) optional offset into the array.
+   * @return The created or provided array.
    */
   toArray(array?: number[], offset?: number): number[];
 
+  /**
+   * Copies x and y into the provided array-like.
+   * @param array array-like to store the vector to.
+   * @param offset (optional) optional offset into the array.
+   * @return The provided array-like.
+   */
+  toArray(array: ArrayLike<number>, offset?: number): ArrayLike<number>;
+
   /**
    * Sets this vector's x and y values from the attribute.
    * @param attribute the source attribute.

+ 17 - 1
src/math/Vector3.d.ts

@@ -112,7 +112,7 @@ export class Vector3 implements Vector {
 
   applyQuaternion(q: Quaternion): this;
 
-  project(camrea: Camera): this;
+  project(camera: Camera): this;
 
   unproject(camera: Camera): this;
 
@@ -246,7 +246,23 @@ export class Vector3 implements Vector {
   equals(v: Vector3): boolean;
 
   fromArray(xyz: number[], offset?: number): Vector3;
+
+  /**
+   * Returns an array [x, y, z], or copies x, y and z into the provided array.
+   * @param array (optional) array to store the vector to. If this is not provided, a new array will be created.
+   * @param offset (optional) optional offset into the array.
+   * @return The created or provided array.
+   */
   toArray(xyz?: number[], offset?: number): number[];
+
+  /**
+   * Copies x, y and z into the provided array-like.
+   * @param array array-like to store the vector to.
+   * @param offset (optional) optional offset into the array.
+   * @return The provided array-like.
+   */
+  toArray(xyz: ArrayLike<number>, offset?: number): ArrayLike<number>;
+
   fromBufferAttribute(
     attribute: BufferAttribute,
     index: number,

+ 40 - 7
src/renderers/shaders/ShaderChunk/bsdfs.glsl.js

@@ -1,4 +1,22 @@
 export default /* glsl */`
+
+// Analytical approximation of the DFG LUT, one half of the
+// split-sum approximation used in indirect specular lighting.
+// via 'environmentBRDF' from "Physically Based Shading on Mobile"
+// https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
+vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {
+	const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );
+
+	const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );
+
+	vec4 r = roughness * c0 + c1;
+
+	float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;
+
+	return vec2( -1.04, 1.04 ) * a004 + r.zw;
+
+}
+
 float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {
 
 #if defined ( PHYSICALLY_CORRECT_LIGHTS )
@@ -239,20 +257,35 @@ vec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in
 
 	float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
 
-	const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );
+	vec2 brdf = integrateSpecularBRDF( dotNV, roughness );
 
-	const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );
+	return specularColor * brdf.x + brdf.y;
 
-	vec4 r = roughness * c0 + c1;
+} // validated
 
-	float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;
+// Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting"
+// Approximates multiscattering in order to preserve energy.
+// http://www.jcgt.org/published/0008/01/03/
+void BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {
 
-	vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;
+	float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
 
-	return specularColor * AB.x + AB.y;
+	vec3 F = F_Schlick( specularColor, dotNV );
+	vec2 brdf = integrateSpecularBRDF( dotNV, roughness );
+	vec3 FssEss = F * brdf.x + brdf.y;
 
-} // validated
+	float Ess = brdf.x + brdf.y;
+	float Ems = 1.0 - Ess;
+
+	// Paper incorrect indicates coefficient is PI/21, and will
+	// be corrected to 1/21 in future updates.
+	vec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619; // 1/21
+	vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );
 
+	singleScatter += FssEss;
+	multiScatter += Fms * Ems;
+
+}
 
 float G_BlinnPhong_Implicit( /* const in float dotNL, const in float dotNV */ ) {
 

+ 1 - 1
src/renderers/shaders/ShaderChunk/lights_fragment_end.glsl.js

@@ -7,7 +7,7 @@ export default /* glsl */`
 
 #if defined( RE_IndirectSpecular )
 
-	RE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );
+	RE_IndirectSpecular( radiance, irradiance, clearCoatRadiance, geometry, material, reflectedLight );
 
 #endif
 `;

+ 31 - 4
src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js

@@ -96,11 +96,17 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricC
 
 void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
 
-	reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
+	// Defer to the IndirectSpecular function to compute
+	// the indirectDiffuse if energy preservation is enabled.
+	#ifndef ENVMAP_TYPE_CUBE_UV
+
+		reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
+
+	#endif
 
 }
 
-void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
+void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
 
 	#ifndef STANDARD
 		float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );
@@ -110,14 +116,35 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCo
 		float clearCoatDHR = 0.0;
 	#endif
 
-	reflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );
+	float clearCoatInv = 1.0 - clearCoatDHR;
+
+	// Both indirect specular and diffuse light accumulate here
+	// if energy preservation enabled, and PMREM provided.
+	#if defined( ENVMAP_TYPE_CUBE_UV )
+
+		vec3 singleScattering = vec3( 0.0 );
+		vec3 multiScattering = vec3( 0.0 );
+		vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;
+
+		BRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );
+
+		vec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );
+
+		reflectedLight.indirectSpecular += clearCoatInv * radiance * singleScattering;
+		reflectedLight.indirectDiffuse += multiScattering * cosineWeightedIrradiance;
+		reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;
+
+	#else
+
+		reflectedLight.indirectSpecular += clearCoatInv * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );
+
+	#endif
 
 	#ifndef STANDARD
 
 		reflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );
 
 	#endif
-
 }
 
 #define RE_Direct				RE_Direct_Physical

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.