PMREMCubeUVPacker.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /**
  2. * @author Prashant Sharma / spidersharma03
  3. * @author Ben Houston / bhouston, https://clara.io
  4. *
  5. * This class takes the cube lods(corresponding to different roughness values), and creates a single cubeUV
  6. * Texture. The format for a given roughness set of faces is simply::
  7. * +X+Y+Z
  8. * -X-Y-Z
  9. * For every roughness a mip map chain is also saved, which is essential to remove the texture artifacts due to
  10. * minification.
  11. * Right now for every face a PlaneMesh is drawn, which leads to a lot of geometry draw calls, but can be replaced
  12. * later by drawing a single buffer and by sending the appropriate faceIndex via vertex attributes.
  13. * The arrangement of the faces is fixed, as assuming this arrangement, the sampling function has been written.
  14. */
  15. import {
  16. BackSide,
  17. CubeUVReflectionMapping,
  18. LinearFilter,
  19. LinearToneMapping,
  20. Mesh,
  21. NoBlending,
  22. OrthographicCamera,
  23. PlaneBufferGeometry,
  24. RGBEEncoding,
  25. RGBM16Encoding,
  26. Scene,
  27. ShaderMaterial,
  28. Vector2,
  29. Vector3,
  30. WebGLRenderTarget
  31. } from "../../../build/three.module.js";
  32. var PMREMCubeUVPacker = ( function () {
  33. var camera = new OrthographicCamera();
  34. var scene = new Scene();
  35. var shader = getShader();
  36. var PMREMCubeUVPacker = function ( cubeTextureLods ) {
  37. this.cubeLods = cubeTextureLods;
  38. var size = cubeTextureLods[ 0 ].width * 4;
  39. var sourceTexture = cubeTextureLods[ 0 ].texture;
  40. var params = {
  41. format: sourceTexture.format,
  42. magFilter: sourceTexture.magFilter,
  43. minFilter: sourceTexture.minFilter,
  44. type: sourceTexture.type,
  45. generateMipmaps: sourceTexture.generateMipmaps,
  46. anisotropy: sourceTexture.anisotropy,
  47. encoding: ( sourceTexture.encoding === RGBEEncoding ) ? RGBM16Encoding : sourceTexture.encoding
  48. };
  49. if ( params.encoding === RGBM16Encoding ) {
  50. params.magFilter = LinearFilter;
  51. params.minFilter = LinearFilter;
  52. }
  53. this.CubeUVRenderTarget = new WebGLRenderTarget( size, size, params );
  54. this.CubeUVRenderTarget.texture.name = "PMREMCubeUVPacker.cubeUv";
  55. this.CubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
  56. this.objects = [];
  57. var geometry = new PlaneBufferGeometry( 1, 1 );
  58. var faceOffsets = [];
  59. faceOffsets.push( new Vector2( 0, 0 ) );
  60. faceOffsets.push( new Vector2( 1, 0 ) );
  61. faceOffsets.push( new Vector2( 2, 0 ) );
  62. faceOffsets.push( new Vector2( 0, 1 ) );
  63. faceOffsets.push( new Vector2( 1, 1 ) );
  64. faceOffsets.push( new Vector2( 2, 1 ) );
  65. var textureResolution = size;
  66. size = cubeTextureLods[ 0 ].width;
  67. var offset2 = 0;
  68. var c = 4.0;
  69. this.numLods = Math.log( cubeTextureLods[ 0 ].width ) / Math.log( 2 ) - 2; // IE11 doesn't support Math.log2
  70. for ( var i = 0; i < this.numLods; i ++ ) {
  71. var offset1 = ( textureResolution - textureResolution / c ) * 0.5;
  72. if ( size > 16 ) c *= 2;
  73. var nMips = size > 16 ? 6 : 1;
  74. var mipOffsetX = 0;
  75. var mipOffsetY = 0;
  76. var mipSize = size;
  77. for ( var j = 0; j < nMips; j ++ ) {
  78. // Mip Maps
  79. for ( var k = 0; k < 6; k ++ ) {
  80. // 6 Cube Faces
  81. var material = shader.clone();
  82. material.uniforms[ 'envMap' ].value = this.cubeLods[ i ].texture;
  83. material.envMap = this.cubeLods[ i ].texture;
  84. material.uniforms[ 'faceIndex' ].value = k;
  85. material.uniforms[ 'mapSize' ].value = mipSize;
  86. var planeMesh = new Mesh( geometry, material );
  87. planeMesh.position.x = faceOffsets[ k ].x * mipSize - offset1 + mipOffsetX;
  88. planeMesh.position.y = faceOffsets[ k ].y * mipSize - offset1 + offset2 + mipOffsetY;
  89. planeMesh.material.side = BackSide;
  90. planeMesh.scale.setScalar( mipSize );
  91. this.objects.push( planeMesh );
  92. }
  93. mipOffsetY += 1.75 * mipSize;
  94. mipOffsetX += 1.25 * mipSize;
  95. mipSize /= 2;
  96. }
  97. offset2 += 2 * size;
  98. if ( size > 16 ) size /= 2;
  99. }
  100. };
  101. PMREMCubeUVPacker.prototype = {
  102. constructor: PMREMCubeUVPacker,
  103. update: function ( renderer ) {
  104. var size = this.cubeLods[ 0 ].width * 4;
  105. // top and bottom are swapped for some reason?
  106. camera.left = - size * 0.5;
  107. camera.right = size * 0.5;
  108. camera.top = - size * 0.5;
  109. camera.bottom = size * 0.5;
  110. camera.near = 0;
  111. camera.far = 1;
  112. camera.updateProjectionMatrix();
  113. for ( var i = 0; i < this.objects.length; i ++ ) {
  114. scene.add( this.objects[ i ] );
  115. }
  116. var gammaInput = renderer.gammaInput;
  117. var gammaOutput = renderer.gammaOutput;
  118. var toneMapping = renderer.toneMapping;
  119. var toneMappingExposure = renderer.toneMappingExposure;
  120. var currentRenderTarget = renderer.getRenderTarget();
  121. renderer.gammaInput = false;
  122. renderer.gammaOutput = false;
  123. renderer.toneMapping = LinearToneMapping;
  124. renderer.toneMappingExposure = 1.0;
  125. renderer.setRenderTarget( this.CubeUVRenderTarget );
  126. renderer.render( scene, camera );
  127. renderer.setRenderTarget( currentRenderTarget );
  128. renderer.toneMapping = toneMapping;
  129. renderer.toneMappingExposure = toneMappingExposure;
  130. renderer.gammaInput = gammaInput;
  131. renderer.gammaOutput = gammaOutput;
  132. for ( var i = 0; i < this.objects.length; i ++ ) {
  133. scene.remove( this.objects[ i ] );
  134. }
  135. },
  136. dispose: function () {
  137. for ( var i = 0, l = this.objects.length; i < l; i ++ ) {
  138. this.objects[ i ].material.dispose();
  139. }
  140. this.objects[ 0 ].geometry.dispose();
  141. }
  142. };
  143. function getShader() {
  144. var shaderMaterial = new ShaderMaterial( {
  145. uniforms: {
  146. "faceIndex": { value: 0 },
  147. "mapSize": { value: 0 },
  148. "envMap": { value: null },
  149. "testColor": { value: new Vector3( 1, 1, 1 ) }
  150. },
  151. vertexShader:
  152. "precision highp float;\
  153. varying vec2 vUv;\
  154. void main() {\
  155. vUv = uv;\
  156. gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\
  157. }",
  158. fragmentShader:
  159. "precision highp float;\
  160. varying vec2 vUv;\
  161. uniform samplerCube envMap;\
  162. uniform float mapSize;\
  163. uniform vec3 testColor;\
  164. uniform int faceIndex;\
  165. \
  166. void main() {\
  167. vec3 sampleDirection;\
  168. vec2 uv = vUv;\
  169. uv = uv * 2.0 - 1.0;\
  170. uv.y *= -1.0;\
  171. if(faceIndex == 0) {\
  172. sampleDirection = normalize(vec3(1.0, uv.y, -uv.x));\
  173. } else if(faceIndex == 1) {\
  174. sampleDirection = normalize(vec3(uv.x, 1.0, uv.y));\
  175. } else if(faceIndex == 2) {\
  176. sampleDirection = normalize(vec3(uv.x, uv.y, 1.0));\
  177. } else if(faceIndex == 3) {\
  178. sampleDirection = normalize(vec3(-1.0, uv.y, uv.x));\
  179. } else if(faceIndex == 4) {\
  180. sampleDirection = normalize(vec3(uv.x, -1.0, -uv.y));\
  181. } else {\
  182. sampleDirection = normalize(vec3(-uv.x, uv.y, -1.0));\
  183. }\
  184. vec4 color = envMapTexelToLinear( textureCube( envMap, sampleDirection ) );\
  185. gl_FragColor = linearToOutputTexel( color );\
  186. }",
  187. blending: NoBlending
  188. } );
  189. shaderMaterial.type = 'PMREMCubeUVPacker';
  190. return shaderMaterial;
  191. }
  192. return PMREMCubeUVPacker;
  193. } )();
  194. export { PMREMCubeUVPacker };