WebGLTextureUtils.js 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144
  1. import { LinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, FloatType, MirroredRepeatWrapping, ClampToEdgeWrapping, RepeatWrapping, SRGBColorSpace, NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare } from '../../../constants.js';
  2. let initialized = false, wrappingToGL, filterToGL, compareToGL;
  3. /**
  4. * A WebGL 2 backend utility module for managing textures.
  5. *
  6. * @private
  7. */
  8. class WebGLTextureUtils {
  9. /**
  10. * Constructs a new utility object.
  11. *
  12. * @param {WebGLBackend} backend - The WebGL 2 backend.
  13. */
  14. constructor( backend ) {
  15. /**
  16. * A reference to the WebGL 2 backend.
  17. *
  18. * @type {WebGLBackend}
  19. */
  20. this.backend = backend;
  21. /**
  22. * A reference to the rendering context.
  23. *
  24. * @type {WebGL2RenderingContext}
  25. */
  26. this.gl = backend.gl;
  27. /**
  28. * A reference to a backend module holding extension-related
  29. * utility functions.
  30. *
  31. * @type {WebGLExtensions}
  32. */
  33. this.extensions = backend.extensions;
  34. /**
  35. * A dictionary for managing default textures. The key
  36. * is the binding point (target), the value the WEbGL texture object.
  37. *
  38. * @type {Object<GLenum,WebGLTexture>}
  39. */
  40. this.defaultTextures = {};
  41. if ( initialized === false ) {
  42. this._init();
  43. initialized = true;
  44. }
  45. }
  46. /**
  47. * Inits the state of the utility.
  48. *
  49. * @private
  50. */
  51. _init() {
  52. const gl = this.gl;
  53. // Store only WebGL constants here.
  54. wrappingToGL = {
  55. [ RepeatWrapping ]: gl.REPEAT,
  56. [ ClampToEdgeWrapping ]: gl.CLAMP_TO_EDGE,
  57. [ MirroredRepeatWrapping ]: gl.MIRRORED_REPEAT
  58. };
  59. filterToGL = {
  60. [ NearestFilter ]: gl.NEAREST,
  61. [ NearestMipmapNearestFilter ]: gl.NEAREST_MIPMAP_NEAREST,
  62. [ NearestMipmapLinearFilter ]: gl.NEAREST_MIPMAP_LINEAR,
  63. [ LinearFilter ]: gl.LINEAR,
  64. [ LinearMipmapNearestFilter ]: gl.LINEAR_MIPMAP_NEAREST,
  65. [ LinearMipmapLinearFilter ]: gl.LINEAR_MIPMAP_LINEAR
  66. };
  67. compareToGL = {
  68. [ NeverCompare ]: gl.NEVER,
  69. [ AlwaysCompare ]: gl.ALWAYS,
  70. [ LessCompare ]: gl.LESS,
  71. [ LessEqualCompare ]: gl.LEQUAL,
  72. [ EqualCompare ]: gl.EQUAL,
  73. [ GreaterEqualCompare ]: gl.GEQUAL,
  74. [ GreaterCompare ]: gl.GREATER,
  75. [ NotEqualCompare ]: gl.NOTEQUAL
  76. };
  77. }
  78. /**
  79. * Returns the native texture type for the given texture.
  80. *
  81. * @param {Texture} texture - The texture.
  82. * @return {GLenum} The native texture type.
  83. */
  84. getGLTextureType( texture ) {
  85. const { gl } = this;
  86. let glTextureType;
  87. if ( texture.isCubeTexture === true ) {
  88. glTextureType = gl.TEXTURE_CUBE_MAP;
  89. } else if ( texture.isDataArrayTexture === true || texture.isCompressedArrayTexture === true ) {
  90. glTextureType = gl.TEXTURE_2D_ARRAY;
  91. } else if ( texture.isData3DTexture === true ) { // TODO: isCompressed3DTexture, wait for #26642
  92. glTextureType = gl.TEXTURE_3D;
  93. } else {
  94. glTextureType = gl.TEXTURE_2D;
  95. }
  96. return glTextureType;
  97. }
  98. /**
  99. * Returns the native texture type for the given texture.
  100. *
  101. * @param {?string} internalFormatName - The internal format name. When `null`, the internal format is derived from the subsequent parameters.
  102. * @param {GLenum} glFormat - The WebGL format.
  103. * @param {GLenum} glType - The WebGL type.
  104. * @param {string} colorSpace - The texture's color space.
  105. * @param {boolean} [forceLinearTransfer=false] - Whether to force a linear transfer or not.
  106. * @return {GLenum} The internal format.
  107. */
  108. getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false ) {
  109. const { gl, extensions } = this;
  110. if ( internalFormatName !== null ) {
  111. if ( gl[ internalFormatName ] !== undefined ) return gl[ internalFormatName ];
  112. console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' );
  113. }
  114. let internalFormat = glFormat;
  115. if ( glFormat === gl.RED ) {
  116. if ( glType === gl.FLOAT ) internalFormat = gl.R32F;
  117. if ( glType === gl.HALF_FLOAT ) internalFormat = gl.R16F;
  118. if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.R8;
  119. if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.R16;
  120. if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.R32UI;
  121. if ( glType === gl.BYTE ) internalFormat = gl.R8I;
  122. if ( glType === gl.SHORT ) internalFormat = gl.R16I;
  123. if ( glType === gl.INT ) internalFormat = gl.R32I;
  124. }
  125. if ( glFormat === gl.RED_INTEGER ) {
  126. if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.R8UI;
  127. if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.R16UI;
  128. if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.R32UI;
  129. if ( glType === gl.BYTE ) internalFormat = gl.R8I;
  130. if ( glType === gl.SHORT ) internalFormat = gl.R16I;
  131. if ( glType === gl.INT ) internalFormat = gl.R32I;
  132. }
  133. if ( glFormat === gl.RG ) {
  134. if ( glType === gl.FLOAT ) internalFormat = gl.RG32F;
  135. if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RG16F;
  136. if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RG8;
  137. if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.RG16;
  138. if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.RG32UI;
  139. if ( glType === gl.BYTE ) internalFormat = gl.RG8I;
  140. if ( glType === gl.SHORT ) internalFormat = gl.RG16I;
  141. if ( glType === gl.INT ) internalFormat = gl.RG32I;
  142. }
  143. if ( glFormat === gl.RG_INTEGER ) {
  144. if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RG8UI;
  145. if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.RG16UI;
  146. if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.RG32UI;
  147. if ( glType === gl.BYTE ) internalFormat = gl.RG8I;
  148. if ( glType === gl.SHORT ) internalFormat = gl.RG16I;
  149. if ( glType === gl.INT ) internalFormat = gl.RG32I;
  150. }
  151. if ( glFormat === gl.RGB ) {
  152. if ( glType === gl.FLOAT ) internalFormat = gl.RGB32F;
  153. if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RGB16F;
  154. if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RGB8;
  155. if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.RGB16;
  156. if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.RGB32UI;
  157. if ( glType === gl.BYTE ) internalFormat = gl.RGB8I;
  158. if ( glType === gl.SHORT ) internalFormat = gl.RGB16I;
  159. if ( glType === gl.INT ) internalFormat = gl.RGB32I;
  160. if ( glType === gl.UNSIGNED_BYTE ) internalFormat = ( colorSpace === SRGBColorSpace && forceLinearTransfer === false ) ? gl.SRGB8 : gl.RGB8;
  161. if ( glType === gl.UNSIGNED_SHORT_5_6_5 ) internalFormat = gl.RGB565;
  162. if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = gl.RGB5_A1;
  163. if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = gl.RGB4;
  164. if ( glType === gl.UNSIGNED_INT_5_9_9_9_REV ) internalFormat = gl.RGB9_E5;
  165. }
  166. if ( glFormat === gl.RGB_INTEGER ) {
  167. if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RGB8UI;
  168. if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.RGB16UI;
  169. if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.RGB32UI;
  170. if ( glType === gl.BYTE ) internalFormat = gl.RGB8I;
  171. if ( glType === gl.SHORT ) internalFormat = gl.RGB16I;
  172. if ( glType === gl.INT ) internalFormat = gl.RGB32I;
  173. }
  174. if ( glFormat === gl.RGBA ) {
  175. if ( glType === gl.FLOAT ) internalFormat = gl.RGBA32F;
  176. if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RGBA16F;
  177. if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RGBA8;
  178. if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.RGBA16;
  179. if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.RGBA32UI;
  180. if ( glType === gl.BYTE ) internalFormat = gl.RGBA8I;
  181. if ( glType === gl.SHORT ) internalFormat = gl.RGBA16I;
  182. if ( glType === gl.INT ) internalFormat = gl.RGBA32I;
  183. if ( glType === gl.UNSIGNED_BYTE ) internalFormat = ( colorSpace === SRGBColorSpace && forceLinearTransfer === false ) ? gl.SRGB8_ALPHA8 : gl.RGBA8;
  184. if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = gl.RGBA4;
  185. if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = gl.RGB5_A1;
  186. }
  187. if ( glFormat === gl.RGBA_INTEGER ) {
  188. if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RGBA8UI;
  189. if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.RGBA16UI;
  190. if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.RGBA32UI;
  191. if ( glType === gl.BYTE ) internalFormat = gl.RGBA8I;
  192. if ( glType === gl.SHORT ) internalFormat = gl.RGBA16I;
  193. if ( glType === gl.INT ) internalFormat = gl.RGBA32I;
  194. }
  195. if ( glFormat === gl.DEPTH_COMPONENT ) {
  196. if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.DEPTH_COMPONENT16;
  197. if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.DEPTH_COMPONENT24;
  198. if ( glType === gl.FLOAT ) internalFormat = gl.DEPTH_COMPONENT32F;
  199. }
  200. if ( glFormat === gl.DEPTH_STENCIL ) {
  201. if ( glType === gl.UNSIGNED_INT_24_8 ) internalFormat = gl.DEPTH24_STENCIL8;
  202. }
  203. if ( internalFormat === gl.R16F || internalFormat === gl.R32F ||
  204. internalFormat === gl.RG16F || internalFormat === gl.RG32F ||
  205. internalFormat === gl.RGBA16F || internalFormat === gl.RGBA32F ) {
  206. extensions.get( 'EXT_color_buffer_float' );
  207. }
  208. return internalFormat;
  209. }
  210. /**
  211. * Sets the texture parameters for the given texture.
  212. *
  213. * @param {GLenum} textureType - The texture type.
  214. * @param {Texture} texture - The texture.
  215. */
  216. setTextureParameters( textureType, texture ) {
  217. const { gl, extensions, backend } = this;
  218. gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
  219. gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
  220. gl.pixelStorei( gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
  221. gl.pixelStorei( gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE );
  222. gl.texParameteri( textureType, gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] );
  223. gl.texParameteri( textureType, gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] );
  224. if ( textureType === gl.TEXTURE_3D || textureType === gl.TEXTURE_2D_ARRAY ) {
  225. gl.texParameteri( textureType, gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] );
  226. }
  227. gl.texParameteri( textureType, gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] );
  228. const hasMipmaps = texture.mipmaps !== undefined && texture.mipmaps.length > 0;
  229. // follow WebGPU backend mapping for texture filtering
  230. const minFilter = texture.minFilter === LinearFilter && hasMipmaps ? LinearMipmapLinearFilter : texture.minFilter;
  231. gl.texParameteri( textureType, gl.TEXTURE_MIN_FILTER, filterToGL[ minFilter ] );
  232. if ( texture.compareFunction ) {
  233. gl.texParameteri( textureType, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE );
  234. gl.texParameteri( textureType, gl.TEXTURE_COMPARE_FUNC, compareToGL[ texture.compareFunction ] );
  235. }
  236. if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) {
  237. if ( texture.magFilter === NearestFilter ) return;
  238. if ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return;
  239. if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2
  240. if ( texture.anisotropy > 1 ) {
  241. const extension = extensions.get( 'EXT_texture_filter_anisotropic' );
  242. gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, backend.getMaxAnisotropy() ) );
  243. }
  244. }
  245. }
  246. /**
  247. * Creates a default texture for the given texture that can be used
  248. * as a placeholder until the actual texture is ready for usage.
  249. *
  250. * @param {Texture} texture - The texture to create a default texture for.
  251. */
  252. createDefaultTexture( texture ) {
  253. const { gl, backend, defaultTextures } = this;
  254. const glTextureType = this.getGLTextureType( texture );
  255. let textureGPU = defaultTextures[ glTextureType ];
  256. if ( textureGPU === undefined ) {
  257. textureGPU = gl.createTexture();
  258. backend.state.bindTexture( glTextureType, textureGPU );
  259. gl.texParameteri( glTextureType, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
  260. gl.texParameteri( glTextureType, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
  261. // gl.texImage2D( glTextureType, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
  262. defaultTextures[ glTextureType ] = textureGPU;
  263. }
  264. backend.set( texture, {
  265. textureGPU,
  266. glTextureType,
  267. isDefault: true
  268. } );
  269. }
  270. /**
  271. * Defines a texture on the GPU for the given texture object.
  272. *
  273. * @param {Texture} texture - The texture.
  274. * @param {Object} [options={}] - Optional configuration parameter.
  275. * @return {undefined}
  276. */
  277. createTexture( texture, options ) {
  278. const { gl, backend } = this;
  279. const { levels, width, height, depth } = options;
  280. const glFormat = backend.utils.convert( texture.format, texture.colorSpace );
  281. const glType = backend.utils.convert( texture.type );
  282. const glInternalFormat = this.getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture );
  283. const textureGPU = gl.createTexture();
  284. const glTextureType = this.getGLTextureType( texture );
  285. backend.state.bindTexture( glTextureType, textureGPU );
  286. this.setTextureParameters( glTextureType, texture );
  287. if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
  288. gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth );
  289. } else if ( texture.isData3DTexture ) {
  290. gl.texStorage3D( gl.TEXTURE_3D, levels, glInternalFormat, width, height, depth );
  291. } else if ( ! texture.isVideoTexture ) {
  292. gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height );
  293. }
  294. backend.set( texture, {
  295. textureGPU,
  296. glTextureType,
  297. glFormat,
  298. glType,
  299. glInternalFormat
  300. } );
  301. }
  302. /**
  303. * Uploads texture buffer data to the GPU memory.
  304. *
  305. * @param {WebGLBuffer} buffer - The buffer data.
  306. * @param {Texture} texture - The texture,
  307. */
  308. copyBufferToTexture( buffer, texture ) {
  309. const { gl, backend } = this;
  310. const { textureGPU, glTextureType, glFormat, glType } = backend.get( texture );
  311. const { width, height } = texture.source.data;
  312. gl.bindBuffer( gl.PIXEL_UNPACK_BUFFER, buffer );
  313. backend.state.bindTexture( glTextureType, textureGPU );
  314. gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, false );
  315. gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false );
  316. gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, 0 );
  317. gl.bindBuffer( gl.PIXEL_UNPACK_BUFFER, null );
  318. backend.state.unbindTexture();
  319. // debug
  320. // const framebuffer = gl.createFramebuffer();
  321. // gl.bindFramebuffer( gl.FRAMEBUFFER, framebuffer );
  322. // gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, glTextureType, textureGPU, 0 );
  323. // const readout = new Float32Array( width * height * 4 );
  324. // const altFormat = gl.getParameter( gl.IMPLEMENTATION_COLOR_READ_FORMAT );
  325. // const altType = gl.getParameter( gl.IMPLEMENTATION_COLOR_READ_TYPE );
  326. // gl.readPixels( 0, 0, width, height, altFormat, altType, readout );
  327. // gl.bindFramebuffer( gl.FRAMEBUFFER, null );
  328. // console.log( readout );
  329. }
  330. /**
  331. * Uploads the updated texture data to the GPU.
  332. *
  333. * @param {Texture} texture - The texture.
  334. * @param {Object} [options={}] - Optional configuration parameter.
  335. */
  336. updateTexture( texture, options ) {
  337. const { gl } = this;
  338. const { width, height } = options;
  339. const { textureGPU, glTextureType, glFormat, glType, glInternalFormat } = this.backend.get( texture );
  340. if ( texture.isRenderTargetTexture || ( textureGPU === undefined /* unsupported texture format */ ) )
  341. return;
  342. const getImage = ( source ) => {
  343. if ( source.isDataTexture ) {
  344. return source.image.data;
  345. } else if ( ( typeof HTMLImageElement !== 'undefined' && source instanceof HTMLImageElement ) ||
  346. ( typeof HTMLCanvasElement !== 'undefined' && source instanceof HTMLCanvasElement ) ||
  347. ( typeof ImageBitmap !== 'undefined' && source instanceof ImageBitmap ) ||
  348. source instanceof OffscreenCanvas ) {
  349. return source;
  350. }
  351. return source.data;
  352. };
  353. this.backend.state.bindTexture( glTextureType, textureGPU );
  354. this.setTextureParameters( glTextureType, texture );
  355. if ( texture.isCompressedTexture ) {
  356. const mipmaps = texture.mipmaps;
  357. const image = options.image;
  358. for ( let i = 0; i < mipmaps.length; i ++ ) {
  359. const mipmap = mipmaps[ i ];
  360. if ( texture.isCompressedArrayTexture ) {
  361. if ( texture.format !== gl.RGBA ) {
  362. if ( glFormat !== null ) {
  363. gl.compressedTexSubImage3D( gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data );
  364. } else {
  365. console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );
  366. }
  367. } else {
  368. gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data );
  369. }
  370. } else {
  371. if ( glFormat !== null ) {
  372. gl.compressedTexSubImage2D( gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data );
  373. } else {
  374. console.warn( 'Unsupported compressed texture format' );
  375. }
  376. }
  377. }
  378. } else if ( texture.isCubeTexture ) {
  379. const images = options.images;
  380. for ( let i = 0; i < 6; i ++ ) {
  381. const image = getImage( images[ i ] );
  382. gl.texSubImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, width, height, glFormat, glType, image );
  383. }
  384. } else if ( texture.isDataArrayTexture ) {
  385. const image = options.image;
  386. gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );
  387. } else if ( texture.isData3DTexture ) {
  388. const image = options.image;
  389. gl.texSubImage3D( gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );
  390. } else if ( texture.isVideoTexture ) {
  391. texture.update();
  392. gl.texImage2D( glTextureType, 0, glInternalFormat, glFormat, glType, options.image );
  393. } else {
  394. const image = getImage( options.image );
  395. gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, image );
  396. }
  397. }
  398. /**
  399. * Generates mipmaps for the given texture.
  400. *
  401. * @param {Texture} texture - The texture.
  402. */
  403. generateMipmaps( texture ) {
  404. const { gl, backend } = this;
  405. const { textureGPU, glTextureType } = backend.get( texture );
  406. backend.state.bindTexture( glTextureType, textureGPU );
  407. gl.generateMipmap( glTextureType );
  408. }
  409. /**
  410. * Deallocates the render buffers of the given render target.
  411. *
  412. * @param {RenderTarget} renderTarget - The render target.
  413. */
  414. deallocateRenderBuffers( renderTarget ) {
  415. const { gl, backend } = this;
  416. // remove framebuffer reference
  417. if ( renderTarget ) {
  418. const renderContextData = backend.get( renderTarget );
  419. renderContextData.renderBufferStorageSetup = undefined;
  420. if ( renderContextData.framebuffers ) {
  421. for ( const cacheKey in renderContextData.framebuffers ) {
  422. gl.deleteFramebuffer( renderContextData.framebuffers[ cacheKey ] );
  423. }
  424. delete renderContextData.framebuffers;
  425. }
  426. if ( renderContextData.depthRenderbuffer ) {
  427. gl.deleteRenderbuffer( renderContextData.depthRenderbuffer );
  428. delete renderContextData.depthRenderbuffer;
  429. }
  430. if ( renderContextData.stencilRenderbuffer ) {
  431. gl.deleteRenderbuffer( renderContextData.stencilRenderbuffer );
  432. delete renderContextData.stencilRenderbuffer;
  433. }
  434. if ( renderContextData.msaaFrameBuffer ) {
  435. gl.deleteFramebuffer( renderContextData.msaaFrameBuffer );
  436. delete renderContextData.msaaFrameBuffer;
  437. }
  438. if ( renderContextData.msaaRenderbuffers ) {
  439. for ( let i = 0; i < renderContextData.msaaRenderbuffers.length; i ++ ) {
  440. gl.deleteRenderbuffer( renderContextData.msaaRenderbuffers[ i ] );
  441. }
  442. delete renderContextData.msaaRenderbuffers;
  443. }
  444. }
  445. }
  446. /**
  447. * Destroys the GPU data for the given texture object.
  448. *
  449. * @param {Texture} texture - The texture.
  450. */
  451. destroyTexture( texture ) {
  452. const { gl, backend } = this;
  453. const { textureGPU, renderTarget } = backend.get( texture );
  454. this.deallocateRenderBuffers( renderTarget );
  455. gl.deleteTexture( textureGPU );
  456. backend.delete( texture );
  457. }
  458. /**
  459. * Copies data of the given source texture to the given destination texture.
  460. *
  461. * @param {Texture} srcTexture - The source texture.
  462. * @param {Texture} dstTexture - The destination texture.
  463. * @param {?(Box3|Box2)} [srcRegion=null] - The region of the source texture to copy.
  464. * @param {?(Vector2|Vector3)} [dstPosition=null] - The destination position of the copy.
  465. * @param {number} [srcLevel=0] - The source mip level to copy from.
  466. * @param {number} [dstLevel=0] - The destination mip level to copy to.
  467. */
  468. copyTextureToTexture( srcTexture, dstTexture, srcRegion = null, dstPosition = null, srcLevel = 0, dstLevel = 0 ) {
  469. const { gl, backend } = this;
  470. const { state } = this.backend;
  471. const { textureGPU: dstTextureGPU, glTextureType, glType, glFormat } = backend.get( dstTexture );
  472. state.bindTexture( glTextureType, dstTextureGPU );
  473. // gather the necessary dimensions to copy
  474. let width, height, depth, minX, minY, minZ;
  475. let dstX, dstY, dstZ;
  476. const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ dstLevel ] : srcTexture.image;
  477. if ( srcRegion !== null ) {
  478. width = srcRegion.max.x - srcRegion.min.x;
  479. height = srcRegion.max.y - srcRegion.min.y;
  480. depth = srcRegion.isBox3 ? srcRegion.max.z - srcRegion.min.z : 1;
  481. minX = srcRegion.min.x;
  482. minY = srcRegion.min.y;
  483. minZ = srcRegion.isBox3 ? srcRegion.min.z : 0;
  484. } else {
  485. const levelScale = Math.pow( 2, - srcLevel );
  486. width = Math.floor( image.width * levelScale );
  487. height = Math.floor( image.height * levelScale );
  488. if ( srcTexture.isDataArrayTexture ) {
  489. depth = image.depth;
  490. } else if ( srcTexture.isData3DTexture ) {
  491. depth = Math.floor( image.depth * levelScale );
  492. } else {
  493. depth = 1;
  494. }
  495. minX = 0;
  496. minY = 0;
  497. minZ = 0;
  498. }
  499. if ( dstPosition !== null ) {
  500. dstX = dstPosition.x;
  501. dstY = dstPosition.y;
  502. dstZ = dstPosition.z;
  503. } else {
  504. dstX = 0;
  505. dstY = 0;
  506. dstZ = 0;
  507. }
  508. gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );
  509. gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );
  510. gl.pixelStorei( gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );
  511. // used for copying data from cpu
  512. const currentUnpackRowLen = gl.getParameter( gl.UNPACK_ROW_LENGTH );
  513. const currentUnpackImageHeight = gl.getParameter( gl.UNPACK_IMAGE_HEIGHT );
  514. const currentUnpackSkipPixels = gl.getParameter( gl.UNPACK_SKIP_PIXELS );
  515. const currentUnpackSkipRows = gl.getParameter( gl.UNPACK_SKIP_ROWS );
  516. const currentUnpackSkipImages = gl.getParameter( gl.UNPACK_SKIP_IMAGES );
  517. gl.pixelStorei( gl.UNPACK_ROW_LENGTH, image.width );
  518. gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, image.height );
  519. gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, minX );
  520. gl.pixelStorei( gl.UNPACK_SKIP_ROWS, minY );
  521. gl.pixelStorei( gl.UNPACK_SKIP_IMAGES, minZ );
  522. // set up the src texture
  523. const isDst3D = dstTexture.isDataArrayTexture || dstTexture.isData3DTexture;
  524. if ( srcTexture.isRenderTargetTexture || srcTexture.isDepthTexture ) {
  525. const srcTextureData = backend.get( srcTexture );
  526. const dstTextureData = backend.get( dstTexture );
  527. const srcRenderContextData = backend.get( srcTextureData.renderTarget );
  528. const dstRenderContextData = backend.get( dstTextureData.renderTarget );
  529. const srcFramebuffer = srcRenderContextData.framebuffers[ srcTextureData.cacheKey ];
  530. const dstFramebuffer = dstRenderContextData.framebuffers[ dstTextureData.cacheKey ];
  531. state.bindFramebuffer( gl.READ_FRAMEBUFFER, srcFramebuffer );
  532. state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, dstFramebuffer );
  533. let mask = gl.COLOR_BUFFER_BIT;
  534. if ( srcTexture.isDepthTexture ) mask = gl.DEPTH_BUFFER_BIT;
  535. gl.blitFramebuffer( minX, minY, width, height, dstX, dstY, width, height, mask, gl.NEAREST );
  536. state.bindFramebuffer( gl.READ_FRAMEBUFFER, null );
  537. state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null );
  538. } else {
  539. if ( isDst3D ) {
  540. // copy data into the 3d texture
  541. if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) {
  542. gl.texSubImage3D( glTextureType, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image.data );
  543. } else if ( dstTexture.isCompressedArrayTexture ) {
  544. gl.compressedTexSubImage3D( glTextureType, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, image.data );
  545. } else {
  546. gl.texSubImage3D( glTextureType, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image );
  547. }
  548. } else {
  549. // copy data into the 2d texture
  550. if ( srcTexture.isDataTexture ) {
  551. gl.texSubImage2D( glTextureType, dstLevel, dstX, dstY, width, height, glFormat, glType, image.data );
  552. } else if ( srcTexture.isCompressedTexture ) {
  553. gl.compressedTexSubImage2D( glTextureType, dstLevel, dstX, dstY, image.width, image.height, glFormat, image.data );
  554. } else {
  555. gl.texSubImage2D( glTextureType, dstLevel, dstX, dstY, width, height, glFormat, glType, image );
  556. }
  557. }
  558. }
  559. // reset values
  560. gl.pixelStorei( gl.UNPACK_ROW_LENGTH, currentUnpackRowLen );
  561. gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight );
  562. gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels );
  563. gl.pixelStorei( gl.UNPACK_SKIP_ROWS, currentUnpackSkipRows );
  564. gl.pixelStorei( gl.UNPACK_SKIP_IMAGES, currentUnpackSkipImages );
  565. // Generate mipmaps only when copying level 0
  566. if ( dstLevel === 0 && dstTexture.generateMipmaps ) {
  567. gl.generateMipmap( glTextureType );
  568. }
  569. state.unbindTexture();
  570. }
  571. /**
  572. * Copies the current bound framebuffer to the given texture.
  573. *
  574. * @param {Texture} texture - The destination texture.
  575. * @param {RenderContext} renderContext - The render context.
  576. * @param {Vector4} rectangle - A four dimensional vector defining the origin and dimension of the copy.
  577. */
  578. copyFramebufferToTexture( texture, renderContext, rectangle ) {
  579. const { gl } = this;
  580. const { state } = this.backend;
  581. const { textureGPU } = this.backend.get( texture );
  582. const { x, y, z: width, w: height } = rectangle;
  583. const requireDrawFrameBuffer = texture.isDepthTexture === true || ( renderContext.renderTarget && renderContext.renderTarget.samples > 0 );
  584. const srcHeight = renderContext.renderTarget ? renderContext.renderTarget.height : this.backend.getDrawingBufferSize().y;
  585. if ( requireDrawFrameBuffer ) {
  586. const partial = ( x !== 0 || y !== 0 );
  587. let mask;
  588. let attachment;
  589. if ( texture.isDepthTexture === true ) {
  590. mask = gl.DEPTH_BUFFER_BIT;
  591. attachment = gl.DEPTH_ATTACHMENT;
  592. if ( renderContext.stencil ) {
  593. mask |= gl.STENCIL_BUFFER_BIT;
  594. }
  595. } else {
  596. mask = gl.COLOR_BUFFER_BIT;
  597. attachment = gl.COLOR_ATTACHMENT0;
  598. }
  599. if ( partial ) {
  600. const renderTargetContextData = this.backend.get( renderContext.renderTarget );
  601. const fb = renderTargetContextData.framebuffers[ renderContext.getCacheKey() ];
  602. const msaaFrameBuffer = renderTargetContextData.msaaFrameBuffer;
  603. state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
  604. state.bindFramebuffer( gl.READ_FRAMEBUFFER, msaaFrameBuffer );
  605. const flippedY = srcHeight - y - height;
  606. gl.blitFramebuffer( x, flippedY, x + width, flippedY + height, x, flippedY, x + width, flippedY + height, mask, gl.NEAREST );
  607. state.bindFramebuffer( gl.READ_FRAMEBUFFER, fb );
  608. state.bindTexture( gl.TEXTURE_2D, textureGPU );
  609. gl.copyTexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, x, flippedY, width, height );
  610. state.unbindTexture();
  611. } else {
  612. const fb = gl.createFramebuffer();
  613. state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
  614. gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, attachment, gl.TEXTURE_2D, textureGPU, 0 );
  615. gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, gl.NEAREST );
  616. gl.deleteFramebuffer( fb );
  617. }
  618. } else {
  619. state.bindTexture( gl.TEXTURE_2D, textureGPU );
  620. gl.copyTexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, x, srcHeight - height - y, width, height );
  621. state.unbindTexture();
  622. }
  623. if ( texture.generateMipmaps ) this.generateMipmaps( texture );
  624. this.backend._setFramebuffer( renderContext );
  625. }
  626. /**
  627. * SetupS storage for internal depth/stencil buffers and bind to correct framebuffer.
  628. *
  629. * @param {WebGLRenderbuffer} renderbuffer - The render buffer.
  630. * @param {RenderContext} renderContext - The render context.
  631. * @param {number} samples - The MSAA sample count.
  632. * @param {boolean} [useMultisampledRTT=false] - Whether to use WEBGL_multisampled_render_to_texture or not.
  633. */
  634. setupRenderBufferStorage( renderbuffer, renderContext, samples, useMultisampledRTT = false ) {
  635. const { gl } = this;
  636. const renderTarget = renderContext.renderTarget;
  637. const { depthTexture, depthBuffer, stencilBuffer, width, height } = renderTarget;
  638. gl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer );
  639. if ( depthBuffer && ! stencilBuffer ) {
  640. let glInternalFormat = gl.DEPTH_COMPONENT24;
  641. if ( useMultisampledRTT === true ) {
  642. const multisampledRTTExt = this.extensions.get( 'WEBGL_multisampled_render_to_texture' );
  643. multisampledRTTExt.renderbufferStorageMultisampleEXT( gl.RENDERBUFFER, renderTarget.samples, glInternalFormat, width, height );
  644. } else if ( samples > 0 ) {
  645. if ( depthTexture && depthTexture.isDepthTexture ) {
  646. if ( depthTexture.type === gl.FLOAT ) {
  647. glInternalFormat = gl.DEPTH_COMPONENT32F;
  648. }
  649. }
  650. gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, glInternalFormat, width, height );
  651. } else {
  652. gl.renderbufferStorage( gl.RENDERBUFFER, glInternalFormat, width, height );
  653. }
  654. gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );
  655. } else if ( depthBuffer && stencilBuffer ) {
  656. if ( samples > 0 ) {
  657. gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, gl.DEPTH24_STENCIL8, width, height );
  658. } else {
  659. gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height );
  660. }
  661. gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );
  662. }
  663. }
  664. /**
  665. * Returns texture data as a typed array.
  666. *
  667. * @async
  668. * @param {Texture} texture - The texture to copy.
  669. * @param {number} x - The x coordinate of the copy origin.
  670. * @param {number} y - The y coordinate of the copy origin.
  671. * @param {number} width - The width of the copy.
  672. * @param {number} height - The height of the copy.
  673. * @param {number} faceIndex - The face index.
  674. * @return {Promise<TypedArray>} A Promise that resolves with a typed array when the copy operation has finished.
  675. */
  676. async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) {
  677. const { backend, gl } = this;
  678. const { textureGPU, glFormat, glType } = this.backend.get( texture );
  679. const fb = gl.createFramebuffer();
  680. gl.bindFramebuffer( gl.READ_FRAMEBUFFER, fb );
  681. const target = texture.isCubeTexture ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex : gl.TEXTURE_2D;
  682. gl.framebufferTexture2D( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, target, textureGPU, 0 );
  683. const typedArrayType = this._getTypedArrayType( glType );
  684. const bytesPerTexel = this._getBytesPerTexel( glType, glFormat );
  685. const elementCount = width * height;
  686. const byteLength = elementCount * bytesPerTexel;
  687. const buffer = gl.createBuffer();
  688. gl.bindBuffer( gl.PIXEL_PACK_BUFFER, buffer );
  689. gl.bufferData( gl.PIXEL_PACK_BUFFER, byteLength, gl.STREAM_READ );
  690. gl.readPixels( x, y, width, height, glFormat, glType, 0 );
  691. gl.bindBuffer( gl.PIXEL_PACK_BUFFER, null );
  692. await backend.utils._clientWaitAsync();
  693. const dstBuffer = new typedArrayType( byteLength / typedArrayType.BYTES_PER_ELEMENT );
  694. gl.bindBuffer( gl.PIXEL_PACK_BUFFER, buffer );
  695. gl.getBufferSubData( gl.PIXEL_PACK_BUFFER, 0, dstBuffer );
  696. gl.bindBuffer( gl.PIXEL_PACK_BUFFER, null );
  697. gl.deleteFramebuffer( fb );
  698. return dstBuffer;
  699. }
  700. /**
  701. * Returns the corresponding typed array type for the given WebGL data type.
  702. *
  703. * @private
  704. * @param {GLenum} glType - The WebGL data type.
  705. * @return {TypedArray.constructor} The typed array type.
  706. */
  707. _getTypedArrayType( glType ) {
  708. const { gl } = this;
  709. if ( glType === gl.UNSIGNED_BYTE ) return Uint8Array;
  710. if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) return Uint16Array;
  711. if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) return Uint16Array;
  712. if ( glType === gl.UNSIGNED_SHORT_5_6_5 ) return Uint16Array;
  713. if ( glType === gl.UNSIGNED_SHORT ) return Uint16Array;
  714. if ( glType === gl.UNSIGNED_INT ) return Uint32Array;
  715. if ( glType === gl.HALF_FLOAT ) return Uint16Array;
  716. if ( glType === gl.FLOAT ) return Float32Array;
  717. throw new Error( `Unsupported WebGL type: ${glType}` );
  718. }
  719. /**
  720. * Returns the bytes-per-texel value for the given WebGL data type and texture format.
  721. *
  722. * @private
  723. * @param {GLenum} glType - The WebGL data type.
  724. * @param {GLenum} glFormat - The WebGL texture format.
  725. * @return {number} The bytes-per-texel.
  726. */
  727. _getBytesPerTexel( glType, glFormat ) {
  728. const { gl } = this;
  729. let bytesPerComponent = 0;
  730. if ( glType === gl.UNSIGNED_BYTE ) bytesPerComponent = 1;
  731. if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ||
  732. glType === gl.UNSIGNED_SHORT_5_5_5_1 ||
  733. glType === gl.UNSIGNED_SHORT_5_6_5 ||
  734. glType === gl.UNSIGNED_SHORT ||
  735. glType === gl.HALF_FLOAT ) bytesPerComponent = 2;
  736. if ( glType === gl.UNSIGNED_INT ||
  737. glType === gl.FLOAT ) bytesPerComponent = 4;
  738. if ( glFormat === gl.RGBA ) return bytesPerComponent * 4;
  739. if ( glFormat === gl.RGB ) return bytesPerComponent * 3;
  740. if ( glFormat === gl.ALPHA ) return bytesPerComponent;
  741. }
  742. }
  743. export default WebGLTextureUtils;
粤ICP备19079148号