ShadowMapShader.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. import defined from "../Core/defined.js";
  2. import ShaderSource from "../Renderer/ShaderSource.js";
  3. /**
  4. * @private
  5. */
  6. function ShadowMapShader() {}
  7. ShadowMapShader.getShadowCastShaderKeyword = function (
  8. isPointLight,
  9. isTerrain,
  10. usesDepthTexture,
  11. isOpaque
  12. ) {
  13. return `castShadow ${isPointLight} ${isTerrain} ${usesDepthTexture} ${isOpaque}`;
  14. };
  15. ShadowMapShader.createShadowCastVertexShader = function (
  16. vs,
  17. isPointLight,
  18. isTerrain
  19. ) {
  20. const defines = vs.defines.slice(0);
  21. const sources = vs.sources.slice(0);
  22. defines.push("SHADOW_MAP");
  23. if (isTerrain) {
  24. defines.push("GENERATE_POSITION");
  25. }
  26. const positionVaryingName = ShaderSource.findPositionVarying(vs);
  27. const hasPositionVarying = defined(positionVaryingName);
  28. if (isPointLight && !hasPositionVarying) {
  29. const length = sources.length;
  30. for (let j = 0; j < length; ++j) {
  31. sources[j] = ShaderSource.replaceMain(sources[j], "czm_shadow_cast_main");
  32. }
  33. const shadowVS =
  34. "out vec3 v_positionEC; \n" +
  35. "void main() \n" +
  36. "{ \n" +
  37. " czm_shadow_cast_main(); \n" +
  38. " v_positionEC = (czm_inverseProjection * gl_Position).xyz; \n" +
  39. "}";
  40. sources.push(shadowVS);
  41. }
  42. return new ShaderSource({
  43. defines: defines,
  44. sources: sources,
  45. });
  46. };
  47. ShadowMapShader.createShadowCastFragmentShader = function (
  48. fs,
  49. isPointLight,
  50. usesDepthTexture,
  51. opaque
  52. ) {
  53. const defines = fs.defines.slice(0);
  54. const sources = fs.sources.slice(0);
  55. defines.push("SHADOW_MAP");
  56. let positionVaryingName = ShaderSource.findPositionVarying(fs);
  57. const hasPositionVarying = defined(positionVaryingName);
  58. if (!hasPositionVarying) {
  59. positionVaryingName = "v_positionEC";
  60. }
  61. const length = sources.length;
  62. for (let i = 0; i < length; ++i) {
  63. sources[i] = ShaderSource.replaceMain(sources[i], "czm_shadow_cast_main");
  64. }
  65. let fsSource = "";
  66. if (isPointLight) {
  67. if (!hasPositionVarying) {
  68. fsSource += "in vec3 v_positionEC; \n";
  69. }
  70. fsSource += "uniform vec4 shadowMap_lightPositionEC; \n";
  71. }
  72. if (opaque) {
  73. fsSource += "void main() \n" + "{ \n";
  74. } else {
  75. fsSource +=
  76. "void main() \n" +
  77. "{ \n" +
  78. " czm_shadow_cast_main(); \n" +
  79. " if (out_FragColor.a == 0.0) \n" +
  80. " { \n" +
  81. " discard; \n" +
  82. " } \n";
  83. }
  84. if (isPointLight) {
  85. fsSource +=
  86. ` float distance = length(${positionVaryingName}); \n` +
  87. ` if (distance >= shadowMap_lightPositionEC.w) \n` +
  88. ` { \n` +
  89. ` discard; \n` +
  90. ` } \n` +
  91. ` distance /= shadowMap_lightPositionEC.w; // radius \n` +
  92. ` out_FragColor = czm_packDepth(distance); \n`;
  93. } else if (usesDepthTexture) {
  94. fsSource += " out_FragColor = vec4(1.0); \n";
  95. } else {
  96. fsSource += " out_FragColor = czm_packDepth(gl_FragCoord.z); \n";
  97. }
  98. fsSource += "} \n";
  99. sources.push(fsSource);
  100. return new ShaderSource({
  101. defines: defines,
  102. sources: sources,
  103. });
  104. };
  105. ShadowMapShader.getShadowReceiveShaderKeyword = function (
  106. shadowMap,
  107. castShadows,
  108. isTerrain,
  109. hasTerrainNormal
  110. ) {
  111. const usesDepthTexture = shadowMap._usesDepthTexture;
  112. const polygonOffsetSupported = shadowMap._polygonOffsetSupported;
  113. const isPointLight = shadowMap._isPointLight;
  114. const isSpotLight = shadowMap._isSpotLight;
  115. const hasCascades = shadowMap._numberOfCascades > 1;
  116. const debugCascadeColors = shadowMap.debugCascadeColors;
  117. const softShadows = shadowMap.softShadows;
  118. return `receiveShadow ${usesDepthTexture}${polygonOffsetSupported}${isPointLight}${isSpotLight}${hasCascades}${debugCascadeColors}${softShadows}${castShadows}${isTerrain}${hasTerrainNormal}`;
  119. };
  120. ShadowMapShader.createShadowReceiveVertexShader = function (
  121. vs,
  122. isTerrain,
  123. hasTerrainNormal
  124. ) {
  125. const defines = vs.defines.slice(0);
  126. const sources = vs.sources.slice(0);
  127. defines.push("SHADOW_MAP");
  128. if (isTerrain) {
  129. if (hasTerrainNormal) {
  130. defines.push("GENERATE_POSITION_AND_NORMAL");
  131. } else {
  132. defines.push("GENERATE_POSITION");
  133. }
  134. }
  135. return new ShaderSource({
  136. defines: defines,
  137. sources: sources,
  138. });
  139. };
  140. ShadowMapShader.createShadowReceiveFragmentShader = function (
  141. fs,
  142. shadowMap,
  143. castShadows,
  144. isTerrain,
  145. hasTerrainNormal
  146. ) {
  147. const normalVaryingName = ShaderSource.findNormalVarying(fs);
  148. const hasNormalVarying =
  149. (!isTerrain && defined(normalVaryingName)) ||
  150. (isTerrain && hasTerrainNormal);
  151. const positionVaryingName = ShaderSource.findPositionVarying(fs);
  152. const hasPositionVarying = defined(positionVaryingName);
  153. const usesDepthTexture = shadowMap._usesDepthTexture;
  154. const polygonOffsetSupported = shadowMap._polygonOffsetSupported;
  155. const isPointLight = shadowMap._isPointLight;
  156. const isSpotLight = shadowMap._isSpotLight;
  157. const hasCascades = shadowMap._numberOfCascades > 1;
  158. const debugCascadeColors = shadowMap.debugCascadeColors;
  159. const softShadows = shadowMap.softShadows;
  160. const bias = isPointLight
  161. ? shadowMap._pointBias
  162. : isTerrain
  163. ? shadowMap._terrainBias
  164. : shadowMap._primitiveBias;
  165. const defines = fs.defines.slice(0);
  166. const sources = fs.sources.slice(0);
  167. const length = sources.length;
  168. for (let i = 0; i < length; ++i) {
  169. sources[i] = ShaderSource.replaceMain(
  170. sources[i],
  171. "czm_shadow_receive_main"
  172. );
  173. }
  174. if (isPointLight) {
  175. defines.push("USE_CUBE_MAP_SHADOW");
  176. } else if (usesDepthTexture) {
  177. defines.push("USE_SHADOW_DEPTH_TEXTURE");
  178. }
  179. if (softShadows && !isPointLight) {
  180. defines.push("USE_SOFT_SHADOWS");
  181. }
  182. // Enable day-night shading so that the globe is dark when the light is below the horizon
  183. if (hasCascades && castShadows && isTerrain) {
  184. if (hasNormalVarying) {
  185. defines.push("ENABLE_VERTEX_LIGHTING");
  186. } else {
  187. defines.push("ENABLE_DAYNIGHT_SHADING");
  188. }
  189. }
  190. if (castShadows && bias.normalShading && hasNormalVarying) {
  191. defines.push("USE_NORMAL_SHADING");
  192. if (bias.normalShadingSmooth > 0.0) {
  193. defines.push("USE_NORMAL_SHADING_SMOOTH");
  194. }
  195. }
  196. let fsSource = "";
  197. if (isPointLight) {
  198. fsSource += "uniform samplerCube shadowMap_textureCube; \n";
  199. } else {
  200. fsSource += "uniform sampler2D shadowMap_texture; \n";
  201. }
  202. let returnPositionEC;
  203. if (hasPositionVarying) {
  204. returnPositionEC = ` return vec4(${positionVaryingName}, 1.0); \n`;
  205. } else {
  206. returnPositionEC =
  207. "#ifndef LOG_DEPTH \n" +
  208. " return czm_windowToEyeCoordinates(gl_FragCoord); \n" +
  209. "#else \n" +
  210. " return vec4(v_logPositionEC, 1.0); \n" +
  211. "#endif \n";
  212. }
  213. fsSource +=
  214. `${
  215. "uniform mat4 shadowMap_matrix; \n" +
  216. "uniform vec3 shadowMap_lightDirectionEC; \n" +
  217. "uniform vec4 shadowMap_lightPositionEC; \n" +
  218. "uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness; \n" +
  219. "uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth; \n" +
  220. "#ifdef LOG_DEPTH \n" +
  221. "in vec3 v_logPositionEC; \n" +
  222. "#endif \n" +
  223. "vec4 getPositionEC() \n" +
  224. "{ \n"
  225. }${returnPositionEC}} \n` +
  226. `vec3 getNormalEC() \n` +
  227. `{ \n${
  228. hasNormalVarying
  229. ? ` return normalize(${normalVaryingName}); \n`
  230. : " return vec3(1.0); \n"
  231. }} \n` +
  232. // Offset the shadow position in the direction of the normal for perpendicular and back faces
  233. `void applyNormalOffset(inout vec4 positionEC, vec3 normalEC, float nDotL) \n` +
  234. `{ \n${
  235. bias.normalOffset && hasNormalVarying
  236. ? " float normalOffset = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.x; \n" +
  237. " float normalOffsetScale = 1.0 - nDotL; \n" +
  238. " vec3 offset = normalOffset * normalOffsetScale * normalEC; \n" +
  239. " positionEC.xyz += offset; \n"
  240. : ""
  241. }} \n`;
  242. fsSource +=
  243. "void main() \n" +
  244. "{ \n" +
  245. " czm_shadow_receive_main(); \n" +
  246. " vec4 positionEC = getPositionEC(); \n" +
  247. " vec3 normalEC = getNormalEC(); \n" +
  248. " float depth = -positionEC.z; \n";
  249. fsSource +=
  250. " czm_shadowParameters shadowParameters; \n" +
  251. " shadowParameters.texelStepSize = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy; \n" +
  252. " shadowParameters.depthBias = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z; \n" +
  253. " shadowParameters.normalShadingSmooth = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w; \n" +
  254. " shadowParameters.darkness = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w; \n";
  255. if (isTerrain) {
  256. // Scale depth bias based on view distance to reduce z-fighting in distant terrain
  257. fsSource += " shadowParameters.depthBias *= max(depth * 0.01, 1.0); \n";
  258. } else if (!polygonOffsetSupported) {
  259. // If polygon offset isn't supported push the depth back based on view, however this
  260. // causes light leaking at further away views
  261. fsSource +=
  262. " shadowParameters.depthBias *= mix(1.0, 100.0, depth * 0.0015); \n";
  263. }
  264. if (isPointLight) {
  265. fsSource +=
  266. " vec3 directionEC = positionEC.xyz - shadowMap_lightPositionEC.xyz; \n" +
  267. " float distance = length(directionEC); \n" +
  268. " directionEC = normalize(directionEC); \n" +
  269. " float radius = shadowMap_lightPositionEC.w; \n" +
  270. " // Stop early if the fragment is beyond the point light radius \n" +
  271. " if (distance > radius) \n" +
  272. " { \n" +
  273. " return; \n" +
  274. " } \n" +
  275. " vec3 directionWC = czm_inverseViewRotation * directionEC; \n" +
  276. " shadowParameters.depth = distance / radius; \n" +
  277. " shadowParameters.nDotL = clamp(dot(normalEC, -directionEC), 0.0, 1.0); \n" +
  278. " shadowParameters.texCoords = directionWC; \n" +
  279. " float visibility = czm_shadowVisibility(shadowMap_textureCube, shadowParameters); \n";
  280. } else if (isSpotLight) {
  281. fsSource +=
  282. " vec3 directionEC = normalize(positionEC.xyz - shadowMap_lightPositionEC.xyz); \n" +
  283. " float nDotL = clamp(dot(normalEC, -directionEC), 0.0, 1.0); \n" +
  284. " applyNormalOffset(positionEC, normalEC, nDotL); \n" +
  285. " vec4 shadowPosition = shadowMap_matrix * positionEC; \n" +
  286. " // Spot light uses a perspective projection, so perform the perspective divide \n" +
  287. " shadowPosition /= shadowPosition.w; \n" +
  288. " // Stop early if the fragment is not in the shadow bounds \n" +
  289. " if (any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))) \n" +
  290. " { \n" +
  291. " return; \n" +
  292. " } \n" +
  293. " shadowParameters.texCoords = shadowPosition.xy; \n" +
  294. " shadowParameters.depth = shadowPosition.z; \n" +
  295. " shadowParameters.nDotL = nDotL; \n" +
  296. " float visibility = czm_shadowVisibility(shadowMap_texture, shadowParameters); \n";
  297. } else if (hasCascades) {
  298. fsSource += `${
  299. " float maxDepth = shadowMap_cascadeSplits[1].w; \n" +
  300. " // Stop early if the eye depth exceeds the last cascade \n" +
  301. " if (depth > maxDepth) \n" +
  302. " { \n" +
  303. " return; \n" +
  304. " } \n" +
  305. " // Get the cascade based on the eye-space depth \n" +
  306. " vec4 weights = czm_cascadeWeights(depth); \n" +
  307. " // Apply normal offset \n" +
  308. " float nDotL = clamp(dot(normalEC, shadowMap_lightDirectionEC), 0.0, 1.0); \n" +
  309. " applyNormalOffset(positionEC, normalEC, nDotL); \n" +
  310. " // Transform position into the cascade \n" +
  311. " vec4 shadowPosition = czm_cascadeMatrix(weights) * positionEC; \n" +
  312. " // Get visibility \n" +
  313. " shadowParameters.texCoords = shadowPosition.xy; \n" +
  314. " shadowParameters.depth = shadowPosition.z; \n" +
  315. " shadowParameters.nDotL = nDotL; \n" +
  316. " float visibility = czm_shadowVisibility(shadowMap_texture, shadowParameters); \n" +
  317. " // Fade out shadows that are far away \n" +
  318. " float shadowMapMaximumDistance = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.z; \n" +
  319. " float fade = max((depth - shadowMapMaximumDistance * 0.8) / (shadowMapMaximumDistance * 0.2), 0.0); \n" +
  320. " visibility = mix(visibility, 1.0, fade); \n"
  321. }${
  322. debugCascadeColors
  323. ? " // Draw cascade colors for debugging \n" +
  324. " out_FragColor *= czm_cascadeColor(weights); \n"
  325. : ""
  326. }`;
  327. } else {
  328. fsSource +=
  329. " float nDotL = clamp(dot(normalEC, shadowMap_lightDirectionEC), 0.0, 1.0); \n" +
  330. " applyNormalOffset(positionEC, normalEC, nDotL); \n" +
  331. " vec4 shadowPosition = shadowMap_matrix * positionEC; \n" +
  332. " // Stop early if the fragment is not in the shadow bounds \n" +
  333. " if (any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))) \n" +
  334. " { \n" +
  335. " return; \n" +
  336. " } \n" +
  337. " shadowParameters.texCoords = shadowPosition.xy; \n" +
  338. " shadowParameters.depth = shadowPosition.z; \n" +
  339. " shadowParameters.nDotL = nDotL; \n" +
  340. " float visibility = czm_shadowVisibility(shadowMap_texture, shadowParameters); \n";
  341. }
  342. fsSource += " out_FragColor.rgb *= visibility; \n" + "} \n";
  343. sources.push(fsSource);
  344. return new ShaderSource({
  345. defines: defines,
  346. sources: sources,
  347. });
  348. };
  349. export default ShadowMapShader;