├── .gitignore ├── LICENSE ├── README.md └── src ├── SensorShadow.fragment.shader.glsl └── SensorShadow.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SensorShadow Class for Cesium 2 | This repository contains the `SensorShadow` class for Cesium.js, a JavaScript library for creating 3D maps and globes in a web browser. The `SensorShadow` class allows you to create, update, and manage sensor shadow entities, including sensor shadow visualization, color coding, frustum support, and many more. 3 | 4 | ## Screenshot 5 | 6 | 7 | 8 | ## Demo 9 | 10 | [Sandcastle Link](https://sandcastle.cesium.com/#c=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) 11 | ## Installation 12 | 13 | This class requires the Cesium.js library. You need to install Cesium.js and import the SensorShadow class into your project. 14 | 15 | Please refer to the official [Cesium.js Installation Guide](https://cesium.com/docs/tutorials/quick-start/) for more information on how to install and set up Cesium.js. 16 | 17 | ## Usage 18 | 19 | To create a new SensorShadow instance, you need a reference to the Cesium viewer instance and an optional configuration object. The configuration object can include various properties like `cameraPosition`, `viewPosition`, `viewAreaColor`, `shadowAreaColor`, `alpha`, `frustum`, `size` and `depthBias`. 20 | 21 | Below is an example of how to create a new SensorShadow instance: 22 | 23 | ```javascript 24 | let sensorShadow = new SensorShadow(viewer, { 25 | cameraPosition: new Cartesian3(0, 0, 0), 26 | viewPosition: new Cartesian3(1, 1, 1), 27 | viewAreaColor: new Color(0, 1, 0), 28 | shadowAreaColor: new Color(1, 0, 0), 29 | alpha: 0.5, 30 | frustum: true, 31 | size: 512, 32 | }); 33 | ``` 34 | 35 | To Enable the terrain to terrain casting please use the Cesium Viewer configuration to enable the shadow casting. Please check the updated [Sandcastle Link](https://sandcastle.cesium.com/#c=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) 36 | 37 | ```javascript 38 | const viewer = new Cesium.Viewer("cesiumContainer", { 39 | shadows: true, 40 | terrainShadows: Cesium.ShadowMode.ENABLED, 41 | terrain: Cesium.Terrain.fromWorldTerrain({ 42 | requestWaterMask: true, 43 | requestVertexNormals: true, 44 | }), 45 | }); 46 | ``` 47 | 48 | ## Contributing 49 | 50 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Please make sure to update tests as appropriate. 51 | 52 | ## License 53 | 54 | ### [Apache 2.0](https://choosealicense.com/licenses/apache-2.0/) 55 | -------------------------------------------------------------------------------- /src/SensorShadow.fragment.shader.glsl: -------------------------------------------------------------------------------- 1 | export default ` 2 | #define USE_NORMAL_SHADING 3 | uniform float view_distance; // Maximum distance for shadow effect 4 | uniform vec3 viewArea_color; // Color for visible areas 5 | uniform vec3 shadowArea_color; // Color for invisible areas 6 | uniform float percentShade; // Mix number for color blending 7 | uniform sampler2D colorTexture; // Texture for color 8 | uniform sampler2D shadowMap; // Shadow map texture 9 | uniform sampler2D depthTexture; // Depth texture 10 | uniform mat4 shadowMap_matrix; // Shadow map matrix 11 | uniform vec3 viewPosition_WC; // Uniform for view position 12 | uniform vec3 cameraPosition_WC; // Uniform for camera position 13 | uniform vec4 shadowMap_camera_positionEC; // Light position in eye coordinates 14 | uniform vec4 shadowMap_camera_directionEC; // Light direction in eye coordinates 15 | uniform vec3 ellipsoidInverseRadii; 16 | uniform vec3 shadowMap_camera_up; // Light up direction 17 | uniform vec3 shadowMap_camera_dir; // Light direction 18 | uniform vec3 shadowMap_camera_right; // Light right direction 19 | uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness; // Shadow map parameters 20 | uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth; // Shadow map parameters 21 | uniform vec4 _shadowMap_cascadeSplits[2]; 22 | uniform mat4 _shadowMap_cascadeMatrices[4]; 23 | uniform vec4 _shadowMap_cascadeDistances; 24 | uniform bool exclude_terrain; 25 | 26 | in vec2 v_textureCoordinates; 27 | out vec4 FragColor; 28 | 29 | vec4 toEye(in vec2 uv, in float depth){ 30 | float x = uv.x * 2.0 - 1.0; 31 | float y = uv.y * 2.0 - 1.0; 32 | vec4 camPosition = czm_inverseProjection * vec4(x, y, depth, 1.0); 33 | float reciprocalW = 1.0 / camPosition.w; 34 | camPosition *= reciprocalW; 35 | return camPosition; 36 | } 37 | 38 | // This function gets the depth from a depth texture. 39 | float getDepth(in vec4 depth){ 40 | // Unpack the depth value from the depth texture 41 | float z_window = czm_unpackDepth(depth); 42 | // Reverse the logarithmic depth value to get the linear depth 43 | z_window = czm_reverseLogDepth(z_window); 44 | // Get the near and far values of the depth range 45 | float n_range = czm_depthRange.near; 46 | float f_range = czm_depthRange.far; 47 | // Convert the depth value from window coordinates to normalized device coordinates 48 | return (2.0 * z_window - n_range - f_range) / (f_range - n_range); 49 | } 50 | 51 | /** 52 | * Projects a point onto a plane. 53 | * 54 | * @param planeNormal - A vector representing the normal of the plane. 55 | * @param planeOrigin - A point on the plane. 56 | * @param point - The point to be projected onto the plane. 57 | * @return The projection of the point on the plane. 58 | */ 59 | vec3 pointProjectOnPlane(in vec3 planeNormal, in vec3 planeOrigin, in vec3 point){ 60 | // Calculate the vector from the plane origin to the point 61 | vec3 v01 = point - planeOrigin; 62 | 63 | // Calculate the perpendicular distance from the point to the plane 64 | float d = dot(planeNormal, v01); 65 | 66 | // Subtract the product of the plane normal and d from the point 67 | // to get the projection of the point on the plane 68 | return (point - planeNormal * d); 69 | } 70 | 71 | /** 72 | * Calculates the magnitude (length) of a vector. 73 | * 74 | * @param pt - The input vector. 75 | * @return The magnitude of the vector. 76 | */ 77 | float point2mag(vec3 point){ 78 | // Square each component of the vector, add them together, 79 | // and take the square root of the result 80 | return sqrt(point.x*point.x + point.y*point.y + point.z*point.z); 81 | } 82 | 83 | /** 84 | * Main function for the fragment shader. 85 | */ 86 | void main() 87 | { 88 | // Get the color and depth at the current texture coordinates 89 | vec4 color = texture(colorTexture, v_textureCoordinates); 90 | vec4 cDepth = texture(depthTexture, v_textureCoordinates); 91 | 92 | // Get the depth and position in eye coordinates 93 | float depth = getDepth(cDepth); 94 | vec4 positionEC = toEye(v_textureCoordinates, depth); 95 | 96 | // If the depth is at its maximum value, set the fragment color to the texture color and return 97 | if(cDepth.r >= 1.0){ 98 | FragColor = color; 99 | return; 100 | } 101 | 102 | //check to see if we are within distance of the view target 103 | float cameraDistance = length(cameraPosition_WC.xyz - viewPosition_WC.xyz); 104 | 105 | // Get the fragment position in world coordinates 106 | vec4 fragPosition_WC = vec4(v_textureCoordinates, 0.0, 1.0); 107 | 108 | if ( 109 | cDepth.r >= 1.0 || 110 | (exclude_terrain && czm_ellipsoidContainsPoint(ellipsoidInverseRadii, positionEC.xyz)) 111 | ){ 112 | FragColor = color; 113 | return; 114 | } 115 | 116 | // Initialize shadow parameters 117 | czm_shadowParameters shadowParameters; 118 | shadowParameters.texelStepSize = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy; 119 | shadowParameters.depthBias = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z; 120 | shadowParameters.normalShadingSmooth = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w; 121 | shadowParameters.darkness = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w; 122 | 123 | // Adjust the depth bias 124 | shadowParameters.depthBias *= max(depth * 0.01, 1.0); 125 | 126 | // Calculate the direction in eye coordinates 127 | vec3 directionEC = normalize(positionEC.xyz - shadowMap_camera_positionEC.xyz); 128 | 129 | // Calculate the dot product of the normal and the negative direction 130 | float nDotL = clamp(dot(vec3(1.0), -directionEC), 0.0, 1.0); 131 | 132 | // Calculate the shadow position 133 | vec4 shadowPosition = shadowMap_matrix * positionEC; 134 | shadowPosition /= shadowPosition.w; 135 | 136 | // If the shadow position is outside the [0, 1] range in any dimension, set the fragment color to the texture color and return 137 | if (any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))) 138 | { 139 | FragColor = color; 140 | return; 141 | } 142 | 143 | // If the distance between the coordinates and the viewpoint is greater than the maximum distance, the shadow effect is discarded 144 | vec4 lw = czm_inverseView* vec4(shadowMap_camera_positionEC.xyz, 1.0); 145 | vec4 vw = czm_inverseView* vec4(positionEC.xyz, 1.0); 146 | 147 | if(distance(lw.xyz,vw.xyz)>view_distance){ 148 | FragColor = color; 149 | return; 150 | } 151 | 152 | // Set the shadow parameters 153 | shadowParameters.texCoords = shadowPosition.xy; 154 | shadowParameters.depth = shadowPosition.z; 155 | shadowParameters.nDotL = nDotL; 156 | 157 | // Calculate the shadow visibility 158 | float visibility = czm_shadowVisibility(shadowMap, shadowParameters); 159 | 160 | // If the visibility is 1.0, mix the color with the visible color 161 | if(visibility==1.0){ 162 | FragColor = mix(texture(colorTexture, v_textureCoordinates),vec4(viewArea_color,1.0),percentShade); 163 | }else{ 164 | if(abs(shadowPosition.z-0.0)<0.01){ 165 | FragColor = color; 166 | return; 167 | } 168 | FragColor = mix(texture(colorTexture, v_textureCoordinates),vec4(shadowArea_color,1.0),percentShade); 169 | } 170 | }`; -------------------------------------------------------------------------------- /src/SensorShadow.js: -------------------------------------------------------------------------------- 1 | import defaultValue from "@cesium/engine/Source/Core/defaultValue"; 2 | import Color from "@cesium/engine/Source/Core/Color"; 3 | import Camera from "@cesium/engine/Source/Scene/Camera"; 4 | import PerspectiveFrustum from "@cesium/engine/Source/Core/PerspectiveFrustum"; 5 | import CesiumMath from "@cesium/engine/Source/Core/Math"; 6 | import ShadowMap from "@cesium/engine/Source/Scene/ShadowMap"; 7 | import { 8 | PositionProperty, 9 | ConstantPositionProperty, 10 | Cartesian2, 11 | Cartesian3, 12 | Cartesian4, 13 | EllipsoidTerrainProvider, 14 | } from "@cesium/engine"; 15 | import PostProcessStage from "@cesium/engine/Source/Scene/PostProcessStage"; 16 | import ViewShead3D_FS from "./SensorShadow.fragment.shader.glsl"; 17 | import DeveloperError from "@cesium/engine/Source/Core/DeveloperError"; 18 | 19 | const defaultValues = { 20 | cameraPosition: new ConstantPositionProperty(), 21 | viewPosition: new ConstantPositionProperty(), 22 | viewAreaColor: new Color(0, 1, 0), 23 | shadowAreaColor: new Color(1, 0, 0), 24 | alpha: 0.5, 25 | frustum: true, 26 | size: 4096, 27 | depthBias: 2e-12, 28 | }; 29 | 30 | /** 31 | * SensorShadow Class. 32 | * This class handles the creation, update and management of sensor shadow entities. 33 | * 34 | * @property {Object} viewer - A reference to the Cesium viewer instance. 35 | * @property {ConstantPositionProperty|PositionProperty|Cartesian3} cameraPosition - The camera position. 36 | * @property {ConstantPositionProperty|PositionProperty|Cartesian3} viewPosition - The view position. 37 | * @property {Color} viewAreaColor - The color of the visible area of the sensor shadow. 38 | * @property {Color} shadowAreaColor - The color of the hidden area of the sensor shadow. 39 | * @property {number} alpha - The alpha value for the sensor shadow. 40 | * @property {boolean} frustum - Whether the frustum is enabled. 41 | * @property {number} size - The size of the sensor shadow. 42 | */ 43 | class SensorShadow { 44 | /** 45 | * Constructs a new SensorShadow instance. 46 | * 47 | * @param {Object} viewer - A reference to the Cesium viewer instance. 48 | * @param {Object} options - An optional configuration object. 49 | * 50 | * @example 51 | * let sensorShadow = new SensorShadow(viewer, { 52 | * cameraPosition: new Cartesian3(0, 0, 0), 53 | * viewPosition: new Cartesian3(1, 1, 1), 54 | * viewAreaColor: new Color(0, 1, 0), 55 | * shadowAreaColor: new Color(1, 0, 0), 56 | * alpha: 0.5, 57 | * frustum: true, 58 | * size: 512 59 | * }); 60 | */ 61 | constructor(viewer, options = {}) { 62 | this.viewer = viewer; 63 | this._isDestroyed = false; 64 | 65 | this.cameraPosition = 66 | typeof options.cameraPosition.getValue === "function" 67 | ? options.cameraPosition 68 | : new ConstantPositionProperty(options.cameraPosition); 69 | 70 | this.viewPosition = 71 | typeof options.viewPosition.getValue === "function" 72 | ? options.viewPosition 73 | : new ConstantPositionProperty(options.viewPosition); 74 | 75 | this.viewAreaColor = defaultValue( 76 | options.viewAreaColor, 77 | defaultValues.viewAreaColor 78 | ); 79 | 80 | this.shadowAreaColor = defaultValue( 81 | options.shadowAreaColor, 82 | defaultValues.shadowAreaColor 83 | ); 84 | 85 | this.alpha = defaultValue(options.alpha, defaultValues.alpha); 86 | this.size = defaultValue(options.size, defaultValues.size); 87 | this.frustum = defaultValue(options.frustum, defaultValues.frustum); 88 | this.depthBias = defaultValue(options.depthBias, defaultValues.depthBias); 89 | 90 | this.preUpdateListener = null; 91 | 92 | if (this.cameraPosition && this.viewPosition) { 93 | this._addToScene(); 94 | } 95 | } 96 | 97 | /** 98 | * Get the actual position of the camera. 99 | * This method calculates the position vector based on the current time. 100 | * 101 | * @private 102 | * @returns {Cartesian3} The calculated camera position vector. 103 | */ 104 | get _getVectors() { 105 | let positionVector = this.cameraPosition.getValue( 106 | this.viewer.clock.currentTime 107 | ); 108 | let viewVector = this.viewPosition.getValue(this.viewer.clock.currentTime); 109 | let distanceBetweenVectors = Number( 110 | Cartesian3.distance(viewVector, positionVector).toFixed(1) 111 | ); 112 | 113 | if (distanceBetweenVectors > 10000) { 114 | let multiple = 1 - 10000 / distanceBetweenVectors; 115 | positionVector = Cartesian3.lerp( 116 | positionVector, 117 | viewVector, 118 | multiple, 119 | new Cartesian3() 120 | ); 121 | } 122 | 123 | return { positionVector, viewVector }; 124 | } 125 | 126 | destroy() { 127 | // If a pre-update listener was added, remove it 128 | if (this.preUpdateListener) { 129 | this.viewer.scene.preUpdate.removeEventListener(this.preUpdateListener); 130 | this.preUpdateListener = null; 131 | } 132 | 133 | // If there's a shadow map, dispose of it 134 | if (this.viewShadowMap) { 135 | this.viewShadowMap.dispose(); 136 | this.viewShadowMap = null; 137 | } 138 | 139 | // Remove the post-process stage if it has been added 140 | if (this.postProcess) { 141 | this.viewer.scene.postProcessStages.remove(this.postProcess); 142 | this.postProcess = null; 143 | } 144 | 145 | // Remove this object from the scene primitives if it has been added 146 | this.viewer.scene.primitives.remove(this); 147 | 148 | // Explicitly remove references to potentially large objects to assist with garbage collection 149 | for (let property in this) { 150 | if (this.hasOwnProperty(property)) { 151 | delete this[property]; 152 | } 153 | } 154 | 155 | // Set the destroyed flag 156 | this._isDestroyed = true; 157 | } 158 | 159 | isDestroyed() { 160 | // Return the destroyed status 161 | return this._isDestroyed; 162 | } 163 | 164 | 165 | /** 166 | * Adds the SensorShadow to the scene. 167 | * 168 | * @private 169 | */ 170 | _addToScene() { 171 | this._createShadowMap(); 172 | this._addPostProcess(); 173 | 174 | this.viewer.scene.primitives.add(this); 175 | 176 | } 177 | 178 | /** 179 | * Creates the shadow map. 180 | * 181 | * @private 182 | */ 183 | _createShadowMap(updateOnly) { 184 | let { positionVector, viewVector } = this._getVectors; 185 | 186 | const distance = Number( 187 | Cartesian3.distance(viewVector, positionVector).toFixed(1) 188 | ); 189 | 190 | if (distance > 10000) { 191 | const multiple = 1 - 10000 / distance; 192 | positionVector = Cartesian3.lerp( 193 | positionVector, 194 | viewVector, 195 | multiple, 196 | new Cartesian3() 197 | ); 198 | } 199 | 200 | const scene = this.viewer.scene; 201 | 202 | const camera = new Camera(scene); 203 | 204 | camera.position = positionVector; 205 | 206 | camera.direction = Cartesian3.subtract( 207 | viewVector, 208 | positionVector, 209 | new Cartesian3(0, 0, 0) 210 | ); 211 | 212 | camera.up = Cartesian3.normalize(positionVector, new Cartesian3(0, 0, 0)); 213 | 214 | camera.frustum = new PerspectiveFrustum({ 215 | fov: CesiumMath.toRadians(120), 216 | aspectRatio: scene.canvas.clientWidth / scene.canvas.clientHeight, 217 | near: 0.1, 218 | far: distance, 219 | }); 220 | 221 | if (!updateOnly) { 222 | this.viewShadowMap = new ShadowMap({ 223 | lightCamera: camera, 224 | enable: true, 225 | isPointLight: false, 226 | isSpotLight: true, 227 | cascadesEnabled: false, 228 | context: scene.context, 229 | size: this.size, 230 | pointLightRadius: distance, 231 | fromLightSource: false, 232 | maximumDistance: distance, 233 | }); 234 | } else { 235 | this.viewShadowMap._lightCamera.position = positionVector; 236 | } 237 | 238 | this.viewShadowMap.normalOffset = true; 239 | this.viewShadowMap._terrainBias.depthBias = 0.0; 240 | } 241 | 242 | /** 243 | * Adds post processing to the SensorShadow. 244 | * 245 | * @private 246 | */ 247 | _addPostProcess() { 248 | const SensorShadow = this; 249 | 250 | const viewShadowMap = this.viewShadowMap; 251 | const primitiveBias = viewShadowMap._isPointLight 252 | ? viewShadowMap._pointBias 253 | : viewShadowMap._primitiveBias; 254 | this.postProcess = this.viewer.scene.postProcessStages.add( 255 | new PostProcessStage({ 256 | fragmentShader: fsShader, 257 | uniforms: { 258 | view_distance: function () { 259 | return SensorShadow.distance; 260 | }, 261 | viewArea_color: function () { 262 | return SensorShadow.viewAreaColor; 263 | }, 264 | shadowArea_color: function () { 265 | return SensorShadow.shadowAreaColor; 266 | }, 267 | percentShade: function () { 268 | return SensorShadow.alpha; 269 | }, 270 | shadowMap: function () { 271 | return viewShadowMap._shadowMapTexture; 272 | }, 273 | _shadowMap_cascadeSplits: function () { 274 | return viewShadowMap._cascadeSplits; 275 | }, 276 | _shadowMap_cascadeMatrices: function () { 277 | return viewShadowMap._cascadeMatrices; 278 | }, 279 | _shadowMap_cascadeDistances: function () { 280 | return viewShadowMap._cascadeDistances; 281 | }, 282 | shadowMap_matrix: function () { 283 | return viewShadowMap._shadowMapMatrix; 284 | }, 285 | shadowMap_camera_positionEC: function () { 286 | return viewShadowMap._lightPositionEC; 287 | }, 288 | shadowMap_camera_directionEC: function () { 289 | return viewShadowMap._lightDirectionEC; 290 | }, 291 | cameraPosition_WC: function () { 292 | return SensorShadow.viewer.camera.positionWC; 293 | }, 294 | viewPosition_WC: function () { 295 | return SensorShadow.viewPosition.getValue( 296 | SensorShadow.viewer.clock.currentTime 297 | ); 298 | }, 299 | shadowMap_camera_up: function () { 300 | return viewShadowMap._lightCamera.up; 301 | }, 302 | shadowMap_camera_dir: function () { 303 | return viewShadowMap._lightCamera.direction; 304 | }, 305 | shadowMap_camera_right: function () { 306 | return viewShadowMap._lightCamera.right; 307 | }, 308 | ellipsoidInverseRadii: function () { 309 | let radii = SensorShadow.viewer.scene.globe.ellipsoid.radii; 310 | return new Cartesian3(1 / radii.x, 1 / radii.y, 1 / radii.z); 311 | }, 312 | shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: function () { 313 | var viewShed2D = new Cartesian2(); 314 | viewShed2D.x = 1 / viewShadowMap._textureSize.x; 315 | viewShed2D.y = 1 / viewShadowMap._textureSize.y; 316 | 317 | return Cartesian4.fromElements( 318 | viewShed2D.x, 319 | viewShed2D.y, 320 | this.depthBias, 321 | primitiveBias.normalShadingSmooth, 322 | this.combinedUniforms1 323 | ); 324 | }, 325 | shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: 326 | function () { 327 | return Cartesian4.fromElements( 328 | primitiveBias.normalOffsetScale, 329 | viewShadowMap._distance, 330 | viewShadowMap.maximumDistance, 331 | viewShadowMap._darkness, 332 | this.combinedUniforms2 333 | ); 334 | }, 335 | exclude_terrain: function () { 336 | return ( 337 | SensorShadow.viewer.terrainProvider instanceof 338 | EllipsoidTerrainProvider 339 | ); 340 | }, 341 | }, 342 | }) 343 | ); 344 | 345 | // If a previous listener was added, remove it 346 | if (this.preUpdateListener) { 347 | viewer.scene.preUpdate.removeEventListener(this.preUpdateListener); 348 | } 349 | 350 | // Add a new listener 351 | this.preUpdateListener = () => { 352 | if (!this.viewShadowMap._shadowMapTexture) { 353 | this.postProcess.enabled = false; 354 | } else { 355 | this.postProcess.enabled = true; 356 | } 357 | }; 358 | 359 | viewer.scene.preUpdate.addEventListener(this.preUpdateListener); 360 | } 361 | 362 | update(frameState) { 363 | this._createShadowMap(true); 364 | frameState.shadowMaps.push(this.viewShadowMap); 365 | } 366 | 367 | destroy() { 368 | if (this.preUpdateListener) { 369 | viewer.scene.preUpdate.removeEventListener(this.preUpdateListener); 370 | } 371 | this.viewer.scene.postProcessStages.remove(this.postProcess); 372 | for (let property in this) { 373 | if (this.hasOwnProperty(property)) { 374 | delete this[property]; 375 | } 376 | } 377 | } 378 | 379 | get size() { 380 | return this._size; 381 | } 382 | 383 | set size(v) { 384 | this._size = v; 385 | } 386 | 387 | get depthBias() { 388 | return this._depthBias; 389 | } 390 | 391 | set depthBias(v) { 392 | this._depthBias = v; 393 | } 394 | 395 | get cameraPosition() { 396 | return this._cameraPosition; 397 | } 398 | 399 | set cameraPosition(v) { 400 | this._cameraPosition = v; 401 | } 402 | 403 | get viewPosition() { 404 | return this._viewPosition; 405 | } 406 | 407 | set viewPosition(v) { 408 | this._viewPosition = v; 409 | } 410 | 411 | get frustum() { 412 | return this._frustum; 413 | } 414 | 415 | set frustum(v) { 416 | this._frustum = v; 417 | } 418 | 419 | get distance() { 420 | return this._distance; 421 | } 422 | 423 | set distance(v) { 424 | this._distance = v; 425 | } 426 | 427 | get viewAreaColor() { 428 | return this._viewAreaColor; 429 | } 430 | 431 | set viewAreaColor(v) { 432 | this._viewAreaColor = v; 433 | } 434 | 435 | get shadowAreaColor() { 436 | return this._shadowAreaColor; 437 | } 438 | 439 | set shadowAreaColor(v) { 440 | this._shadowAreaColor = v; 441 | } 442 | 443 | get alpha() { 444 | return this._alpha; 445 | } 446 | 447 | set alpha(v) { 448 | this._alpha = v; 449 | } 450 | } 451 | 452 | export default SensorShadow; 453 | --------------------------------------------------------------------------------