Browse Source

Merge pull request #14239 from WestLangley/dev-object_space_normal_map

Support object-space normal maps
Mr.doob 7 years ago
parent
commit
6e89128f1a

+ 7 - 0
docs/api/materials/MeshPhongMaterial.html

@@ -178,6 +178,13 @@
 			the way the color is lit. Normal maps do not change the actual shape of the surface, only the lighting.
 		</p>
 
+		<h3>[property:Integer normalMapType]</h3>
+		<p>
+			The type of normal map.<br /><br />
+
+			Options are [page:constant THREE.TangentSpaceNormalMap] (default), and [page:constant THREE.ObjectSpaceNormalMap].
+		</p>
+
 		<h3>[property:Vector2 normalScale]</h3>
 		<p>
 			How much the normal map affects the material. Typical ranges are 0-1.

+ 7 - 0
docs/api/materials/MeshStandardMaterial.html

@@ -220,6 +220,13 @@
 			the way the color is lit. Normal maps do not change the actual shape of the surface, only the lighting.
 		</p>
 
+		<h3>[property:Integer normalMapType]</h3>
+		<p>
+			The type of normal map.<br /><br />
+
+			Options are [page:constant THREE.TangentSpaceNormalMap] (default), and [page:constant THREE.ObjectSpaceNormalMap].
+		</p>
+
 		<h3>[property:Vector2 normalScale]</h3>
 		<p>
 			How much the normal map affects the material. Typical ranges are 0-1.

+ 2 - 0
src/constants.js

@@ -137,3 +137,5 @@ export var RGBM16Encoding = 3005;
 export var RGBDEncoding = 3006;
 export var BasicDepthPacking = 3200;
 export var RGBADepthPacking = 3201;
+export var TangentSpaceNormalMap = 0;
+export var ObjectSpaceNormalMap = 1;

+ 2 - 1
src/materials/Material.js

@@ -1,5 +1,5 @@
 import { EventDispatcher } from '../core/EventDispatcher.js';
