From e5f809a6ed02d6a8c7d117c758e04f3df042721e Mon Sep 17 00:00:00 2001
From: WestLangley
Date: Fri, 8 Jun 2018 00:34:27 -0400
Subject: [PATCH] Support object-space normal maps
---
docs/api/materials/MeshPhongMaterial.html | 7 +++
docs/api/materials/MeshStandardMaterial.html | 7 +++
src/constants.js | 2 +
src/materials/Material.js | 3 +-
src/materials/MeshNormalMaterial.js | 4 ++
src/materials/MeshPhongMaterial.js | 5 ++-
src/materials/MeshStandardMaterial.js | 4 ++
.../ShaderChunk/normal_fragment_maps.glsl | 24 +++++++++-
.../ShaderChunk/normalmap_pars_fragment.glsl | 44 +++++++++++--------
.../shaders/ShaderLib/normal_frag.glsl | 2 +-
.../shaders/ShaderLib/normal_vert.glsl | 4 +-
src/renderers/webgl/WebGLProgram.js | 4 +-
src/renderers/webgl/WebGLPrograms.js | 5 ++-
13 files changed, 88 insertions(+), 27 deletions(-)
diff --git a/docs/api/materials/MeshPhongMaterial.html b/docs/api/materials/MeshPhongMaterial.html
index 2f024e38db597b..08c031e2b136fe 100644
--- a/docs/api/materials/MeshPhongMaterial.html
+++ b/docs/api/materials/MeshPhongMaterial.html
@@ -178,6 +178,13 @@ [property:Texture normalMap]
the way the color is lit. Normal maps do not change the actual shape of the surface, only the lighting.
+ [property:Integer normalMapType]
+
+ The type of normal map.
+
+ Options are [page:constant THREE.TangentSpaceNormalMap] (default), and [page:constant THREE.ObjectSpaceNormalMap].
+
+
[property:Vector2 normalScale]
How much the normal map affects the material. Typical ranges are 0-1.
diff --git a/docs/api/materials/MeshStandardMaterial.html b/docs/api/materials/MeshStandardMaterial.html
index f13d11aa6b8a51..a1aef746ceb963 100644
--- a/docs/api/materials/MeshStandardMaterial.html
+++ b/docs/api/materials/MeshStandardMaterial.html
@@ -220,6 +220,13 @@
[property:Texture normalMap]
the way the color is lit. Normal maps do not change the actual shape of the surface, only the lighting.
+ [property:Integer normalMapType]
+
+ The type of normal map.
+
+ Options are [page:constant THREE.TangentSpaceNormalMap] (default), and [page:constant THREE.ObjectSpaceNormalMap].
+
+
[property:Vector2 normalScale]
How much the normal map affects the material. Typical ranges are 0-1.
diff --git a/src/constants.js b/src/constants.js
index 82dc80f4c67ddc..85f678827b393b 100644
--- a/src/constants.js
+++ b/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;
diff --git a/src/materials/Material.js b/src/materials/Material.js
index 3d8f37c3e344c9..f4aa807eee53c2 100644
--- a/src/materials/Material.js
+++ b/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();
}
diff --git a/src/materials/MeshNormalMaterial.js b/src/materials/MeshNormalMaterial.js
index 109225f949359d..c95f335dd02ca6 100644
--- a/src/materials/MeshNormalMaterial.js
+++ b/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: ,
*
* normalMap: new THREE.Texture( ),
+ * normalMapType: THREE.TangentSpaceNormalMap,
* normalScale: ,
*
* displacementMap: new THREE.Texture( ),
@@ -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;
diff --git a/src/materials/MeshPhongMaterial.js b/src/materials/MeshPhongMaterial.js
index aacf4aa7586a50..e040b1fd037ec6 100644
--- a/src/materials/MeshPhongMaterial.js
+++ b/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: ,
*
* normalMap: new THREE.Texture( ),
+ * normalMapType: THREE.TangentSpaceNormalMap,
* normalScale: ,
*
* displacementMap: new THREE.Texture( ),
@@ -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;
diff --git a/src/materials/MeshStandardMaterial.js b/src/materials/MeshStandardMaterial.js
index b1ca6d4b8ba3e9..cbbe32dc34d1cf 100644
--- a/src/materials/MeshStandardMaterial.js
+++ b/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: ,
*
* normalMap: new THREE.Texture( ),
+ * normalMapType: THREE.TangentSpaceNormalMap,
* normalScale: ,
*
* displacementMap: new THREE.Texture( ),
@@ -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;
diff --git a/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl b/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl
index f3abc7c95b2644..5339430ae94691 100644
--- a/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl
+++ b/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 )
diff --git a/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl b/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl
index 9f3e6171ebb1ed..c3470182c4b3ea 100644
--- a/src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl
+++ b/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
diff --git a/src/renderers/shaders/ShaderLib/normal_frag.glsl b/src/renderers/shaders/ShaderLib/normal_frag.glsl
index 2d8bbe016ad6bc..88a2c4331c659e 100644
--- a/src/renderers/shaders/ShaderLib/normal_frag.glsl
+++ b/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;
diff --git a/src/renderers/shaders/ShaderLib/normal_vert.glsl b/src/renderers/shaders/ShaderLib/normal_vert.glsl
index 034d580c983e27..131edc6a03cf5a 100644
--- a/src/renderers/shaders/ShaderLib/normal_vert.glsl
+++ b/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
#include
-#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;
diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js
index b2ffa49f26971b..4757403495009a 100644
--- a/src/renderers/webgl/WebGLProgram.js
+++ b/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' : '',
diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js
index 633a3397e7c7a1..8c83379f9c3653 100644
--- a/src/renderers/webgl/WebGLPrograms.js
+++ b/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,