GroundProjectedEnv.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. ( function () {
  2. /**
  3. * Ground projected env map adapted from @react-three/drei.
  4. * https://github.com/pmndrs/drei/blob/master/src/core/Environment.tsx
  5. */
  6. class GroundProjectedEnv extends THREE.Mesh {
  7. constructor( texture, options ) {
  8. const isCubeMap = texture.isCubeTexture;
  9. const w = ( isCubeMap ? texture.image[ 0 ]?.width : texture.image.width ) ?? 1024;
  10. const cubeSize = w / 4;
  11. const _lodMax = Math.floor( Math.log2( cubeSize ) );
  12. const _cubeSize = Math.pow( 2, _lodMax );
  13. const width = 3 * Math.max( _cubeSize, 16 * 7 );
  14. const height = 4 * _cubeSize;
  15. const defines = [ isCubeMap ? '#define ENVMAP_TYPE_CUBE' : '', `#define CUBEUV_TEXEL_WIDTH ${1.0 / width}`, `#define CUBEUV_TEXEL_HEIGHT ${1.0 / height}`, `#define CUBEUV_MAX_MIP ${_lodMax}.0` ];
  16. const vertexShader =
  17. /* glsl */
  18. `
  19. varying vec3 vWorldPosition;
  20. void main()
  21. {
  22. vec4 worldPosition = ( modelMatrix * vec4( position, 1.0 ) );
  23. vWorldPosition = worldPosition.xyz;
  24. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  25. }
  26. `;
  27. const fragmentShader = defines.join( '\n' ) +
  28. /* glsl */
  29. `
  30. #define ENVMAP_TYPE_CUBE_UV
  31. varying vec3 vWorldPosition;
  32. uniform float radius;
  33. uniform float height;
  34. uniform float angle;
  35. #ifdef ENVMAP_TYPE_CUBE
  36. uniform samplerCube map;
  37. #else
  38. uniform sampler2D map;
  39. #endif
  40. // From: https://www.shadertoy.com/view/4tsBD7
  41. float diskIntersectWithBackFaceCulling( vec3 ro, vec3 rd, vec3 c, vec3 n, float r )
  42. {
  43. float d = dot ( rd, n );
  44. if( d > 0.0 ) { return 1e6; }
  45. vec3 o = ro - c;
  46. float t = - dot( n, o ) / d;
  47. vec3 q = o + rd * t;
  48. return ( dot( q, q ) < r * r ) ? t : 1e6;
  49. }
  50. // From: https://www.iquilezles.org/www/articles/intersectors/intersectors.htm
  51. float sphereIntersect( vec3 ro, vec3 rd, vec3 ce, float ra )
  52. {
  53. vec3 oc = ro - ce;
  54. float b = dot( oc, rd );
  55. float c = dot( oc, oc ) - ra * ra;
  56. float h = b * b - c;
  57. if( h < 0.0 ) { return -1.0; }
  58. h = sqrt( h );
  59. return - b + h;
  60. }
  61. vec3 project()
  62. {
  63. vec3 p = normalize( vWorldPosition );
  64. vec3 camPos = cameraPosition;
  65. camPos.y -= height;
  66. float intersection = sphereIntersect( camPos, p, vec3( 0.0 ), radius );
  67. if( intersection > 0.0 ) {
  68. vec3 h = vec3( 0.0, - height, 0.0 );
  69. float intersection2 = diskIntersectWithBackFaceCulling( camPos, p, h, vec3( 0.0, 1.0, 0.0 ), radius );
  70. p = ( camPos + min( intersection, intersection2 ) * p ) / radius;
  71. } else {
  72. p = vec3( 0.0, 1.0, 0.0 );
  73. }
  74. return p;
  75. }
  76. #include <common>
  77. #include <cube_uv_reflection_fragment>
  78. void main()
  79. {
  80. vec3 projectedWorldPosition = project();
  81. #ifdef ENVMAP_TYPE_CUBE
  82. vec3 outcolor = textureCube( map, projectedWorldPosition ).rgb;
  83. #else
  84. vec3 direction = normalize( projectedWorldPosition );
  85. vec2 uv = equirectUv( direction );
  86. vec3 outcolor = texture2D( map, uv ).rgb;
  87. #endif
  88. gl_FragColor = vec4( outcolor, 1.0 );
  89. #include <tonemapping_fragment>
  90. #include <encodings_fragment>
  91. }
  92. `;
  93. const uniforms = {
  94. map: {
  95. value: texture
  96. },
  97. height: {
  98. value: options?.height || 15
  99. },
  100. radius: {
  101. value: options?.radius || 100
  102. }
  103. };
  104. const geometry = new THREE.IcosahedronGeometry( 1, 16 );
  105. const material = new THREE.ShaderMaterial( {
  106. uniforms,
  107. fragmentShader,
  108. vertexShader,
  109. side: THREE.DoubleSide
  110. } );
  111. super( geometry, material );
  112. }
  113. set radius( radius ) {
  114. this.material.uniforms.radius.value = radius;
  115. }
  116. get radius() {
  117. return this.material.uniforms.radius.value;
  118. }
  119. set height( height ) {
  120. this.material.uniforms.height.value = height;
  121. }
  122. get height() {
  123. return this.material.uniforms.height.value;
  124. }
  125. }
  126. THREE.GroundProjectedEnv = GroundProjectedEnv;
  127. } )();