Sky.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /**
  2. * @author zz85 / https://github.com/zz85
  3. *
  4. * Based on "A Practical Analytic Model for Daylight"
  5. * aka The Preetham Model, the de facto standard analytic skydome model
  6. * http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf
  7. *
  8. * First implemented by Simon Wallner
  9. * http://www.simonwallner.at/projects/atmospheric-scattering
  10. *
  11. * Improved by Martin Upitis
  12. * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR
  13. *
  14. * Three.js integration by zz85 http://twitter.com/blurspline
  15. */
  16. import {
  17. BackSide,
  18. BoxBufferGeometry,
  19. Mesh,
  20. ShaderMaterial,
  21. UniformsUtils,
  22. Vector3
  23. } from "../../../build/three.module.js";
  24. var Sky = function () {
  25. var shader = Sky.SkyShader;
  26. var material = new ShaderMaterial( {
  27. fragmentShader: shader.fragmentShader,
  28. vertexShader: shader.vertexShader,
  29. uniforms: UniformsUtils.clone( shader.uniforms ),
  30. side: BackSide
  31. } );
  32. Mesh.call( this, new BoxBufferGeometry( 1, 1, 1 ), material );
  33. };
  34. Sky.prototype = Object.create( Mesh.prototype );
  35. Sky.SkyShader = {
  36. uniforms: {
  37. "luminance": { value: 1 },
  38. "turbidity": { value: 2 },
  39. "rayleigh": { value: 1 },
  40. "mieCoefficient": { value: 0.005 },
  41. "mieDirectionalG": { value: 0.8 },
  42. "sunPosition": { value: new Vector3() },
  43. "up": { value: new Vector3( 0, 1, 0 ) }
  44. },
  45. vertexShader: [
  46. 'uniform vec3 sunPosition;',
  47. 'uniform float rayleigh;',
  48. 'uniform float turbidity;',
  49. 'uniform float mieCoefficient;',
  50. 'uniform vec3 up;',
  51. 'varying vec3 vWorldPosition;',
  52. 'varying vec3 vSunDirection;',
  53. 'varying float vSunfade;',
  54. 'varying vec3 vBetaR;',
  55. 'varying vec3 vBetaM;',
  56. 'varying float vSunE;',
  57. // constants for atmospheric scattering
  58. 'const float e = 2.71828182845904523536028747135266249775724709369995957;',
  59. 'const float pi = 3.141592653589793238462643383279502884197169;',
  60. // wavelength of used primaries, according to preetham
  61. 'const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 );',
  62. // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function:
  63. // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn))
  64. 'const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 );',
  65. // mie stuff
  66. // K coefficient for the primaries
  67. 'const float v = 4.0;',
  68. 'const vec3 K = vec3( 0.686, 0.678, 0.666 );',
  69. // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K
  70. 'const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 );',
  71. // earth shadow hack
  72. // cutoffAngle = pi / 1.95;
  73. 'const float cutoffAngle = 1.6110731556870734;',
  74. 'const float steepness = 1.5;',
  75. 'const float EE = 1000.0;',
  76. 'float sunIntensity( float zenithAngleCos ) {',
  77. ' zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 );',
  78. ' return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) );',
  79. '}',
  80. 'vec3 totalMie( float T ) {',
  81. ' float c = ( 0.2 * T ) * 10E-18;',
  82. ' return 0.434 * c * MieConst;',
  83. '}',
  84. 'void main() {',
  85. ' vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
  86. ' vWorldPosition = worldPosition.xyz;',
  87. ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
  88. ' gl_Position.z = gl_Position.w;', // set z to camera.far
  89. ' vSunDirection = normalize( sunPosition );',
  90. ' vSunE = sunIntensity( dot( vSunDirection, up ) );',
  91. ' vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 );',
  92. ' float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) );',
  93. // extinction (absorbtion + out scattering)
  94. // rayleigh coefficients
  95. ' vBetaR = totalRayleigh * rayleighCoefficient;',
  96. // mie coefficients
  97. ' vBetaM = totalMie( turbidity ) * mieCoefficient;',
  98. '}'
  99. ].join( '\n' ),
  100. fragmentShader: [
  101. 'varying vec3 vWorldPosition;',
  102. 'varying vec3 vSunDirection;',
  103. 'varying float vSunfade;',
  104. 'varying vec3 vBetaR;',
  105. 'varying vec3 vBetaM;',
  106. 'varying float vSunE;',
  107. 'uniform float luminance;',
  108. 'uniform float mieDirectionalG;',
  109. 'uniform vec3 up;',
  110. 'const vec3 cameraPos = vec3( 0.0, 0.0, 0.0 );',
  111. // constants for atmospheric scattering
  112. 'const float pi = 3.141592653589793238462643383279502884197169;',
  113. 'const float n = 1.0003;', // refractive index of air
  114. 'const float N = 2.545E25;', // number of molecules per unit volume for air at 288.15K and 1013mb (sea level -45 celsius)
  115. // optical length at zenith for molecules
  116. 'const float rayleighZenithLength = 8.4E3;',
  117. 'const float mieZenithLength = 1.25E3;',
  118. // 66 arc seconds -> degrees, and the cosine of that
  119. 'const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;',
  120. // 3.0 / ( 16.0 * pi )
  121. 'const float THREE_OVER_SIXTEENPI = 0.05968310365946075;',
  122. // 1.0 / ( 4.0 * pi )
  123. 'const float ONE_OVER_FOURPI = 0.07957747154594767;',
  124. 'float rayleighPhase( float cosTheta ) {',
  125. ' return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) );',
  126. '}',
  127. 'float hgPhase( float cosTheta, float g ) {',
  128. ' float g2 = pow( g, 2.0 );',
  129. ' float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 );',
  130. ' return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse );',
  131. '}',
  132. // Filmic ToneMapping http://filmicgames.com/archives/75
  133. 'const float A = 0.15;',
  134. 'const float B = 0.50;',
  135. 'const float C = 0.10;',
  136. 'const float D = 0.20;',
  137. 'const float E = 0.02;',
  138. 'const float F = 0.30;',
  139. 'const float whiteScale = 1.0748724675633854;', // 1.0 / Uncharted2Tonemap(1000.0)
  140. 'vec3 Uncharted2Tonemap( vec3 x ) {',
  141. ' return ( ( x * ( A * x + C * B ) + D * E ) / ( x * ( A * x + B ) + D * F ) ) - E / F;',
  142. '}',
  143. 'void main() {',
  144. // optical length
  145. // cutoff angle at 90 to avoid singularity in next formula.
  146. ' float zenithAngle = acos( max( 0.0, dot( up, normalize( vWorldPosition - cameraPos ) ) ) );',
  147. ' float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) );',
  148. ' float sR = rayleighZenithLength * inverse;',
  149. ' float sM = mieZenithLength * inverse;',
  150. // combined extinction factor
  151. ' vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) );',
  152. // in scattering
  153. ' float cosTheta = dot( normalize( vWorldPosition - cameraPos ), vSunDirection );',
  154. ' float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 );',
  155. ' vec3 betaRTheta = vBetaR * rPhase;',
  156. ' float mPhase = hgPhase( cosTheta, mieDirectionalG );',
  157. ' vec3 betaMTheta = vBetaM * mPhase;',
  158. ' vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) );',
  159. ' Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) );',
  160. // nightsky
  161. ' vec3 direction = normalize( vWorldPosition - cameraPos );',
  162. ' float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2]',
  163. ' float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2]',
  164. ' vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 );',
  165. ' vec3 L0 = vec3( 0.1 ) * Fex;',
  166. // composition + solar disc
  167. ' float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta );',
  168. ' L0 += ( vSunE * 19000.0 * Fex ) * sundisk;',
  169. ' vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 );',
  170. ' vec3 curr = Uncharted2Tonemap( ( log2( 2.0 / pow( luminance, 4.0 ) ) ) * texColor );',
  171. ' vec3 color = curr * whiteScale;',
  172. ' vec3 retColor = pow( color, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) );',
  173. ' gl_FragColor = vec4( retColor, 1.0 );',
  174. '}'
  175. ].join( '\n' )
  176. };
  177. export { Sky };