提交 ffaa6bf0 编写于 作者: M Mr.doob

Merge pull request #7153 from mkkellogg/dev

Added shadow functionality for point lights.
......@@ -362,6 +362,7 @@
"webgl_shadowmap",
"webgl_shadowmap_performance",
"webgl_shadowmap_viewer",
"webgl_shadowmap_omnidirectional",
"webgl_shadowmesh",
"webgl_skinning_simple",
"webgl_sprites",
......
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - Omni-directional Shadow map viewer example </title>
<meta charset="utf-8">
<style>
body {
font-family: Monospace;
background-color: #000;
color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 10px;
width: 100%;
text-align: center;
z-index: 100;
display:block;
}
#info a { color: #f00; font-weight: bold; text-decoration: underline; cursor: pointer }
</style>
</head>
<body>
<div id="info">
<a href="http://threejs.org" target="_blank">three.js</a> - Omni-directional Shadow map viewer example by <a href="https://github.com/mkkellogg">mkkellogg</a>
</div>
<script src="../build/three.min.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="js/Detector.js"></script>
<script src="js/libs/stats.min.js"></script>
<script>
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var camera, scene, renderer, clock, stats;
var dirLight, pointLight;
var pointLightParent;
var torusKnot, cube, cube2, cube3, cube4;
var cubeMaterial;
var wallMaterial;
var ground;
init();
animate();
function init() {
initScene();
initMisc();
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
}
function initScene() {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 15, 35 );
scene = new THREE.Scene();
// Lights
var ambient = new THREE.AmbientLight( 0x404040 );
scene.add( ambient );
pointLight = new THREE.PointLight( 0xffffff );
pointLight.position.set( 0, 11, 4 );
pointLight.castShadow = true;
pointLight.shadowCameraNear = 1;
pointLight.shadowCameraFar = 30;
pointLight.shadowDarkness = 0.5;
pointLight.shadowCameraVisible = true;
pointLight.shadowMapWidth = 2048;
pointLight.shadowMapHeight = 1024;
pointLight.shadowBias = 0.1;
pointLight.name = 'Point Light';
scene.add( pointLight );
/*dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
dirLight.position.set( 0, 50, 0 );
dirLight.castShadow = true;
dirLight.shadowCameraNear = 1;
dirLight.shadowCameraFar = 100;
dirLight.shadowCameraRight = 15;
dirLight.shadowCameraLeft = -15;
dirLight.shadowCameraTop = 15;
dirLight.shadowCameraBottom = -15;
dirLight.shadowDarkness = 0.5;
dirLight.shadowCameraVisible = true;
dirLight.shadowMapWidth = 1024;
dirLight.shadowMapHeight = 1024;
dirLight.name = 'Dir. Light';
scene.add( dirLight );*/
cubeMaterial = new THREE.MeshPhongMaterial( {
color: 0xff0000,
shininess: 150,
specular: 0x222222,
shading: THREE.SmoothShading,
} );
var cubeGeometry = new THREE.BoxGeometry( 3, 3, 3 );
cube = new THREE.Mesh( cubeGeometry, cubeMaterial );
cube.name = "cube 1";
cube.position.set( 8, 3, 6 );
cube.castShadow = true;
cube.receiveShadow = true;
scene.add( cube );
cube2 = new THREE.Mesh( cubeGeometry, cubeMaterial );
cube2.name = "cube 2";
cube2.position.set( -8, 3, 4 );
cube2.castShadow = true;
cube2.receiveShadow = true;
scene.add( cube2 );
cube3 = new THREE.Mesh( cubeGeometry, cubeMaterial );
cube3.name = "cube 3";
cube3.position.set( -4, 15, -6 );
cube3.castShadow = true;
cube3.receiveShadow = true;
scene.add( cube3 );
var torusGeometry = new THREE.TorusKnotGeometry( 25, 8, 75, 20 );
torusKnot = new THREE.Mesh( torusGeometry, cubeMaterial );
torusKnot.scale.multiplyScalar( 1 / 18 );
torusKnot.position.set( -1, 3, -4 );
torusKnot.castShadow = true;
torusKnot.receiveShadow = true;
scene.add( torusKnot );
wallMaterial = new THREE.MeshPhongMaterial( {
color: 0xa0adaf,
shininess: 10,
specular: 0x111111,
shading: THREE.SmoothShading
} );
var wallGeometry = new THREE.BoxGeometry( 10, 0.15, 10 );
ground = new THREE.Mesh( wallGeometry, wallMaterial );
ground.name = "ground";
ground.scale.multiplyScalar( 3 );
ground.castShadow = false;
ground.receiveShadow = true;
scene.add( ground );
ground.position.set( 0, -5, 0 );
var ceiling = new THREE.Mesh( wallGeometry, wallMaterial );
ceiling.name = "ceiling";
ceiling.scale.multiplyScalar( 3 );
ceiling.castShadow = false;
ceiling.receiveShadow = true;
scene.add( ceiling );
ceiling.position.set( 0, 24, 0 );
var wall = new THREE.Mesh( wallGeometry, wallMaterial );
wall.name = "left wall";
wall.scale.multiplyScalar( 3 );
wall.castShadow = false;
wall.receiveShadow = true;
scene.add( wall );
wall.position.set( -14, 10, 0 );
wall.rotation.z = Math.PI / 2;
wall = new THREE.Mesh( wallGeometry, wallMaterial );
wall.name = "right wall";
wall.scale.multiplyScalar( 3 );
wall.castShadow = false;
wall.receiveShadow = true;
scene.add( wall );
wall.position.set(14,10,0);
wall.rotation.z = Math.PI / 2;
wall = new THREE.Mesh( wallGeometry, wallMaterial );
wall.name = "back wall";
wall.scale.multiplyScalar( 3 );
wall.castShadow = false;
wall.receiveShadow = true;
scene.add( wall );
wall.position.set( 0, 10, -14 );
wall.rotation.y = Math.PI / 2;
wall.rotation.z = Math.PI / 2;
/*wall = new THREE.Mesh( wallGeometry, wallMaterial );
wall.name = "front wall";
wall.scale.multiplyScalar( 3 );
wall.castShadow = false;
wall.receiveShadow = true;
scene.add( wall );
wall.position.set( 0, 10, 14 );
wall.rotation.y = Math.PI / 2;
wall.rotation.z = Math.PI / 2;*/
var sphereGeometry = new THREE.SphereGeometry( 1, 32, 32 );
var material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
var sphere = new THREE.Mesh( sphereGeometry, material );
sphere.castShadow = false;
sphere.receiveShadow = false;
sphere.position.set( 0, 11, 4 );
scene.add( sphere );
pointLightParent = new THREE.Object3D();
pointLightParent.add( pointLight );
pointLightParent.add( sphere );
scene.add( pointLightParent );
}
function initMisc() {
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x000000 );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap;
// Mouse control
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 2, 0 );
controls.update();
clock = new THREE.Clock();
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.right = '0px';
stats.domElement.style.top = '0px';
document.body.appendChild( stats.domElement );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function renderScene() {
renderer.render( scene, camera );
}
function render() {
var delta = clock.getDelta();
pointLightParent.rotation.y += delta * 2;
renderScene();
cube.rotation.x += 0.25 * delta;
cube.rotation.y += 2 * delta;
cube.rotation.z += 1 * delta;
cube2.rotation.x += 0.25 * delta;
cube2.rotation.y += 2 * delta;
cube2.rotation.z += 1 * delta;
cube3.rotation.x += 0.25 * delta;
cube3.rotation.y += 2 * delta;
cube3.rotation.z += 1 * delta;
}
</script>
</body>
</html>
......@@ -65,7 +65,7 @@
function initScene() {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 1000 );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 15, 35 );
scene = new THREE.Scene();
......@@ -89,7 +89,7 @@
dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
dirLight.position.set( 0, 10, 0 );
dirLight.castShadow = true;
dirLight.shadowCameraNear = 0.01;
dirLight.shadowCameraNear = 1;
dirLight.shadowCameraFar = 10;
dirLight.shadowCameraRight = 15;
dirLight.shadowCameraLeft = -15;
......
......@@ -2,6 +2,7 @@
* @author mrdoob / http://mrdoob.com/
*/
THREE.PointLight = function ( color, intensity, distance, decay ) {
THREE.Light.call( this, color );
......@@ -12,6 +13,30 @@ THREE.PointLight = function ( color, intensity, distance, decay ) {
this.distance = ( distance !== undefined ) ? distance : 0;
this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.
this.castShadow = false;
this.onlyShadow = false;
//
this.shadowCameraNear = 1;
this.shadowCameraFar = 500;
this.shadowCameraFov = 90;
this.shadowCameraVisible = false;
this.shadowBias = 0;
this.shadowDarkness = 0.5;
this.shadowMapWidth = 512;
this.shadowMapHeight = 512;
//
this.shadowMap = null;
this.shadowMapSize = null;
this.shadowCamera = null;
this.shadowMatrix = null;
};
THREE.PointLight.prototype = Object.create( THREE.Light.prototype );
......@@ -25,6 +50,21 @@ THREE.PointLight.prototype.copy = function ( source ) {
this.distance = source.distance;
this.decay = source.decay;
this.castShadow = source.castShadow;
this.onlyShadow = source.onlyShadow;
this.shadowCameraNear = source.shadowCameraNear;
this.shadowCameraFar = source.shadowCameraFar;
this.shadowCameraFov = source.shadowCameraFov;
this.shadowCameraVisible = source.shadowCameraVisible;
this.shadowBias = source.shadowBias;
this.shadowDarkness = source.shadowDarkness;
this.shadowMapWidth = source.shadowMapWidth;
this.shadowMapHeight = source.shadowMapHeight;
return this;
};
......
......@@ -374,6 +374,16 @@ THREE.WebGLRenderer = function ( parameters ) {
};
this.getViewport = function ( dimensions ) {
dimensions.x = _viewportX;
dimensions.y = _viewportY;
dimensions.z = _viewportWidth;
dimensions.w = _viewportHeight;
};
this.setScissor = function ( x, y, width, height ) {
_gl.scissor(
......@@ -1701,7 +1711,7 @@ THREE.WebGLRenderer = function ( parameters ) {
if ( object.receiveShadow && ! material._shadowPass ) {
refreshUniformsShadow( m_uniforms, lights );
refreshUniformsShadow( m_uniforms, lights, camera );
}
......@@ -1953,7 +1963,7 @@ THREE.WebGLRenderer = function ( parameters ) {
}
function refreshUniformsShadow ( uniforms, lights ) {
function refreshUniformsShadow ( uniforms, lights, camera ) {
if ( uniforms.shadowMatrix ) {
......@@ -1965,14 +1975,22 @@ THREE.WebGLRenderer = function ( parameters ) {
if ( ! light.castShadow ) continue;
if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight ) ) {
if ( light instanceof THREE.PointLight || light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) {
uniforms.shadowMap.value[ j ] = light.shadowMap;
uniforms.shadowMapSize.value[ j ] = light.shadowMapSize;
if ( light instanceof THREE.PointLight ) {
uniforms.shadowMatrix.value[ j ] = light.shadowMatrix;
// for point lights we set the sign of the shadowDarkness uniform to be negative
uniforms.shadowDarkness.value[ j ] = - light.shadowDarkness;
uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
} else {
uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
}
uniforms.shadowMatrix.value[ j ] = light.shadowMatrix;
uniforms.shadowMap.value[ j ] = light.shadowMap;
uniforms.shadowMapSize.value[ j ] = light.shadowMapSize;
uniforms.shadowBias.value[ j ] = light.shadowBias;
j ++;
......@@ -2308,7 +2326,7 @@ THREE.WebGLRenderer = function ( parameters ) {
case 'tv':
// array of THREE.Texture (2d)
// array of THREE.Texture (2d or cube)
if ( uniform._array === undefined ) {
......@@ -2331,7 +2349,22 @@ THREE.WebGLRenderer = function ( parameters ) {
if ( ! texture ) continue;
_this.setTexture( texture, textureUnit );
if ( texture instanceof THREE.CubeTexture ||
( texture.image instanceof Array && texture.image.length === 6 ) ) {
// CompressedTexture can have Array in image :/
setCubeTexture( texture, textureUnit );
} else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
setCubeTextureDynamic( texture, textureUnit );
} else {
_this.setTexture( texture, textureUnit );
}
}
......@@ -3011,7 +3044,6 @@ THREE.WebGLRenderer = function ( parameters ) {
renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();
renderTargetProperties.__webglRenderbuffer[ i ] = _gl.createRenderbuffer();
state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
setupFrameBuffer( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
......@@ -3124,6 +3156,13 @@ THREE.WebGLRenderer = function ( parameters ) {
}
if ( isCube ) {
var renderTargetProperties = properties.get( renderTarget );
_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, renderTargetProperties.__webglTexture, 0 );
}
_currentWidth = width;
_currentHeight = height;
......
......@@ -3,3 +3,9 @@
varying vec3 vWorldPosition;
#endif
#if MAX_POINT_LIGHTS > 0
uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];
#endif
......@@ -14,10 +14,25 @@
for( int i = 0; i < MAX_SHADOWS; i ++ ) {
// to save on uniform space, we use the sign of @shadowDarkness[ i ] to determine
// whether or not this light is a point light ( shadowDarkness[ i ] < 0 == point light)
bool isPointLight = shadowDarkness[ i ] < 0.0;
// get the real shadow darkness
float realShadowDarkness = abs( shadowDarkness[ i ] );
// for point lights, the uniform @vShadowCoord is re-purposed to hold
// the distance from the light to the world-space position of the fragment.
vec3 lightToPosition = vShadowCoord[ i ].xyz;
float texelSizeX = 1.0 / shadowMapSize[ i ].x;
float texelSizeY = 1.0 / shadowMapSize[ i ].y;
vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;
float shadow = 0.0;
// if ( something && something ) breaks ATI OpenGL shader compiler
// if ( all( something, something ) ) using this instead
// if ( something && something ) breaks ATI OpenGL shader compiler
// if ( all( something, something ) ) using this instead
bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );
bool inFrustum = all( inFrustumVec );
......@@ -26,148 +41,252 @@
bool frustumTest = all( frustumTestVec );
if ( frustumTest ) {
shadowCoord.z += shadowBias[ i ];
if ( frustumTest || isPointLight ) {
#if defined( SHADOWMAP_TYPE_PCF )
#if defined(POINT_LIGHT_SHADOWS)
if( isPointLight ) {
float cubeTexelSize = 1.0 / ( shadowMapSize[ i ].x * 0.25 );
vec3 baseDirection3D = normalize( lightToPosition );
vec2 baseDirection2D = cubeToUV( baseDirection3D, texelSizeX, texelSizeY );
initGridSamplingDisk();
float diskRadius = 1.25;
float numSamples = 1.0;
shadow = 0.0;
vec3 baseDirection = normalize( lightToPosition );
float curDistance = length( lightToPosition );
float dist = unpack1K( texture2D( shadowMap[ i ], baseDirection2D ) ) + 0.1;
if ( curDistance >= dist )
shadow += 1.0;
// evaluate each sampling direction
for( int s = 0; s < 20; s++ ) {
vec3 offset = gridSamplingDisk[ s ] * diskRadius * cubeTexelSize;
vec3 adjustedBaseDirection3D = baseDirection3D + offset;
vec2 adjustedBaseDirection2D = cubeToUV( adjustedBaseDirection3D, texelSizeX, texelSizeY );
dist = unpack1K( texture2D( shadowMap[ i ], adjustedBaseDirection2D ) ) + shadowBias[ i ];
if ( curDistance >= dist )
shadow += 1.0;
numSamples += 1.0;
}
shadow /= numSamples;
} else {
#endif
// Percentage-close filtering
// (9 pixel kernel)
// http://fabiensanglard.net/shadowmappingPCF/
/*
// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL
// must enroll loop manually
for ( float y = -1.25; y <= 1.25; y += 1.25 )
for ( float x = -1.25; x <= 1.25; x += 1.25 ) {
vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );
// doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup
//vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );
float fDepth = unpackDepth( rgbaDepth );
if ( fDepth < shadowCoord.z )
shadow += 1.0;
}
shadow /= 9.0;
*/
float shadow = 0.0;
shadowCoord.z += shadowBias[ i ];
/*
// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL
// must enroll loop manually
const float shadowDelta = 1.0 / 9.0;
for ( float y = -1.25; y <= 1.25; y += 1.25 )
for ( float x = -1.25; x <= 1.25; x += 1.25 ) {
float xPixelOffset = texelSizeX;
float yPixelOffset = texelSizeY;
vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );
float dx0 = -1.25 * xPixelOffset;
float dy0 = -1.25 * yPixelOffset;
float dx1 = 1.25 * xPixelOffset;
float dy1 = 1.25 * yPixelOffset;
// doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup
//vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
float fDepth = unpackDepth( rgbaDepth );
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
if ( fDepth < shadowCoord.z )
shadow += 1.0;
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
}
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
shadow /= 9.0;
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
*/
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
const float shadowDelta = 1.0 / 9.0;
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
float xPixelOffset = 1.0 / shadowMapSize[ i ].x;
float yPixelOffset = 1.0 / shadowMapSize[ i ].y;
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
float dx0 = -1.25 * xPixelOffset;
float dy0 = -1.25 * yPixelOffset;
float dx1 = 1.25 * xPixelOffset;
float dy1 = 1.25 * yPixelOffset;
#if defined(POINT_LIGHT_SHADOWS)
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
}
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
#endif
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
shadowColor = shadowColor * vec3( ( 1.0 - realShadowDarkness * shadow ) );
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
#elif defined( SHADOWMAP_TYPE_PCF_SOFT )
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
#if defined(POINT_LIGHT_SHADOWS)
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
if( isPointLight ) {
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
float cubeTexelSize = 1.0 / ( shadowMapSize[ i ].x * 0.25 );
vec3 baseDirection3D = normalize( lightToPosition );
vec2 baseDirection2D = cubeToUV( baseDirection3D, texelSizeX, texelSizeY );
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
initGridSamplingDisk();
fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );
if ( fDepth < shadowCoord.z ) shadow += shadowDelta;
float diskRadius = 2.25;
float numSamples = 1.0;
shadow = 0.0;
shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );
vec3 baseDirection = normalize( lightToPosition );
float curDistance = length( lightToPosition );
#elif defined( SHADOWMAP_TYPE_PCF_SOFT )
float dist = unpack1K( texture2D( shadowMap[ i ], baseDirection2D ) ) + 0.1;
if ( curDistance >= dist )
shadow += 1.0;
// evaluate each sampling direction
for( int s = 0; s < 20; s++ ) {
vec3 offset = gridSamplingDisk[ s ] * diskRadius * cubeTexelSize;
vec3 adjustedBaseDirection3D = baseDirection3D + offset;
vec2 adjustedBaseDirection2D = cubeToUV( adjustedBaseDirection3D, texelSizeX, texelSizeY );
dist = unpack1K( texture2D( shadowMap[ i ], adjustedBaseDirection2D ) ) + shadowBias[ i ];
if ( curDistance >= dist )
shadow += 1.0;
numSamples += 1.0;
}
shadow /= numSamples;
} else {
#endif
// Percentage-close filtering
// (9 pixel kernel)
// http://fabiensanglard.net/shadowmappingPCF/
float shadow = 0.0;
shadowCoord.z += shadowBias[ i ];
float xPixelOffset = texelSizeX;
float yPixelOffset = texelSizeY;
float dx0 = -1.0 * xPixelOffset;
float dy0 = -1.0 * yPixelOffset;
float dx1 = 1.0 * xPixelOffset;
float dy1 = 1.0 * yPixelOffset;
float xPixelOffset = 1.0 / shadowMapSize[ i ].x;
float yPixelOffset = 1.0 / shadowMapSize[ i ].y;
mat3 shadowKernel;
mat3 depthKernel;
float dx0 = -1.0 * xPixelOffset;
float dy0 = -1.0 * yPixelOffset;
float dx1 = 1.0 * xPixelOffset;
float dy1 = 1.0 * yPixelOffset;
depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );
depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );
depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );
depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );
depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );
depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );
depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );
depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );
depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );
mat3 shadowKernel;
mat3 depthKernel;
vec3 shadowZ = vec3( shadowCoord.z );
shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));
shadowKernel[0] *= vec3(0.25);
depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );
depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );
depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );
depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );
depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );
depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );
depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );
depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );
depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );
shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));
shadowKernel[1] *= vec3(0.25);
vec3 shadowZ = vec3( shadowCoord.z );
shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));
shadowKernel[0] *= vec3(0.25);
shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));
shadowKernel[2] *= vec3(0.25);
shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));
shadowKernel[1] *= vec3(0.25);
vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );
shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));
shadowKernel[2] *= vec3(0.25);
shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );
shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );
vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );
vec4 shadowValues;
shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );
shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );
shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );
shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );
shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );
shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );
shadow = dot( shadowValues, vec4( 1.0 ) );
vec4 shadowValues;
shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );
shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );
shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );
shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );
#if defined(POINT_LIGHT_SHADOWS)
}
shadow = dot( shadowValues, vec4( 1.0 ) );
#endif
shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );
shadowColor = shadowColor * vec3( ( 1.0 - realShadowDarkness * shadow ) );
#else
vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );
float fDepth = unpackDepth( rgbaDepth );
#if defined(POINT_LIGHT_SHADOWS)
if( isPointLight ) {
vec3 baseDirection3D = normalize( lightToPosition );
vec2 baseDirection2D = cubeToUV( baseDirection3D, texelSizeX, texelSizeY );
vec4 data = texture2D( shadowMap[ i ], baseDirection2D );
float dist = unpack1K( data ) + shadowBias[ i ];
if ( length( lightToPosition ) >= dist)
shadowColor = shadowColor * vec3( 1.0 - realShadowDarkness );
} else {
#endif
shadowCoord.z += shadowBias[ i ];
vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );
float fDepth = unpackDepth( rgbaDepth );
if ( fDepth < shadowCoord.z )
// spot with multiple shadows is darker
shadowColor = shadowColor * vec3( 1.0 - realShadowDarkness );
if ( fDepth < shadowCoord.z )
// spot with multiple shadows has the same color as single shadow spot
// spot with multiple shadows is darker
// shadowColor = min( shadowColor, vec3( realShadowDarkness ) );
shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );
#if defined(POINT_LIGHT_SHADOWS)
// spot with multiple shadows has the same color as single shadow spot
}
// shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );
#endif
#endif
......
#ifdef USE_SHADOWMAP
uniform sampler2D shadowMap[ MAX_SHADOWS ];
uniform vec2 shadowMapSize[ MAX_SHADOWS ];
......@@ -16,4 +16,124 @@
}
#if defined(POINT_LIGHT_SHADOWS)
float unpack1K ( vec4 color ) {
const vec4 bitSh = vec4( 1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0 );
return dot( color, bitSh ) * 1000.0;
}
/**
* cubeToUV() maps a 3D direction vector suitable for cube texture mapping to a 2D
* vector suitable for 2D texture mapping. This code uses the following layout for the
* 2D texture:
*
* xzXZ
* y Y
*
* Y - Positive y direction
* y - Negative y direction
* X - Positive x direction
* x - Negative x direction
* Z - Positive z direction
* z - Negative z direction
*
* Alternate code for a horizontal cross layout can be found here:
* https://gist.github.com/tschw/da10c43c467ce8afd0c4
*/
vec2 cubeToUV( vec3 v, float texelSizeX, float texelSizeY ) {
// Number of texels to avoid at the edge of each square
vec3 absV = abs( v );
// Intersect unit cube
float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );
absV *= scaleToCube;
// Apply scale to avoid seams
// two texels less per square (one texel will do for NEAREST)
v *= scaleToCube * ( 1.0 - 4.0 * texelSizeY );
// Unwrap
// space: -1 ... 1 range for each square
//
// #X## dim := ( 4 , 2 )
// # # center := ( 1 , 1 )
vec2 planar = v.xy;
float almostATexel = 1.5 * texelSizeY;
float almostOne = 1.0 - almostATexel;
if ( absV.z >= almostOne ) {
if ( v.z > 0.0 )
planar.x = 4.0 - v.x;
} else if ( absV.x >= almostOne ) {
float signX = sign( v.x );
planar.x = v.z * signX + 2.0 * signX;
} else if ( absV.y >= almostOne ) {
float signY = sign( v.y );
planar.x = v.x + 2.0 * signY + 2.0;
planar.y = v.z * signY - 2.0;
}
// Transform to UV space
// scale := 0.5 / dim
// translate := ( center + 0.5 ) / dim
return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );
}
vec3 gridSamplingDisk[ 20 ];
bool gridSamplingInitialized = false;
void initGridSamplingDisk(){
if( gridSamplingInitialized ){
return;
}
gridSamplingDisk[0] = vec3(1, 1, 1);
gridSamplingDisk[1] = vec3(1, -1, 1);
gridSamplingDisk[2] = vec3(-1, -1, 1);
gridSamplingDisk[3] = vec3(-1, 1, 1);
gridSamplingDisk[4] = vec3(1, 1, -1);
gridSamplingDisk[5] = vec3(1, -1, -1);
gridSamplingDisk[6] = vec3(-1, -1, -1);
gridSamplingDisk[7] = vec3(-1, 1, -1);
gridSamplingDisk[8] = vec3(1, 1, 0);
gridSamplingDisk[9] = vec3(1, -1, 0);
gridSamplingDisk[10] = vec3(-1, -1, 0);
gridSamplingDisk[11] = vec3(-1, 1, 0);
gridSamplingDisk[12] = vec3(1, 0, 1);
gridSamplingDisk[13] = vec3(-1, 0, 1);
gridSamplingDisk[14] = vec3(1, 0, -1);
gridSamplingDisk[15] = vec3(-1, 0, -1);
gridSamplingDisk[16] = vec3(0, 1, 1);
gridSamplingDisk[17] = vec3(0, -1, 1);
gridSamplingDisk[18] = vec3(0, -1, -1);
gridSamplingDisk[19] = vec3(0, 1, -1);
gridSamplingInitialized = true;
}
#endif
#endif
\ No newline at end of file
#ifdef USE_SHADOWMAP
varying vec4 vShadowCoord[ MAX_SHADOWS ];
uniform float shadowDarkness[ MAX_SHADOWS ];
uniform mat4 shadowMatrix[ MAX_SHADOWS ];
varying vec4 vShadowCoord[ MAX_SHADOWS ];
#endif
\ No newline at end of file
......@@ -2,7 +2,39 @@
for( int i = 0; i < MAX_SHADOWS; i ++ ) {
vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;
#if defined(POINT_LIGHT_SHADOWS)
// if shadowDarkness[ i ] < 0.0, that means we have a point light with a cube
// shadow map
if( shadowDarkness[ i ] < 0.0 ) {
// calculate vector from light to vertex in view space
vec3 fromLight = mvPosition.xyz - pointLightPosition[ i ];
// Transform 'fromLight' into world space by multiplying it on the left
// side of 'viewMatrix'. This is equivalent to multiplying it on the right
// side of the transpose of 'viewMatrix'. Since 'viewMatrix' is orthogonal,
// its transpose is the same as its inverse.
fromLight = fromLight * mat3( viewMatrix );
// We repurpose vShadowCoord to hold the distance in world space from the
// light to the vertex. This value will be interpolated correctly in the fragment shader.
vShadowCoord[ i ] = vec4( fromLight, 1.0 );
} else {
vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;
}
#else
vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;
#endif
}
......
......@@ -835,6 +835,74 @@ THREE.ShaderLib = {
].join( "\n" )
},
'distanceRGBA': {
uniforms: {
"lightPos": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) }
},
vertexShader: [
"varying vec4 vWorldPosition;",
THREE.ShaderChunk[ "common" ],
THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
THREE.ShaderChunk[ "skinning_pars_vertex" ],
"void main() {",
THREE.ShaderChunk[ "skinbase_vertex" ],
THREE.ShaderChunk[ "begin_vertex" ],
THREE.ShaderChunk[ "morphtarget_vertex" ],
THREE.ShaderChunk[ "skinning_vertex" ],
THREE.ShaderChunk[ "project_vertex" ],
THREE.ShaderChunk[ "worldpos_vertex" ],
"vWorldPosition = worldPosition;",
"}"
].join( "\n" ),
fragmentShader: [
"uniform vec3 lightPos;",
"varying vec4 vWorldPosition;",
THREE.ShaderChunk[ "common" ],
"vec4 pack1K ( float depth ) {",
" depth /= 1000.0;",
" const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );",
" const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );",
" vec4 res = fract( depth * bitSh );",
" res -= res.xxyz * bitMsk;",
" return res; ",
"}",
"float unpack1K ( vec4 color ) {",
" const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );",
" return dot( color, bitSh ) * 1000.0;",
"}",
"void main () {",
" gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );",
"}"
].join( "\n" )
}
};
......@@ -219,6 +219,7 @@ THREE.WebGLProgram = ( function () {
parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '',
parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',
......@@ -330,6 +331,7 @@ THREE.WebGLProgram = ( function () {
parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
parameters.pointLightShadows > 0 ? '#define POINT_LIGHT_SHADOWS' : '',
parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
......
......@@ -20,7 +20,7 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
"maxBones", "useVertexTexture", "morphTargets", "morphNormals",
"maxMorphTargets", "maxMorphNormals", "maxDirLights", "maxPointLights",
"maxSpotLights", "maxHemiLights", "maxShadows", "shadowMapEnabled",
"maxSpotLights", "maxHemiLights", "maxShadows", "shadowMapEnabled", "pointLightShadows",
"shadowMapType", "shadowMapDebug", "alphaTest", "metal", "doubleSided",
"flipSided"
];
......@@ -91,6 +91,7 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
function allocateShadows( lights ) {
var maxShadows = 0;
var pointLightShadows = 0;
for ( var l = 0, ll = lights.length; l < ll; l ++ ) {
......@@ -98,12 +99,17 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
if ( ! light.castShadow ) continue;
if ( light instanceof THREE.SpotLight ) maxShadows ++;
if ( light instanceof THREE.DirectionalLight ) maxShadows ++;
if ( light instanceof THREE.SpotLight || light instanceof THREE.DirectionalLight ) maxShadows ++;
if ( light instanceof THREE.PointLight ) {
maxShadows ++;
pointLightShadows ++;
}
}
return maxShadows;
return { 'maxShadows': maxShadows, 'pointLightShadows': pointLightShadows };
}
......@@ -114,7 +120,7 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
// (not to blow over maxLights budget)
var maxLightCount = allocateLights( lights );
var maxShadows = allocateShadows( lights );
var allocatedShadows = allocateShadows( lights );
var maxBones = allocateBones( object );
var precision = renderer.getPrecision();
......@@ -176,8 +182,9 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
maxSpotLights: maxLightCount.spot,
maxHemiLights: maxLightCount.hemi,
maxShadows: maxShadows,
shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && maxShadows > 0,
maxShadows: allocatedShadows.maxShadows,
pointLightShadows: allocatedShadows.pointLightShadows,
shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && allocatedShadows.maxShadows > 0,
shadowMapType: renderer.shadowMap.type,
shadowMapDebug: renderer.shadowMap.debug,
......
......@@ -13,36 +13,51 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
_min = new THREE.Vector3(),
_max = new THREE.Vector3(),
_matrixPosition = new THREE.Vector3(),
_lookTarget = new THREE.Vector3(),
_lightPositionWorld = new THREE.Vector3(),
_renderList = [];
var _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
_distanceMaterial, _distanceMaterialMorph, _distanceMaterialSkin, _distanceMaterialMorphSkin;
var cubeDirections = [ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ),
new THREE.Vector3( 0, 0, - 1 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, - 1, 0 ) ];
var cubeUps = [ new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ),
new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, - 1 ) ];
var cube2DViewPorts = [ new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(),
new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4() ];
var _vector4 = new THREE.Vector4();
// init
var depthShader = THREE.ShaderLib[ "depthRGBA" ];
var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
var _depthMaterial = new THREE.ShaderMaterial( {
_depthMaterial = new THREE.ShaderMaterial( {
uniforms: depthUniforms,
vertexShader: depthShader.vertexShader,
fragmentShader: depthShader.fragmentShader
} );
var _depthMaterialMorph = new THREE.ShaderMaterial( {
_depthMaterialMorph = new THREE.ShaderMaterial( {
uniforms: depthUniforms,
vertexShader: depthShader.vertexShader,
fragmentShader: depthShader.fragmentShader,
morphTargets: true
} );
var _depthMaterialSkin = new THREE.ShaderMaterial( {
_depthMaterialSkin = new THREE.ShaderMaterial( {
uniforms: depthUniforms,
vertexShader: depthShader.vertexShader,
fragmentShader: depthShader.fragmentShader,
skinning: true
} );
var _depthMaterialMorphSkin = new THREE.ShaderMaterial( {
_depthMaterialMorphSkin = new THREE.ShaderMaterial( {
uniforms: depthUniforms,
vertexShader: depthShader.vertexShader,
fragmentShader: depthShader.fragmentShader,
......@@ -55,6 +70,43 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
_depthMaterialSkin._shadowPass = true;
_depthMaterialMorphSkin._shadowPass = true;
var distanceShader = THREE.ShaderLib[ "distanceRGBA" ];
var distanceUniforms = THREE.UniformsUtils.clone( distanceShader.uniforms );
_distanceMaterial = new THREE.ShaderMaterial( {
uniforms: distanceUniforms,
vertexShader: distanceShader.vertexShader,
fragmentShader: distanceShader.fragmentShader
} );
_distanceMaterialMorph = new THREE.ShaderMaterial( {
uniforms: distanceUniforms,
vertexShader: distanceShader.vertexShader,
fragmentShader: distanceShader.fragmentShader,
morphTargets: true
} );
_distanceMaterialSkin = new THREE.ShaderMaterial( {
uniforms: distanceUniforms,
vertexShader: distanceShader.vertexShader,
fragmentShader: distanceShader.fragmentShader,
skinning: true
} );
_distanceMaterialMorphSkin = new THREE.ShaderMaterial( {
uniforms: distanceUniforms,
vertexShader: distanceShader.vertexShader,
fragmentShader: distanceShader.fragmentShader,
morphTargets: true,
skinning: true
} );
_distanceMaterial._shadowPass = true;
_distanceMaterialMorph._shadowPass = true;
_distanceMaterialSkin._shadowPass = true;
_distanceMaterialMorphSkin._shadowPass = true;
//
var scope = this;
......@@ -69,6 +121,8 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
this.render = function ( scene ) {
var faceCount, isPointLight;
if ( scope.enabled === false ) return;
if ( scope.autoUpdate === false && scope.needsUpdate === false ) return;
......@@ -98,13 +152,58 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
var light = _lights[ i ];
if ( light instanceof THREE.PointLight ) {
faceCount = 6;
isPointLight = true;
var vpWidth = light.shadowMapWidth / 4.0;
var vpHeight = light.shadowMapHeight / 2.0;
/*
*
* These viewports map a cube-map onto a 2D texture with the
* following orientation:
*
* xzXZ
* y Y
*
* Y - Positive y direction
* y - Negative y direction
* X - Positive x direction
* x - Negative x direction
* Z - Positive z direction
* z - Negative z direction
*
*/
// positive X
cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );
// negative X
cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight );
// positive Z
cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight );
// negative Z
cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight );
// positive Y
cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight );
// negative Y
cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight );
} else {
faceCount = 1;
isPointLight = false;
}
if ( ! light.castShadow ) continue;
if ( ! light.shadowMap ) {
var shadowFilter = THREE.LinearFilter;
if ( scope.type === THREE.PCFSoftShadowMap ) {
if ( scope.type === THREE.PCFSoftShadowMap) {
shadowFilter = THREE.NearestFilter;
......@@ -131,8 +230,7 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
} else {
console.error( "THREE.ShadowMapPlugin: Unsupported light type for shadow", light );
continue;
light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, 1.0, light.shadowCameraNear, light.shadowCameraFar );
}
......@@ -153,78 +251,99 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
var shadowMatrix = light.shadowMatrix;
var shadowCamera = light.shadowCamera;
//
_lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
shadowCamera.position.copy( _lightPositionWorld );
shadowCamera.position.setFromMatrixPosition( light.matrixWorld );
_matrixPosition.setFromMatrixPosition( light.target.matrixWorld );
shadowCamera.lookAt( _matrixPosition );
shadowCamera.updateMatrixWorld();
// save the existing viewport so it can be restored later
_renderer.getViewport( _vector4 );
shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
_renderer.setRenderTarget( shadowMap );
_renderer.clear();
//
// render shadow map for each cube face (if omni-directional) or
// run a single pass if not
if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible;
if ( light.shadowCameraVisible ) light.cameraHelper.update();
for ( var face = 0; face < faceCount; face ++ ) {
// compute shadow matrix
if ( isPointLight ) {
shadowMatrix.set(
0.5, 0.0, 0.0, 0.5,
0.0, 0.5, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0
);
_lookTarget.copy( shadowCamera.position );
_lookTarget.add( cubeDirections[ face ] );
shadowCamera.up.copy( cubeUps[ face ] );
shadowCamera.lookAt( _lookTarget );
var vpDimensions = cube2DViewPorts[ face ];
_renderer.setViewport( vpDimensions.x, vpDimensions.y, vpDimensions.z, vpDimensions.w );
shadowMatrix.multiply( shadowCamera.projectionMatrix );
shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
} else {
// update camera matrices and frustum
_lookTarget.setFromMatrixPosition( light.target.matrixWorld );
shadowCamera.lookAt( _lookTarget );
_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
_frustum.setFromMatrix( _projScreenMatrix );
}
// render shadow map
shadowCamera.updateMatrixWorld();
shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
_renderer.setRenderTarget( shadowMap );
_renderer.clear();
if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible;
if ( light.shadowCameraVisible ) light.cameraHelper.update();
// set object matrices & frustum culling
// compute shadow matrix
_renderList.length = 0;
shadowMatrix.set(
0.5, 0.0, 0.0, 0.5,
0.0, 0.5, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0
);
projectObject( scene, shadowCamera );
shadowMatrix.multiply( shadowCamera.projectionMatrix );
shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
// update camera matrices and frustum
// render regular objects
_projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
_frustum.setFromMatrix( _projScreenMatrix );
for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) {
// set object matrices & frustum culling
var object = _renderList[ j ];
var geometry = _objects.update( object );
var material = object.material;
_renderList.length = 0;
projectObject( scene, shadowCamera );
// render shadow map
// render regular objects
for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) {
if ( material instanceof THREE.MeshFaceMaterial ) {
var object = _renderList[ j ];
var geometry = _objects.update( object );
var material = object.material;
var groups = geometry.groups;
var materials = material.materials;
if ( material instanceof THREE.MeshFaceMaterial ) {
for ( var k = 0, kl = groups.length; k < kl; k ++ ) {
var groups = geometry.groups;
var materials = material.materials;
var group = groups[ k ];
var groupMaterial = materials[ group.materialIndex ];
for ( var k = 0, kl = groups.length; k < kl; k ++ ) {
if ( groupMaterial.visible === true ) {
var group = groups[ k ];
var groupMaterial = materials[ group.materialIndex ];
_renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, getDepthMaterial( object, groupMaterial ), object, group );
if ( groupMaterial.visible === true ) {
var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld );
_renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, group );
}
}
}
} else {
} else {
var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld );
_renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, depthMaterial, object, null );
_renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, getDepthMaterial( object, material ), object, null );
}
}
......@@ -246,44 +365,78 @@ THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) {
}
_renderer.setViewport( _vector4.x, _vector4.y, _vector4.z, _vector4.w );
_renderer.resetGLState();
scope.needsUpdate = false;
};
function getDepthMaterial( object, material ) {
function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) {
var geometry = object.geometry;
var useMorphing = geometry.morphTargets !== undefined && geometry.morphTargets.length > 0 && material.morphTargets;
var useSkinning = object instanceof THREE.SkinnedMesh && material.skinning;
var depthMaterial;
var newMaterial;
var depthMaterial = _depthMaterial;
var depthMaterialMorph = _depthMaterialMorph;
var depthMaterialSkin = _depthMaterialSkin;
var depthMaterialMorphSkin = _depthMaterialMorphSkin;
if ( object.customDepthMaterial ) {
if ( isPointLight ) {
depthMaterial = _distanceMaterial;
depthMaterialMorph = _distanceMaterialMorph;
depthMaterialSkin = _distanceMaterialSkin;
depthMaterialMorphSkin = _distanceMaterialMorphSkin;
}
depthMaterial = object.customDepthMaterial;
if ( object.customDepthMaterial || object.customDistanceMaterial ) {
if ( isPointLight ) {
newMaterial = object.customDistanceMaterial;
} else {
newMaterial = object.customDepthMaterial;
}
} else if ( useSkinning ) {
depthMaterial = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
newMaterial = useMorphing ? depthMaterialMorphSkin : depthMaterialSkin;
} else if ( useMorphing ) {
depthMaterial = _depthMaterialMorph;
newMaterial = depthMaterialMorph;
} else {
depthMaterial = _depthMaterial;
newMaterial = depthMaterial;
}
depthMaterial.visible = material.visible;
depthMaterial.wireframe = material.wireframe;
depthMaterial.wireframeLinewidth = material.wireframeLinewidth;
newMaterial.visible = material.visible;
newMaterial.wireframe = material.wireframe;
newMaterial.wireframeLinewidth = material.wireframeLinewidth;
if ( isPointLight ) {
if ( newMaterial.uniforms.lightPos ) {
newMaterial.uniforms.lightPos.value.copy( lightPositionWorld );
}
}
return depthMaterial;
return newMaterial;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册