shadows.frag.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /**
  2. * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Ludovic Autin <ludovic.autin@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. export const shadows_frag = `
  8. precision highp float;
  9. precision highp int;
  10. precision highp sampler2D;
  11. #include common
  12. uniform sampler2D tDepth;
  13. uniform vec2 uTexSize;
  14. uniform vec4 uBounds;
  15. uniform float uNear;
  16. uniform float uFar;
  17. #if dLightCount != 0
  18. uniform vec3 uLightDirection[dLightCount];
  19. uniform vec3 uLightColor[dLightCount];
  20. #endif
  21. uniform mat4 uProjection;
  22. uniform mat4 uInvProjection;
  23. uniform float uMaxDistance;
  24. uniform float uTolerance;
  25. uniform float uBias;
  26. bool isBackground(const in float depth) {
  27. return depth == 1.0;
  28. }
  29. bool outsideBounds(const in vec2 p) {
  30. return p.x < uBounds.x || p.y < uBounds.y || p.x > uBounds.z || p.y > uBounds.w;
  31. }
  32. float getViewZ(const in float depth) {
  33. #if dOrthographic == 1
  34. return orthographicDepthToViewZ(depth, uNear, uFar);
  35. #else
  36. return perspectiveDepthToViewZ(depth, uNear, uFar);
  37. #endif
  38. }
  39. float getDepth(const in vec2 coords) {
  40. #ifdef depthTextureSupport
  41. return texture2D(tDepth, coords).r;
  42. #else
  43. return unpackRGBAToDepth(texture2D(tDepth, coords));
  44. #endif
  45. }
  46. float screenFade(const in vec2 coords) {
  47. vec2 c = (coords - uBounds.xy) / (uBounds.zw - uBounds.xy);
  48. vec2 fade = max(12.0 * abs(c - 0.5) - 5.0, vec2(0.0));
  49. return saturate(1.0 - dot(fade, fade));
  50. }
  51. // based on https://panoskarabelas.com/posts/screen_space_shadows/
  52. float screenSpaceShadow(const in vec3 position, const in vec3 lightDirection, const in float stepLength) {
  53. // Ray position and direction (in view-space)
  54. vec3 rayPos = position;
  55. vec3 rayDir = -lightDirection;
  56. // Compute ray step
  57. vec3 rayStep = rayDir * stepLength;
  58. // Ray march towards the light
  59. float occlusion = 0.0;
  60. vec4 rayCoords = vec4(0.0);
  61. for (int i = 0; i < dSteps; ++i) {
  62. // Step the ray
  63. rayPos += rayStep;
  64. rayCoords = uProjection * vec4(rayPos, 1.0);
  65. rayCoords.xyz = (rayCoords.xyz / rayCoords.w) * 0.5 + 0.5;
  66. if (outsideBounds(rayCoords.xy))
  67. return 1.0;
  68. // Compute the difference between the ray's and the camera's depth
  69. float depth = getDepth(rayCoords.xy);
  70. float viewZ = getViewZ(depth);
  71. float zDelta = rayPos.z - viewZ;
  72. if (zDelta < uTolerance) {
  73. occlusion = 1.0;
  74. // Fade out as we approach the edges of the screen
  75. occlusion *= screenFade(rayCoords.xy);
  76. break;
  77. }
  78. }
  79. return 1.0 - (uBias * occlusion);
  80. }
  81. void main(void) {
  82. vec2 invTexSize = 1.0 / uTexSize;
  83. vec2 selfCoords = gl_FragCoord.xy * invTexSize;
  84. float selfDepth = getDepth(selfCoords);
  85. if (isBackground(selfDepth)) {
  86. gl_FragColor = vec4(0.0);
  87. return;
  88. }
  89. vec3 selfViewPos = screenSpaceToViewSpace(vec3(selfCoords, selfDepth), uInvProjection);
  90. float stepLength = uMaxDistance / float(dSteps);
  91. float o = 1.0;
  92. #if dLightCount != 0
  93. float sh[dLightCount];
  94. #pragma unroll_loop_start
  95. for (int i = 0; i < dLightCount; ++i) {
  96. sh[i] = screenSpaceShadow(selfViewPos, uLightDirection[i], stepLength);
  97. o = min(o, sh[i]);
  98. }
  99. #pragma unroll_loop_end
  100. #endif
  101. gl_FragColor = vec4(o);
  102. }
  103. `;