deeptwins-engine-3d 0.1.49 → 0.1.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/esm/constant.d.ts +9 -0
  2. package/dist/esm/constant.js +16 -0
  3. package/dist/esm/drawCommand/index.d.ts +2 -0
  4. package/dist/esm/drawCommand/index.js +2 -0
  5. package/dist/esm/drawCommand/viewShed/Event.d.ts +42 -0
  6. package/dist/esm/drawCommand/viewShed/Event.js +142 -0
  7. package/dist/esm/drawCommand/viewShed/RectangularSensorPrimitive.d.ts +266 -0
  8. package/dist/esm/drawCommand/viewShed/RectangularSensorPrimitive.js +1271 -0
  9. package/dist/esm/drawCommand/viewShed/ViewShadowPrimitive.d.ts +29 -0
  10. package/dist/esm/drawCommand/viewShed/ViewShadowPrimitive.js +66 -0
  11. package/dist/esm/drawCommand/viewShed/ViewShedAnalyserLayer.d.ts +182 -0
  12. package/dist/esm/drawCommand/viewShed/ViewShedAnalyserLayer.js +466 -0
  13. package/dist/esm/drawCommand/viewShed/ViewShedMap.d.ts +22 -0
  14. package/dist/esm/drawCommand/viewShed/ViewShedMap.js +1238 -0
  15. package/dist/esm/drawCommand/viewShed/config.d.ts +20 -0
  16. package/dist/esm/drawCommand/viewShed/config.js +21 -0
  17. package/dist/esm/graphicLayer/PolygonGroundPrimitiveImageMaterialInstance.js +1 -1
  18. package/dist/esm/index.d.ts +2 -1
  19. package/dist/esm/index.js +7 -4
  20. package/dist/esm/map/Map.js +1 -1
  21. package/dist/esm/material/primitive/ImageMaterialAppearance.d.ts +1 -1
  22. package/dist/esm/tool/BuildClampedPolygonGrid.d.ts +2 -9
  23. package/dist/esm/tool/BuildClampedPolygonGrid.js +195 -73
  24. package/dist/esm/tool/utils.d.ts +1 -0
  25. package/dist/esm/tool/utils.js +21 -0
  26. package/dist/esm/videoFusion/BaseVideo.js +17 -17
  27. package/dist/esm/videoFusion/VideoProject.d.ts +5 -5
  28. package/dist/esm/videoFusion/VideoProject.js +174 -184
  29. package/dist/esm/videoFusion/VideoTexture.d.ts +5 -1
  30. package/dist/esm/videoFusion/VideoTexture.js +133 -46
  31. package/dist/umd/deeptwins-engine-3d.min.js +1 -1
  32. package/package.json +4 -2
