TextureHelperGPU.js 4.5 KB

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