diff --git a/docs/api/lights/SpotLight.html b/docs/api/lights/SpotLight.html
index 0d3c6b41e45caf..0a6b25363f6820 100644
--- a/docs/api/lights/SpotLight.html
+++ b/docs/api/lights/SpotLight.html
@@ -77,6 +77,12 @@
+ Additional angle, in radians, for the light to smoothly fall off.
+ Default — 0.
+
+
Rapidity of the falloff of light from its target direction.
diff --git a/examples/index.html b/examples/index.html
index 103459b9e51d10..9051137c9a66bf 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -166,6 +166,7 @@
"webgl_lights_hemisphere",
"webgl_lights_pointlights",
"webgl_lights_pointlights2",
+ "webgl_lights_spotlights_penumbra",
"webgl_lines_colors",
"webgl_lines_cubes",
"webgl_lines_dashed",
diff --git a/examples/textures/WoodFloor_DIF.png b/examples/textures/WoodFloor_DIF.png
new file mode 100755
index 00000000000000..e9e91803713449
Binary files /dev/null and b/examples/textures/WoodFloor_DIF.png differ
diff --git a/examples/textures/WoodFloor_NRM.png b/examples/textures/WoodFloor_NRM.png
new file mode 100755
index 00000000000000..59983c6ad403b9
Binary files /dev/null and b/examples/textures/WoodFloor_NRM.png differ
diff --git a/examples/textures/WoodFloor_SPC.png b/examples/textures/WoodFloor_SPC.png
new file mode 100755
index 00000000000000..05a991a3dbe83c
Binary files /dev/null and b/examples/textures/WoodFloor_SPC.png differ
diff --git a/examples/webgl_lights_spotlights_penumbra.html b/examples/webgl_lights_spotlights_penumbra.html
new file mode 100644
index 00000000000000..895e02e14436ea
--- /dev/null
+++ b/examples/webgl_lights_spotlights_penumbra.html
@@ -0,0 +1,168 @@
+
+
+
+
three.js webgl - lights - spot lights - penumbra
+
+
+
+
+
+
+
+
+
three.js - spot lights penumbra WebGL demo.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lights/SpotLight.js b/src/lights/SpotLight.js
index 79d0b928e6755b..0240526a34bd06 100644
--- a/src/lights/SpotLight.js
+++ b/src/lights/SpotLight.js
@@ -12,6 +12,7 @@ THREE.SpotLight = function ( color, intensity, distance, angle, exponent ) {
this.intensity = ( intensity !== undefined ) ? intensity : 1;
this.distance = ( distance !== undefined ) ? distance : 0;
this.angle = ( angle !== undefined ) ? angle : Math.PI / 3;
+ this.penumbraAngle = 0;
this.exponent = ( exponent !== undefined ) ? exponent : 10;
this.castShadow = false;
diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js
index e1712f986aaf78..af6c05c8363505 100644
--- a/src/renderers/WebGLRenderer.js
+++ b/src/renderers/WebGLRenderer.js
@@ -170,7 +170,7 @@ THREE.WebGLRenderer = function ( parameters ) {
ambient: [ 0, 0, 0 ],
directional: { length: 0, colors:[], positions: [] },
point: { length: 0, colors: [], positions: [], distances: [] },
- spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [] },
+ spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], outerAnglesCos: [], angleCosDiffs: [], exponents: [] },
hemi: { length: 0, skyColors: [], groundColors: [], positions: [] }
};
@@ -4728,6 +4728,8 @@ THREE.WebGLRenderer = function ( parameters ) {
uniforms.spotLightDistance.value = lights.spot.distances;
uniforms.spotLightDirection.value = lights.spot.directions;
uniforms.spotLightAngleCos.value = lights.spot.anglesCos;
+ uniforms.spotLightOuterAngleCos.value = lights.spot.outerAnglesCos;
+ uniforms.spotLightAngleCosDiff.value = lights.spot.angleCosDiffs;
uniforms.spotLightExponent.value = lights.spot.exponents;
uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors;
@@ -5208,6 +5210,8 @@ THREE.WebGLRenderer = function ( parameters ) {
spotDistances = zlights.spot.distances,
spotDirections = zlights.spot.directions,
spotAnglesCos = zlights.spot.anglesCos,
+ spotOuterAnglesCos = zlights.spot.outerAnglesCos,
+ spotAngleCosDiffs = zlights.spot.angleCosDiffs,
spotExponents = zlights.spot.exponents,
hemiSkyColors = zlights.hemi.skyColors,
@@ -5350,6 +5354,8 @@ THREE.WebGLRenderer = function ( parameters ) {
spotDirections[ spotOffset + 2 ] = _direction.z;
spotAnglesCos[ spotLength ] = Math.cos( light.angle );
+ spotOuterAnglesCos[ spotLength ] = Math.cos( light.angle + light.penumbraAngle );
+ spotAngleCosDiffs[ spotLength ] = spotAnglesCos[ spotLength ] - spotOuterAnglesCos[ spotLength ]
spotExponents[ spotLength ] = light.exponent;
spotLength += 1;
diff --git a/src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl b/src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl
index 511c1a83ec33eb..2bebc94d95dc86 100644
--- a/src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl
+++ b/src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl
@@ -34,6 +34,8 @@ uniform vec3 ambientLightColor;
uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];
uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];
uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];
+ uniform float spotLightOuterAngleCos[ MAX_SPOT_LIGHTS ];
+ uniform float spotLightAngleCosDiff[ MAX_SPOT_LIGHTS ];
uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];
#endif
diff --git a/src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl b/src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl
index c67bc2c953ab87..e474c920fca6c7 100644
--- a/src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl
+++ b/src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl
@@ -117,9 +117,14 @@ for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {
float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );
- if ( spotEffect > spotLightAngleCos[ i ] ) {
+ if ( spotEffect > spotLightOuterAngleCos[ i ] ) {
+
+ float falloff = 0.0;
+ falloff = (spotEffect - spotLightOuterAngleCos[ i ]) / spotLightAngleCosDiff[ i ];
+ falloff = clamp( falloff, 0.0, 1.0 );
spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );
+ spotEffect *= falloff;
float lDistance = 1.0;
if ( spotLightDistance[ i ] > 0.0 )
@@ -199,4 +204,4 @@ vLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;
vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;
-#endif
\ No newline at end of file
+#endif
diff --git a/src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl b/src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl
index 49d949d1820468..d23f204498857e 100644
--- a/src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl
+++ b/src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl
@@ -85,9 +85,14 @@ vec3 viewPosition = normalize( vViewPosition );
float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );
- if ( spotEffect > spotLightAngleCos[ i ] ) {
+ if ( spotEffect > spotLightOuterAngleCos[ i ] ) {
+
+ float falloff = 0.0;
+ falloff = (spotEffect - spotLightOuterAngleCos[ i ]) / spotLightAngleCosDiff[ i ];
+ falloff = clamp( falloff, 0.0, 1.0 );
spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );
+ spotEffect *= falloff;
// diffuse
@@ -275,4 +280,4 @@ vec3 totalSpecular = vec3( 0.0 );
gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;
-#endif
\ No newline at end of file
+#endif
diff --git a/src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl b/src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl
index 54228c49bd5b8d..dcabf87b179b44 100644
--- a/src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl
+++ b/src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl
@@ -30,6 +30,8 @@ uniform vec3 ambientLightColor;
uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];
uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];
uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];
+ uniform float spotLightOuterAngleCos[ MAX_SPOT_LIGHTS ];
+ uniform float spotLightAngleCosDiff[ MAX_SPOT_LIGHTS ];
uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];
uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];
@@ -49,4 +51,4 @@ uniform vec3 ambientLightColor;
#endif
varying vec3 vViewPosition;
-varying vec3 vNormal;
\ No newline at end of file
+varying vec3 vNormal;
diff --git a/src/renderers/shaders/UniformsLib.js b/src/renderers/shaders/UniformsLib.js
index e84336bde82b8d..3dd83da07f00e4 100644
--- a/src/renderers/shaders/UniformsLib.js
+++ b/src/renderers/shaders/UniformsLib.js
@@ -69,6 +69,8 @@ THREE.UniformsLib = {
"spotLightDirection" : { type: "fv", value: [] },
"spotLightDistance" : { type: "fv1", value: [] },
"spotLightAngleCos" : { type: "fv1", value: [] },
+ "spotLightOuterAngleCos" : { type: "fv1", value: [] },
+ "spotLightAngleCosDiff" : { type: "fv1", value: [] },
"spotLightExponent" : { type: "fv1", value: [] }
},