-import { NoColors, FrontSide, FlatShading, NormalBlending, LessEqualDepth, AddEquation, OneMinusSrcAlphaFactor, SrcAlphaFactor } from '../constants.js';
+import { NoColors, FrontSide, FlatShading, NormalBlending, LessEqualDepth, AddEquation, OneMinusSrcAlphaFactor, SrcAlphaFactor, TangentSpaceNormalMap } from '../constants.js';
 import { _Math } from '../math/Math.js';
 
 /**
@@ -194,6 +194,7 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 		if ( this.normalMap && this.normalMap.isTexture ) {
 
 			data.normalMap = this.normalMap.toJSON( meta ).uuid;
+			if ( this.normalMapType !== TangentSpaceNormalMap ) data.normalMapType = this.normalMapType;
 			data.normalScale = this.normalScale.toArray();
 
 		}

+ 4 - 0
src/materials/MeshNormalMaterial.js

@@ -1,3 +1,4 @@
+import { TangentSpaceNormalMap } from '../constants.js';
 import { Material } from './Material.js';
 import { Vector2 } from '../math/Vector2.js';
 
@@ -12,6 +13,7 @@ import { Vector2 } from '../math/Vector2.js';
  *  bumpScale: <float>,
  *
  *  normalMap: new THREE.Texture( <Image> ),
+ *  normalMapType: THREE.TangentSpaceNormalMap,
  *  normalScale: <Vector2>,
  *
  *  displacementMap: new THREE.Texture( <Image> ),
@@ -37,6 +39,7 @@ function MeshNormalMaterial( parameters ) {
 	this.bumpScale = 1;
 
 	this.normalMap = null;
+	this.normalMapType = TangentSpaceNormalMap;
 	this.normalScale = new Vector2( 1, 1 );
 
 	this.displacementMap = null;
@@ -70,6 +73,7 @@ MeshNormalMaterial.prototype.copy = function ( source ) {
 	this.bumpScale = source.bumpScale;
 
 	this.normalMap = source.normalMap;
+	this.normalMapType = source.normalMapType;
 	this.normalScale.copy( source.normalScale );
 
 	this.displacementMap = source.displacementMap;

+ 4 - 1
src/materials/MeshPhongMaterial.js

@@ -1,5 +1,5 @@
+import { MultiplyOperation, TangentSpaceNormalMap } from '../constants.js';
 import { Material } from './Material.js';
-import { MultiplyOperation } from '../constants.js';
 import { Vector2 } from '../math/Vector2.js';
 import { Color } from '../math/Color.js';
 
@@ -29,6 +29,7 @@ import { Color } from '../math/Color.js';
  *  bumpScale: <float>,
  *
  *  normalMap: new THREE.Texture( <Image> ),
+ *  normalMapType: THREE.TangentSpaceNormalMap,
  *  normalScale: <Vector2>,
  *
  *  displacementMap: new THREE.Texture( <Image> ),
@@ -79,6 +80,7 @@ function MeshPhongMaterial( parameters ) {
 	this.bumpScale = 1;
 
 	this.normalMap = null;
+	this.normalMapType = TangentSpaceNormalMap;
 	this.normalScale = new Vector2( 1, 1 );
 
 	this.displacementMap = null;
@@ -136,6 +138,7 @@ MeshPhongMaterial.prototype.copy = function ( source ) {
 	this.bumpScale = source.bumpScale;
 
 	this.normalMap = source.normalMap;
+	this.normalMapType = source.normalMapType;
 	this.normalScale.copy( source.normalScale );
 
 	this.displacementMap = source.displacementMap;

+ 4 - 0
src/materials/MeshStandardMaterial.js

@@ -1,3 +1,4 @@
+import { TangentSpaceNormalMap } from '../constants.js';
 import { Material } from './Material.js';
 import { Vector2 } from '../math/Vector2.js';
 import { Color } from '../math/Color.js';
@@ -27,6 +28,7 @@ import { Color } from '../math/Color.js';
  *  bumpScale: <float>,
  *
  *  normalMap: new THREE.Texture( <Image> ),
+ *  normalMapType: THREE.TangentSpaceNormalMap,
  *  normalScale: <Vector2>,
  *
  *  displacementMap: new THREE.Texture( <Image> ),
@@ -81,6 +83,7 @@ function MeshStandardMaterial( parameters ) {
 	this.bumpScale = 1;
 
 	this.normalMap = null;
+	this.normalMapType = TangentSpaceNormalMap;
 	this.normalScale = new Vector2( 1, 1 );
 
 	this.displacementMap = null;
@@ -142,6 +145,7 @@ MeshStandardMaterial.prototype.copy = function ( source ) {
 	this.bumpScale = source.bumpScale;
 
 	this.normalMap = source.normalMap;
+	this.normalMapType = source.normalMapType;
 	this.normalScale.copy( source.normalScale );
 
 	this.displacementMap = source.displacementMap;

+ 23 - 1
src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl

@@ -1,6 +1,28 @@
 #ifdef USE_NORMALMAP
 
-	normal = perturbNormal2Arb( -vViewPosition, normal );
+	#ifdef OBJECTSPACE_NORMALMAP
+
+		normal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0; // overrides both flatShading and attribute normals
+
+		#ifdef FLIP_SIDED
+
+			normal = - normal;
+
+		#endif
+
+		#ifdef DOUBLE_SIDED
+
+			normal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );
+
+		#endif
+
+		normal = normalize( normalMatrix * normal );
+
+	#else // tangent-space normal map
+
+		normal = perturbNormal2Arb( -vViewPosition, normal );
+
+	#endif
 
 #elif defined( USE_BUMPMAP )
 

+ 26 - 18
src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl

@@ -3,32 +3,40 @@
 	uniform sampler2D normalMap;
 	uniform vec2 normalScale;
 
-	// Per-Pixel Tangent Space Normal Mapping
-	// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
+	#ifdef OBJECTSPACE_NORMALMAP
 
-	vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {
+		uniform mat3 normalMatrix;
 
-		// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
+	#else
 
-		vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );
-		vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );
-		vec2 st0 = dFdx( vUv.st );
-		vec2 st1 = dFdy( vUv.st );
+		// Per-Pixel Tangent Space Normal Mapping
+		// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
 
-		float scale = sign( st1.t * st0.s - st0.t * st1.s ); // we do not care about the magnitude
+		vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {
 
-		vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );
-		vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );
-		vec3 N = normalize( surf_norm );
-		mat3 tsn = mat3( S, T, N );
+			// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
 
-		vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;
+			vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );
+			vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );
+			vec2 st0 = dFdx( vUv.st );
+			vec2 st1 = dFdy( vUv.st );
 
-		mapN.xy *= normalScale;
-		mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );
+			float scale = sign( st1.t * st0.s - st0.t * st1.s ); // we do not care about the magnitude
 
-		return normalize( tsn * mapN );
+			vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );
+			vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );
+			vec3 N = normalize( surf_norm );
+			mat3 tsn = mat3( S, T, N );
 
-	}
+			vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;
+
+			mapN.xy *= normalScale;
+			mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );
+
+			return normalize( tsn * mapN );
+
+		}
+
+	#endif
 
 #endif

+ 1 - 1
src/renderers/shaders/ShaderLib/normal_frag.glsl

@@ -2,7 +2,7 @@
 
 uniform float opacity;
 
-#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )
+#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )
 
 	varying vec3 vViewPosition;
 

+ 2 - 2
src/renderers/shaders/ShaderLib/normal_vert.glsl

@@ -1,6 +1,6 @@
 #define NORMAL
 
-#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )
+#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )
 
 	varying vec3 vViewPosition;
 
@@ -41,7 +41,7 @@ void main() {
 	#include <project_vertex>
 	#include <logdepthbuf_vertex>
 
-#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )
+#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )
 
 	vViewPosition = - mvPosition.xyz;
 

+ 3 - 1
src/renderers/webgl/WebGLProgram.js

@@ -84,7 +84,7 @@ function generateExtensions( extensions, parameters, rendererExtensions ) {
 	extensions = extensions || {};
 
 	var chunks = [
-		( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',
+		( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || ( parameters.normalMap && ! parameters.objectSpaceNormalMap ) || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',
 		( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',
 		( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '',
 		( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : ''
@@ -349,6 +349,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters
 			parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
 			parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 			parameters.normalMap ? '#define USE_NORMALMAP' : '',
+			( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '',
 			parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',
 			parameters.specularMap ? '#define USE_SPECULARMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
@@ -455,6 +456,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters
 			parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
 			parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 			parameters.normalMap ? '#define USE_NORMALMAP' : '',
+			( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '',
 			parameters.specularMap ? '#define USE_SPECULARMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
 			parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',

+ 3 - 2
src/renderers/webgl/WebGLPrograms.js

@@ -2,7 +2,7 @@
  * @author mrdoob / http://mrdoob.com/
  */
 
-import { BackSide, DoubleSide, CubeUVRefractionMapping, CubeUVReflectionMapping, GammaEncoding, LinearEncoding } from '../../constants.js';
+import { BackSide, DoubleSide, CubeUVRefractionMapping, CubeUVReflectionMapping, GammaEncoding, LinearEncoding, ObjectSpaceNormalMap } from '../../constants.js';
 import { WebGLProgram } from './WebGLProgram.js';
 
 function WebGLPrograms( renderer, extensions, capabilities ) {
@@ -27,7 +27,7 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 
 	var parameterNames = [
 		"precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding",
-		"lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap",
+		"lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "displacementMap", "specularMap",
 		"roughnessMap", "metalnessMap", "gradientMap",
 		"alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp",
 		"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
@@ -148,6 +148,7 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 			emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ),
 			bumpMap: !! material.bumpMap,
 			normalMap: !! material.normalMap,
+			objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap,
 			displacementMap: !! material.displacementMap,
 			roughnessMap: !! material.roughnessMap,
 			metalnessMap: !! material.metalnessMap,