PMREMGenerator.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. import NodeMaterial from '../../../nodes/materials/NodeMaterial.js';
  2. import { getDirection, blur } from '../../../nodes/pmrem/PMREMUtils.js';
  3. import { equirectUV } from '../../../nodes/utils/EquirectUVNode.js';
  4. import { uniform } from '../../../nodes/core/UniformNode.js';
  5. import { uniforms } from '../../../nodes/accessors/UniformsNode.js';
  6. import { texture } from '../../../nodes/accessors/TextureNode.js';
  7. import { cubeTexture } from '../../../nodes/accessors/CubeTextureNode.js';
  8. import { float, vec3 } from '../../../nodes/shadernode/ShaderNode.js';
  9. import { uv } from '../../../nodes/accessors/UVNode.js';
  10. import { attribute } from '../../../nodes/core/AttributeNode.js';
  11. import {
  12. OrthographicCamera,
  13. Color,
  14. Vector3,
  15. BufferGeometry,
  16. BufferAttribute,
  17. RenderTarget,
  18. Mesh,
  19. CubeReflectionMapping,
  20. CubeRefractionMapping,
  21. CubeUVReflectionMapping,
  22. LinearFilter,
  23. NoToneMapping,
  24. NoBlending,
  25. RGBAFormat,
  26. HalfFloatType,
  27. BackSide,
  28. LinearSRGBColorSpace,
  29. PerspectiveCamera,
  30. MeshBasicMaterial,
  31. BoxGeometry
  32. } from 'three';
  33. const LOD_MIN = 4;
  34. // The standard deviations (radians) associated with the extra mips. These are
  35. // chosen to approximate a Trowbridge-Reitz distribution function times the
  36. // geometric shadowing function. These sigma values squared must match the
  37. // variance #defines in cube_uv_reflection_fragment.glsl.js.
  38. const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ];
  39. // The maximum length of the blur for loop. Smaller sigmas will use fewer
  40. // samples and exit early, but not recompile the shader.
  41. const MAX_SAMPLES = 20;
  42. const _flatCamera = /*@__PURE__*/ new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
  43. const _clearColor = /*@__PURE__*/ new Color();
  44. let _oldTarget = null;
  45. let _oldActiveCubeFace = 0;
  46. let _oldActiveMipmapLevel = 0;
  47. // Golden Ratio
  48. const PHI = ( 1 + Math.sqrt( 5 ) ) / 2;
  49. const INV_PHI = 1 / PHI;
  50. // Vertices of a dodecahedron (except the opposites, which represent the
  51. // same axis), used as axis directions evenly spread on a sphere.
  52. const _axisDirections = [
  53. /*@__PURE__*/ new Vector3( 1, 1, 1 ),
  54. /*@__PURE__*/ new Vector3( - 1, 1, 1 ),
  55. /*@__PURE__*/ new Vector3( 1, 1, - 1 ),
  56. /*@__PURE__*/ new Vector3( - 1, 1, - 1 ),
  57. /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ),
  58. /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ),
  59. /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ),
  60. /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ),
  61. /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ),
  62. /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 )
  63. ];
  64. //
  65. // WebGPU Face indices
  66. const _faceLib = [
  67. 3, 1, 5,
  68. 0, 4, 2
  69. ];
  70. const direction = getDirection( uv(), attribute( 'faceIndex' ) ).normalize();
  71. const outputDirection = vec3( direction.x, direction.y.negate(), direction.z );
  72. /**
  73. * This class generates a Prefiltered, Mipmapped Radiance Environment Map
  74. * (PMREM) from a cubeMap environment texture. This allows different levels of
  75. * blur to be quickly accessed based on material roughness. It is packed into a
  76. * special CubeUV format that allows us to perform custom interpolation so that
  77. * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap
  78. * chain, it only goes down to the LOD_MIN level (above), and then creates extra
  79. * even more filtered 'mips' at the same LOD_MIN resolution, associated with
  80. * higher roughness levels. In this way we maintain resolution to smoothly
  81. * interpolate diffuse lighting while limiting sampling computation.
  82. *
  83. * Paper: Fast, Accurate Image-Based Lighting
  84. * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view
  85. */
  86. class PMREMGenerator {
  87. constructor( renderer ) {
  88. this._renderer = renderer;
  89. this._pingPongRenderTarget = null;
  90. this._lodMax = 0;
  91. this._cubeSize = 0;
  92. this._lodPlanes = [];
  93. this._sizeLods = [];
  94. this._sigmas = [];
  95. this._blurMaterial = null;
  96. this._cubemapMaterial = null;
  97. this._equirectMaterial = null;
  98. }
  99. /**
  100. * Generates a PMREM from a supplied Scene, which can be faster than using an
  101. * image if networking bandwidth is low. Optional sigma specifies a blur radius
  102. * in radians to be applied to the scene before PMREM generation. Optional near
  103. * and far planes ensure the scene is rendered in its entirety (the cubeCamera
  104. * is placed at the origin).
  105. */
  106. fromScene( scene, sigma = 0, near = 0.1, far = 100 ) {
  107. _oldTarget = this._renderer.getRenderTarget();
  108. _oldActiveCubeFace = this._renderer.getActiveCubeFace();
  109. _oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();
  110. this._setSize( 256 );
  111. const cubeUVRenderTarget = this._allocateTargets();
  112. cubeUVRenderTarget.depthBuffer = true;
  113. this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget );
  114. if ( sigma > 0 ) {
  115. this._blur( cubeUVRenderTarget, 0, 0, sigma );
  116. }
  117. this._applyPMREM( cubeUVRenderTarget );
  118. this._cleanup( cubeUVRenderTarget );
  119. return cubeUVRenderTarget;
  120. }
  121. /**
  122. * Generates a PMREM from an equirectangular texture, which can be either LDR
  123. * or HDR. The ideal input image size is 1k (1024 x 512),
  124. * as this matches best with the 256 x 256 cubemap output.
  125. */
  126. fromEquirectangular( equirectangular, renderTarget = null ) {
  127. return this._fromTexture( equirectangular, renderTarget );
  128. }
  129. /**
  130. * Generates a PMREM from an cubemap texture, which can be either LDR
  131. * or HDR. The ideal input cube size is 256 x 256,
  132. * as this matches best with the 256 x 256 cubemap output.
  133. */
  134. fromCubemap( cubemap, renderTarget = null ) {
  135. return this._fromTexture( cubemap, renderTarget );
  136. }
  137. /**
  138. * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during
  139. * your texture's network fetch for increased concurrency.
  140. */
  141. compileCubemapShader() {
  142. if ( this._cubemapMaterial === null ) {
  143. this._cubemapMaterial = _getCubemapMaterial();
  144. this._compileMaterial( this._cubemapMaterial );
  145. }
  146. }
  147. /**
  148. * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during
  149. * your texture's network fetch for increased concurrency.
  150. */
  151. compileEquirectangularShader() {
  152. if ( this._equirectMaterial === null ) {
  153. this._equirectMaterial = _getEquirectMaterial();
  154. this._compileMaterial( this._equirectMaterial );
  155. }
  156. }
  157. /**
  158. * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class,
  159. * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on
  160. * one of them will cause any others to also become unusable.
  161. */
  162. dispose() {
  163. this._dispose();
  164. if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose();
  165. if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose();
  166. }
  167. // private interface
  168. _setSize( cubeSize ) {
  169. this._lodMax = Math.floor( Math.log2( cubeSize ) );
  170. this._cubeSize = Math.pow( 2, this._lodMax );
  171. }
  172. _dispose() {
  173. if ( this._blurMaterial !== null ) this._blurMaterial.dispose();
  174. if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose();
  175. for ( let i = 0; i < this._lodPlanes.length; i ++ ) {
  176. this._lodPlanes[ i ].dispose();
  177. }
  178. }
  179. _cleanup( outputTarget ) {
  180. this._renderer.setRenderTarget( _oldTarget, _oldActiveCubeFace, _oldActiveMipmapLevel );
  181. outputTarget.scissorTest = false;
  182. _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height );
  183. }
  184. _fromTexture( texture, renderTarget ) {
  185. if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) {
  186. this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) );
  187. } else { // Equirectangular
  188. this._setSize( texture.image.width / 4 );
  189. }
  190. _oldTarget = this._renderer.getRenderTarget();
  191. _oldActiveCubeFace = this._renderer.getActiveCubeFace();
  192. _oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();
  193. const cubeUVRenderTarget = renderTarget || this._allocateTargets();
  194. this._textureToCubeUV( texture, cubeUVRenderTarget );
  195. this._applyPMREM( cubeUVRenderTarget );
  196. this._cleanup( cubeUVRenderTarget );
  197. return cubeUVRenderTarget;
  198. }
  199. _allocateTargets() {
  200. const width = 3 * Math.max( this._cubeSize, 16 * 7 );
  201. const height = 4 * this._cubeSize;
  202. const params = {
  203. magFilter: LinearFilter,
  204. minFilter: LinearFilter,
  205. generateMipmaps: false,
  206. type: HalfFloatType,
  207. format: RGBAFormat,
  208. colorSpace: LinearSRGBColorSpace,
  209. //depthBuffer: false
  210. };
  211. const cubeUVRenderTarget = _createRenderTarget( width, height, params );
  212. if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) {
  213. if ( this._pingPongRenderTarget !== null ) {
  214. this._dispose();
  215. }
  216. this._pingPongRenderTarget = _createRenderTarget( width, height, params );
  217. const { _lodMax } = this;
  218. ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) );
  219. this._blurMaterial = _getBlurShader( _lodMax, width, height );
  220. }
  221. return cubeUVRenderTarget;
  222. }
  223. _compileMaterial( material ) {
  224. const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material );
  225. this._renderer.compile( tmpMesh, _flatCamera );
  226. }
  227. _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) {
  228. const fov = 90;
  229. const aspect = 1;
  230. const cubeCamera = new PerspectiveCamera( fov, aspect, near, far );
  231. // px, py, pz, nx, ny, nz
  232. const upSign = [ - 1, 1, - 1, - 1, - 1, - 1 ];
  233. const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ];
  234. const renderer = this._renderer;
  235. const originalAutoClear = renderer.autoClear;
  236. const toneMapping = renderer.toneMapping;
  237. renderer.getClearColor( _clearColor );
  238. renderer.toneMapping = NoToneMapping;
  239. renderer.autoClear = false;
  240. const backgroundMaterial = new MeshBasicMaterial( {
  241. name: 'PMREM.Background',
  242. side: BackSide,
  243. depthWrite: false,
  244. depthTest: false
  245. } );
  246. const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial );
  247. let useSolidColor = false;
  248. const background = scene.background;
  249. if ( background ) {
  250. if ( background.isColor ) {
  251. backgroundMaterial.color.copy( background );
  252. scene.background = null;
  253. useSolidColor = true;
  254. }
  255. } else {
  256. backgroundMaterial.color.copy( _clearColor );
  257. useSolidColor = true;
  258. }
  259. renderer.setRenderTarget( cubeUVRenderTarget );
  260. renderer.clear();
  261. if ( useSolidColor ) {
  262. renderer.render( backgroundBox, cubeCamera );
  263. }
  264. for ( let i = 0; i < 6; i ++ ) {
  265. const col = i % 3;
  266. if ( col === 0 ) {
  267. cubeCamera.up.set( 0, upSign[ i ], 0 );
  268. cubeCamera.lookAt( forwardSign[ i ], 0, 0 );
  269. } else if ( col === 1 ) {
  270. cubeCamera.up.set( 0, 0, upSign[ i ] );
  271. cubeCamera.lookAt( 0, forwardSign[ i ], 0 );
  272. } else {
  273. cubeCamera.up.set( 0, upSign[ i ], 0 );
  274. cubeCamera.lookAt( 0, 0, forwardSign[ i ] );
  275. }
  276. const size = this._cubeSize;
  277. _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size );
  278. renderer.render( scene, cubeCamera );
  279. }
  280. backgroundBox.geometry.dispose();
  281. backgroundBox.material.dispose();
  282. renderer.toneMapping = toneMapping;
  283. renderer.autoClear = originalAutoClear;
  284. scene.background = background;
  285. }
  286. _textureToCubeUV( texture, cubeUVRenderTarget ) {
  287. const renderer = this._renderer;
  288. const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping );
  289. if ( isCubeTexture ) {
  290. if ( this._cubemapMaterial === null ) {
  291. this._cubemapMaterial = _getCubemapMaterial( texture );
  292. }
  293. } else {
  294. if ( this._equirectMaterial === null ) {
  295. this._equirectMaterial = _getEquirectMaterial( texture );
  296. }
  297. }
  298. const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial;
  299. const mesh = new Mesh( this._lodPlanes[ 0 ], material );
  300. material.fragmentNode.value = texture;
  301. const size = this._cubeSize;
  302. _setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size );
  303. renderer.setRenderTarget( cubeUVRenderTarget );
  304. renderer.render( mesh, _flatCamera );
  305. }
  306. _applyPMREM( cubeUVRenderTarget ) {
  307. const renderer = this._renderer;
  308. const autoClear = renderer.autoClear;
  309. renderer.autoClear = false;
  310. for ( let i = 1; i < this._lodPlanes.length; i ++ ) {
  311. const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] );
  312. const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ];
  313. this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis );
  314. }
  315. renderer.autoClear = autoClear;
  316. }
  317. /**
  318. * This is a two-pass Gaussian blur for a cubemap. Normally this is done
  319. * vertically and horizontally, but this breaks down on a cube. Here we apply
  320. * the blur latitudinally (around the poles), and then longitudinally (towards
  321. * the poles) to approximate the orthogonally-separable blur. It is least
  322. * accurate at the poles, but still does a decent job.
  323. */
  324. _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) {
  325. const pingPongRenderTarget = this._pingPongRenderTarget;
  326. this._halfBlur(
  327. cubeUVRenderTarget,
  328. pingPongRenderTarget,
  329. lodIn,
  330. lodOut,
  331. sigma,
  332. 'latitudinal',
  333. poleAxis );
  334. this._halfBlur(
  335. pingPongRenderTarget,
  336. cubeUVRenderTarget,
  337. lodOut,
  338. lodOut,
  339. sigma,
  340. 'longitudinal',
  341. poleAxis );
  342. }
  343. _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) {
  344. const renderer = this._renderer;
  345. const blurMaterial = this._blurMaterial;
  346. if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) {
  347. console.error(
  348. 'blur direction must be either latitudinal or longitudinal!' );
  349. }
  350. // Number of standard deviations at which to cut off the discrete approximation.
  351. const STANDARD_DEVIATIONS = 3;
  352. const blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial );
  353. const blurUniforms = blurMaterial.uniforms;
  354. const pixels = this._sizeLods[ lodIn ] - 1;
  355. const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 );
  356. const sigmaPixels = sigmaRadians / radiansPerPixel;
  357. const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES;
  358. if ( samples > MAX_SAMPLES ) {
  359. console.warn( `sigmaRadians, ${
  360. sigmaRadians}, is too large and will clip, as it requested ${
  361. samples} samples when the maximum is set to ${MAX_SAMPLES}` );
  362. }
  363. const weights = [];
  364. let sum = 0;
  365. for ( let i = 0; i < MAX_SAMPLES; ++ i ) {
  366. const x = i / sigmaPixels;
  367. const weight = Math.exp( - x * x / 2 );
  368. weights.push( weight );
  369. if ( i === 0 ) {
  370. sum += weight;
  371. } else if ( i < samples ) {
  372. sum += 2 * weight;
  373. }
  374. }
  375. for ( let i = 0; i < weights.length; i ++ ) {
  376. weights[ i ] = weights[ i ] / sum;
  377. }
  378. blurUniforms.envMap.value = targetIn.texture;
  379. blurUniforms.samples.value = samples;
  380. blurUniforms.weights.array = weights;
  381. blurUniforms.latitudinal.value = direction === 'latitudinal' ? 1 : 0;
  382. if ( poleAxis ) {
  383. blurUniforms.poleAxis.value = poleAxis;
  384. }
  385. const { _lodMax } = this;
  386. blurUniforms.dTheta.value = radiansPerPixel;
  387. blurUniforms.mipInt.value = _lodMax - lodIn;
  388. const outputSize = this._sizeLods[ lodOut ];
  389. const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 );
  390. const y = 4 * ( this._cubeSize - outputSize );
  391. _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize );
  392. renderer.setRenderTarget( targetOut );
  393. renderer.render( blurMesh, _flatCamera );
  394. }
  395. }
  396. function _createPlanes( lodMax ) {
  397. const lodPlanes = [];
  398. const sizeLods = [];
  399. const sigmas = [];
  400. let lod = lodMax;
  401. const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;
  402. for ( let i = 0; i < totalLods; i ++ ) {
  403. const sizeLod = Math.pow( 2, lod );
  404. sizeLods.push( sizeLod );
  405. let sigma = 1.0 / sizeLod;
  406. if ( i > lodMax - LOD_MIN ) {
  407. sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ];
  408. } else if ( i === 0 ) {
  409. sigma = 0;
  410. }
  411. sigmas.push( sigma );
  412. const texelSize = 1.0 / ( sizeLod - 2 );
  413. const min = - texelSize;
  414. const max = 1 + texelSize;
  415. const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ];
  416. const cubeFaces = 6;
  417. const vertices = 6;
  418. const positionSize = 3;
  419. const uvSize = 2;
  420. const faceIndexSize = 1;
  421. const position = new Float32Array( positionSize * vertices * cubeFaces );
  422. const uv = new Float32Array( uvSize * vertices * cubeFaces );
  423. const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces );
  424. for ( let face = 0; face < cubeFaces; face ++ ) {
  425. const x = ( face % 3 ) * 2 / 3 - 1;
  426. const y = face > 2 ? 0 : - 1;
  427. const coordinates = [
  428. x, y, 0,
  429. x + 2 / 3, y, 0,
  430. x + 2 / 3, y + 1, 0,
  431. x, y, 0,
  432. x + 2 / 3, y + 1, 0,
  433. x, y + 1, 0
  434. ];
  435. const faceIdx = _faceLib[ face ];
  436. position.set( coordinates, positionSize * vertices * faceIdx );
  437. uv.set( uv1, uvSize * vertices * faceIdx );
  438. const fill = [ faceIdx, faceIdx, faceIdx, faceIdx, faceIdx, faceIdx ];
  439. faceIndex.set( fill, faceIndexSize * vertices * faceIdx );
  440. }
  441. const planes = new BufferGeometry();
  442. planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) );
  443. planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) );
  444. planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) );
  445. lodPlanes.push( planes );
  446. if ( lod > LOD_MIN ) {
  447. lod --;
  448. }
  449. }
  450. return { lodPlanes, sizeLods, sigmas };
  451. }
  452. function _createRenderTarget( width, height, params ) {
  453. const cubeUVRenderTarget = new RenderTarget( width, height, params );
  454. cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
  455. cubeUVRenderTarget.texture.name = 'PMREM.cubeUv';
  456. cubeUVRenderTarget.texture.isPMREMTexture = true;
  457. cubeUVRenderTarget.scissorTest = true;
  458. return cubeUVRenderTarget;
  459. }
  460. function _setViewport( target, x, y, width, height ) {
  461. const viewY = target.height - height - y;
  462. target.viewport.set( x, viewY, width, height );
  463. target.scissor.set( x, viewY, width, height );
  464. }
  465. function _getMaterial() {
  466. const material = new NodeMaterial();
  467. material.colorSpaced = false;
  468. material.toneMapped = false;
  469. material.depthTest = false;
  470. material.depthWrite = false;
  471. material.blending = NoBlending;
  472. return material;
  473. }
  474. function _getBlurShader( lodMax, width, height ) {
  475. const weights = uniforms( new Array( MAX_SAMPLES ).fill( 0 ) );
  476. const poleAxis = uniform( new Vector3( 0, 1, 0 ) );
  477. const dTheta = uniform( 0 );
  478. const n = float( MAX_SAMPLES );
  479. const latitudinal = uniform( 0 ); // false, bool
  480. const samples = uniform( 1 ); // int
  481. const envMap = texture( null );
  482. const mipInt = uniform( 0 ); // int
  483. const CUBEUV_TEXEL_WIDTH = float( 1 / width );
  484. const CUBEUV_TEXEL_HEIGHT = float( 1 / height );
  485. const CUBEUV_MAX_MIP = float( lodMax );
  486. const materialUniforms = {
  487. n,
  488. latitudinal,
  489. weights,
  490. poleAxis,
  491. outputDirection,
  492. dTheta,
  493. samples,
  494. envMap,
  495. mipInt,
  496. CUBEUV_TEXEL_WIDTH,
  497. CUBEUV_TEXEL_HEIGHT,
  498. CUBEUV_MAX_MIP
  499. };
  500. const material = _getMaterial();
  501. material.uniforms = materialUniforms; // TODO: Move to outside of the material
  502. material.fragmentNode = blur( { ...materialUniforms, latitudinal: latitudinal.equal( 1 ) } );
  503. return material;
  504. }
  505. function _getCubemapMaterial( envTexture ) {
  506. const material = _getMaterial();
  507. material.fragmentNode = cubeTexture( envTexture, outputDirection );
  508. return material;
  509. }
  510. function _getEquirectMaterial( envTexture ) {
  511. const material = _getMaterial();
  512. material.fragmentNode = texture( envTexture, equirectUV( outputDirection ), 0 );
  513. return material;
  514. }
  515. export default PMREMGenerator;