ShadowMapShader.js 13 KB

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