Clouds.glsl.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. All material copyright ESRI, All Rights Reserved, unless otherwise specified.
  3. See https://js.arcgis.com/4.24/esri/copyright.txt for details.
  4. */
  5. import{c as t}from"./mat3f64.js";import{RayMarchingSteps as e}from"../views/3d/environment/CloudsTechniqueConfiguration.js";import{ATLAS_SIZE as a,TILE_ROWS as o,TILE_SIZE as i,TEXTURE_SCALE as n,WEATHER_MAP_SCALE as r}from"../views/3d/environment/NoiseTextureAtlasDimensions.js";import{ScreenSpacePass as s}from"../views/3d/webgl-engine/core/shaderLibrary/ScreenSpacePass.js";import{FloatUniform as l}from"../views/3d/webgl-engine/core/shaderModules/FloatUniform.js";import{NoParameters as c,glsl as d}from"../views/3d/webgl-engine/core/shaderModules/interfaces.js";import{Matrix3DrawUniform as u}from"../views/3d/webgl-engine/core/shaderModules/Matrix3DrawUniform.js";import{ShaderBuilder as f}from"../views/3d/webgl-engine/core/shaderModules/ShaderBuilder.js";import{Texture2DUniform as m}from"../views/3d/webgl-engine/core/shaderModules/Texture2DUniform.js";class p extends c{constructor(){super(...arguments),this.viewMatrix=t()}}function g(t){const c=new f;return c.include(s,!1),c.fragment.uniforms.add(new l("cloudRadius")),c.fragment.uniforms.add(new l("halfCubeMapSize")),c.fragment.uniforms.add(new l("power")),c.fragment.uniforms.add(new l("sigmaE")),c.fragment.uniforms.add(new l("density")),c.fragment.uniforms.add(new l("cloudSize")),c.fragment.uniforms.add(new l("detailSize")),c.fragment.uniforms.add(new l("smoothness")),c.fragment.uniforms.add(new l("cloudHeight")),c.fragment.uniforms.add(new l("coverage")),c.fragment.uniforms.add(new u("view",(t=>t.viewMatrix))),c.fragment.uniforms.add(new m("cloudShapeTexture")),c.fragment.code.add(d`
  6. const int STEPS = ${t.steps===e.SIXTEEN?d`16`:t.steps===e.HUNDRED?d`100`:d`200`};
  7. const int STEPS_LIGHT = 6;
  8. const float stepL = 300.0 / float(STEPS_LIGHT);
  9. const float cloudStart = 1500.0;
  10. vec3 rayDirection(vec2 fragCoord) {
  11. vec2 xy = fragCoord - halfCubeMapSize;
  12. return normalize(vec3(-xy, -halfCubeMapSize));
  13. }
  14. float remap(float x, float low1, float high1, float low2, float high2) {
  15. return low2 + (x - low1) * (high2 - low2) / (high1 - low1);
  16. }
  17. float saturate(float x) {
  18. return clamp(x, 0.0, 1.0);
  19. }`),c.fragment.code.add(d`
  20. float getCloudShape(vec3 pos, float pOffset) {
  21. const float textureWidth = ${d.float(a)};
  22. const float dataWidth = ${d.float(a)};
  23. const float tileRows = ${d.float(o)};
  24. const vec3 atlasDimensions = vec3(${d.float(i)}, ${d.float(i)}, tileRows * tileRows);
  25. //Change from Y being height to Z being height
  26. vec3 p = float(${d.float(n)}) * pos.xzy;
  27. //Pixel coordinates of point in the 3D data
  28. vec3 coord = vec3(mod(p - pOffset * atlasDimensions, atlasDimensions));
  29. float f = fract(coord.z);
  30. float level = floor(coord.z);
  31. float tileY = floor(level / tileRows);
  32. float tileX = level - tileY * tileRows;
  33. //The data coordinates are offset by the x and y tile, the two boundary cells between each tile pair and the initial boundary cell on the first row/column
  34. vec2 offset = atlasDimensions.x * vec2(tileX, tileY) + 2.0 * vec2(tileX, tileY) + 1.0;
  35. vec2 pixel = coord.xy + offset;
  36. vec2 data = texture2D(cloudShapeTexture, mod(pixel, dataWidth) / textureWidth).xy;
  37. return 1.0 - mix(data.x, data.y, f);
  38. }
  39. float getCloudMap(vec2 p){
  40. // Non-power-of-two textures can't be tiled using WebGL1
  41. // Get fractional part of uv to tile
  42. // Shift the texture center to origin to avoid seam artifacts
  43. vec2 uv = fract((${d.float(r)} * p) / ${d.float(a)} + 0.5);
  44. return texture2D(cloudShapeTexture, uv).a;
  45. }
  46. `),c.fragment.code.add(d`float clouds(vec3 p) {
  47. float cloud = saturate(0.5 * mix(0.0, 1.0, min(2.0 * coverage, 1.0)));
  48. if (cloud <= 0.0) {
  49. return 0.0;
  50. }
  51. float cloudMap = getCloudMap(cloudSize * p.xy);
  52. cloud = mix(cloud, min(2.0 * (coverage), 1.0) * cloudMap, min(2.0 * (1.0 - coverage), 1.0));
  53. if (cloud <= 0.0) {
  54. return 0.0;
  55. }
  56. float shape = getCloudShape(8.0 * cloudSize * p, 0.0);
  57. cloud = saturate(remap(cloud, smoothness * shape, 1.0, 0.0, 1.0));
  58. if (cloud <= 0.0) {
  59. return 0.0;
  60. }
  61. float heightFraction = saturate((length(p) - cloudRadius - cloudStart) / cloudHeight);
  62. cloud *= saturate(remap(heightFraction, 0.0, 0.25, 0.0, 1.0)) * smoothstep(1.0, 0.25, heightFraction);
  63. if (cloud <= 0.0) {
  64. return 0.0;
  65. }
  66. return density * saturate(remap(cloud, 0.35 * smoothness * getCloudShape(detailSize * p, 0.0), 1.0, 0.0, 1.0));
  67. }`),c.fragment.code.add(d`vec2 sphereIntersections(vec3 start, vec3 dir, float radius) {
  68. float a = dot(dir, dir);
  69. float b = 2.0 * dot(dir, start);
  70. float c = dot(start, start) - (radius * radius);
  71. float d = (b * b) - 4.0 * a * c;
  72. if (d < 0.0) {
  73. return vec2(1e5, -1e5);
  74. }
  75. return vec2((-b - sqrt(d)) / (2.0 * a), (-b + sqrt(d)) / (2.0 * a));
  76. }
  77. float HenyeyGreenstein(float g, float costh) {
  78. return (1.0 / (4.0 * 3.1415)) * ((1.0 - g * g) / pow(1.0 + g * g - 2.0 * g * costh, 1.5));
  79. }`),c.fragment.code.add("\n vec3 multipleOctaves(float extinction, float mu, float stepL) {\n float attenuation = 1.0;\n float contribution = 1.0;\n float phaseAttenuation = 1.0;\n vec3 luminance = vec3(0);\n\n for (int i = 0; i < 4; i++) {\n float phase = mix(HenyeyGreenstein(0.0, mu), HenyeyGreenstein(0.3 * phaseAttenuation, mu), 0.7);\n luminance += contribution * phase * exp(-stepL * extinction * sigmaE * attenuation);\n attenuation *= 0.2;\n contribution *= 0.6;\n phaseAttenuation *= 0.5;\n }\n\n return luminance;\n }"),c.fragment.code.add(d`vec3 lightRay(vec3 org, vec3 p, float phaseFunction, float mu, vec3 sunDirection) {
  80. float lightRayDensity = clouds(p);
  81. lightRayDensity += clouds(p + sunDirection * 1.0 * stepL);
  82. lightRayDensity += clouds(p + sunDirection * 2.0 * stepL);
  83. lightRayDensity += clouds(p + sunDirection * 3.0 * stepL);
  84. lightRayDensity += clouds(p + sunDirection * 4.0 * stepL);
  85. lightRayDensity += clouds(p + sunDirection * 5.0 * stepL);
  86. vec3 beersLaw = multipleOctaves(lightRayDensity, mu, stepL);
  87. return mix(beersLaw * 2.0 * (1.0 - (exp(-stepL * lightRayDensity * 2.0 * sigmaE ))), beersLaw, 0.5 + 0.5 * mu);
  88. }`),c.fragment.code.add(d`vec3 mainRay(vec3 org, vec3 dir, vec3 sunDirection, float distToStart, float totalDistance, out float totalTransmittance) {
  89. if (dir.z < 0.0) {
  90. return vec3(0);
  91. }
  92. totalTransmittance = 1.0;
  93. float stepS = totalDistance / float(STEPS);
  94. float cameraHeight = length(org);
  95. float mu = 0.5 + 0.5 * dot(sunDirection, dir);
  96. float phaseFunction = mix(HenyeyGreenstein(-0.3, mu), HenyeyGreenstein(0.3, mu), 0.7);
  97. vec3 p = org + distToStart * dir;
  98. float dist = distToStart;
  99. vec3 color = vec3(0.0);
  100. for (int i = 0; i < STEPS; i++) {
  101. float sampleDensity = clouds(p);
  102. float sampleSigmaE = sampleDensity * sigmaE;
  103. if (sampleDensity > 0.0 ) {
  104. float ambient = mix((1.2), (1.6), saturate((length(p) - cloudRadius - cloudStart) / cloudHeight));
  105. vec3 luminance = sampleDensity * (ambient + power * phaseFunction * lightRay(org, p, phaseFunction, mu, sunDirection));
  106. float transmittance = exp(-sampleSigmaE * stepS);
  107. color += totalTransmittance * (luminance - luminance * transmittance) / sampleSigmaE;
  108. totalTransmittance *= transmittance;
  109. if (totalTransmittance <= 0.001) {
  110. totalTransmittance = 0.0;
  111. break;
  112. }
  113. }
  114. dist += stepS;
  115. p = org + dir * dist;
  116. }
  117. return color;
  118. }`),c.fragment.code.add(d`void main() {
  119. vec3 rayDir = rayDirection(gl_FragCoord.xy);
  120. rayDir = normalize(view * rayDir);
  121. vec3 viewPos = vec3(0, 0, cloudRadius + 1.0);
  122. bool hitsPlanet = rayDir.z < 0.0;
  123. if (hitsPlanet) {
  124. gl_FragColor = vec4(vec3(0), 1);
  125. return;
  126. }
  127. vec2 rayStartIntersect = sphereIntersections(viewPos, rayDir, cloudRadius + cloudStart);
  128. vec2 rayEndIntersect = sphereIntersections(viewPos, rayDir, cloudRadius + cloudStart + cloudHeight);
  129. float distToStart = rayStartIntersect.y;
  130. float totalDistance = rayEndIntersect.y - distToStart;
  131. float totalTransmittance = 1.0;
  132. vec3 sunDirection = normalize(vec3(0, 0, 1));
  133. vec3 col = 0.5 * mainRay(viewPos, rayDir, sunDirection, distToStart, totalDistance, totalTransmittance).rgb;
  134. gl_FragColor = vec4(col, totalTransmittance);
  135. }`),c}const h=Object.freeze(Object.defineProperty({__proto__:null,CloudsDrawParameters:p,build:g},Symbol.toStringTag,{value:"Module"}));export{p as C,h as a,g as b};