@@ -0,0 +1,1238 @@
1
+ import * as Cesium from 'deeptwins-cesium';
2
+
3
+ /**
4
+ *
5
+ * @alias ViewshedMap
6
+ * @param {Object} options An object containing the following properties:
7
+ * @param {Cesium.Camera} options.lightCamera A camera representing the light source.
8
+ * @param {Boolean} [options.enabled=true] Whether the shadow map is enabled.
9
+ * @param {Boolean} [options.isPointLight=false] Whether the light source is a point light. Point light shadows do not use cascades.
10
+ * @param {Boolean} [options.pointLightRadius=100.0] Radius of the point light.
11
+ * @param {Boolean} [options.cascadesEnabled=true] Use multiple shadow maps to cover different partitions of the view frustum.
12
+ * @param {Number} [options.numberOfCascades=4] The number of cascades to use for the shadow map. Supported values are one and four.
13
+ * @param {Number} [options.maximumDistance=5000.0] The maximum distance used for generating cascaded shadows. Lower values improve shadow quality.
14
+ * @param {Number} [options.size=2048] The width and height, in pixels, of each shadow map.
15
+ * @param {Boolean} [options.softShadows=false] Whether percentage-closer-filtering is enabled for producing softer shadows.
16
+ * @param {Number} [options.darkness=0.3] The shadow darkness.
17
+ * @param {Boolean} [options.normalOffset=true] Whether a normal bias is applied to shadows.
18
+ * @demo {@link https://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Shadows.html|Cesium Sandcastle Shadows Demo}
19
+ */
20
+ function ViewshedMap(options) {
21
+ var _options, _options$enabled, _options$softShadows, _options$normalOffset, _options$fromLightSou, _options$maximumDista, _options$boundingSphe, _options$isPointLight, _options$pointLightRa, _options$numberOfCasc, _options$size;
22
+ options = (_options = options) !== null && _options !== void 0 ? _options : Cesium.defaultValue.EMPTY_OBJECT;
23
+ var context = options.context;
24
+ if (!Cesium.defined(context)) {
25
+ throw new Cesium.DeveloperError('context is required.');
26
+ }
27
+ if (!Cesium.defined(options.lightCamera)) {
28
+ throw new Cesium.DeveloperError('lightCamera is required.');
29
+ }
30
+ if (Cesium.defined(options.numberOfCascades) && options.numberOfCascades !== 1 && options.numberOfCascades !== 4) {
31
+ throw new Cesium.DeveloperError('Only one or four cascades are supported.');
32
+ }
33
+ this._enabled = (_options$enabled = options.enabled) !== null && _options$enabled !== void 0 ? _options$enabled : true;
34
+ this._softShadows = (_options$softShadows = options.softShadows) !== null && _options$softShadows !== void 0 ? _options$softShadows : false;
35
+ this._normalOffset = (_options$normalOffset = options.normalOffset) !== null && _options$normalOffset !== void 0 ? _options$normalOffset : true;
36
+ this.dirty = true;
37
+ this.fromLightSource = (_options$fromLightSou = options.fromLightSource) !== null && _options$fromLightSou !== void 0 ? _options$fromLightSou : true;
38
+ this.darkness = 0.0;
39
+ this._darkness = this.darkness;
40
+ this.maximumDistance = (_options$maximumDista = options.maximumDistance) !== null && _options$maximumDista !== void 0 ? _options$maximumDista : 5000.0;
41
+ this._outOfView = false;
42
+ this._outOfViewPrevious = false;
43
+ this._needsUpdate = true;
44
+ // Re-enable once https://github.com/AnalyticalGraphicsInc/cesium/issues/4560 is resolved.
45
+ var polygonOffsetSupported = true;
46
+ if (Cesium.FeatureDetection.isInternetExplorer() || Cesium.FeatureDetection.isEdge() || (Cesium.FeatureDetection.isChrome() || Cesium.FeatureDetection.isFirefox()) && Cesium.FeatureDetection.isWindows() && !context.depthTexture) {
47
+ polygonOffsetSupported = false;
48
+ }
49
+ this._polygonOffsetSupported = polygonOffsetSupported;
50
+ this._terrainBias = {
51
+ polygonOffset: polygonOffsetSupported,
52
+ polygonOffsetFactor: 1.1,
53
+ polygonOffsetUnits: 4.0,
54
+ normalOffset: this._normalOffset,
55
+ normalOffsetScale: 0.5,
56
+ normalShading: true,
57
+ normalShadingSmooth: 0.3,
58
+ depthBias: 0.0001
59
+ };
60
+ this._primitiveBias = {
61
+ polygonOffset: polygonOffsetSupported,
62
+ polygonOffsetFactor: 1.1,
63
+ polygonOffsetUnits: 4.0,
64
+ normalOffset: this._normalOffset,
65
+ normalOffsetScale: 0.1,
66
+ normalShading: true,
67
+ normalShadingSmooth: 0.05,
68
+ depthBias: 0.00002
69
+ };
70
+ this._pointBias = {
71
+ polygonOffset: false,
72
+ polygonOffsetFactor: 1.1,
73
+ polygonOffsetUnits: 4.0,
74
+ normalOffset: this._normalOffset,
75
+ normalOffsetScale: 0.0,
76
+ normalShading: true,
77
+ normalShadingSmooth: 0.1,
78
+ depthBias: 0.0005
79
+ };
80
+
81
+ // Cesium.Framebuffer resources
82
+ this._depthAttachment = undefined;
83
+ this._colorAttachment = undefined;
84
+
85
+ // Uniforms
86
+ this._shadowMapMatrix = new Cesium.Matrix4();
87
+ this._shadowMapTexture = undefined;
88
+ this._lightDirectionEC = new Cesium.Cartesian3();
89
+ this._lightPositionEC = new Cesium.Cartesian4();
90
+ this._distance = 0.0;
91
+ this._lightCamera = options.lightCamera;
92
+ this._shadowMapCamera = new ShadowMapCamera();
93
+ this._shadowMapCullingVolume = undefined;
94
+ this._sceneCamera = undefined;
95
+ // this._boundingSphere = new Cesium.BoundingSphere();
96
+ this._boundingSphere = (_options$boundingSphe = options.boundingSphere) !== null && _options$boundingSphe !== void 0 ? _options$boundingSphe : new Cesium.BoundingSphere();
97
+ this._isPointLight = (_options$isPointLight = options.isPointLight) !== null && _options$isPointLight !== void 0 ? _options$isPointLight : false;
98
+ this._pointLightRadius = (_options$pointLightRa = options.pointLightRadius) !== null && _options$pointLightRa !== void 0 ? _options$pointLightRa : 100.0;
99
+ this._cascadesEnabled = false;
100
+ this._numberOfCascades = !this._cascadesEnabled ? 0 : (_options$numberOfCasc = options.numberOfCascades) !== null && _options$numberOfCasc !== void 0 ? _options$numberOfCasc : 4;
101
+ this._fitNearFar = true;
102
+ this._maximumCascadeDistances = [25.0, 150.0, 700.0, Number.MAX_VALUE];
103
+ this._textureSize = new Cesium.Cartesian2();
104
+ this._isSpotLight = false;
105
+ if (this._cascadesEnabled) {
106
+ // Cascaded shadows are always orthographic. The frustum dimensions are calculated on the fly.
107
+ this._shadowMapCamera.frustum = new Cesium.OrthographicOffCenterFrustum();
108
+ } else if (Cesium.defined(this._lightCamera.frustum.fov)) {
109
+ // If the light camera uses a perspective frustum, then the light source is a spot light
110
+ this._isSpotLight = true;
111
+ }
112
+
113
+ // Uniforms
114
+ this._cascadeSplits = [new Cesium.Cartesian4(), new Cesium.Cartesian4()];
115
+ this._cascadeMatrices = [new Cesium.Matrix4(), new Cesium.Matrix4(), new Cesium.Matrix4(), new Cesium.Matrix4()];
116
+ this._cascadeDistances = new Cesium.Cartesian4();
117
+ var numberOfPasses;
118
+ if (this._isPointLight) {
119
+ numberOfPasses = 6; // One shadow map for each direction
120
+ } else if (!this._cascadesEnabled) {
121
+ numberOfPasses = 1;
122
+ } else {
123
+ numberOfPasses = this._numberOfCascades;
124
+ }
125
+ this._passes = new Array(numberOfPasses);
126
+ for (var i = 0; i < numberOfPasses; ++i) {
127
+ this._passes[i] = new ShadowPass(context);
128
+ }
129
+ this.debugShow = false;
130
+ this.debugFreezeFrame = false;
131
+ this._debugFreezeFrame = false;
132
+ this._debugCascadeColors = false;
133
+ this._debugLightFrustum = undefined;
134
+ this._debugCameraFrustum = undefined;
135
+ this._debugCascadeFrustums = new Array(this._numberOfCascades);
136
+ this._debugShadowViewCommand = undefined;
137
+ this._usesDepthTexture = context.depthTexture;
138
+ if (this._isPointLight) {
139
+ this._usesDepthTexture = false;
140
+ }
141
+
142
+ // Create render states for shadow casters
143
+ this._primitiveRenderState = undefined;
144
+ this._terrainRenderState = undefined;
145
+ this._pointRenderState = undefined;
146
+ createRenderStates(this);
147
+
148
+ // For clearing the shadow map texture every frame
149
+ this._clearCommand = new Cesium.ClearCommand({
150
+ depth: 1.0,
151
+ color: new Cesium.Color()
152
+ });
153
+ this._clearPassState = new Cesium.PassState(context);
154
+ this._size = (_options$size = options.size) !== null && _options$size !== void 0 ? _options$size : 2048;
155
+ this.size = this._size;
156
+ }
157
+
158
+ /**
159
+ * Global maximum shadow distance used to prevent far off receivers from extending
160
+ * the shadow far plane. This helps set a tighter near/far when viewing objects from space.
161
+ *
162
+ * @private
163
+ */
164
+ ViewshedMap.MAXIMUM_DISTANCE = 20000.0;
165
+ function ShadowPass(context) {
166
+ this.camera = new ShadowMapCamera();
167
+ this.passState = new Cesium.PassState(context);
168
+ this.framebuffer = undefined;
169
+ this.textureOffsets = undefined;
170
+ this.commandList = [];
171
+ this.cullingVolume = undefined;
172
+ }
173
+ function createRenderState(colorMask, bias) {
174
+ return Cesium.RenderState.fromCache({
175
+ cull: {
176
+ enabled: true,
177
+ face: Cesium.CullFace.BACK
178
+ },
179
+ depthTest: {
180
+ enabled: true
181
+ },
182
+ colorMask: {
183
+ red: colorMask,
184
+ green: colorMask,
185
+ blue: colorMask,
186
+ alpha: colorMask
187
+ },
188
+ depthMask: true,
189
+ polygonOffset: {
190
+ enabled: bias.polygonOffset,
191
+ factor: bias.polygonOffsetFactor,
192
+ units: bias.polygonOffsetUnits
193
+ }
194
+ });
195
+ }
196
+ function createRenderStates(shadowMap) {
197
+ // Enable the color mask if the shadow map is backed by a color texture, e.g. when depth textures aren't supported
198
+ var colorMask = !shadowMap._usesDepthTexture;
199
+ shadowMap._primitiveRenderState = createRenderState(colorMask, shadowMap._primitiveBias);
200
+ shadowMap._terrainRenderState = createRenderState(colorMask, shadowMap._terrainBias);
201
+ shadowMap._pointRenderState = createRenderState(colorMask, shadowMap._pointBias);
202
+ }
203
+
204
+ /**
205
+ * @private
206
+ */
207
+ ViewshedMap.prototype.debugCreateRenderStates = function () {
208
+ createRenderStates(this);
209
+ };
210
+ Object.defineProperties(ViewshedMap.prototype, {
211
+ enabled: {
212
+ get: function get() {
213
+ return this._enabled;
214
+ },
215
+ set: function set(value) {
216
+ this.dirty = this._enabled !== value;
217
+ this._enabled = value;
218
+ }
219
+ },
220
+ isViewShed: {
221
+ get: function get() {
222
+ return true;
223
+ }
224
+ },
225
+ normalOffset: {
226
+ get: function get() {
227
+ return this._normalOffset;
228
+ },
229
+ set: function set(value) {
230
+ this.dirty = this._normalOffset !== value;
231
+ this._normalOffset = value;
232
+ this._terrainBias.normalOffset = value;
233
+ this._primitiveBias.normalOffset = value;
234
+ this._pointBias.normalOffset = value;
235
+ }
236
+ },
237
+ softShadows: {
238
+ get: function get() {
239
+ return this._softShadows;
240
+ },
241
+ set: function set(value) {
242
+ this.dirty = this._softShadows !== value;
243
+ this._softShadows = value;
244
+ }
245
+ },
246
+ size: {
247
+ get: function get() {
248
+ return this._size;
249
+ },
250
+ set: function set(value) {
251
+ resize(this, value);
252
+ }
253
+ },
254
+ outOfView: {
255
+ get: function get() {
256
+ return this._outOfView;
257
+ }
258
+ },
259
+ shadowMapCullingVolume: {
260
+ get: function get() {
261
+ return this._shadowMapCullingVolume;
262
+ }
263
+ },
264
+ passes: {
265
+ get: function get() {
266
+ return this._passes;
267
+ }
268
+ },
269
+ isPointLight: {
270
+ get: function get() {
271
+ return this._isPointLight;
272
+ }
273
+ },
274
+ debugCascadeColors: {
275
+ get: function get() {
276
+ return this._debugCascadeColors;
277
+ },
278
+ set: function set(value) {
279
+ this.dirty = this._debugCascadeColors !== value;
280
+ this._debugCascadeColors = value;
281
+ }
282
+ }
283
+ });
284
+ function destroyFramebuffer(shadowMap) {
285
+ var length = shadowMap._passes.length;
286
+ for (var i = 0; i < length; ++i) {
287
+ var pass = shadowMap._passes[i];
288
+ var framebuffer = pass.framebuffer;
289
+ if (Cesium.defined(framebuffer) && !framebuffer.isDestroyed()) {
290
+ framebuffer.destroy();
291
+ }
292
+ pass.framebuffer = undefined;
293
+ }
294
+
295
+ // Destroy the framebuffer attachments
296
+ shadowMap._depthAttachment = shadowMap._depthAttachment && shadowMap._depthAttachment.destroy();
297
+ shadowMap._colorAttachment = shadowMap._colorAttachment && shadowMap._colorAttachment.destroy();
298
+ }
299
+ function createSampler() {
300
+ return new Cesium.Sampler({
301
+ wrapS: Cesium.TextureWrap.CLAMP_TO_EDGE,
302
+ wrapT: Cesium.TextureWrap.CLAMP_TO_EDGE,
303
+ minificationFilter: Cesium.TextureMinificationFilter.NEAREST,
304
+ magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST
305
+ });
306
+ }
307
+ function createFramebufferColor(shadowMap, context) {
308
+ var depthRenderbuffer = new Cesium.Renderbuffer({
309
+ context: context,
310
+ width: shadowMap._textureSize.x,
311
+ height: shadowMap._textureSize.y,
312
+ format: Cesium.RenderbufferFormat.DEPTH_COMPONENT16
313
+ });
314
+ var colorTexture = new Cesium.Texture({
315
+ context: context,
316
+ width: shadowMap._textureSize.x,
317
+ height: shadowMap._textureSize.y,
318
+ pixelFormat: Cesium.PixelFormat.RGBA,
319
+ pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE,
320
+ sampler: createSampler()
321
+ });
322
+ var framebuffer = new Cesium.Framebuffer({
323
+ context: context,
324
+ depthRenderbuffer: depthRenderbuffer,
325
+ colorTextures: [colorTexture],
326
+ destroyAttachments: false
327
+ });
328
+ var length = shadowMap._passes.length;
329
+ for (var i = 0; i < length; ++i) {
330
+ var pass = shadowMap._passes[i];
331
+ pass.framebuffer = framebuffer;
332
+ pass.passState.framebuffer = framebuffer;
333
+ }
334
+ shadowMap._shadowMapTexture = colorTexture;
335
+ shadowMap._depthAttachment = depthRenderbuffer;
336
+ shadowMap._colorAttachment = colorTexture;
337
+ }
338
+ function createFramebufferDepth(shadowMap, context) {
339
+ var depthStencilTexture = new Cesium.Texture({
340
+ context: context,
341
+ width: shadowMap._textureSize.x,
342
+ height: shadowMap._textureSize.y,
343
+ pixelFormat: Cesium.PixelFormat.DEPTH_STENCIL,
344
+ pixelDatatype: Cesium.PixelDatatype.UNSIGNED_INT_24_8,
345
+ sampler: createSampler()
346
+ });
347
+ var framebuffer = new Cesium.Framebuffer({
348
+ context: context,
349
+ depthStencilTexture: depthStencilTexture,
350
+ destroyAttachments: false
351
+ });
352
+ var length = shadowMap._passes.length;
353
+ for (var i = 0; i < length; ++i) {
354
+ var pass = shadowMap._passes[i];
355
+ pass.framebuffer = framebuffer;
356
+ pass.passState.framebuffer = framebuffer;
357
+ }
358
+ shadowMap._shadowMapTexture = depthStencilTexture;
359
+ shadowMap._depthAttachment = depthStencilTexture;
360
+ }
361
+ function createFramebufferCube(shadowMap, context) {
362
+ var depthRenderbuffer = new Cesium.Renderbuffer({
363
+ context: context,
364
+ width: shadowMap._textureSize.x,
365
+ height: shadowMap._textureSize.y,
366
+ format: Cesium.RenderbufferFormat.DEPTH_COMPONENT16
367
+ });
368
+ var cubeMap = new Cesium.CubeMap({
369
+ context: context,
370
+ width: shadowMap._textureSize.x,
371
+ height: shadowMap._textureSize.y,
372
+ pixelFormat: Cesium.PixelFormat.RGBA,
373
+ pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE,
374
+ sampler: createSampler()
375
+ });
376
+ var faces = [cubeMap.negativeX, cubeMap.negativeY, cubeMap.negativeZ, cubeMap.positiveX, cubeMap.positiveY, cubeMap.positiveZ];
377
+ for (var i = 0; i < 6; ++i) {
378
+ var framebuffer = new Cesium.Framebuffer({
379
+ context: context,
380
+ depthRenderbuffer: depthRenderbuffer,
381
+ colorTextures: [faces[i]],
382
+ destroyAttachments: false
383
+ });
384
+ var pass = shadowMap._passes[i];
385
+ pass.framebuffer = framebuffer;
386
+ pass.passState.framebuffer = framebuffer;
387
+ }
388
+ shadowMap._shadowMapTexture = cubeMap;
389
+ shadowMap._depthAttachment = depthRenderbuffer;
390
+ shadowMap._colorAttachment = cubeMap;
391
+ }
392
+ function createFramebuffer(shadowMap, context) {
393
+ if (shadowMap._isPointLight) {
394
+ createFramebufferCube(shadowMap, context);
395
+ } else if (shadowMap._usesDepthTexture) {
396
+ createFramebufferDepth(shadowMap, context);
397
+ } else {
398
+ createFramebufferColor(shadowMap, context);
399
+ }
400
+ }
401
+ function checkFramebuffer(shadowMap, context) {
402
+ // Attempt to make an FBO with only a depth texture. If it fails, fallback to a color texture.
403
+ if (shadowMap._usesDepthTexture && shadowMap._passes[0].framebuffer.status !== Cesium.WebGLConstants.FRAMEBUFFER_COMPLETE) {
404
+ shadowMap._usesDepthTexture = false;
405
+ createRenderStates(shadowMap);
406
+ destroyFramebuffer(shadowMap);
407
+ createFramebuffer(shadowMap, context);
408
+ }
409
+ }
410
+ function updateFramebuffer(shadowMap, context) {
411
+ if (!Cesium.defined(shadowMap._passes[0].framebuffer) || shadowMap._shadowMapTexture.width !== shadowMap._textureSize.x) {
412
+ destroyFramebuffer(shadowMap);
413
+ createFramebuffer(shadowMap, context);
414
+ checkFramebuffer(shadowMap, context);
415
+ clearFramebuffer(shadowMap, context);
416
+ }
417
+ }
418
+ function clearFramebuffer(shadowMap, context, shadowPass) {
419
+ var _shadowPass;
420
+ shadowPass = (_shadowPass = shadowPass) !== null && _shadowPass !== void 0 ? _shadowPass : 0;
421
+ if (shadowMap._isPointLight || shadowPass === 0) {
422
+ shadowMap._clearCommand.framebuffer = shadowMap._passes[shadowPass].framebuffer;
423
+ shadowMap._clearCommand.execute(context, shadowMap._clearPassState);
424
+ }
425
+ }
426
+ function resize(shadowMap, size) {
427
+ shadowMap._size = size;
428
+ var passes = shadowMap._passes;
429
+ var numberOfPasses = passes.length;
430
+ var textureSize = shadowMap._textureSize;
431
+ if (shadowMap._isPointLight) {
432
+ size = Cesium.ContextLimits.maximumCubeMapSize >= size ? size : Cesium.ContextLimits.maximumCubeMapSize;
433
+ textureSize.x = size;
434
+ textureSize.y = size;
435
+ var faceViewport = new Cesium.BoundingRectangle(0, 0, size, size);
436
+ passes[0].passState.viewport = faceViewport;
437
+ passes[1].passState.viewport = faceViewport;
438
+ passes[2].passState.viewport = faceViewport;
439
+ passes[3].passState.viewport = faceViewport;
440
+ passes[4].passState.viewport = faceViewport;
441
+ passes[5].passState.viewport = faceViewport;
442
+ } else if (numberOfPasses === 1) {
443
+ // +----+
444
+ // | 1 |
445
+ // +----+
446
+ size = Cesium.ContextLimits.maximumTextureSize >= size ? size : Cesium.ContextLimits.maximumTextureSize;
447
+ textureSize.x = size;
448
+ textureSize.y = size;
449
+ passes[0].passState.viewport = new Cesium.BoundingRectangle(0, 0, size, size);
450
+ } else if (numberOfPasses === 4) {
451
+ // +----+----+
452
+ // | 3 | 4 |
453
+ // +----+----+
454
+ // | 1 | 2 |
455
+ // +----+----+
456
+ size = Cesium.ContextLimits.maximumTextureSize >= size * 2 ? size : Cesium.ContextLimits.maximumTextureSize / 2;
457
+ textureSize.x = size * 2;
458
+ textureSize.y = size * 2;
459
+ passes[0].passState.viewport = new Cesium.BoundingRectangle(0, 0, size, size);
460
+ passes[1].passState.viewport = new Cesium.BoundingRectangle(size, 0, size, size);
461
+ passes[2].passState.viewport = new Cesium.BoundingRectangle(0, size, size, size);
462
+ passes[3].passState.viewport = new Cesium.BoundingRectangle(size, size, size, size);
463
+ }
464
+
465
+ // Update clear pass state
466
+ shadowMap._clearPassState.viewport = new Cesium.BoundingRectangle(0, 0, textureSize.x, textureSize.y);
467
+
468
+ // Transforms shadow coordinates [0, 1] into the pass's region of the texture
469
+ for (var i = 0; i < numberOfPasses; ++i) {
470
+ var pass = passes[i];
471
+ var viewport = pass.passState.viewport;
472
+ var biasX = viewport.x / textureSize.x;
473
+ var biasY = viewport.y / textureSize.y;
474
+ var scaleX = viewport.width / textureSize.x;
475
+ var scaleY = viewport.height / textureSize.y;
476
+ pass.textureOffsets = new Cesium.Matrix4(scaleX, 0.0, 0.0, biasX, 0.0, scaleY, 0.0, biasY, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0);
477
+ }
478
+ }
479
+ var scratchViewport = new Cesium.BoundingRectangle();
480
+ function createDebugShadowViewCommand(shadowMap, context) {
481
+ var fs;
482
+ if (shadowMap._isPointLight) {
483
+ fs = 'uniform samplerCube shadowMap_textureCube; \n' + 'in vec2 v_textureCoordinates; \n' + 'void main() \n' + '{ \n' + ' vec2 uv = v_textureCoordinates; \n' + ' vec3 dir; \n' + ' \n' + ' if (uv.y < 0.5) \n' + ' { \n' + ' if (uv.x < 0.333) \n' + ' { \n' + ' dir.x = -1.0; \n' + ' dir.y = uv.x * 6.0 - 1.0; \n' + ' dir.z = uv.y * 4.0 - 1.0; \n' + ' } \n' + ' else if (uv.x < 0.666) \n' + ' { \n' + ' dir.y = -1.0; \n' + ' dir.x = uv.x * 6.0 - 3.0; \n' + ' dir.z = uv.y * 4.0 - 1.0; \n' + ' } \n' + ' else \n' + ' { \n' + ' dir.z = -1.0; \n' + ' dir.x = uv.x * 6.0 - 5.0; \n' + ' dir.y = uv.y * 4.0 - 1.0; \n' + ' } \n' + ' } \n' + ' else \n' + ' { \n' + ' if (uv.x < 0.333) \n' + ' { \n' + ' dir.x = 1.0; \n' + ' dir.y = uv.x * 6.0 - 1.0; \n' + ' dir.z = uv.y * 4.0 - 3.0; \n' + ' } \n' + ' else if (uv.x < 0.666) \n' + ' { \n' + ' dir.y = 1.0; \n' + ' dir.x = uv.x * 6.0 - 3.0; \n' + ' dir.z = uv.y * 4.0 - 3.0; \n' + ' } \n' + ' else \n' + ' { \n' + ' dir.z = 1.0; \n' + ' dir.x = uv.x * 6.0 - 5.0; \n' + ' dir.y = uv.y * 4.0 - 3.0; \n' + ' } \n' + ' } \n' + ' \n' + ' float shadow = czm_unpackDepth(textureCube(shadowMap_textureCube, dir)); \n' + ' out_FragColor = vec4(vec3(shadow), 1.0); \n' + '} \n';
484
+ } else {
485
+ fs = 'uniform sampler2D shadowMap_texture; \n' + 'in vec2 v_textureCoordinates; \n' + 'void main() \n' + '{ \n' + (shadowMap._usesDepthTexture ? ' float shadow = texture2D(shadowMap_texture, v_textureCoordinates).r; \n' : ' float shadow = czm_unpackDepth(texture2D(shadowMap_texture, v_textureCoordinates)); \n') + ' out_FragColor = vec4(vec3(shadow), 1.0); \n' + '} \n';
486
+ }
487
+ var drawCommand = context.createViewportQuadCommand(fs, {
488
+ uniformMap: {
489
+ shadowMap_texture: function shadowMap_texture() {
490
+ return shadowMap._shadowMapTexture;
491
+ },
492
+ shadowMap_textureCube: function shadowMap_textureCube() {
493
+ return shadowMap._shadowMapTexture;
494
+ }
495
+ }
496
+ });
497
+ drawCommand.pass = Cesium.Pass.OVERLAY;
498
+ return drawCommand;
499
+ }
500
+ function updateDebugShadowViewCommand(shadowMap, frameState) {
501
+ // Draws the shadow map on the bottom-right corner of the screen
502
+ var context = frameState.context;
503
+ var screenWidth = frameState.context.drawingBufferWidth;
504
+ var screenHeight = frameState.context.drawingBufferHeight;
505
+ var size = Math.min(screenWidth, screenHeight) * 0.3;
506
+ var viewport = scratchViewport;
507
+ viewport.x = screenWidth - size;
508
+ viewport.y = 0;
509
+ viewport.width = size;
510
+ viewport.height = size;
511
+ var debugCommand = shadowMap._debugShadowViewCommand;
512
+ if (!Cesium.defined(debugCommand)) {
513
+ debugCommand = createDebugShadowViewCommand(shadowMap, context);
514
+ shadowMap._debugShadowViewCommand = debugCommand;
515
+ }
516
+
517
+ // Get a new Cesium.RenderState for the updated viewport size
518
+ if (!Cesium.defined(debugCommand.renderState) || !Cesium.BoundingRectangle.equals(debugCommand.renderState.viewport, viewport)) {
519
+ debugCommand.renderState = Cesium.RenderState.fromCache({
520
+ viewport: Cesium.BoundingRectangle.clone(viewport)
521
+ });
522
+ }
523
+ frameState.commandList.push(shadowMap._debugShadowViewCommand);
524
+ }
525
+ var frustumCornersNDC = new Array(8);
526
+ frustumCornersNDC[0] = new Cesium.Cartesian4(-1.0, -1.0, -1.0, 1.0);
527
+ frustumCornersNDC[1] = new Cesium.Cartesian4(1.0, -1.0, -1.0, 1.0);
528
+ frustumCornersNDC[2] = new Cesium.Cartesian4(1.0, 1.0, -1.0, 1.0);
529
+ frustumCornersNDC[3] = new Cesium.Cartesian4(-1.0, 1.0, -1.0, 1.0);
530
+ frustumCornersNDC[4] = new Cesium.Cartesian4(-1.0, -1.0, 1.0, 1.0);
531
+ frustumCornersNDC[5] = new Cesium.Cartesian4(1.0, -1.0, 1.0, 1.0);
532
+ frustumCornersNDC[6] = new Cesium.Cartesian4(1.0, 1.0, 1.0, 1.0);
533
+ frustumCornersNDC[7] = new Cesium.Cartesian4(-1.0, 1.0, 1.0, 1.0);
534
+ var scratchMatrix = new Cesium.Matrix4();
535
+ var scratchFrustumCorners = new Array(8);
536
+ for (var i = 0; i < 8; ++i) {
537
+ scratchFrustumCorners[i] = new Cesium.Cartesian4();
538
+ }
539
+ function createDebugPointLight(modelMatrix, color) {
540
+ var box = new Cesium.GeometryInstance({
541
+ geometry: new Cesium.BoxOutlineGeometry({
542
+ minimum: new Cesium.Cartesian3(-0.5, -0.5, -0.5),
543
+ maximum: new Cesium.Cartesian3(0.5, 0.5, 0.5)
544
+ }),
545
+ attributes: {
546
+ color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
547
+ }
548
+ });
549
+ var sphere = new Cesium.GeometryInstance({
550
+ geometry: new Cesium.SphereOutlineGeometry({
551
+ radius: 0.5
552
+ }),
553
+ attributes: {
554
+ color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
555
+ }
556
+ });
557
+ return new Cesium.Primitive({
558
+ geometryInstances: [box, sphere],
559
+ appearance: new Cesium.PerInstanceColorAppearance({
560
+ translucent: false,
561
+ flat: true
562
+ }),
563
+ asynchronous: false,
564
+ modelMatrix: modelMatrix
565
+ });
566
+ }
567
+ var debugOutlineColors = [Cesium.Color.RED, Cesium.Color.GREEN, Cesium.Color.BLUE, Cesium.Color.MAGENTA];
568
+ var scratchScale = new Cesium.Cartesian3();
569
+ function applyDebugSettings(shadowMap, frameState) {
570
+ updateDebugShadowViewCommand(shadowMap, frameState);
571
+ var enterFreezeFrame = shadowMap.debugFreezeFrame && !shadowMap._debugFreezeFrame;
572
+ shadowMap._debugFreezeFrame = shadowMap.debugFreezeFrame;
573
+
574
+ // Draw scene camera in freeze frame mode
575
+ if (shadowMap.debugFreezeFrame) {
576
+ if (enterFreezeFrame) {
577
+ // Recreate debug camera when entering freeze frame mode
578
+ shadowMap._debugCameraFrustum = shadowMap._debugCameraFrustum && shadowMap._debugCameraFrustum.destroy();
579
+ shadowMap._debugCameraFrustum = new Cesium.DebugCameraPrimitive({
580
+ camera: shadowMap._sceneCamera,
581
+ color: Cesium.Color.CYAN,
582
+ updateOnChange: false
583
+ });
584
+ }
585
+ shadowMap._debugCameraFrustum.update(frameState);
586
+ }
587
+ if (shadowMap._cascadesEnabled) {
588
+ // Draw cascades only in freeze frame mode
589
+ if (shadowMap.debugFreezeFrame) {
590
+ if (enterFreezeFrame) {
591
+ // Recreate debug frustum when entering freeze frame mode
592
+ shadowMap._debugLightFrustum = shadowMap._debugLightFrustum && shadowMap._debugLightFrustum.destroy();
593
+ shadowMap._debugLightFrustum = new Cesium.DebugCameraPrimitive({
594
+ camera: shadowMap._shadowMapCamera,
595
+ color: Cesium.Color.YELLOW,
596
+ updateOnChange: false
597
+ });
598
+ }
599
+ shadowMap._debugLightFrustum.update(frameState);
600
+ for (var _i = 0; _i < shadowMap._numberOfCascades; ++_i) {
601
+ if (enterFreezeFrame) {
602
+ // Recreate debug frustum when entering freeze frame mode
603
+ shadowMap._debugCascadeFrustums[_i] = shadowMap._debugCascadeFrustums[_i] && shadowMap._debugCascadeFrustums[_i].destroy();
604
+ shadowMap._debugCascadeFrustums[_i] = new Cesium.DebugCameraPrimitive({
605
+ camera: shadowMap._passes[_i].camera,
606
+ color: debugOutlineColors[_i],
607
+ updateOnChange: false
608
+ });
609
+ }
610
+ shadowMap._debugCascadeFrustums[_i].update(frameState);
611
+ }
612
+ }
613
+ } else if (shadowMap._isPointLight) {
614
+ if (!Cesium.defined(shadowMap._debugLightFrustum) || shadowMap._needsUpdate) {
615
+ var translation = shadowMap._shadowMapCamera.positionWC;
616
+ var rotation = Cesium.Quaternion.IDENTITY;
617
+ var uniformScale = shadowMap._pointLightRadius * 2.0;
618
+ var scale = Cesium.Cartesian3.fromElements(uniformScale, uniformScale, uniformScale, scratchScale);
619
+ var modelMatrix = Cesium.Matrix4.fromTranslationQuaternionRotationScale(translation, rotation, scale, scratchMatrix);
620
+ shadowMap._debugLightFrustum = shadowMap._debugLightFrustum && shadowMap._debugLightFrustum.destroy();
621
+ shadowMap._debugLightFrustum = createDebugPointLight(modelMatrix, Cesium.Color.YELLOW);
622
+ }
623
+ shadowMap._debugLightFrustum.update(frameState);
624
+ } else {
625
+ if (!Cesium.defined(shadowMap._debugLightFrustum) || shadowMap._needsUpdate) {
626
+ shadowMap._debugLightFrustum = new Cesium.DebugCameraPrimitive({
627
+ camera: shadowMap._shadowMapCamera,
628
+ color: Cesium.Color.YELLOW,
629
+ updateOnChange: false
630
+ });
631
+ }
632
+ shadowMap._debugLightFrustum.update(frameState);
633
+ }
634
+ }
635
+ function ShadowMapCamera() {
636
+ this.viewMatrix = new Cesium.Matrix4();
637
+ this.inverseViewMatrix = new Cesium.Matrix4();
638
+ this.frustum = undefined;
639
+ this.positionCartographic = new Cesium.Cartographic();
640
+ this.positionWC = new Cesium.Cartesian3();
641
+ this.directionWC = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_Z);
642
+ this.upWC = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_Y);
643
+ this.rightWC = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_X);
644
+ this.viewProjectionMatrix = new Cesium.Matrix4();
645
+ }
646
+ ShadowMapCamera.prototype.clone = function (camera) {
647
+ Cesium.Matrix4.clone(camera.viewMatrix, this.viewMatrix);
648
+ Cesium.Matrix4.clone(camera.inverseViewMatrix, this.inverseViewMatrix);
649
+ this.frustum = camera.frustum.clone(this.frustum);
650
+ Cesium.Cartographic.clone(camera.positionCartographic, this.positionCartographic);
651
+ Cesium.Cartesian3.clone(camera.positionWC, this.positionWC);
652
+ Cesium.Cartesian3.clone(camera.directionWC, this.directionWC);
653
+ Cesium.Cartesian3.clone(camera.upWC, this.upWC);
654
+ Cesium.Cartesian3.clone(camera.rightWC, this.rightWC);
655
+ };
656
+
657
+ // Converts from NDC space to texture space
658
+ var scaleBiasMatrix = new Cesium.Matrix4(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);
659
+ ShadowMapCamera.prototype.getViewProjection = function () {
660
+ var view = this.viewMatrix;
661
+ var projection = this.frustum.projectionMatrix;
662
+ Cesium.Matrix4.multiply(projection, view, this.viewProjectionMatrix);
663
+ Cesium.Matrix4.multiply(scaleBiasMatrix, this.viewProjectionMatrix, this.viewProjectionMatrix);
664
+ return this.viewProjectionMatrix;
665
+ };
666
+ var scratchSplits = new Array(5);
667
+ var scratchFrustum = new Cesium.PerspectiveFrustum();
668
+ var scratchCascadeDistances = new Array(4);
669
+ var scratchMin = new Cesium.Cartesian3();
670
+ var scratchMax = new Cesium.Cartesian3();
671
+ function computeCascades(shadowMap, frameState) {
672
+ var shadowMapCamera = shadowMap._shadowMapCamera;
673
+ var sceneCamera = shadowMap._sceneCamera;
674
+ var cameraNear = sceneCamera.frustum.near;
675
+ var cameraFar = sceneCamera.frustum.far;
676
+ var numberOfCascades = shadowMap._numberOfCascades;
677
+
678
+ // Split cascades. Use a mix of linear and log splits.
679
+ var i;
680
+ var range = cameraFar - cameraNear;
681
+ var ratio = cameraFar / cameraNear;
682
+ var lambda = 0.9;
683
+ var clampCascadeDistances = false;
684
+
685
+ // When the camera is close to a relatively small model, provide more detail in the closer cascades.
686
+ // If the camera is near or inside a large model, such as the root tile of a city, then use the default values.
687
+ // To get the most accurate cascade splits we would need to find the min and max values from the depth texture.
688
+ if (frameState.shadowState.closestObjectSize < 200.0) {
689
+ clampCascadeDistances = true;
690
+ lambda = 0.9;
691
+ }
692
+ var cascadeDistances = scratchCascadeDistances;
693
+ var splits = scratchSplits;
694
+ splits[0] = cameraNear;
695
+ splits[numberOfCascades] = cameraFar;
696
+
697
+ // Find initial splits
698
+ for (i = 0; i < numberOfCascades; ++i) {
699
+ var p = (i + 1) / numberOfCascades;
700
+ var logScale = cameraNear * Math.pow(ratio, p);
701
+ var uniformScale = cameraNear + range * p;
702
+ var split = Cesium.Math.lerp(uniformScale, logScale, lambda);
703
+ splits[i + 1] = split;
704
+ cascadeDistances[i] = split - splits[i];
705
+ }
706
+ if (clampCascadeDistances) {
707
+ // Clamp each cascade to its maximum distance
708
+ for (i = 0; i < numberOfCascades; ++i) {
709
+ cascadeDistances[i] = Math.min(cascadeDistances[i], shadowMap._maximumCascadeDistances[i]);
710
+ }
711
+
712
+ // Recompute splits
713
+ var distance = splits[0];
714
+ for (i = 0; i < numberOfCascades - 1; ++i) {
715
+ distance += cascadeDistances[i];
716
+ splits[i + 1] = distance;
717
+ }
718
+ }
719
+ Cesium.Cartesian4.unpack(splits, 0, shadowMap._cascadeSplits[0]);
720
+ Cesium.Cartesian4.unpack(splits, 1, shadowMap._cascadeSplits[1]);
721
+ Cesium.Cartesian4.unpack(cascadeDistances, 0, shadowMap._cascadeDistances);
722
+ var shadowFrustum = shadowMapCamera.frustum;
723
+ var left = shadowFrustum.left;
724
+ var right = shadowFrustum.right;
725
+ var bottom = shadowFrustum.bottom;
726
+ var top = shadowFrustum.top;
727
+ var near = shadowFrustum.near;
728
+ var far = shadowFrustum.far;
729
+ var position = shadowMapCamera.positionWC;
730
+ var direction = shadowMapCamera.directionWC;
731
+ var up = shadowMapCamera.upWC;
732
+ var cascadeSubFrustum = sceneCamera.frustum.clone(scratchFrustum);
733
+ var shadowViewProjection = shadowMapCamera.getViewProjection();
734
+ for (i = 0; i < numberOfCascades; ++i) {
735
+ // Find the bounding box of the camera sub-frustum in shadow map texture space
736
+ cascadeSubFrustum.near = splits[i];
737
+ cascadeSubFrustum.far = splits[i + 1];
738
+ var viewProjection = Cesium.Matrix4.multiply(cascadeSubFrustum.projectionMatrix, sceneCamera.viewMatrix, scratchMatrix);
739
+ var inverseViewProjection = Cesium.Matrix4.inverse(viewProjection, scratchMatrix);
740
+ var shadowMapMatrix = Cesium.Matrix4.multiply(shadowViewProjection, inverseViewProjection, scratchMatrix);
741
+
742
+ // Project each corner from camera NDC space to shadow map texture space. Min and max will be from 0 to 1.
743
+ var min = Cesium.Cartesian3.fromElements(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, scratchMin);
744
+ var max = Cesium.Cartesian3.fromElements(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE, scratchMax);
745
+ for (var k = 0; k < 8; ++k) {
746
+ var corner = Cesium.Cartesian4.clone(frustumCornersNDC[k], scratchFrustumCorners[k]);
747
+ Cesium.Matrix4.multiplyByVector(shadowMapMatrix, corner, corner);
748
+ Cesium.Cartesian3.divideByScalar(corner, corner.w, corner); // Handle the perspective divide
749
+ Cesium.Cartesian3.minimumByComponent(corner, min, min);
750
+ Cesium.Cartesian3.maximumByComponent(corner, max, max);
751
+ }
752
+
753
+ // Limit light-space coordinates to the [0, 1] range
754
+ min.x = Math.max(min.x, 0.0);
755
+ min.y = Math.max(min.y, 0.0);
756
+ min.z = 0.0; // Always start cascade frustum at the top of the light frustum to capture objects in the light's path
757
+ max.x = Math.min(max.x, 1.0);
758
+ max.y = Math.min(max.y, 1.0);
759
+ max.z = Math.min(max.z, 1.0);
760
+ var pass = shadowMap._passes[i];
761
+ var cascadeCamera = pass.camera;
762
+ cascadeCamera.clone(shadowMapCamera); // PERFORMANCE_IDEA : could do a shallow clone for all properties except the frustum
763
+
764
+ var frustum = cascadeCamera.frustum;
765
+ frustum.left = left + min.x * (right - left);
766
+ frustum.right = left + max.x * (right - left);
767
+ frustum.bottom = bottom + min.y * (top - bottom);
768
+ frustum.top = bottom + max.y * (top - bottom);
769
+ frustum.near = near + min.z * (far - near);
770
+ frustum.far = near + max.z * (far - near);
771
+ pass.cullingVolume = cascadeCamera.frustum.computeCullingVolume(position, direction, up);
772
+
773
+ // Transforms from eye space to the cascade's texture space
774
+ var cascadeMatrix = shadowMap._cascadeMatrices[i];
775
+ Cesium.Matrix4.multiply(cascadeCamera.getViewProjection(), sceneCamera.inverseViewMatrix, cascadeMatrix);
776
+ Cesium.Matrix4.multiply(pass.textureOffsets, cascadeMatrix, cascadeMatrix);
777
+ }
778
+ }
779
+ var scratchLightView = new Cesium.Matrix4();
780
+ var scratchRight = new Cesium.Cartesian3();
781
+ var scratchUp = new Cesium.Cartesian3();
782
+ var scratchTranslation = new Cesium.Cartesian3();
783
+ function fitShadowMapToScene(shadowMap, frameState) {
784
+ var shadowMapCamera = shadowMap._shadowMapCamera;
785
+ var sceneCamera = shadowMap._sceneCamera;
786
+
787
+ // 1. First find a tight bounding box in light space that contains the entire camera frustum.
788
+ var viewProjection = Cesium.Matrix4.multiply(sceneCamera.frustum.projectionMatrix, sceneCamera.viewMatrix, scratchMatrix);
789
+ var inverseViewProjection = Cesium.Matrix4.inverse(viewProjection, scratchMatrix);
790
+
791
+ // Start to construct the light view matrix. Set translation later once the bounding box is found.
792
+ var lightDir = shadowMapCamera.directionWC;
793
+ var lightUp = sceneCamera.directionWC; // Align shadows to the camera view.
794
+ var lightRight = Cesium.Cartesian3.cross(lightDir, lightUp, scratchRight);
795
+ lightUp = Cesium.Cartesian3.cross(lightRight, lightDir, scratchUp); // Recalculate up now that right is derived
796
+ Cesium.Cartesian3.normalize(lightUp, lightUp);
797
+ Cesium.Cartesian3.normalize(lightRight, lightRight);
798
+ var lightPosition = Cesium.Cartesian3.fromElements(0.0, 0.0, 0.0, scratchTranslation);
799
+ var lightView = Cesium.Matrix4.computeView(lightPosition, lightDir, lightUp, lightRight, scratchLightView);
800
+ var cameraToLight = Cesium.Matrix4.multiply(lightView, inverseViewProjection, scratchMatrix);
801
+
802
+ // Project each corner from NDC space to light view space, and calculate a min and max in light view space
803
+ var min = Cesium.Cartesian3.fromElements(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, scratchMin);
804
+ var max = Cesium.Cartesian3.fromElements(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE, scratchMax);
805
+ for (var _i2 = 0; _i2 < 8; ++_i2) {
806
+ var corner = Cesium.Cartesian4.clone(frustumCornersNDC[_i2], scratchFrustumCorners[_i2]);
807
+ Cesium.Matrix4.multiplyByVector(cameraToLight, corner, corner);
808
+ Cesium.Cartesian3.divideByScalar(corner, corner.w, corner); // Handle the perspective divide
809
+ Cesium.Cartesian3.minimumByComponent(corner, min, min);
810
+ Cesium.Cartesian3.maximumByComponent(corner, max, max);
811
+ }
812
+
813
+ // 2. Set bounding box back to include objects in the light's view
814
+ max.z += 1000.0; // Note: in light space, a positive number is behind the camera
815
+ min.z -= 10.0; // Extend the shadow volume forward slightly to avoid problems right at the edge
816
+
817
+ // 3. Adjust light view matrix so that it is centered on the bounding volume
818
+ var translation = scratchTranslation;
819
+ translation.x = -(0.5 * (min.x + max.x));
820
+ translation.y = -(0.5 * (min.y + max.y));
821
+ translation.z = -max.z;
822
+ var translationMatrix = Cesium.Matrix4.fromTranslation(translation, scratchMatrix);
823
+ lightView = Cesium.Matrix4.multiply(translationMatrix, lightView, lightView);
824
+
825
+ // 4. Create an orthographic frustum that covers the bounding box extents
826
+ var halfWidth = 0.5 * (max.x - min.x);
827
+ var halfHeight = 0.5 * (max.y - min.y);
828
+ var depth = max.z - min.z;
829
+ var frustum = shadowMapCamera.frustum;
830
+ frustum.left = -halfWidth;
831
+ frustum.right = halfWidth;
832
+ frustum.bottom = -halfHeight;
833
+ frustum.top = halfHeight;
834
+ frustum.near = 0.01;
835
+ frustum.far = depth;
836
+
837
+ // 5. Update the shadow map camera
838
+ Cesium.Matrix4.clone(lightView, shadowMapCamera.viewMatrix);
839
+ Cesium.Matrix4.inverse(lightView, shadowMapCamera.inverseViewMatrix);
840
+ Cesium.Matrix4.getTranslation(shadowMapCamera.inverseViewMatrix, shadowMapCamera.positionWC);
841
+ frameState.mapProjection.ellipsoid.cartesianToCartographic(shadowMapCamera.positionWC, shadowMapCamera.positionCartographic);
842
+ Cesium.Cartesian3.clone(lightDir, shadowMapCamera.directionWC);
843
+ Cesium.Cartesian3.clone(lightUp, shadowMapCamera.upWC);
844
+ Cesium.Cartesian3.clone(lightRight, shadowMapCamera.rightWC);
845
+ }
846
+ var directions = [new Cesium.Cartesian3(-1.0, 0.0, 0.0), new Cesium.Cartesian3(0.0, -1.0, 0.0), new Cesium.Cartesian3(0.0, 0.0, -1.0), new Cesium.Cartesian3(1.0, 0.0, 0.0), new Cesium.Cartesian3(0.0, 1.0, 0.0), new Cesium.Cartesian3(0.0, 0.0, 1.0)];
847
+ var ups = [new Cesium.Cartesian3(0.0, -1.0, 0.0), new Cesium.Cartesian3(0.0, 0.0, -1.0), new Cesium.Cartesian3(0.0, -1.0, 0.0), new Cesium.Cartesian3(0.0, -1.0, 0.0), new Cesium.Cartesian3(0.0, 0.0, 1.0), new Cesium.Cartesian3(0.0, -1.0, 0.0)];
848
+ var rights = [new Cesium.Cartesian3(0.0, 0.0, 1.0), new Cesium.Cartesian3(1.0, 0.0, 0.0), new Cesium.Cartesian3(-1.0, 0.0, 0.0), new Cesium.Cartesian3(0.0, 0.0, -1.0), new Cesium.Cartesian3(1.0, 0.0, 0.0), new Cesium.Cartesian3(1.0, 0.0, 0.0)];
849
+ function computeOmnidirectional(shadowMap, frameState) {
850
+ // All sides share the same frustum
851
+ var frustum = new Cesium.PerspectiveFrustum();
852
+ frustum.fov = Cesium.Math.PI_OVER_TWO;
853
+ frustum.near = 1.0;
854
+ frustum.far = shadowMap._pointLightRadius;
855
+ frustum.aspectRatio = 1.0;
856
+ for (var _i3 = 0; _i3 < 6; ++_i3) {
857
+ var camera = shadowMap._passes[_i3].camera;
858
+ camera.positionWC = shadowMap._shadowMapCamera.positionWC;
859
+ camera.positionCartographic = frameState.mapProjection.ellipsoid.cartesianToCartographic(camera.positionWC, camera.positionCartographic);
860
+ camera.directionWC = directions[_i3];
861
+ camera.upWC = ups[_i3];
862
+ camera.rightWC = rights[_i3];
863
+ Cesium.Matrix4.computeView(camera.positionWC, camera.directionWC, camera.upWC, camera.rightWC, camera.viewMatrix);
864
+ Cesium.Matrix4.inverse(camera.viewMatrix, camera.inverseViewMatrix);
865
+ camera.frustum = frustum;
866
+ }
867
+ }
868
+ var scratchCartesian1 = new Cesium.Cartesian3();
869
+ var scratchCartesian2 = new Cesium.Cartesian3();
870
+ var scratchBoundingSphere = new Cesium.BoundingSphere();
871
+ var scratchCenter = scratchBoundingSphere.center;
872
+ function checkVisibility(shadowMap, frameState) {
873
+ var sceneCamera = shadowMap._sceneCamera;
874
+ var shadowMapCamera = shadowMap._shadowMapCamera;
875
+ var boundingSphere = scratchBoundingSphere;
876
+
877
+ // Check whether the shadow map is in view and needs to be updated
878
+ if (shadowMap._cascadesEnabled) {
879
+ // If the nearest shadow receiver is further than the shadow map's maximum distance then the shadow map is out of view.
880
+ if (sceneCamera.frustum.near >= shadowMap.maximumDistance) {
881
+ shadowMap._outOfView = true;
882
+ shadowMap._needsUpdate = false;
883
+ return;
884
+ }
885
+
886
+ // If the light source is below the horizon then the shadow map is out of view
887
+ var surfaceNormal = frameState.mapProjection.ellipsoid.geodeticSurfaceNormal(sceneCamera.positionWC, scratchCartesian1);
888
+ var lightDirection = Cesium.Cartesian3.negate(shadowMapCamera.directionWC, scratchCartesian2);
889
+ var dot = Cesium.Cartesian3.dot(surfaceNormal, lightDirection);
890
+
891
+ // Shadows start to fade out once the light gets closer to the horizon.
892
+ // At this point the globe uses vertex lighting alone to darken the surface.
893
+ var darknessAmount = Cesium.Math.clamp(dot / 0.1, 0.0, 1.0);
894
+ shadowMap._darkness = Cesium.Math.lerp(1.0, shadowMap.darkness, darknessAmount);
895
+ if (dot < 0.0) {
896
+ shadowMap._outOfView = true;
897
+ shadowMap._needsUpdate = false;
898
+ return;
899
+ }
900
+
901
+ // By default cascaded shadows need to update and are always in view
902
+ shadowMap._needsUpdate = true;
903
+ shadowMap._outOfView = false;
904
+ } else if (shadowMap._isPointLight) {
905
+ // Sphere-frustum intersection test
906
+ boundingSphere.center = shadowMapCamera.positionWC;
907
+ boundingSphere.radius = shadowMap._pointLightRadius;
908
+ shadowMap._outOfView = frameState.cullingVolume.computeVisibility(boundingSphere) === Cesium.Intersect.OUTSIDE;
909
+ shadowMap._needsUpdate = !shadowMap._outOfView && !shadowMap._boundingSphere.equals(boundingSphere);
910
+ Cesium.BoundingSphere.clone(boundingSphere, shadowMap._boundingSphere);
911
+ } else {
912
+ // Simplify frustum-frustum intersection test as a sphere-frustum test
913
+ var frustumRadius = shadowMapCamera.frustum.far / 2.0;
914
+ var frustumCenter = Cesium.Cartesian3.add(shadowMapCamera.positionWC, Cesium.Cartesian3.multiplyByScalar(shadowMapCamera.directionWC, frustumRadius, scratchCenter), scratchCenter);
915
+ boundingSphere.center = frustumCenter;
916
+ boundingSphere.radius = frustumRadius;
917
+ shadowMap._outOfView = frameState.cullingVolume.computeVisibility(boundingSphere) === Cesium.Intersect.OUTSIDE;
918
+ shadowMap._needsUpdate = !shadowMap._outOfView && !shadowMap._boundingSphere.equals(boundingSphere);
919
+ Cesium.BoundingSphere.clone(boundingSphere, shadowMap._boundingSphere);
920
+ }
921
+ }
922
+ function updateCameras(shadowMap, frameState) {
923
+ var camera = frameState.camera; // The actual camera in the scene
924
+ var lightCamera = shadowMap._lightCamera; // The external camera representing the light source
925
+ var sceneCamera = shadowMap._sceneCamera; // Clone of camera, with clamped near and far planes
926
+ var shadowMapCamera = shadowMap._shadowMapCamera; // Cesium.Camera representing the shadow volume, initially cloned from lightCamera
927
+
928
+ // Clone light camera into the shadow map camera
929
+ if (shadowMap._cascadesEnabled) {
930
+ Cesium.Cartesian3.clone(lightCamera.directionWC, shadowMapCamera.directionWC);
931
+ } else if (shadowMap._isPointLight) {
932
+ Cesium.Cartesian3.clone(lightCamera.positionWC, shadowMapCamera.positionWC);
933
+ } else {
934
+ shadowMapCamera.clone(lightCamera);
935
+ }
936
+
937
+ // Get the light direction in eye coordinates
938
+ var lightDirection = shadowMap._lightDirectionEC;
939
+ Cesium.Matrix4.multiplyByPointAsVector(camera.viewMatrix, shadowMapCamera.directionWC, lightDirection);
940
+ Cesium.Cartesian3.normalize(lightDirection, lightDirection);
941
+ Cesium.Cartesian3.negate(lightDirection, lightDirection);
942
+
943
+ // Get the light position in eye coordinates
944
+ Cesium.Matrix4.multiplyByPoint(camera.viewMatrix, shadowMapCamera.positionWC, shadowMap._lightPositionEC);
945
+ shadowMap._lightPositionEC.w = shadowMap._pointLightRadius;
946
+
947
+ // Get the near and far of the scene camera
948
+ var near;
949
+ var far;
950
+ if (shadowMap.isViewShed) {
951
+ near = lightCamera.frustum.near;
952
+ far = lightCamera.frustum.far;
953
+ } else if (shadowMap._fitNearFar) {
954
+ // shadowFar can be very large, so limit to shadowMap.maximumDistance
955
+ // Push the far plane slightly further than the near plane to avoid degenerate frustum
956
+ near = Math.min(frameState.shadowState.nearPlane, shadowMap.maximumDistance);
957
+ far = Math.min(frameState.shadowState.farPlane, shadowMap.maximumDistance + 1.0);
958
+ } else {
959
+ near = camera.frustum.near;
960
+ far = shadowMap.maximumDistance;
961
+ }
962
+ shadowMap._sceneCamera = Cesium.Camera.clone(camera, sceneCamera);
963
+ camera.frustum.clone(shadowMap._sceneCamera.frustum);
964
+ shadowMap._sceneCamera.frustum.near = near;
965
+ shadowMap._sceneCamera.frustum.far = far;
966
+ shadowMap._distance = far - near;
967
+ checkVisibility(shadowMap, frameState);
968
+ if (!shadowMap._outOfViewPrevious && shadowMap._outOfView) {
969
+ shadowMap._needsUpdate = true;
970
+ }
971
+ shadowMap._outOfViewPrevious = shadowMap._outOfView;
972
+ }
973
+
974
+ /**
975
+ * @private
976
+ */
977
+ ViewshedMap.prototype.update = function (frameState) {
978
+ updateCameras(this, frameState);
979
+ if (this._needsUpdate) {
980
+ updateFramebuffer(this, frameState.context);
981
+ if (this._isPointLight) {
982
+ computeOmnidirectional(this, frameState);
983
+ }
984
+ if (this._cascadesEnabled) {
985
+ fitShadowMapToScene(this, frameState);
986
+ if (this._numberOfCascades > 1) {
987
+ computeCascades(this, frameState);
988
+ }
989
+ }
990
+ if (!this._isPointLight) {
991
+ // Compute the culling volume
992
+ var shadowMapCamera = this._shadowMapCamera;
993
+ var position = shadowMapCamera.positionWC;
994
+ var direction = shadowMapCamera.directionWC;
995
+ var up = shadowMapCamera.upWC;
996
+ this._shadowMapCullingVolume = shadowMapCamera.frustum.computeCullingVolume(position, direction, up);
997
+ if (this._passes.length === 1) {
998
+ // Since there is only one pass, use the shadow map camera as the pass camera.
999
+ this._passes[0].camera.clone(shadowMapCamera);
1000
+ }
1001
+ } else {
1002
+ this._shadowMapCullingVolume = Cesium.CullingVolume.fromBoundingSphere(this._boundingSphere);
1003
+ }
1004
+ }
1005
+ if (this._passes.length === 1) {
1006
+ // Transforms from eye space to shadow texture space.
1007
+ // Always requires an update since the scene camera constantly changes.
1008
+ var inverseView = this._sceneCamera.inverseViewMatrix;
1009
+ Cesium.Matrix4.multiply(this._shadowMapCamera.getViewProjection(), inverseView, this._shadowMapMatrix);
1010
+ }
1011
+ if (this.debugShow) {
1012
+ applyDebugSettings(this, frameState);
1013
+ }
1014
+ };
1015
+
1016
+ /**
1017
+ * @private
1018
+ */
1019
+ ViewshedMap.prototype.updatePass = function (context, shadowPass) {
1020
+ clearFramebuffer(this, context, shadowPass);
1021
+ };
1022
+ var scratchTexelStepSize = new Cesium.Cartesian2();
1023
+ function combineUniforms(shadowMap, uniforms, isTerrain) {
1024
+ var bias = shadowMap._isPointLight ? shadowMap._pointBias : isTerrain ? shadowMap._terrainBias : shadowMap._primitiveBias;
1025
+ var mapUniforms = {
1026
+ shadowMap_texture: function shadowMap_texture() {
1027
+ return shadowMap._shadowMapTexture;
1028
+ },
1029
+ shadowMap_textureCube: function shadowMap_textureCube() {
1030
+ return shadowMap._shadowMapTexture;
1031
+ },
1032
+ shadowMap_matrix: function shadowMap_matrix() {
1033
+ return shadowMap._shadowMapMatrix;
1034
+ },
1035
+ shadowMap_cascadeSplits: function shadowMap_cascadeSplits() {
1036
+ return shadowMap._cascadeSplits;
1037
+ },
1038
+ shadowMap_cascadeMatrices: function shadowMap_cascadeMatrices() {
1039
+ return shadowMap._cascadeMatrices;
1040
+ },
1041
+ shadowMap_lightDirectionEC: function shadowMap_lightDirectionEC() {
1042
+ return shadowMap._lightDirectionEC;
1043
+ },
1044
+ shadowMap_lightPositionEC: function shadowMap_lightPositionEC() {
1045
+ return shadowMap._lightPositionEC;
1046
+ },
1047
+ shadowMap_cascadeDistances: function shadowMap_cascadeDistances() {
1048
+ return shadowMap._cascadeDistances;
1049
+ },
1050
+ shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: function shadowMap_texelSizeDepthBiasAndNormalShadingSmooth() {
1051
+ var texelStepSize = scratchTexelStepSize;
1052
+ texelStepSize.x = 1.0 / shadowMap._textureSize.x;
1053
+ texelStepSize.y = 1.0 / shadowMap._textureSize.y;
1054
+ return Cesium.Cartesian4.fromElements(texelStepSize.x, texelStepSize.y, bias.depthBias, bias.normalShadingSmooth, this.combinedUniforms1);
1055
+ },
1056
+ shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: function shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness() {
1057
+ return Cesium.Cartesian4.fromElements(bias.normalOffsetScale, shadowMap._distance, shadowMap.maximumDistance, shadowMap._darkness, this.combinedUniforms2);
1058
+ },
1059
+ combinedUniforms1: new Cesium.Cartesian4(),
1060
+ combinedUniforms2: new Cesium.Cartesian4()
1061
+ };
1062
+ return Cesium.combine(uniforms, mapUniforms, false);
1063
+ }
1064
+ function getShadowReceiveShaderKeyword(shadowMap, castShadows, isTerrain, hasTerrainNormal) {
1065
+ var usesDepthTexture = shadowMap._usesDepthTexture;
1066
+ var polygonOffsetSupported = shadowMap._polygonOffsetSupported;
1067
+ var isPointLight = shadowMap._isPointLight;
1068
+ var isSpotLight = shadowMap._isSpotLight;
1069
+ var hasCascades = shadowMap._numberOfCascades > 1;
1070
+ var debugCascadeColors = shadowMap.debugCascadeColors;
1071
+ var softShadows = shadowMap.softShadows;
1072
+ return 'view receiveShadow ' + usesDepthTexture + polygonOffsetSupported + isPointLight + isSpotLight + hasCascades + debugCascadeColors + softShadows + castShadows + isTerrain + hasTerrainNormal;
1073
+ }
1074
+ function createShadowReceiveFragmentShader(fs, shadowMap, castShadows, isTerrain, hasTerrainNormal) {
1075
+ var normalVaryingName = Cesium.ShaderSource.findNormalVarying(fs);
1076
+ var hasNormalVarying = !isTerrain && Cesium.defined(normalVaryingName) || isTerrain && hasTerrainNormal;
1077
+ var positionVaryingName = Cesium.ShaderSource.findPositionVarying(fs);
1078
+ var hasPositionVarying = Cesium.defined(positionVaryingName);
1079
+ var usesDepthTexture = shadowMap._usesDepthTexture;
1080
+ var polygonOffsetSupported = shadowMap._polygonOffsetSupported;
1081
+ var isPointLight = shadowMap._isPointLight;
1082
+ var isSpotLight = shadowMap._isSpotLight;
1083
+ var hasCascades = shadowMap._numberOfCascades > 1;
1084
+ var debugCascadeColors = shadowMap.debugCascadeColors;
1085
+ var softShadows = shadowMap.softShadows;
1086
+ var bias = isPointLight ? shadowMap._pointBias : isTerrain ? shadowMap._terrainBias : shadowMap._primitiveBias;
1087
+ var defines = fs.defines.slice(0);
1088
+ var sources = fs.sources.slice(0);
1089
+ var length = sources.length;
1090
+ for (var _i4 = 0; _i4 < length; ++_i4) {
1091
+ sources[_i4] = Cesium.ShaderSource.replaceMain(sources[_i4], 'czm_shadow_receive_main');
1092
+ }
1093
+ if (isPointLight) {
1094
+ defines.push('USE_CUBE_MAP_SHADOW');
1095
+ } else if (usesDepthTexture) {
1096
+ defines.push('USE_SHADOW_DEPTH_TEXTURE');
1097
+ }
1098
+ if (softShadows && !isPointLight) {
1099
+ defines.push('USE_SOFT_SHADOWS');
1100
+ }
1101
+
1102
+ // Enable day-night shading so that the globe is dark when the light is below the horizon
1103
+ if (hasCascades && castShadows && isTerrain) {
1104
+ if (hasNormalVarying) {
1105
+ defines.push('ENABLE_VERTEX_LIGHTING');
1106
+ } else {
1107
+ defines.push('ENABLE_DAYNIGHT_SHADING');
1108
+ }
1109
+ }
1110
+ if (castShadows && bias.normalShading && hasNormalVarying) {
1111
+ defines.push('USE_NORMAL_SHADING');
1112
+ if (bias.normalShadingSmooth > 0.0) {
1113
+ defines.push('USE_NORMAL_SHADING_SMOOTH');
1114
+ }
1115
+ }
1116
+ var fsSource = '';
1117
+ if (isPointLight) {
1118
+ fsSource += 'uniform samplerCube shadowMap_textureCube; \n';
1119
+ } else {
1120
+ fsSource += 'uniform sampler2D shadowMap_texture; \n';
1121
+ }
1122
+ var returnPositionEC;
1123
+ if (hasPositionVarying) {
1124
+ returnPositionEC = ' return vec4(' + positionVaryingName + ', 1.0); \n';
1125
+ } else {
1126
+ returnPositionEC = '#ifndef LOG_DEPTH \n' + ' return czm_windowToEyeCoordinates(gl_FragCoord); \n' + '#else \n' + ' return vec4(v_logPositionEC, 1.0); \n' + '#endif \n';
1127
+ }
1128
+ fsSource += 'uniform mat4 shadowMap_matrix; \n' + 'uniform vec3 shadowMap_lightDirectionEC; \n' + 'uniform vec4 shadowMap_lightPositionEC; \n' + 'uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness; \n' + 'uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth; \n' + '#ifdef LOG_DEPTH \n' + 'in vec3 v_logPositionEC; \n' + '#endif \n' + 'vec4 getPositionEC() \n' + '{ \n' + returnPositionEC + '} \n' + 'vec3 getNormalEC() \n' + '{ \n' + (hasNormalVarying ? ' return normalize(' + normalVaryingName + '); \n' : ' return vec3(1.0); \n') + '} \n' +
1129
+ // Offset the shadow position in the direction of the normal for perpendicular and back faces
1130
+ 'void applyNormalOffset(inout vec4 positionEC, vec3 normalEC, float nDotL) \n' + '{ \n' + (bias.normalOffset && hasNormalVarying ? ' float normalOffset = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.x; \n' + ' float normalOffsetScale = 1.0 - nDotL; \n' + ' vec3 offset = normalOffset * normalOffsetScale * normalEC; \n' + ' positionEC.xyz += offset; \n' : '') + '} \n';
1131
+ fsSource += 'void main() \n' + '{ \n' + ' czm_shadow_receive_main(); \n' + ' vec4 positionEC = getPositionEC(); \n' + ' vec3 normalEC = getNormalEC(); \n' + ' float depth = -positionEC.z; \n';
1132
+ fsSource += ' czm_shadowParameters shadowParameters; \n' + ' shadowParameters.texelStepSize = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy; \n' + ' shadowParameters.depthBias = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z; \n' + ' shadowParameters.normalShadingSmooth = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w; \n' + ' shadowParameters.darkness = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w; \n';
1133
+ if (isTerrain) {
1134
+ // Scale depth bias based on view distance to reduce z-fighting in distant terrain
1135
+ fsSource += ' shadowParameters.depthBias *= max(depth * 0.01, 1.0); \n';
1136
+ } else if (!polygonOffsetSupported) {
1137
+ // If polygon offset isn't supported push the depth back based on view, however this
1138
+ // causes light leaking at further away views
1139
+ fsSource += ' shadowParameters.depthBias *= mix(1.0, 100.0, depth * 0.0015); \n';
1140
+ }
1141
+ if (isPointLight) {
1142
+ fsSource += ' vec3 directionEC = positionEC.xyz - shadowMap_lightPositionEC.xyz; \n' + ' float distance = length(directionEC); \n' + ' directionEC = normalize(directionEC); \n' + ' float radius = shadowMap_lightPositionEC.w; \n' + ' // Stop early if the fragment is beyond the point light radius \n' + ' if (distance > radius) \n' + ' { \n' + ' return; \n' + ' } \n' + ' vec3 directionWC = czm_inverseViewRotation * directionEC; \n' + ' shadowParameters.depth = distance / radius; \n' + ' shadowParameters.nDotL = clamp(dot(normalEC, -directionEC), 0.0, 1.0); \n' + ' shadowParameters.texCoords = directionWC; \n' + ' float visibility = czm_shadowVisibility(shadowMap_textureCube, shadowParameters); \n';
1143
+ } else if (isSpotLight) {
1144
+ fsSource += ' vec3 directionEC1 = positionEC.xyz - shadowMap_lightPositionEC.xyz; \n' + ' float distance = length(directionEC1); \n' + ' if (distance > shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.y) \n' + ' { \n' + ' return; \n' + ' } \n' + ' vec3 directionEC = normalize(positionEC.xyz - shadowMap_lightPositionEC.xyz); \n' + ' float nDotL = clamp(dot(normalEC, -directionEC), 0.0, 1.0); \n' + ' applyNormalOffset(positionEC, normalEC, nDotL); \n' + ' vec4 shadowPosition = shadowMap_matrix * positionEC; \n' + ' // Spot light uses a perspective projection, so perform the perspective divide \n' + ' shadowPosition /= shadowPosition.w; \n' + ' // Stop early if the fragment is not in the shadow bounds \n' + ' if (any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))) \n' + ' { \n' + ' return; \n' + ' } \n' + ' shadowParameters.texCoords = shadowPosition.xy; \n' + ' shadowParameters.depth = shadowPosition.z; \n' + ' shadowParameters.nDotL = nDotL; \n' + ' float visibility = czm_shadowVisibility(shadowMap_texture, shadowParameters); \n';
1145
+ } else if (hasCascades) {
1146
+ fsSource += ' float maxDepth = shadowMap_cascadeSplits[1].w; \n' + ' // Stop early if the eye depth exceeds the last cascade \n' + ' if (depth > maxDepth) \n' + ' { \n' + ' return; \n' + ' } \n' + ' // Get the cascade based on the eye-space depth \n' + ' vec4 weights = czm_cascadeWeights(depth); \n' + ' // Apply normal offset \n' + ' float nDotL = clamp(dot(normalEC, shadowMap_lightDirectionEC), 0.0, 1.0); \n' + ' applyNormalOffset(positionEC, normalEC, nDotL); \n' + ' // Transform position into the cascade \n' + ' vec4 shadowPosition = czm_cascadeMatrix(weights) * positionEC; \n' + ' // Get visibility \n' + ' shadowParameters.texCoords = shadowPosition.xy; \n' + ' shadowParameters.depth = shadowPosition.z; \n' + ' shadowParameters.nDotL = nDotL; \n' + ' float visibility = czm_shadowVisibility(shadowMap_texture, shadowParameters); \n' + ' // Fade out shadows that are far away \n' + ' float shadowMapMaximumDistance = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.z; \n' + ' float fade = max((depth - shadowMapMaximumDistance * 0.8) / (shadowMapMaximumDistance * 0.2), 0.0); \n' + ' visibility = mix(visibility, 1.0, fade); \n' + (debugCascadeColors ? ' // Draw cascade colors for debugging \n' + ' out_FragColor *= czm_cascadeColor(weights); \n' : '');
1147
+ } else {
1148
+ fsSource += ' float nDotL = clamp(dot(normalEC, shadowMap_lightDirectionEC), 0.0, 1.0); \n' + ' applyNormalOffset(positionEC, normalEC, nDotL); \n' + ' vec4 shadowPosition = shadowMap_matrix * positionEC; \n' + ' // Stop early if the fragment is not in the shadow bounds \n' + ' if (any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))) \n' + ' { \n' + ' return; \n' + ' } \n' + ' shadowParameters.texCoords = shadowPosition.xy; \n' + ' shadowParameters.depth = shadowPosition.z; \n' + ' shadowParameters.nDotL = nDotL; \n' + ' float visibility = czm_shadowVisibility(shadowMap_texture, shadowParameters); \n';
1149
+ }
1150
+ fsSource += ' if(visibility==0.0) \n' + ' {\n' + ' out_FragColor.rgb *= vec3(0.8,0.0,0.0);\n' + ' }else{\n' + ' out_FragColor.rgb *= vec3(0.0,0.8,0.0) * visibility;\n' + ' } \n ' + '} \n ';
1151
+ sources.push(fsSource);
1152
+ return new Cesium.ShaderSource({
1153
+ defines: defines,
1154
+ sources: sources
1155
+ });
1156
+ }
1157
+ Cesium.ShadowMap.createReceiveDerivedCommand = function (lightShadowMaps, command, shadowsDirty, context, result) {
1158
+ if (!Cesium.defined(result)) {
1159
+ result = {};
1160
+ }
1161
+ var lightShadowMapsEnabled = lightShadowMaps.length > 0;
1162
+ var shaderProgram = command.shaderProgram;
1163
+ var vertexShaderSource = shaderProgram.vertexShaderSource;
1164
+ var fragmentShaderSource = shaderProgram.fragmentShaderSource;
1165
+ var isTerrain = command.pass === Cesium.Pass.GLOBE;
1166
+ var hasTerrainNormal = false;
1167
+ if (isTerrain) {
1168
+ hasTerrainNormal = command.owner.data.renderedMesh.encoding.hasVertexNormals;
1169
+ }
1170
+ if (command.receiveShadows && lightShadowMapsEnabled) {
1171
+ // Only generate a receiveCommand if there is a shadow map originating from a light source.
1172
+ var receiveShader;
1173
+ var receiveUniformMap;
1174
+ if (Cesium.defined(result.receiveCommand)) {
1175
+ receiveShader = result.receiveCommand.shaderProgram;
1176
+ receiveUniformMap = result.receiveCommand.uniformMap;
1177
+ }
1178
+ result.receiveCommand = Cesium.DrawCommand.shallowClone(command, result.receiveCommand);
1179
+ result.castShadows = false;
1180
+ result.receiveShadows = true;
1181
+
1182
+ // If castShadows changed, recompile the receive shadows shader. The normal shading technique simulates
1183
+ // self-shadowing so it should be turned off if castShadows is false.
1184
+ var castShadowsDirty = result.receiveShaderCastShadows !== command.castShadows;
1185
+ var shaderDirty = result.receiveShaderProgramId !== command.shaderProgram.id;
1186
+ if (!Cesium.defined(receiveShader) || shaderDirty || shadowsDirty || castShadowsDirty) {
1187
+ var keyword;
1188
+ if (lightShadowMaps[0].isViewShed) {
1189
+ keyword = getShadowReceiveShaderKeyword(lightShadowMaps[0], command.castShadows, isTerrain, hasTerrainNormal);
1190
+ } else {
1191
+ keyword = Cesium.ShadowMapShader.getShadowReceiveShaderKeyword(lightShadowMaps[0], command.castShadows, isTerrain, hasTerrainNormal);
1192
+ }
1193
+ receiveShader = context.shaderCache.getDerivedShaderProgram(shaderProgram, keyword);
1194
+ if (!Cesium.defined(receiveShader)) {
1195
+ var receiveVS = Cesium.ShadowMapShader.createShadowReceiveVertexShader(vertexShaderSource, isTerrain, hasTerrainNormal);
1196
+ var receiveFS;
1197
+ if (lightShadowMaps[0].isViewShed) {
1198
+ receiveFS = createShadowReceiveFragmentShader(fragmentShaderSource, lightShadowMaps[0], command.castShadows, isTerrain, hasTerrainNormal);
1199
+ } else {
1200
+ receiveFS = Cesium.ShadowMapShader.createShadowReceiveFragmentShader(fragmentShaderSource, lightShadowMaps[0], command.castShadows, isTerrain, hasTerrainNormal);
1201
+ }
1202
+ receiveShader = context.shaderCache.createDerivedShaderProgram(shaderProgram, keyword, {
1203
+ vertexShaderSource: receiveVS,
1204
+ fragmentShaderSource: receiveFS,
1205
+ attributeLocations: shaderProgram._attributeLocations
1206
+ });
1207
+ }
1208
+ receiveUniformMap = combineUniforms(lightShadowMaps[0], command.uniformMap, isTerrain);
1209
+ }
1210
+ result.receiveCommand.shaderProgram = receiveShader;
1211
+ result.receiveCommand.uniformMap = receiveUniformMap;
1212
+ result.receiveShaderProgramId = command.shaderProgram.id;
1213
+ result.receiveShaderCastShadows = command.castShadows;
1214
+ }
1215
+ return result;
1216
+ };
1217
+
1218
+ /**
1219
+ * @private
1220
+ */
1221
+ ViewshedMap.prototype.isDestroyed = function () {
1222
+ return false;
1223
+ };
1224
+
1225
+ /**
1226
+ * @private
1227
+ */
1228
+ ViewshedMap.prototype.destroy = function () {
1229
+ destroyFramebuffer(this);
1230
+ this._debugLightFrustum = this._debugLightFrustum && this._debugLightFrustum.destroy();
1231
+ this._debugCameraFrustum = this._debugCameraFrustum && this._debugCameraFrustum.destroy();
1232
+ this._debugShadowViewCommand = this._debugShadowViewCommand && this._debugShadowViewCommand.shaderProgram && this._debugShadowViewCommand.shaderProgram.destroy();
1233
+ for (var _i5 = 0; _i5 < this._numberOfCascades; ++_i5) {
1234
+ this._debugCascadeFrustums[_i5] = this._debugCascadeFrustums[_i5] && this._debugCascadeFrustums[_i5].destroy();
1235
+ }
1236
+ return Cesium.destroyObject(this);
1237
+ };
1238
+ export default ViewshedMap;