TextureHelper.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import {
  2. BoxGeometry,
  3. BufferAttribute,
  4. DoubleSide,
  5. Mesh,
  6. PlaneGeometry,
  7. ShaderMaterial,
  8. Vector3,
  9. } from 'three';
  10. import { mergeGeometries } from '../utils/BufferGeometryUtils.js';
  11. /**
  12. * A helper that can be used to display any type of texture for
  13. * debugging purposes. Depending on the type of texture (2D, 3D, Array),
  14. * the helper becomes a plane or box mesh.
  15. *
  16. * This helper can only be used with {@link WebGLRenderer}.
  17. * When using {@link WebGPURenderer}, import from `TextureHelperGPU.js`.
  18. *
  19. * @augments Mesh
  20. * @three_import import { TextureHelper } from 'three/addons/helpers/TextureHelper.js';
  21. */
  22. class TextureHelper extends Mesh {
  23. /**
  24. * Constructs a new texture helper.
  25. *
  26. * @param {Texture} texture - The texture to visualize.
  27. * @param {number} [width=1] - The helper's width.
  28. * @param {number} [height=1] - The helper's height.
  29. * @param {number} [depth=1] - The helper's depth.
  30. */
  31. constructor( texture, width = 1, height = 1, depth = 1 ) {
  32. const material = new ShaderMaterial( {
  33. type: 'TextureHelperMaterial',
  34. side: DoubleSide,
  35. transparent: true,
  36. uniforms: {
  37. map: { value: texture },
  38. alpha: { value: getAlpha( texture ) },
  39. },
  40. vertexShader: [
  41. 'attribute vec3 uvw;',
  42. 'varying vec3 vUvw;',
  43. 'void main() {',
  44. ' vUvw = uvw;',
  45. ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
  46. '}',
  47. ].join( '\n' ),
  48. fragmentShader: [
  49. 'precision highp float;',
  50. 'precision highp sampler2DArray;',
  51. 'precision highp sampler3D;',
  52. 'uniform {samplerType} map;',
  53. 'uniform float alpha;',
  54. 'varying vec3 vUvw;',
  55. 'vec4 textureHelper( in sampler2D map ) { return texture( map, vUvw.xy ); }',
  56. 'vec4 textureHelper( in sampler2DArray map ) { return texture( map, vUvw ); }',
  57. 'vec4 textureHelper( in sampler3D map ) { return texture( map, vUvw ); }',
  58. 'vec4 textureHelper( in samplerCube map ) { return texture( map, vUvw ); }',
  59. 'void main() {',
  60. ' gl_FragColor = linearToOutputTexel( vec4( textureHelper( map ).xyz, alpha ) );',
  61. '}'
  62. ].join( '\n' ).replace( '{samplerType}', getSamplerType( texture ) )
  63. } );
  64. const geometry = texture.isCubeTexture
  65. ? createCubeGeometry( width, height, depth )
  66. : createSliceGeometry( texture, width, height, depth );
  67. super( geometry, material );
  68. /**
  69. * The texture to visualize.
  70. *
  71. * @type {Texture}
  72. */
  73. this.texture = texture;
  74. this.type = 'TextureHelper';
  75. }
  76. /**
  77. * Frees the GPU-related resources allocated by this instance. Call this
  78. * method whenever this instance is no longer used in your app.
  79. */
  80. dispose() {
  81. this.geometry.dispose();
  82. this.material.dispose();
  83. }
  84. }
  85. function getSamplerType( texture ) {
  86. if ( texture.isCubeTexture ) {
  87. return 'samplerCube';
  88. } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
  89. return 'sampler2DArray';
  90. } else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) {
  91. return 'sampler3D';
  92. } else {
  93. return 'sampler2D';
  94. }
  95. }
  96. function getImageCount( texture ) {
  97. if ( texture.isCubeTexture ) {
  98. return 6;
  99. } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
  100. return texture.image.depth;
  101. } else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) {
  102. return texture.image.depth;
  103. } else {
  104. return 1;
  105. }
  106. }
  107. function getAlpha( texture ) {
  108. if ( texture.isCubeTexture ) {
  109. return 1;
  110. } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
  111. return Math.max( 1 / texture.image.depth, 0.25 );
  112. } else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) {
  113. return Math.max( 1 / texture.image.depth, 0.25 );
  114. } else {
  115. return 1;
  116. }
  117. }
  118. function createCubeGeometry( width, height, depth ) {
  119. const geometry = new BoxGeometry( width, height, depth );
  120. const position = geometry.attributes.position;
  121. const uv = geometry.attributes.uv;
  122. const uvw = new BufferAttribute( new Float32Array( uv.count * 3 ), 3 );
  123. const _direction = new Vector3();
  124. for ( let j = 0, jl = uv.count; j < jl; ++ j ) {
  125. _direction.fromBufferAttribute( position, j ).normalize();
  126. const u = _direction.x;
  127. const v = _direction.y;
  128. const w = _direction.z;
  129. uvw.setXYZ( j, u, v, w );
  130. }
  131. geometry.deleteAttribute( 'uv' );
  132. geometry.setAttribute( 'uvw', uvw );
  133. return geometry;
  134. }
  135. function createSliceGeometry( texture, width, height, depth ) {
  136. const sliceCount = getImageCount( texture );
  137. const geometries = [];
  138. for ( let i = 0; i < sliceCount; ++ i ) {
  139. const geometry = new PlaneGeometry( width, height );
  140. if ( sliceCount > 1 ) {
  141. geometry.translate( 0, 0, depth * ( i / ( sliceCount - 1 ) - 0.5 ) );
  142. }
  143. const uv = geometry.attributes.uv;
  144. const uvw = new BufferAttribute( new Float32Array( uv.count * 3 ), 3 );
  145. for ( let j = 0, jl = uv.count; j < jl; ++ j ) {
  146. const u = uv.getX( j );
  147. const v = texture.flipY ? uv.getY( j ) : 1 - uv.getY( j );
  148. const w = sliceCount === 1
  149. ? 1
  150. : texture.isDataArrayTexture || texture.isCompressedArrayTexture
  151. ? i
  152. : i / ( sliceCount - 1 );
  153. uvw.setXYZ( j, u, v, w );
  154. }
  155. geometry.deleteAttribute( 'uv' );
  156. geometry.setAttribute( 'uvw', uvw );
  157. geometries.push( geometry );
  158. }
  159. return mergeGeometries( geometries );
  160. }
  161. export { TextureHelper };