1 | /**
|
---|
2 | * @author alteredq / http://alteredqualia.com/
|
---|
3 | */
|
---|
4 |
|
---|
5 | THREE.ShadowMapPlugin = function () {
|
---|
6 |
|
---|
7 | var _gl,
|
---|
8 | _renderer,
|
---|
9 | _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
|
---|
10 |
|
---|
11 | _frustum = new THREE.Frustum(),
|
---|
12 | _projScreenMatrix = new THREE.Matrix4(),
|
---|
13 |
|
---|
14 | _min = new THREE.Vector3(),
|
---|
15 | _max = new THREE.Vector3(),
|
---|
16 |
|
---|
17 | _matrixPosition = new THREE.Vector3();
|
---|
18 |
|
---|
19 | this.init = function ( renderer ) {
|
---|
20 |
|
---|
21 | _gl = renderer.context;
|
---|
22 | _renderer = renderer;
|
---|
23 |
|
---|
24 | var depthShader = THREE.ShaderLib[ "depthRGBA" ];
|
---|
25 | var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
|
---|
26 |
|
---|
27 | _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } );
|
---|
28 | _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } );
|
---|
29 | _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } );
|
---|
30 | _depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } );
|
---|
31 |
|
---|
32 | _depthMaterial._shadowPass = true;
|
---|
33 | _depthMaterialMorph._shadowPass = true;
|
---|
34 | _depthMaterialSkin._shadowPass = true;
|
---|
35 | _depthMaterialMorphSkin._shadowPass = true;
|
---|
36 |
|
---|
37 | };
|
---|
38 |
|
---|
39 | this.render = function ( scene, camera ) {
|
---|
40 |
|
---|
41 | if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return;
|
---|
42 |
|
---|
43 | this.update( scene, camera );
|
---|
44 |
|
---|
45 | };
|
---|
46 |
|
---|
47 | this.update = function ( scene, camera ) {
|
---|
48 |
|
---|
49 | var i, il, j, jl, n,
|
---|
50 |
|
---|
51 | shadowMap, shadowMatrix, shadowCamera,
|
---|
52 | program, buffer, material,
|
---|
53 | webglObject, object, light,
|
---|
54 | renderList,
|
---|
55 |
|
---|
56 | lights = [],
|
---|
57 | k = 0,
|
---|
58 |
|
---|
59 | fog = null;
|
---|
60 |
|
---|
61 | // set GL state for depth map
|
---|
62 |
|
---|
63 | _gl.clearColor( 1, 1, 1, 1 );
|
---|
64 | _gl.disable( _gl.BLEND );
|
---|
65 |
|
---|
66 | _gl.enable( _gl.CULL_FACE );
|
---|
67 | _gl.frontFace( _gl.CCW );
|
---|
68 |
|
---|
69 | if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
|
---|
70 |
|
---|
71 | _gl.cullFace( _gl.FRONT );
|
---|
72 |
|
---|
73 | } else {
|
---|
74 |
|
---|
75 | _gl.cullFace( _gl.BACK );
|
---|
76 |
|
---|
77 | }
|
---|
78 |
|
---|
79 | _renderer.setDepthTest( true );
|
---|
80 |
|
---|
81 | // preprocess lights
|
---|
82 | // - skip lights that are not casting shadows
|
---|
83 | // - create virtual lights for cascaded shadow maps
|
---|
84 |
|
---|
85 | for ( i = 0, il = scene.__lights.length; i < il; i ++ ) {
|
---|
86 |
|
---|
87 | light = scene.__lights[ i ];
|
---|
88 |
|
---|
89 | if ( ! light.castShadow ) continue;
|
---|
90 |
|
---|
91 | if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) {
|
---|
92 |
|
---|
93 | for ( n = 0; n < light.shadowCascadeCount; n ++ ) {
|
---|
94 |
|
---|
95 | var virtualLight;
|
---|
96 |
|
---|
97 | if ( ! light.shadowCascadeArray[ n ] ) {
|
---|
98 |
|
---|
99 | virtualLight = createVirtualLight( light, n );
|
---|
100 | virtualLight.originalCamera = camera;
|
---|
101 |
|
---|
102 | var gyro = new THREE.Gyroscope();
|
---|
103 | gyro.position = light.shadowCascadeOffset;
|
---|
104 |
|
---|
105 | gyro.add( virtualLight );
|
---|
106 | gyro.add( virtualLight.target );
|
---|
107 |
|
---|
108 | camera.add( gyro );
|
---|
109 |
|
---|
110 | light.shadowCascadeArray[ n ] = virtualLight;
|
---|
111 |
|
---|
112 | console.log( "Created virtualLight", virtualLight );
|
---|
113 |
|
---|
114 | } else {
|
---|
115 |
|
---|
116 | virtualLight = light.shadowCascadeArray[ n ];
|
---|
117 |
|
---|
118 | }
|
---|
119 |
|
---|
120 | updateVirtualLight( light, n );
|
---|
121 |
|
---|
122 | lights[ k ] = virtualLight;
|
---|
123 | k ++;
|
---|
124 |
|
---|
125 | }
|
---|
126 |
|
---|
127 | } else {
|
---|
128 |
|
---|
129 | lights[ k ] = light;
|
---|
130 | k ++;
|
---|
131 |
|
---|
132 | }
|
---|
133 |
|
---|
134 | }
|
---|
135 |
|
---|
136 | // render depth map
|
---|
137 |
|
---|
138 | for ( i = 0, il = lights.length; i < il; i ++ ) {
|
---|
139 |
|
---|
140 | light = lights[ i ];
|
---|
141 |
|
---|
142 | if ( ! light.shadowMap ) {
|
---|
143 |
|
---|
144 | var shadowFilter = THREE.LinearFilter;
|
---|
145 |
|
---|
146 | if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) {
|
---|
147 |
|
---|
148 | shadowFilter = THREE.NearestFilter;
|
---|
149 |
|
---|
150 | }
|
---|
151 |
|
---|
152 | var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat };
|
---|
153 |
|
---|
154 | light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
|
---|
155 | light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
|
---|
156 |
|
---|
157 | light.shadowMatrix = new THREE.Matrix4();
|
---|
158 |
|
---|
159 | }
|
---|
160 |
|
---|
161 | if ( ! light.shadowCamera ) {
|
---|
162 |
|
---|
163 | if ( light instanceof THREE.SpotLight ) {
|
---|
164 |
|
---|
165 | light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );
|
---|
166 |
|
---|
167 | } else if ( light instanceof THREE.DirectionalLight ) {
|
---|
168 |
|
---|
169 | light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );
|
---|
170 |
|
---|
171 | } else {
|
---|
172 |
|
---|
173 | console.error( "Unsupported light type for shadow" );
|
---|
174 | continue;
|
---|
175 |
|
---|
176 | }
|
---|
177 |
|
---|
178 | scene.add( light.shadowCamera );
|
---|
179 |
|
---|
180 | if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
|
---|
181 |
|
---|
182 | }
|
---|
183 |
|
---|
184 | if ( light.shadowCameraVisible && ! light.cameraHelper ) {
|
---|
185 |
|
---|
186 | light.cameraHelper = new THREE.CameraHelper( light.shadowCamera );
|
---|
187 | light.shadowCamera.add( light.cameraHelper );
|
---|
188 |
|
---|
189 | }
|
---|
190 |
|
---|
191 | if ( light.isVirtual && virtualLight.originalCamera == camera ) {
|
---|
192 |
|
---|
193 | updateShadowCamera( camera, light );
|
---|
194 |
|
---|
195 | }
|
---|
196 |
|
---|
197 | shadowMap = light.shadowMap;
|
---|
198 | shadowMatrix = light.shadowMatrix;
|
---|
199 | shadowCamera = light.shadowCamera;
|
---|
200 |
|
---|
201 | shadowCamera.position.setFromMatrixPosition( light.matrixWorld );
|
---|
202 | _matrixPosition.setFromMatrixPosition( light.target.matrixWorld );
|
---|
203 | shadowCamera.lookAt( _matrixPosition );
|
---|
204 | shadowCamera.updateMatrixWorld();
|
---|
205 |
|
---|
206 | shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
|
---|
207 |
|
---|
208 | if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible;
|
---|
209 | if ( light.shadowCameraVisible ) light.cameraHelper.update();
|
---|
210 |
|
---|
211 | // compute shadow matrix
|
---|
212 |
|
---|
213 | shadowMatrix.set( 0.5, 0.0, 0.0, 0.5,
|
---|
214 | 0.0, 0.5, 0.0, 0.5,
|
---|
215 | 0.0, 0.0, 0.5, 0.5,
|
---|
216 | 0.0, 0.0, 0.0, 1.0 );
|
---|
217 |
|
---|
218 | shadowMatrix.multiply( shadowCamera.projectionMatrix );
|
---|
219 | shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
|
---|
220 |
|
---|
221 | // update camera matrices and frustum
|
---|
222 |
|
---|
223 | _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
|
---|
224 | _frustum.setFromMatrix( _projScreenMatrix );
|
---|
225 |
|
---|
226 | // render shadow map
|
---|
227 |
|
---|
228 | _renderer.setRenderTarget( shadowMap );
|
---|
229 | _renderer.clear();
|
---|
230 |
|
---|
231 | // set object matrices & frustum culling
|
---|
232 |
|
---|
233 | renderList = scene.__webglObjects;
|
---|
234 |
|
---|
235 | for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
|
---|
236 |
|
---|
237 | webglObject = renderList[ j ];
|
---|
238 | object = webglObject.object;
|
---|
239 |
|
---|
240 | webglObject.render = false;
|
---|
241 |
|
---|
242 | if ( object.visible && object.castShadow ) {
|
---|
243 |
|
---|
244 | if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) {
|
---|
245 |
|
---|
246 | object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
|
---|
247 |
|
---|
248 | webglObject.render = true;
|
---|
249 |
|
---|
250 | }
|
---|
251 |
|
---|
252 | }
|
---|
253 |
|
---|
254 | }
|
---|
255 |
|
---|
256 | // render regular objects
|
---|
257 |
|
---|
258 | var objectMaterial, useMorphing, useSkinning;
|
---|
259 |
|
---|
260 | for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
|
---|
261 |
|
---|
262 | webglObject = renderList[ j ];
|
---|
263 |
|
---|
264 | if ( webglObject.render ) {
|
---|
265 |
|
---|
266 | object = webglObject.object;
|
---|
267 | buffer = webglObject.buffer;
|
---|
268 |
|
---|
269 | // culling is overriden globally for all objects
|
---|
270 | // while rendering depth map
|
---|
271 |
|
---|
272 | // need to deal with MeshFaceMaterial somehow
|
---|
273 | // in that case just use the first of material.materials for now
|
---|
274 | // (proper solution would require to break objects by materials
|
---|
275 | // similarly to regular rendering and then set corresponding
|
---|
276 | // depth materials per each chunk instead of just once per object)
|
---|
277 |
|
---|
278 | objectMaterial = getObjectMaterial( object );
|
---|
279 |
|
---|
280 | useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets;
|
---|
281 | useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning;
|
---|
282 |
|
---|
283 | if ( object.customDepthMaterial ) {
|
---|
284 |
|
---|
285 | material = object.customDepthMaterial;
|
---|
286 |
|
---|
287 | } else if ( useSkinning ) {
|
---|
288 |
|
---|
289 | material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin;
|
---|
290 |
|
---|
291 | } else if ( useMorphing ) {
|
---|
292 |
|
---|
293 | material = _depthMaterialMorph;
|
---|
294 |
|
---|
295 | } else {
|
---|
296 |
|
---|
297 | material = _depthMaterial;
|
---|
298 |
|
---|
299 | }
|
---|
300 |
|
---|
301 | if ( buffer instanceof THREE.BufferGeometry ) {
|
---|
302 |
|
---|
303 | _renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object );
|
---|
304 |
|
---|
305 | } else {
|
---|
306 |
|
---|
307 | _renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object );
|
---|
308 |
|
---|
309 | }
|
---|
310 |
|
---|
311 | }
|
---|
312 |
|
---|
313 | }
|
---|
314 |
|
---|
315 | // set matrices and render immediate objects
|
---|
316 |
|
---|
317 | renderList = scene.__webglObjectsImmediate;
|
---|
318 |
|
---|
319 | for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
|
---|
320 |
|
---|
321 | webglObject = renderList[ j ];
|
---|
322 | object = webglObject.object;
|
---|
323 |
|
---|
324 | if ( object.visible && object.castShadow ) {
|
---|
325 |
|
---|
326 | object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
|
---|
327 |
|
---|
328 | _renderer.renderImmediateObject( shadowCamera, scene.__lights, fog, _depthMaterial, object );
|
---|
329 |
|
---|
330 | }
|
---|
331 |
|
---|
332 | }
|
---|
333 |
|
---|
334 | }
|
---|
335 |
|
---|
336 | // restore GL state
|
---|
337 |
|
---|
338 | var clearColor = _renderer.getClearColor(),
|
---|
339 | clearAlpha = _renderer.getClearAlpha();
|
---|
340 |
|
---|
341 | _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
|
---|
342 | _gl.enable( _gl.BLEND );
|
---|
343 |
|
---|
344 | if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) {
|
---|
345 |
|
---|
346 | _gl.cullFace( _gl.BACK );
|
---|
347 |
|
---|
348 | }
|
---|
349 |
|
---|
350 | };
|
---|
351 |
|
---|
352 | function createVirtualLight( light, cascade ) {
|
---|
353 |
|
---|
354 | var virtualLight = new THREE.DirectionalLight();
|
---|
355 |
|
---|
356 | virtualLight.isVirtual = true;
|
---|
357 |
|
---|
358 | virtualLight.onlyShadow = true;
|
---|
359 | virtualLight.castShadow = true;
|
---|
360 |
|
---|
361 | virtualLight.shadowCameraNear = light.shadowCameraNear;
|
---|
362 | virtualLight.shadowCameraFar = light.shadowCameraFar;
|
---|
363 |
|
---|
364 | virtualLight.shadowCameraLeft = light.shadowCameraLeft;
|
---|
365 | virtualLight.shadowCameraRight = light.shadowCameraRight;
|
---|
366 | virtualLight.shadowCameraBottom = light.shadowCameraBottom;
|
---|
367 | virtualLight.shadowCameraTop = light.shadowCameraTop;
|
---|
368 |
|
---|
369 | virtualLight.shadowCameraVisible = light.shadowCameraVisible;
|
---|
370 |
|
---|
371 | virtualLight.shadowDarkness = light.shadowDarkness;
|
---|
372 |
|
---|
373 | virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
|
---|
374 | virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
|
---|
375 | virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];
|
---|
376 |
|
---|
377 | virtualLight.pointsWorld = [];
|
---|
378 | virtualLight.pointsFrustum = [];
|
---|
379 |
|
---|
380 | var pointsWorld = virtualLight.pointsWorld,
|
---|
381 | pointsFrustum = virtualLight.pointsFrustum;
|
---|
382 |
|
---|
383 | for ( var i = 0; i < 8; i ++ ) {
|
---|
384 |
|
---|
385 | pointsWorld[ i ] = new THREE.Vector3();
|
---|
386 | pointsFrustum[ i ] = new THREE.Vector3();
|
---|
387 |
|
---|
388 | }
|
---|
389 |
|
---|
390 | var nearZ = light.shadowCascadeNearZ[ cascade ];
|
---|
391 | var farZ = light.shadowCascadeFarZ[ cascade ];
|
---|
392 |
|
---|
393 | pointsFrustum[ 0 ].set( -1, -1, nearZ );
|
---|
394 | pointsFrustum[ 1 ].set( 1, -1, nearZ );
|
---|
395 | pointsFrustum[ 2 ].set( -1, 1, nearZ );
|
---|
396 | pointsFrustum[ 3 ].set( 1, 1, nearZ );
|
---|
397 |
|
---|
398 | pointsFrustum[ 4 ].set( -1, -1, farZ );
|
---|
399 | pointsFrustum[ 5 ].set( 1, -1, farZ );
|
---|
400 | pointsFrustum[ 6 ].set( -1, 1, farZ );
|
---|
401 | pointsFrustum[ 7 ].set( 1, 1, farZ );
|
---|
402 |
|
---|
403 | return virtualLight;
|
---|
404 |
|
---|
405 | }
|
---|
406 |
|
---|
407 | // Synchronize virtual light with the original light
|
---|
408 |
|
---|
409 | function updateVirtualLight( light, cascade ) {
|
---|
410 |
|
---|
411 | var virtualLight = light.shadowCascadeArray[ cascade ];
|
---|
412 |
|
---|
413 | virtualLight.position.copy( light.position );
|
---|
414 | virtualLight.target.position.copy( light.target.position );
|
---|
415 | virtualLight.lookAt( virtualLight.target );
|
---|
416 |
|
---|
417 | virtualLight.shadowCameraVisible = light.shadowCameraVisible;
|
---|
418 | virtualLight.shadowDarkness = light.shadowDarkness;
|
---|
419 |
|
---|
420 | virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
|
---|
421 |
|
---|
422 | var nearZ = light.shadowCascadeNearZ[ cascade ];
|
---|
423 | var farZ = light.shadowCascadeFarZ[ cascade ];
|
---|
424 |
|
---|
425 | var pointsFrustum = virtualLight.pointsFrustum;
|
---|
426 |
|
---|
427 | pointsFrustum[ 0 ].z = nearZ;
|
---|
428 | pointsFrustum[ 1 ].z = nearZ;
|
---|
429 | pointsFrustum[ 2 ].z = nearZ;
|
---|
430 | pointsFrustum[ 3 ].z = nearZ;
|
---|
431 |
|
---|
432 | pointsFrustum[ 4 ].z = farZ;
|
---|
433 | pointsFrustum[ 5 ].z = farZ;
|
---|
434 | pointsFrustum[ 6 ].z = farZ;
|
---|
435 | pointsFrustum[ 7 ].z = farZ;
|
---|
436 |
|
---|
437 | }
|
---|
438 |
|
---|
439 | // Fit shadow camera's ortho frustum to camera frustum
|
---|
440 |
|
---|
441 | function updateShadowCamera( camera, light ) {
|
---|
442 |
|
---|
443 | var shadowCamera = light.shadowCamera,
|
---|
444 | pointsFrustum = light.pointsFrustum,
|
---|
445 | pointsWorld = light.pointsWorld;
|
---|
446 |
|
---|
447 | _min.set( Infinity, Infinity, Infinity );
|
---|
448 | _max.set( -Infinity, -Infinity, -Infinity );
|
---|
449 |
|
---|
450 | for ( var i = 0; i < 8; i ++ ) {
|
---|
451 |
|
---|
452 | var p = pointsWorld[ i ];
|
---|
453 |
|
---|
454 | p.copy( pointsFrustum[ i ] );
|
---|
455 | THREE.ShadowMapPlugin.__projector.unprojectVector( p, camera );
|
---|
456 |
|
---|
457 | p.applyMatrix4( shadowCamera.matrixWorldInverse );
|
---|
458 |
|
---|
459 | if ( p.x < _min.x ) _min.x = p.x;
|
---|
460 | if ( p.x > _max.x ) _max.x = p.x;
|
---|
461 |
|
---|
462 | if ( p.y < _min.y ) _min.y = p.y;
|
---|
463 | if ( p.y > _max.y ) _max.y = p.y;
|
---|
464 |
|
---|
465 | if ( p.z < _min.z ) _min.z = p.z;
|
---|
466 | if ( p.z > _max.z ) _max.z = p.z;
|
---|
467 |
|
---|
468 | }
|
---|
469 |
|
---|
470 | shadowCamera.left = _min.x;
|
---|
471 | shadowCamera.right = _max.x;
|
---|
472 | shadowCamera.top = _max.y;
|
---|
473 | shadowCamera.bottom = _min.y;
|
---|
474 |
|
---|
475 | // can't really fit near/far
|
---|
476 | //shadowCamera.near = _min.z;
|
---|
477 | //shadowCamera.far = _max.z;
|
---|
478 |
|
---|
479 | shadowCamera.updateProjectionMatrix();
|
---|
480 |
|
---|
481 | }
|
---|
482 |
|
---|
483 | // For the moment just ignore objects that have multiple materials with different animation methods
|
---|
484 | // Only the first material will be taken into account for deciding which depth material to use for shadow maps
|
---|
485 |
|
---|
486 | function getObjectMaterial( object ) {
|
---|
487 |
|
---|
488 | return object.material instanceof THREE.MeshFaceMaterial
|
---|
489 | ? object.material.materials[ 0 ]
|
---|
490 | : object.material;
|
---|
491 |
|
---|
492 | };
|
---|
493 |
|
---|
494 | };
|
---|
495 |
|
---|
496 | THREE.ShadowMapPlugin.__projector = new THREE.Projector();
|
---|