Quellcode durchsuchen

WebGLRenderer: Add support for rotating env maps. (#27758)

* WebGLRenderer: Add support for rotating env maps.

* Fix envMap rotation around x-axes.

* Docs: Add new background and envMap rotation.

* Ensure left-handed frame when rotating env maps.

* Fix typo.

* Clean up.

* Scene: Add `environmentRotation`.

* Clean up.

* Add `toJSON()` support.

* Update Material.js

Fix typo.

* Clean up.
Michael Herzog vor 1 Jahr
Ursprung
Commit
ed15131157

+ 5 - 0
docs/api/en/materials/MeshBasicMaterial.html

@@ -99,6 +99,11 @@
 		<h3>[property:Texture envMap]</h3>
 		<p>The environment map. Default is null.</p>
 
+		<h3>[property:Euler envMapRotation]</h3>
+		<p>
+			The rotation of the environment map in radians. Default is `(0,0,0)`.
+		</p>
+
 		<h3>[property:Boolean fog]</h3>
 		<p>Whether the material is affected by fog. Default is `true`.</p>
 

+ 5 - 0
docs/api/en/materials/MeshLambertMaterial.html

@@ -165,6 +165,11 @@
 		<h3>[property:Texture envMap]</h3>
 		<p>The environment map. Default is null.</p>
 
+		<h3>[property:Euler envMapRotation]</h3>
+		<p>
+			The rotation of the environment map in radians. Default is `(0,0,0)`.
+		</p>
+
 		<h3>[property:Boolean flatShading]</h3>
 		<p>
 			Define whether the material is rendered with flat shading. Default is

+ 5 - 0
docs/api/en/materials/MeshPhongMaterial.html

@@ -163,6 +163,11 @@
 		<h3>[property:Texture envMap]</h3>
 		<p>The environment map. Default is null.</p>
 
+		<h3>[property:Euler envMapRotation]</h3>
+		<p>
+			The rotation of the environment map in radians. Default is `(0,0,0)`.
+		</p>
+
 		<h3>[property:Boolean flatShading]</h3>
 		<p>
 			Define whether the material is rendered with flat shading. Default is

+ 5 - 0
docs/api/en/materials/MeshStandardMaterial.html

@@ -193,6 +193,11 @@
 			[page:PMREMGenerator]. Default is null.
 		</p>
 
+		<h3>[property:Euler envMapRotation]</h3>
+		<p>
+			The rotation of the environment map in radians. Default is `(0,0,0)`.
+		</p>
+
 		<h3>[property:Float envMapIntensity]</h3>
 		<p>Scales the effect of the environment map by multiplying its color.</p>
 

+ 12 - 0
docs/api/en/scenes/Scene.html

@@ -49,6 +49,12 @@
 			textures. Default is `1`.
 		</p>
 
+		<h3>[property:Euler backgroundRotation]</h3>
+		<p>
+			The rotation of the background in radians. Only influences environment maps
+			assigned to [page:Scene.background]. Default is `(0,0,0)`.
+		</p>
+
 		<h3>[property:Texture environment]</h3>
 		<p>
 			Sets the environment map for all physical materials in the scene. However,
@@ -56,6 +62,12 @@
 			[page:MeshStandardMaterial.envMap]. Default is `null`.
 		</p>
 
+		<h3>[property:Euler environmentRotation]</h3>
+		<p>
+			The rotation of the environment map in radians. Only influences physical materials 
+			in the scene when [page:.environment] is used. Default is `(0,0,0)`.
+		</p>
+
 		<h3>[property:Fog fog]</h3>
 
 		<p>

+ 1 - 0
src/loaders/MaterialLoader.js

@@ -300,6 +300,7 @@ class MaterialLoader extends Loader {
 		if ( json.specularColorMap !== undefined ) material.specularColorMap = getTexture( json.specularColorMap );
 
 		if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );
+		if ( json.envMapRotation !== undefined ) material.envMapRotation.fromArray( json.envMapRotation );
 		if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity;
 
 		if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity;

+ 2 - 0
src/loaders/ObjectLoader.js

@@ -797,6 +797,8 @@ class ObjectLoader extends Loader {
 
 				if ( data.backgroundBlurriness !== undefined ) object.backgroundBlurriness = data.backgroundBlurriness;
 				if ( data.backgroundIntensity !== undefined ) object.backgroundIntensity = data.backgroundIntensity;
+				if ( data.backgroundRotation !== undefined ) object.backgroundRotation.fromArray( data.backgroundRotation );
+				if ( data.environmentRotation !== undefined ) object.environmentRotation.fromArray( data.environmentRotation );
 
 				break;
 

+ 1 - 0
src/materials/Material.js

@@ -300,6 +300,7 @@ class Material extends EventDispatcher {
 
 		}
 
+		if ( this.envMapRotation !== undefined ) data.envMapRotation = this.envMapRotation.toArray();
 		if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity;
 		if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity;
 		if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio;

+ 3 - 0
src/materials/MeshBasicMaterial.js

@@ -1,6 +1,7 @@
 import { Material } from './Material.js';
 import { MultiplyOperation } from '../constants.js';
 import { Color } from '../math/Color.js';
+import { Euler } from '../math/Euler.js';
 
 class MeshBasicMaterial extends Material {
 
@@ -27,6 +28,7 @@ class MeshBasicMaterial extends Material {
 		this.alphaMap = null;
 
 		this.envMap = null;
+		this.envMapRotation = new Euler();
 		this.combine = MultiplyOperation;
 		this.reflectivity = 1;
 		this.refractionRatio = 0.98;
@@ -61,6 +63,7 @@ class MeshBasicMaterial extends Material {
 		this.alphaMap = source.alphaMap;
 
 		this.envMap = source.envMap;
+		this.envMapRotation.copy( source.envMapRotation );
 		this.combine = source.combine;
 		this.reflectivity = source.reflectivity;
 		this.refractionRatio = source.refractionRatio;

+ 3 - 0
src/materials/MeshLambertMaterial.js

@@ -2,6 +2,7 @@ import { MultiplyOperation, TangentSpaceNormalMap } from '../constants.js';
 import { Material } from './Material.js';
 import { Vector2 } from '../math/Vector2.js';
 import { Color } from '../math/Color.js';
+import { Euler } from '../math/Euler.js';
 
 class MeshLambertMaterial extends Material {
 
@@ -43,6 +44,7 @@ class MeshLambertMaterial extends Material {
 		this.alphaMap = null;
 
 		this.envMap = null;
+		this.envMapRotation = new Euler();
 		this.combine = MultiplyOperation;
 		this.reflectivity = 1;
 		this.refractionRatio = 0.98;
@@ -94,6 +96,7 @@ class MeshLambertMaterial extends Material {
 		this.alphaMap = source.alphaMap;
 
 		this.envMap = source.envMap;
+		this.envMapRotation.copy( source.envMapRotation );
 		this.combine = source.combine;
 		this.reflectivity = source.reflectivity;
 		this.refractionRatio = source.refractionRatio;

+ 3 - 0
src/materials/MeshPhongMaterial.js

@@ -2,6 +2,7 @@ import { MultiplyOperation, TangentSpaceNormalMap } from '../constants.js';
 import { Material } from './Material.js';
 import { Vector2 } from '../math/Vector2.js';
 import { Color } from '../math/Color.js';
+import { Euler } from '../math/Euler.js';
 
 class MeshPhongMaterial extends Material {
 
@@ -45,6 +46,7 @@ class MeshPhongMaterial extends Material {
 		this.alphaMap = null;
 
 		this.envMap = null;
+		this.envMapRotation = new Euler();
 		this.combine = MultiplyOperation;
 		this.reflectivity = 1;
 		this.refractionRatio = 0.98;
@@ -98,6 +100,7 @@ class MeshPhongMaterial extends Material {
 		this.alphaMap = source.alphaMap;
 
 		this.envMap = source.envMap;
+		this.envMapRotation.copy( source.envMapRotation );
 		this.combine = source.combine;
 		this.reflectivity = source.reflectivity;
 		this.refractionRatio = source.refractionRatio;

+ 3 - 0
src/materials/MeshStandardMaterial.js

@@ -2,6 +2,7 @@ import { TangentSpaceNormalMap } from '../constants.js';
 import { Material } from './Material.js';
 import { Vector2 } from '../math/Vector2.js';
 import { Color } from '../math/Color.js';
+import { Euler } from '../math/Euler.js';
 
 class MeshStandardMaterial extends Material {
 
@@ -49,6 +50,7 @@ class MeshStandardMaterial extends Material {
 		this.alphaMap = null;
 
 		this.envMap = null;
+		this.envMapRotation = new Euler();
 		this.envMapIntensity = 1.0;
 
 		this.wireframe = false;
@@ -104,6 +106,7 @@ class MeshStandardMaterial extends Material {
 		this.alphaMap = source.alphaMap;
 
 		this.envMap = source.envMap;
+		this.envMapRotation.copy( source.envMapRotation );
 		this.envMapIntensity = source.envMapIntensity;
 
 		this.wireframe = source.wireframe;

+ 1 - 0
src/renderers/WebGLRenderer.js

@@ -1612,6 +1612,7 @@ class WebGLRenderer {
 			materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null;
 			materialProperties.fog = scene.fog;
 			materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment );
+			materialProperties.envMapRotation = ( materialProperties.environment !== null && material.envMap === null ) ? scene.environmentRotation : material.envMapRotation;
 
 			if ( programs === undefined ) {
 

+ 1 - 0
src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js

@@ -3,6 +3,7 @@ export default /* glsl */`
 
 	uniform float envMapIntensity;
 	uniform float flipEnvMap;
+	uniform mat3 envMapRotation;
 
 	#ifdef ENVMAP_TYPE_CUBE
 		uniform samplerCube envMap;

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

@@ -36,7 +36,7 @@ export default /* glsl */`
 
 	#ifdef ENVMAP_TYPE_CUBE
 
-		vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );
+		vec4 envColor = textureCube( envMap, envMapRotation * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );
 
 	#else
 

+ 2 - 2
src/renderers/shaders/ShaderChunk/envmap_physical_pars_fragment.glsl.js

@@ -7,7 +7,7 @@ export default /* glsl */`
 
 			vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );
 
-			vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );
+			vec4 envMapColor = textureCubeUV( envMap, envMapRotation * worldNormal, 1.0 );
 
 			return PI * envMapColor.rgb * envMapIntensity;
 
@@ -30,7 +30,7 @@ export default /* glsl */`
 
 			reflectVec = inverseTransformDirection( reflectVec, viewMatrix );
 
-			vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );
+			vec4 envMapColor = textureCubeUV( envMap, envMapRotation * reflectVec, roughness );
 
 			return envMapColor.rgb * envMapIntensity;
 

+ 2 - 1
src/renderers/shaders/ShaderLib.js

@@ -232,7 +232,8 @@ const ShaderLib = {
 			envMap: { value: null },
 			flipEnvMap: { value: - 1 },
 			backgroundBlurriness: { value: 0 },
-			backgroundIntensity: { value: 1 }
+			backgroundIntensity: { value: 1 },
+			backgroundRotation: { value: /*@__PURE__*/ new Matrix3() }
 		},
 
 		vertexShader: ShaderChunk.backgroundCube_vert,

+ 3 - 2
src/renderers/shaders/ShaderLib/backgroundCube.glsl.js

@@ -30,6 +30,7 @@ export const fragment = /* glsl */`
 uniform float flipEnvMap;
 uniform float backgroundBlurriness;
 uniform float backgroundIntensity;
+uniform mat3 backgroundRotation;
 
 varying vec3 vWorldDirection;
 
@@ -39,11 +40,11 @@ void main() {
 
 	#ifdef ENVMAP_TYPE_CUBE
 
-		vec4 texColor = textureCube( envMap, vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) );
+		vec4 texColor = textureCube( envMap, backgroundRotation * vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) );
 
 	#elif defined( ENVMAP_TYPE_CUBE_UV )
 
-		vec4 texColor = textureCubeUV( envMap, vWorldDirection, backgroundBlurriness );
+		vec4 texColor = textureCubeUV( envMap, backgroundRotation * vWorldDirection, backgroundBlurriness );
 
 	#else
 

+ 1 - 0
src/renderers/shaders/UniformsLib.js

@@ -33,6 +33,7 @@ const UniformsLib = {
 	envmap: {
 
 		envMap: { value: null },
+		envMapRotation: { value: /*@__PURE__*/ new Matrix3() },
 		flipEnvMap: { value: - 1 },
 		reflectivity: { value: 1.0 }, // basic, lambert, phong
 		ior: { value: 1.5 }, // physical

+ 17 - 0
src/renderers/webgl/WebGLBackground.js

@@ -4,11 +4,15 @@ import { PlaneGeometry } from '../../geometries/PlaneGeometry.js';
 import { ShaderMaterial } from '../../materials/ShaderMaterial.js';
 import { Color } from '../../math/Color.js';
 import { ColorManagement } from '../../math/ColorManagement.js';
+import { Euler } from '../../math/Euler.js';
+import { Matrix4 } from '../../math/Matrix4.js';
 import { Mesh } from '../../objects/Mesh.js';
 import { ShaderLib } from '../shaders/ShaderLib.js';
 import { cloneUniforms, getUnlitUniformColorSpace } from '../shaders/UniformsUtils.js';
 
 const _rgb = { r: 0, b: 0, g: 0 };
+const _e1 = /*@__PURE__*/ new Euler();
+const _m1 = /*@__PURE__*/ new Matrix4();
 
 function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha, premultipliedAlpha ) {
 
@@ -105,10 +109,23 @@ function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha,
 
 			}
 
+			_e1.copy( scene.backgroundRotation );
+
+			// accommodate left-handed frame
+			_e1.x *= - 1; _e1.y *= - 1; _e1.z *= - 1;
+
+			if ( background.isCubeTexture && background.isRenderTargetTexture === false ) {
+
+				// environment maps which are no cube render targets or PMREMs follow a different px/nx convention
+				_e1.x *= - 1;
+
+			}
+
 			boxMesh.material.uniforms.envMap.value = background;
 			boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1;
 			boxMesh.material.uniforms.backgroundBlurriness.value = scene.backgroundBlurriness;
 			boxMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity;
+			boxMesh.material.uniforms.backgroundRotation.value.setFromMatrix4( _m1.makeRotationFromEuler( _e1 ) );
 			boxMesh.material.toneMapped = ColorManagement.getTransfer( background.colorSpace ) !== SRGBTransfer;
 
 			if ( currentBackground !== background ||

+ 23 - 1
src/renderers/webgl/WebGLMaterials.js

@@ -1,5 +1,10 @@
 import { BackSide } from '../../constants.js';
 import { getUnlitUniformColorSpace } from '../shaders/UniformsUtils.js';
+import { Euler } from '../../math/Euler.js';
+import { Matrix4 } from '../../math/Matrix4.js';
+
+const _e1 = /*@__PURE__*/ new Euler();
+const _m1 = /*@__PURE__*/ new Matrix4();
 
 function WebGLMaterials( renderer, properties ) {
 
@@ -209,12 +214,29 @@ function WebGLMaterials( renderer, properties ) {
 
 		}
 
-		const envMap = properties.get( material ).envMap;
+		const materialProperties = properties.get( material );
+
+		const envMap = materialProperties.envMap;
+		const envMapRotation = materialProperties.envMapRotation;
 
 		if ( envMap ) {
 
 			uniforms.envMap.value = envMap;
 
+			_e1.copy( envMapRotation );
+
+			// accommodate left-handed frame
+			_e1.x *= - 1; _e1.y *= - 1; _e1.z *= - 1;
+
+			if ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) {
+
+				// environment maps which are no cube render targets or PMREMs follow a different px/nx convention
+				_e1.x *= - 1;
+
+			}
+
+			uniforms.envMapRotation.value.setFromMatrix4( _m1.makeRotationFromEuler( _e1 ) );
+
 			uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1;
 
 			uniforms.reflectivity.value = material.reflectivity;

+ 8 - 0
src/scenes/Scene.js

@@ -1,4 +1,5 @@
 import { Object3D } from '../core/Object3D.js';
+import { Euler } from '../math/Euler.js';
 
 class Scene extends Object3D {
 
@@ -16,6 +17,8 @@ class Scene extends Object3D {
 
 		this.backgroundBlurriness = 0;
 		this.backgroundIntensity = 1;
+		this.backgroundRotation = new Euler();
+		this.environmentRotation = new Euler();
 
 		this.overrideMaterial = null;
 
@@ -37,6 +40,8 @@ class Scene extends Object3D {
 
 		this.backgroundBlurriness = source.backgroundBlurriness;
 		this.backgroundIntensity = source.backgroundIntensity;
+		this.backgroundRotation.copy( source.backgroundRotation );
+		this.environmentRotation.copy( source.environmentRotation );
 
 		if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();
 
@@ -54,6 +59,9 @@ class Scene extends Object3D {
 		if ( this.backgroundBlurriness > 0 ) data.object.backgroundBlurriness = this.backgroundBlurriness;
 		if ( this.backgroundIntensity !== 1 ) data.object.backgroundIntensity = this.backgroundIntensity;
 
+		data.object.backgroundRotation = this.backgroundRotation.toArray();
+		data.object.environmentRotation = this.environmentRotation.toArray();
+
 		return data;
 
 	}