WebGLTextureUtils.js 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
  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_INT ) internalFormat = gl.DEPTH24_STENCIL8;
  197. if ( glType === gl.FLOAT ) internalFormat = gl.DEPTH_COMPONENT32F;
  198. }
  199. if ( glFormat === gl.DEPTH_STENCIL ) {
  200. if ( glType === gl.UNSIGNED_INT_24_8 ) internalFormat = gl.DEPTH24_STENCIL8;
  201. }
  202. if ( internalFormat === gl.R16F || internalFormat === gl.R32F ||
  203. internalFormat === gl.RG16F || internalFormat === gl.RG32F ||
  204. internalFormat === gl.RGBA16F || internalFormat === gl.RGBA32F ) {
  205. extensions.get( 'EXT_color_buffer_float' );
  206. }
  207. return internalFormat;
  208. }
  209. /**
  210. * Sets the texture parameters for the given texture.
  211. *
  212. * @param {GLenum} textureType - The texture type.
  213. * @param {Texture} texture - The texture.
  214. */
  215. setTextureParameters( textureType, texture ) {
  216. const { gl, extensions, backend } = this;
  217. gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
  218. gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
  219. gl.pixelStorei( gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
  220. gl.pixelStorei( gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE );
  221. gl.texParameteri( textureType, gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] );
  222. gl.texParameteri( textureType, gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] );
  223. if ( textureType === gl.TEXTURE_3D || textureType === gl.TEXTURE_2D_ARRAY ) {
  224. gl.texParameteri( textureType, gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] );
  225. }
  226. gl.texParameteri( textureType, gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] );
  227. const hasMipmaps = texture.mipmaps !== undefined && texture.mipmaps.length > 0;
  228. // follow WebGPU backend mapping for texture filtering
  229. const minFilter = texture.minFilter === LinearFilter && hasMipmaps ? LinearMipmapLinearFilter : texture.minFilter;
  230. gl.texParameteri( textureType, gl.TEXTURE_MIN_FILTER, filterToGL[ minFilter ] );
  231. if ( texture.compareFunction ) {
  232. gl.texParameteri( textureType, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE );
  233. gl.texParameteri( textureType, gl.TEXTURE_COMPARE_FUNC, compareToGL[ texture.compareFunction ] );
  234. }
  235. if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) {
  236. if ( texture.magFilter === NearestFilter ) return;
  237. if ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return;
  238. if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2
  239. if ( texture.anisotropy > 1 ) {
  240. const extension = extensions.get( 'EXT_texture_filter_anisotropic' );
  241. gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, backend.getMaxAnisotropy() ) );
  242. }
  243. }
  244. }
  245. /**
  246. * Creates a default texture for the given texture that can be used
  247. * as a placeholder until the actual texture is ready for usage.
  248. *
  249. * @param {Texture} texture - The texture to create a default texture for.
  250. */
  251. createDefaultTexture( texture ) {
  252. const { gl, backend, defaultTextures } = this;
  253. const glTextureType = this.getGLTextureType( texture );
  254. let textureGPU = defaultTextures[ glTextureType ];
  255. if ( textureGPU === undefined ) {
  256. textureGPU = gl.createTexture();
  257. backend.state.bindTexture( glTextureType, textureGPU );
  258. gl.texParameteri( glTextureType, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
  259. gl.texParameteri( glTextureType, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
  260. // gl.texImage2D( glTextureType, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
  261. defaultTextures[ glTextureType ] = textureGPU;
  262. }
  263. backend.set( texture, {
  264. textureGPU,
  265. glTextureType,
  266. isDefault: true
  267. } );
  268. }
  269. /**
  270. * Defines a texture on the GPU for the given texture object.
  271. *
  272. * @param {Texture} texture - The texture.
  273. * @param {Object} [options={}] - Optional configuration parameter.
  274. * @return {undefined}
  275. */
  276. createTexture( texture, options ) {
  277. const { gl, backend } = this;
  278. const { levels, width, height, depth } = options;
  279. const glFormat = backend.utils.convert( texture.format, texture.colorSpace );
  280. const glType = backend.utils.convert( texture.type );
  281. const glInternalFormat = this.getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture );
  282. const textureGPU = gl.createTexture();
  283. const glTextureType = this.getGLTextureType( texture );
  284. backend.state.bindTexture( glTextureType, textureGPU );
  285. this.setTextureParameters( glTextureType, texture );
  286. if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
  287. gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth );
  288. } else if ( texture.isData3DTexture ) {
  289. gl.texStorage3D( gl.TEXTURE_3D, levels, glInternalFormat, width, height, depth );
  290. } else if ( ! texture.isVideoTexture ) {
  291. gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height );
  292. }
  293. backend.set( texture, {
  294. textureGPU,
  295. glTextureType,
  296. glFormat,
  297. glType,
  298. glInternalFormat
  299. } );
  300. }
  301. /**
  302. * Uploads texture buffer data to the GPU memory.
  303. *
  304. * @param {WebGLBuffer} buffer - The buffer data.
  305. * @param {Texture} texture - The texture,
  306. */
  307. copyBufferToTexture( buffer, texture ) {
  308. const { gl, backend } = this;
  309. const { textureGPU, glTextureType, glFormat, glType } = backend.get( texture );
  310. const { width, height } = texture.source.data;
  311. gl.bindBuffer( gl.PIXEL_UNPACK_BUFFER, buffer );
  312. backend.state.bindTexture( glTextureType, textureGPU );
  313. gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, false );
  314. gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false );
  315. gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, 0 );
  316. gl.bindBuffer( gl.PIXEL_UNPACK_BUFFER, null );
  317. backend.state.unbindTexture();
  318. // debug
  319. // const framebuffer = gl.createFramebuffer();
  320. // gl.bindFramebuffer( gl.FRAMEBUFFER, framebuffer );
  321. // gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, glTextureType, textureGPU, 0 );
  322. // const readout = new Float32Array( width * height * 4 );
  323. // const altFormat = gl.getParameter( gl.IMPLEMENTATION_COLOR_READ_FORMAT );
  324. // const altType = gl.getParameter( gl.IMPLEMENTATION_COLOR_READ_TYPE );
  325. // gl.readPixels( 0, 0, width, height, altFormat, altType, readout );
  326. // gl.bindFramebuffer( gl.FRAMEBUFFER, null );
  327. // console.log( readout );
  328. }
  329. /**
  330. * Uploads the updated texture data to the GPU.
  331. *
  332. * @param {Texture} texture - The texture.
  333. * @param {Object} [options={}] - Optional configuration parameter.
  334. */
  335. updateTexture( texture, options ) {
  336. const { gl } = this;
  337. const { width, height } = options;
  338. const { textureGPU, glTextureType, glFormat, glType, glInternalFormat } = this.backend.get( texture );
  339. if ( texture.isRenderTargetTexture || ( textureGPU === undefined /* unsupported texture format */ ) )
  340. return;
  341. const getImage = ( source ) => {
  342. if ( source.isDataTexture ) {
  343. return source.image.data;
  344. } else if ( ( typeof HTMLImageElement !== 'undefined' && source instanceof HTMLImageElement ) ||
  345. ( typeof HTMLCanvasElement !== 'undefined' && source instanceof HTMLCanvasElement ) ||
  346. ( typeof ImageBitmap !== 'undefined' && source instanceof ImageBitmap ) ||
  347. source instanceof OffscreenCanvas ) {
  348. return source;
  349. }
  350. return source.data;
  351. };
  352. this.backend.state.bindTexture( glTextureType, textureGPU );
  353. this.setTextureParameters( glTextureType, texture );
  354. if ( texture.isCompressedTexture ) {
  355. const mipmaps = texture.mipmaps;
  356. const image = options.image;
  357. for ( let i = 0; i < mipmaps.length; i ++ ) {
  358. const mipmap = mipmaps[ i ];
  359. if ( texture.isCompressedArrayTexture ) {
  360. if ( texture.format !== gl.RGBA ) {
  361. if ( glFormat !== null ) {
  362. gl.compressedTexSubImage3D( gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data );
  363. } else {
  364. console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );
  365. }
  366. } else {
  367. gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data );
  368. }
  369. } else {
  370. if ( glFormat !== null ) {
  371. gl.compressedTexSubImage2D( gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data );
  372. } else {
  373. console.warn( 'Unsupported compressed texture format' );
  374. }
  375. }
  376. }
  377. } else if ( texture.isCubeTexture ) {
  378. const images = options.images;
  379. for ( let i = 0; i < 6; i ++ ) {
  380. const image = getImage( images[ i ] );
  381. gl.texSubImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, width, height, glFormat, glType, image );
  382. }
  383. } else if ( texture.isDataArrayTexture ) {
  384. const image = options.image;
  385. gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );
  386. } else if ( texture.isData3DTexture ) {
  387. const image = options.image;
  388. gl.texSubImage3D( gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data );
  389. } else if ( texture.isVideoTexture ) {
  390. texture.update();
  391. gl.texImage2D( glTextureType, 0, glInternalFormat, glFormat, glType, options.image );
  392. } else {
  393. const image = getImage( options.image );
  394. gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, image );
  395. }
  396. }
  397. /**
  398. * Generates mipmaps for the given texture.
  399. *
  400. * @param {Texture} texture - The texture.
  401. */
  402. generateMipmaps( texture ) {
  403. const { gl, backend } = this;
  404. const { textureGPU, glTextureType } = backend.get( texture );
  405. backend.state.bindTexture( glTextureType, textureGPU );
  406. gl.generateMipmap( glTextureType );
  407. }
  408. /**
  409. * Deallocates the render buffers of the given render target.
  410. *
  411. * @param {RenderTarget} renderTarget - The render target.
  412. */
  413. deallocateRenderBuffers( renderTarget ) {
  414. const { gl, backend } = this;
  415. // remove framebuffer reference
  416. if ( renderTarget ) {
  417. const renderContextData = backend.get( renderTarget );
  418. renderContextData.renderBufferStorageSetup = undefined;
  419. if ( renderContextData.framebuffers ) {
  420. for ( const cacheKey in renderContextData.framebuffers ) {
  421. gl.deleteFramebuffer( renderContextData.framebuffers[ cacheKey ] );
  422. }
  423. delete renderContextData.framebuffers;
  424. }
  425. if ( renderContextData.depthRenderbuffer ) {
  426. gl.deleteRenderbuffer( renderContextData.depthRenderbuffer );
  427. delete renderContextData.depthRenderbuffer;
  428. }
  429. if ( renderContextData.stencilRenderbuffer ) {
  430. gl.deleteRenderbuffer( renderContextData.stencilRenderbuffer );
  431. delete renderContextData.stencilRenderbuffer;
  432. }
  433. if ( renderContextData.msaaFrameBuffer ) {
  434. gl.deleteFramebuffer( renderContextData.msaaFrameBuffer );
  435. delete renderContextData.msaaFrameBuffer;
  436. }
  437. if ( renderContextData.msaaRenderbuffers ) {
  438. for ( let i = 0; i < renderContextData.msaaRenderbuffers.length; i ++ ) {
  439. gl.deleteRenderbuffer( renderContextData.msaaRenderbuffers[ i ] );
  440. }
  441. delete renderContextData.msaaRenderbuffers;
  442. }
  443. }
  444. }
  445. /**
  446. * Destroys the GPU data for the given texture object.
  447. *
  448. * @param {Texture} texture - The texture.
  449. */
  450. destroyTexture( texture ) {
  451. const { gl, backend } = this;
  452. const { textureGPU, renderTarget } = backend.get( texture );
  453. this.deallocateRenderBuffers( renderTarget );
  454. gl.deleteTexture( textureGPU );
  455. backend.delete( texture );
  456. }
  457. /**
  458. * Copies data of the given source texture to the given destination texture.
  459. *
  460. * @param {Texture} srcTexture - The source texture.
  461. * @param {Texture} dstTexture - The destination texture.
  462. * @param {Vector4?} [srcRegion=null] - The region of the source texture to copy.
  463. * @param {(Vector2|Vector3)?} [dstPosition=null] - The destination position of the copy.
  464. * @param {Number} [level=0] - The mip level to copy.
  465. */
  466. copyTextureToTexture( srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0 ) {
  467. const { gl, backend } = this;
  468. const { state } = this.backend;
  469. const { textureGPU: dstTextureGPU, glTextureType, glType, glFormat } = backend.get( dstTexture );
  470. let width, height, minX, minY;
  471. let dstX, dstY;
  472. if ( srcRegion !== null ) {
  473. width = srcRegion.max.x - srcRegion.min.x;
  474. height = srcRegion.max.y - srcRegion.min.y;
  475. minX = srcRegion.min.x;
  476. minY = srcRegion.min.y;
  477. } else {
  478. width = srcTexture.image.width;
  479. height = srcTexture.image.height;
  480. minX = 0;
  481. minY = 0;
  482. }
  483. if ( dstPosition !== null ) {
  484. dstX = dstPosition.x;
  485. dstY = dstPosition.y;
  486. } else {
  487. dstX = 0;
  488. dstY = 0;
  489. }
  490. state.bindTexture( glTextureType, dstTextureGPU );
  491. // As another texture upload may have changed pixelStorei
  492. // parameters, make sure they are correct for the dstTexture
  493. gl.pixelStorei( gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );
  494. gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );
  495. gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );
  496. gl.pixelStorei( gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );
  497. const currentUnpackRowLen = gl.getParameter( gl.UNPACK_ROW_LENGTH );
  498. const currentUnpackImageHeight = gl.getParameter( gl.UNPACK_IMAGE_HEIGHT );
  499. const currentUnpackSkipPixels = gl.getParameter( gl.UNPACK_SKIP_PIXELS );
  500. const currentUnpackSkipRows = gl.getParameter( gl.UNPACK_SKIP_ROWS );
  501. const currentUnpackSkipImages = gl.getParameter( gl.UNPACK_SKIP_IMAGES );
  502. const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ level ] : srcTexture.image;
  503. gl.pixelStorei( gl.UNPACK_ROW_LENGTH, image.width );
  504. gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, image.height );
  505. gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, minX );
  506. gl.pixelStorei( gl.UNPACK_SKIP_ROWS, minY );
  507. if ( srcTexture.isRenderTargetTexture || srcTexture.isDepthTexture ) {
  508. const srcTextureData = backend.get( srcTexture );
  509. const dstTextureData = backend.get( dstTexture );
  510. const srcRenderContextData = backend.get( srcTextureData.renderTarget );
  511. const dstRenderContextData = backend.get( dstTextureData.renderTarget );
  512. const srcFramebuffer = srcRenderContextData.framebuffers[ srcTextureData.cacheKey ];
  513. const dstFramebuffer = dstRenderContextData.framebuffers[ dstTextureData.cacheKey ];
  514. state.bindFramebuffer( gl.READ_FRAMEBUFFER, srcFramebuffer );
  515. state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, dstFramebuffer );
  516. let mask = gl.COLOR_BUFFER_BIT;
  517. if ( srcTexture.isDepthTexture ) mask = gl.DEPTH_BUFFER_BIT;
  518. gl.blitFramebuffer( minX, minY, width, height, dstX, dstY, width, height, mask, gl.NEAREST );
  519. state.bindFramebuffer( gl.READ_FRAMEBUFFER, null );
  520. state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null );
  521. } else {
  522. if ( srcTexture.isDataTexture ) {
  523. gl.texSubImage2D( gl.TEXTURE_2D, level, dstX, dstY, width, height, glFormat, glType, image.data );
  524. } else {
  525. if ( srcTexture.isCompressedTexture ) {
  526. gl.compressedTexSubImage2D( gl.TEXTURE_2D, level, dstX, dstY, image.width, image.height, glFormat, image.data );
  527. } else {
  528. gl.texSubImage2D( gl.TEXTURE_2D, level, dstX, dstY, width, height, glFormat, glType, image );
  529. }
  530. }
  531. }
  532. gl.pixelStorei( gl.UNPACK_ROW_LENGTH, currentUnpackRowLen );
  533. gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight );
  534. gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels );
  535. gl.pixelStorei( gl.UNPACK_SKIP_ROWS, currentUnpackSkipRows );
  536. gl.pixelStorei( gl.UNPACK_SKIP_IMAGES, currentUnpackSkipImages );
  537. // Generate mipmaps only when copying level 0
  538. if ( level === 0 && dstTexture.generateMipmaps ) gl.generateMipmap( gl.TEXTURE_2D );
  539. state.unbindTexture();
  540. }
  541. /**
  542. * Copies the current bound framebuffer to the given texture.
  543. *
  544. * @param {Texture} texture - The destination texture.
  545. * @param {RenderContext} renderContext - The render context.
  546. * @param {Vector4} rectangle - A four dimensional vector defining the origin and dimension of the copy.
  547. */
  548. copyFramebufferToTexture( texture, renderContext, rectangle ) {
  549. const { gl } = this;
  550. const { state } = this.backend;
  551. const { textureGPU } = this.backend.get( texture );
  552. const { x, y, z: width, w: height } = rectangle;
  553. const requireDrawFrameBuffer = texture.isDepthTexture === true || ( renderContext.renderTarget && renderContext.renderTarget.samples > 0 );
  554. const srcHeight = renderContext.renderTarget ? renderContext.renderTarget.height : this.backend.getDrawingBufferSize().y;
  555. if ( requireDrawFrameBuffer ) {
  556. const partial = ( x !== 0 || y !== 0 );
  557. let mask;
  558. let attachment;
  559. if ( texture.isDepthTexture === true ) {
  560. mask = gl.DEPTH_BUFFER_BIT;
  561. attachment = gl.DEPTH_ATTACHMENT;
  562. if ( renderContext.stencil ) {
  563. mask |= gl.STENCIL_BUFFER_BIT;
  564. }
  565. } else {
  566. mask = gl.COLOR_BUFFER_BIT;
  567. attachment = gl.COLOR_ATTACHMENT0;
  568. }
  569. if ( partial ) {
  570. const renderTargetContextData = this.backend.get( renderContext.renderTarget );
  571. const fb = renderTargetContextData.framebuffers[ renderContext.getCacheKey() ];
  572. const msaaFrameBuffer = renderTargetContextData.msaaFrameBuffer;
  573. state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
  574. state.bindFramebuffer( gl.READ_FRAMEBUFFER, msaaFrameBuffer );
  575. const flippedY = srcHeight - y - height;
  576. gl.blitFramebuffer( x, flippedY, x + width, flippedY + height, x, flippedY, x + width, flippedY + height, mask, gl.NEAREST );
  577. state.bindFramebuffer( gl.READ_FRAMEBUFFER, fb );
  578. state.bindTexture( gl.TEXTURE_2D, textureGPU );
  579. gl.copyTexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, x, flippedY, width, height );
  580. state.unbindTexture();
  581. } else {
  582. const fb = gl.createFramebuffer();
  583. state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
  584. gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, attachment, gl.TEXTURE_2D, textureGPU, 0 );
  585. gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, gl.NEAREST );
  586. gl.deleteFramebuffer( fb );
  587. }
  588. } else {
  589. state.bindTexture( gl.TEXTURE_2D, textureGPU );
  590. gl.copyTexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, x, srcHeight - height - y, width, height );
  591. state.unbindTexture();
  592. }
  593. if ( texture.generateMipmaps ) this.generateMipmaps( texture );
  594. this.backend._setFramebuffer( renderContext );
  595. }
  596. /**
  597. * SetupS storage for internal depth/stencil buffers and bind to correct framebuffer.
  598. *
  599. * @param {WebGLRenderbuffer} renderbuffer - The render buffer.
  600. * @param {RenderContext} renderContext - The render context.
  601. * @param {Number} samples - The MSAA sample count.
  602. */
  603. setupRenderBufferStorage( renderbuffer, renderContext, samples ) {
  604. const { gl } = this;
  605. const renderTarget = renderContext.renderTarget;
  606. const { depthTexture, depthBuffer, stencilBuffer, width, height } = renderTarget;
  607. gl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer );
  608. if ( depthBuffer && ! stencilBuffer ) {
  609. let glInternalFormat = gl.DEPTH_COMPONENT24;
  610. if ( samples > 0 ) {
  611. if ( depthTexture && depthTexture.isDepthTexture ) {
  612. if ( depthTexture.type === gl.FLOAT ) {
  613. glInternalFormat = gl.DEPTH_COMPONENT32F;
  614. }
  615. }
  616. gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, glInternalFormat, width, height );
  617. } else {
  618. gl.renderbufferStorage( gl.RENDERBUFFER, glInternalFormat, width, height );
  619. }
  620. gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );
  621. } else if ( depthBuffer && stencilBuffer ) {
  622. if ( samples > 0 ) {
  623. gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, gl.DEPTH24_STENCIL8, width, height );
  624. } else {
  625. gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height );
  626. }
  627. gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );
  628. }
  629. }
  630. /**
  631. * Returns texture data as a typed array.
  632. *
  633. * @async
  634. * @param {Texture} texture - The texture to copy.
  635. * @param {Number} x - The x coordinate of the copy origin.
  636. * @param {Number} y - The y coordinate of the copy origin.
  637. * @param {Number} width - The width of the copy.
  638. * @param {Number} height - The height of the copy.
  639. * @param {Number} faceIndex - The face index.
  640. * @return {Promise<TypedArray>} A Promise that resolves with a typed array when the copy operation has finished.
  641. */
  642. async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) {
  643. const { backend, gl } = this;
  644. const { textureGPU, glFormat, glType } = this.backend.get( texture );
  645. const fb = gl.createFramebuffer();
  646. gl.bindFramebuffer( gl.READ_FRAMEBUFFER, fb );
  647. const target = texture.isCubeTexture ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex : gl.TEXTURE_2D;
  648. gl.framebufferTexture2D( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, target, textureGPU, 0 );
  649. const typedArrayType = this._getTypedArrayType( glType );
  650. const bytesPerTexel = this._getBytesPerTexel( glType, glFormat );
  651. const elementCount = width * height;
  652. const byteLength = elementCount * bytesPerTexel;
  653. const buffer = gl.createBuffer();
  654. gl.bindBuffer( gl.PIXEL_PACK_BUFFER, buffer );
  655. gl.bufferData( gl.PIXEL_PACK_BUFFER, byteLength, gl.STREAM_READ );
  656. gl.readPixels( x, y, width, height, glFormat, glType, 0 );
  657. gl.bindBuffer( gl.PIXEL_PACK_BUFFER, null );
  658. await backend.utils._clientWaitAsync();
  659. const dstBuffer = new typedArrayType( byteLength / typedArrayType.BYTES_PER_ELEMENT );
  660. gl.bindBuffer( gl.PIXEL_PACK_BUFFER, buffer );
  661. gl.getBufferSubData( gl.PIXEL_PACK_BUFFER, 0, dstBuffer );
  662. gl.bindBuffer( gl.PIXEL_PACK_BUFFER, null );
  663. gl.deleteFramebuffer( fb );
  664. return dstBuffer;
  665. }
  666. /**
  667. * Returns the corresponding typed array type for the given WebGL data type.
  668. *
  669. * @private
  670. * @param {GLenum} glType - The WebGL data type.
  671. * @return {TypedArray.constructor} The typed array type.
  672. */
  673. _getTypedArrayType( glType ) {
  674. const { gl } = this;
  675. if ( glType === gl.UNSIGNED_BYTE ) return Uint8Array;
  676. if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) return Uint16Array;
  677. if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) return Uint16Array;
  678. if ( glType === gl.UNSIGNED_SHORT_5_6_5 ) return Uint16Array;
  679. if ( glType === gl.UNSIGNED_SHORT ) return Uint16Array;
  680. if ( glType === gl.UNSIGNED_INT ) return Uint32Array;
  681. if ( glType === gl.HALF_FLOAT ) return Uint16Array;
  682. if ( glType === gl.FLOAT ) return Float32Array;
  683. throw new Error( `Unsupported WebGL type: ${glType}` );
  684. }
  685. /**
  686. * Returns the bytes-per-texel value for the given WebGL data type and texture format.
  687. *
  688. * @private
  689. * @param {GLenum} glType - The WebGL data type.
  690. * @param {GLenum} glFormat - The WebGL texture format.
  691. * @return {Number} The bytes-per-texel.
  692. */
  693. _getBytesPerTexel( glType, glFormat ) {
  694. const { gl } = this;
  695. let bytesPerComponent = 0;
  696. if ( glType === gl.UNSIGNED_BYTE ) bytesPerComponent = 1;
  697. if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ||
  698. glType === gl.UNSIGNED_SHORT_5_5_5_1 ||
  699. glType === gl.UNSIGNED_SHORT_5_6_5 ||
  700. glType === gl.UNSIGNED_SHORT ||
  701. glType === gl.HALF_FLOAT ) bytesPerComponent = 2;
  702. if ( glType === gl.UNSIGNED_INT ||
  703. glType === gl.FLOAT ) bytesPerComponent = 4;
  704. if ( glFormat === gl.RGBA ) return bytesPerComponent * 4;
  705. if ( glFormat === gl.RGB ) return bytesPerComponent * 3;
  706. if ( glFormat === gl.ALPHA ) return bytesPerComponent;
  707. }
  708. }
  709. export default WebGLTextureUtils;
粤ICP备19079148号