AtmosphereCommon.glsl 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. uniform vec3 u_radiiAndDynamicAtmosphereColor;
  2. uniform float u_atmosphereLightIntensity;
  3. uniform float u_atmosphereRayleighScaleHeight;
  4. uniform float u_atmosphereMieScaleHeight;
  5. uniform float u_atmosphereMieAnisotropy;
  6. uniform vec3 u_atmosphereRayleighCoefficient;
  7. uniform vec3 u_atmosphereMieCoefficient;
  8. const float ATMOSPHERE_THICKNESS = 111e3; // The thickness of the atmosphere in meters.
  9. const int PRIMARY_STEPS_MAX = 16; // Maximum number of times the ray from the camera to the world position (primary ray) is sampled.
  10. const int LIGHT_STEPS_MAX = 4; // Maximum number of times the light is sampled from the light source's intersection with the atmosphere to a sample position on the primary ray.
  11. /**
  12. * Rational approximation to tanh(x)
  13. */
  14. float approximateTanh(float x) {
  15. float x2 = x * x;
  16. return max(-1.0, min(+1.0, x * (27.0 + x2) / (27.0 + 9.0 * x2)));
  17. }
  18. /**
  19. * This function computes the colors contributed by Rayliegh and Mie scattering on a given ray, as well as
  20. * the transmittance value for the ray.
  21. *
  22. * @param {czm_ray} primaryRay The ray from the camera to the position.
  23. * @param {float} primaryRayLength The length of the primary ray.
  24. * @param {vec3} lightDirection The direction of the light to calculate the scattering from.
  25. * @param {vec3} rayleighColor The variable the Rayleigh scattering will be written to.
  26. * @param {vec3} mieColor The variable the Mie scattering will be written to.
  27. * @param {float} opacity The variable the transmittance will be written to.
  28. * @glslFunction
  29. */
  30. void computeScattering(
  31. czm_ray primaryRay,
  32. float primaryRayLength,
  33. vec3 lightDirection,
  34. float atmosphereInnerRadius,
  35. out vec3 rayleighColor,
  36. out vec3 mieColor,
  37. out float opacity
  38. ) {
  39. // Initialize the default scattering amounts to 0.
  40. rayleighColor = vec3(0.0);
  41. mieColor = vec3(0.0);
  42. opacity = 0.0;
  43. float atmosphereOuterRadius = atmosphereInnerRadius + ATMOSPHERE_THICKNESS;
  44. vec3 origin = vec3(0.0);
  45. // Calculate intersection from the camera to the outer ring of the atmosphere.
  46. czm_raySegment primaryRayAtmosphereIntersect = czm_raySphereIntersectionInterval(primaryRay, origin, atmosphereOuterRadius);
  47. // Return empty colors if no intersection with the atmosphere geometry.
  48. if (primaryRayAtmosphereIntersect == czm_emptyRaySegment) {
  49. return;
  50. }
  51. // To deal with smaller values of PRIMARY_STEPS (e.g. 4)
  52. // we implement a split strategy: sky or horizon.
  53. // For performance reasons, instead of a if/else branch
  54. // a soft choice is implemented through a weight 0.0 <= w_stop_gt_lprl <= 1.0
  55. float x = 1e-7 * primaryRayAtmosphereIntersect.stop / length(primaryRayLength);
  56. // Value close to 0.0: close to the horizon
  57. // Value close to 1.0: above in the sky
  58. float w_stop_gt_lprl = 0.5 * (1.0 + approximateTanh(x));
  59. // The ray should start from the first intersection with the outer atmopshere, or from the camera position, if it is inside the atmosphere.
  60. float start_0 = primaryRayAtmosphereIntersect.start;
  61. primaryRayAtmosphereIntersect.start = max(primaryRayAtmosphereIntersect.start, 0.0);
  62. // The ray should end at the exit from the atmosphere or at the distance to the vertex, whichever is smaller.
  63. primaryRayAtmosphereIntersect.stop = min(primaryRayAtmosphereIntersect.stop, length(primaryRayLength));
  64. // For the number of ray steps, distinguish inside or outside atmosphere (outer space)
  65. // (1) from outer space we have to use more ray steps to get a realistic rendering
  66. // (2) within atmosphere we need fewer steps for faster rendering
  67. float x_o_a = start_0 - ATMOSPHERE_THICKNESS; // ATMOSPHERE_THICKNESS used as an ad-hoc constant, no precise meaning here, only the order of magnitude matters
  68. float w_inside_atmosphere = 1.0 - 0.5 * (1.0 + approximateTanh(x_o_a));
  69. int PRIMARY_STEPS = PRIMARY_STEPS_MAX - int(w_inside_atmosphere * 12.0); // Number of times the ray from the camera to the world position (primary ray) is sampled.
  70. int LIGHT_STEPS = LIGHT_STEPS_MAX - int(w_inside_atmosphere * 2.0); // Number of times the light is sampled from the light source's intersection with the atmosphere to a sample position on the primary ray.
  71. // Setup for sampling positions along the ray - starting from the intersection with the outer ring of the atmosphere.
  72. float rayPositionLength = primaryRayAtmosphereIntersect.start;
  73. // (1) Outside the atmosphere: constant rayStepLength
  74. // (2) Inside atmosphere: variable rayStepLength to compensate the rough rendering of the smaller number of ray steps
  75. float totalRayLength = primaryRayAtmosphereIntersect.stop - rayPositionLength;
  76. float rayStepLengthIncrease = w_inside_atmosphere * ((1.0 - w_stop_gt_lprl) * totalRayLength / (float(PRIMARY_STEPS * (PRIMARY_STEPS + 1)) / 2.0));
  77. float rayStepLength = max(1.0 - w_inside_atmosphere, w_stop_gt_lprl) * totalRayLength / max(7.0 * w_inside_atmosphere, float(PRIMARY_STEPS));
  78. vec3 rayleighAccumulation = vec3(0.0);
  79. vec3 mieAccumulation = vec3(0.0);
  80. vec2 opticalDepth = vec2(0.0);
  81. vec2 heightScale = vec2(u_atmosphereRayleighScaleHeight, u_atmosphereMieScaleHeight);
  82. // Sample positions on the primary ray.
  83. for (int i = 0; i < PRIMARY_STEPS_MAX; ++i) {
  84. // The loop should be: for (int i = 0; i < PRIMARY_STEPS; ++i) {...} but WebGL1 cannot
  85. // loop with non-constant condition, so it has to break early instead
  86. if (i >= PRIMARY_STEPS) {
  87. break;
  88. }
  89. // Calculate sample position along viewpoint ray.
  90. vec3 samplePosition = primaryRay.origin + primaryRay.direction * (rayPositionLength + rayStepLength);
  91. // Calculate height of sample position above ellipsoid.
  92. float sampleHeight = length(samplePosition) - atmosphereInnerRadius;
  93. // Calculate and accumulate density of particles at the sample position.
  94. vec2 sampleDensity = exp(-sampleHeight / heightScale) * rayStepLength;
  95. opticalDepth += sampleDensity;
  96. // Generate ray from the sample position segment to the light source, up to the outer ring of the atmosphere.
  97. czm_ray lightRay = czm_ray(samplePosition, lightDirection);
  98. czm_raySegment lightRayAtmosphereIntersect = czm_raySphereIntersectionInterval(lightRay, origin, atmosphereOuterRadius);
  99. float lightStepLength = lightRayAtmosphereIntersect.stop / float(LIGHT_STEPS);
  100. float lightPositionLength = 0.0;
  101. vec2 lightOpticalDepth = vec2(0.0);
  102. // Sample positions along the light ray, to accumulate incidence of light on the latest sample segment.
  103. for (int j = 0; j < LIGHT_STEPS_MAX; ++j) {
  104. // The loop should be: for (int j = 0; i < LIGHT_STEPS; ++j) {...} but WebGL1 cannot
  105. // loop with non-constant condition, so it has to break early instead
  106. if (j >= LIGHT_STEPS) {
  107. break;
  108. }
  109. // Calculate sample position along light ray.
  110. vec3 lightPosition = samplePosition + lightDirection * (lightPositionLength + lightStepLength * 0.5);
  111. // Calculate height of the light sample position above ellipsoid.
  112. float lightHeight = length(lightPosition) - atmosphereInnerRadius;
  113. // Calculate density of photons at the light sample position.
  114. lightOpticalDepth += exp(-lightHeight / heightScale) * lightStepLength;
  115. // Increment distance on light ray.
  116. lightPositionLength += lightStepLength;
  117. }
  118. // Compute attenuation via the primary ray and the light ray.
  119. vec3 attenuation = exp(-((u_atmosphereMieCoefficient * (opticalDepth.y + lightOpticalDepth.y)) + (u_atmosphereRayleighCoefficient * (opticalDepth.x + lightOpticalDepth.x))));
  120. // Accumulate the scattering.
  121. rayleighAccumulation += sampleDensity.x * attenuation;
  122. mieAccumulation += sampleDensity.y * attenuation;
  123. // Increment distance on primary ray.
  124. rayPositionLength += (rayStepLength += rayStepLengthIncrease);
  125. }
  126. // Compute the scattering amount.
  127. rayleighColor = u_atmosphereRayleighCoefficient * rayleighAccumulation;
  128. mieColor = u_atmosphereMieCoefficient * mieAccumulation;
  129. // Compute the transmittance i.e. how much light is passing through the atmosphere.
  130. opacity = length(exp(-((u_atmosphereMieCoefficient * opticalDepth.y) + (u_atmosphereRayleighCoefficient * opticalDepth.x))));
  131. }
  132. vec4 computeAtmosphereColor(
  133. vec3 positionWC,
  134. vec3 lightDirection,
  135. vec3 rayleighColor,
  136. vec3 mieColor,
  137. float opacity
  138. ) {
  139. // Setup the primary ray: from the camera position to the vertex position.
  140. vec3 cameraToPositionWC = positionWC - czm_viewerPositionWC;
  141. vec3 cameraToPositionWCDirection = normalize(cameraToPositionWC);
  142. float cosAngle = dot(cameraToPositionWCDirection, lightDirection);
  143. float cosAngleSq = cosAngle * cosAngle;
  144. float G = u_atmosphereMieAnisotropy;
  145. float GSq = G * G;
  146. // The Rayleigh phase function.
  147. float rayleighPhase = 3.0 / (50.2654824574) * (1.0 + cosAngleSq);
  148. // The Mie phase function.
  149. float miePhase = 3.0 / (25.1327412287) * ((1.0 - GSq) * (cosAngleSq + 1.0)) / (pow(1.0 + GSq - 2.0 * cosAngle * G, 1.5) * (2.0 + GSq));
  150. // The final color is generated by combining the effects of the Rayleigh and Mie scattering.
  151. vec3 rayleigh = rayleighPhase * rayleighColor;
  152. vec3 mie = miePhase * mieColor;
  153. vec3 color = (rayleigh + mie) * u_atmosphereLightIntensity;
  154. return vec4(color, opacity);
  155. }