Texture.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. import { EventDispatcher } from '../core/EventDispatcher.js';
  2. import {
  3. MirroredRepeatWrapping,
  4. ClampToEdgeWrapping,
  5. RepeatWrapping,
  6. UnsignedByteType,
  7. RGBAFormat,
  8. LinearMipmapLinearFilter,
  9. LinearFilter,
  10. UVMapping,
  11. NoColorSpace,
  12. } from '../constants.js';
  13. import { generateUUID } from '../math/MathUtils.js';
  14. import { Vector2 } from '../math/Vector2.js';
  15. import { Vector3 } from '../math/Vector3.js';
  16. import { Matrix3 } from '../math/Matrix3.js';
  17. import { Source } from './Source.js';
  18. let _textureId = 0;
  19. const _tempVec3 = /*@__PURE__*/ new Vector3();
  20. /**
  21. * Base class for all textures.
  22. *
  23. * Note: After the initial use of a texture, its dimensions, format, and type
  24. * cannot be changed. Instead, call {@link Texture#dispose} on the texture and instantiate a new one.
  25. *
  26. * @augments EventDispatcher
  27. */
  28. class Texture extends EventDispatcher {
  29. /**
  30. * Constructs a new texture.
  31. *
  32. * @param {?Object} [image=Texture.DEFAULT_IMAGE] - The image holding the texture data.
  33. * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping.
  34. * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value.
  35. * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value.
  36. * @param {number} [magFilter=LinearFilter] - The mag filter value.
  37. * @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value.
  38. * @param {number} [format=RGBAFormat] - The texture format.
  39. * @param {number} [type=UnsignedByteType] - The texture type.
  40. * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value.
  41. * @param {string} [colorSpace=NoColorSpace] - The color space.
  42. */
  43. constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) {
  44. super();
  45. /**
  46. * This flag can be used for type testing.
  47. *
  48. * @type {boolean}
  49. * @readonly
  50. * @default true
  51. */
  52. this.isTexture = true;
  53. /**
  54. * The ID of the texture.
  55. *
  56. * @name Texture#id
  57. * @type {number}
  58. * @readonly
  59. */
  60. Object.defineProperty( this, 'id', { value: _textureId ++ } );
  61. /**
  62. * The UUID of the material.
  63. *
  64. * @type {string}
  65. * @readonly
  66. */
  67. this.uuid = generateUUID();
  68. /**
  69. * The name of the material.
  70. *
  71. * @type {string}
  72. */
  73. this.name = '';
  74. /**
  75. * The data definition of a texture. A reference to the data source can be
  76. * shared across textures. This is often useful in context of spritesheets
  77. * where multiple textures render the same data but with different texture
  78. * transformations.
  79. *
  80. * @type {Source}
  81. */
  82. this.source = new Source( image );
  83. /**
  84. * An array holding user-defined mipmaps.
  85. *
  86. * @type {Array<Object>}
  87. */
  88. this.mipmaps = [];
  89. /**
  90. * How the texture is applied to the object. The value `UVMapping`
  91. * is the default, where texture or uv coordinates are used to apply the map.
  92. *
  93. * @type {(UVMapping|CubeReflectionMapping|CubeRefractionMapping|EquirectangularReflectionMapping|EquirectangularRefractionMapping|CubeUVReflectionMapping)}
  94. * @default UVMapping
  95. */
  96. this.mapping = mapping;
  97. /**
  98. * Lets you select the uv attribute to map the texture to. `0` for `uv`,
  99. * `1` for `uv1`, `2` for `uv2` and `3` for `uv3`.
  100. *
  101. * @type {number}
  102. * @default 0
  103. */
  104. this.channel = 0;
  105. /**
  106. * This defines how the texture is wrapped horizontally and corresponds to
  107. * *U* in UV mapping.
  108. *
  109. * @type {(RepeatWrapping|ClampToEdgeWrapping|MirroredRepeatWrapping)}
  110. * @default ClampToEdgeWrapping
  111. */
  112. this.wrapS = wrapS;
  113. /**
  114. * This defines how the texture is wrapped horizontally and corresponds to
  115. * *V* in UV mapping.
  116. *
  117. * @type {(RepeatWrapping|ClampToEdgeWrapping|MirroredRepeatWrapping)}
  118. * @default ClampToEdgeWrapping
  119. */
  120. this.wrapT = wrapT;
  121. /**
  122. * How the texture is sampled when a texel covers more than one pixel.
  123. *
  124. * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)}
  125. * @default LinearFilter
  126. */
  127. this.magFilter = magFilter;
  128. /**
  129. * How the texture is sampled when a texel covers less than one pixel.
  130. *
  131. * @type {(NearestFilter|NearestMipmapNearestFilter|NearestMipmapLinearFilter|LinearFilter|LinearMipmapNearestFilter|LinearMipmapLinearFilter)}
  132. * @default LinearMipmapLinearFilter
  133. */
  134. this.minFilter = minFilter;
  135. /**
  136. * The number of samples taken along the axis through the pixel that has the
  137. * highest density of texels. By default, this value is `1`. A higher value
  138. * gives a less blurry result than a basic mipmap, at the cost of more
  139. * texture samples being used.
  140. *
  141. * @type {number}
  142. * @default 0
  143. */
  144. this.anisotropy = anisotropy;
  145. /**
  146. * The format of the texture.
  147. *
  148. * @type {number}
  149. * @default RGBAFormat
  150. */
  151. this.format = format;
  152. /**
  153. * The default internal format is derived from {@link Texture#format} and {@link Texture#type} and
  154. * defines how the texture data is going to be stored on the GPU.
  155. *
  156. * This property allows to overwrite the default format.
  157. *
  158. * @type {?string}
  159. * @default null
  160. */
  161. this.internalFormat = null;
  162. /**
  163. * The data type of the texture.
  164. *
  165. * @type {number}
  166. * @default UnsignedByteType
  167. */
  168. this.type = type;
  169. /**
  170. * How much a single repetition of the texture is offset from the beginning,
  171. * in each direction U and V. Typical range is `0.0` to `1.0`.
  172. *
  173. * @type {Vector2}
  174. * @default (0,0)
  175. */
  176. this.offset = new Vector2( 0, 0 );
  177. /**
  178. * How many times the texture is repeated across the surface, in each
  179. * direction U and V. If repeat is set greater than `1` in either direction,
  180. * the corresponding wrap parameter should also be set to `RepeatWrapping`
  181. * or `MirroredRepeatWrapping` to achieve the desired tiling effect.
  182. *
  183. * @type {Vector2}
  184. * @default (1,1)
  185. */
  186. this.repeat = new Vector2( 1, 1 );
  187. /**
  188. * The point around which rotation occurs. A value of `(0.5, 0.5)` corresponds
  189. * to the center of the texture. Default is `(0, 0)`, the lower left.
  190. *
  191. * @type {Vector2}
  192. * @default (0,0)
  193. */
  194. this.center = new Vector2( 0, 0 );
  195. /**
  196. * How much the texture is rotated around the center point, in radians.
  197. * Positive values are counter-clockwise.
  198. *
  199. * @type {number}
  200. * @default 0
  201. */
  202. this.rotation = 0;
  203. /**
  204. * Whether to update the texture's uv-transformation {@link Texture#matrix}
  205. * from the properties {@link Texture#offset}, {@link Texture#repeat},
  206. * {@link Texture#rotation}, and {@link Texture#center}.
  207. *
  208. * Set this to `false` if you are specifying the uv-transform matrix directly.
  209. *
  210. * @type {boolean}
  211. * @default true
  212. */
  213. this.matrixAutoUpdate = true;
  214. /**
  215. * The uv-transformation matrix of the texture.
  216. *
  217. * @type {Matrix3}
  218. */
  219. this.matrix = new Matrix3();
  220. /**
  221. * Whether to generate mipmaps (if possible) for a texture.
  222. *
  223. * Set this to `false` if you are creating mipmaps manually.
  224. *
  225. * @type {boolean}
  226. * @default true
  227. */
  228. this.generateMipmaps = true;
  229. /**
  230. * If set to `true`, the alpha channel, if present, is multiplied into the
  231. * color channels when the texture is uploaded to the GPU.
  232. *
  233. * Note that this property has no effect when using `ImageBitmap`. You need to
  234. * configure premultiply alpha on bitmap creation instead.
  235. *
  236. * @type {boolean}
  237. * @default false
  238. */
  239. this.premultiplyAlpha = false;
  240. /**
  241. * If set to `true`, the texture is flipped along the vertical axis when
  242. * uploaded to the GPU.
  243. *
  244. * Note that this property has no effect when using `ImageBitmap`. You need to
  245. * configure the flip on bitmap creation instead.
  246. *
  247. * @type {boolean}
  248. * @default true
  249. */
  250. this.flipY = true;
  251. /**
  252. * Specifies the alignment requirements for the start of each pixel row in memory.
  253. * The allowable values are `1` (byte-alignment), `2` (rows aligned to even-numbered bytes),
  254. * `4` (word-alignment), and `8` (rows start on double-word boundaries).
  255. *
  256. * @type {number}
  257. * @default 4
  258. */
  259. this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
  260. /**
  261. * Textures containing color data should be annotated with `SRGBColorSpace` or `LinearSRGBColorSpace`.
  262. *
  263. * @type {string}
  264. * @default NoColorSpace
  265. */
  266. this.colorSpace = colorSpace;
  267. /**
  268. * An object that can be used to store custom data about the texture. It
  269. * should not hold references to functions as these will not be cloned.
  270. *
  271. * @type {Object}
  272. */
  273. this.userData = {};
  274. /**
  275. * This can be used to only update a subregion or specific rows of the texture (for example, just the
  276. * first 3 rows). Use the `addUpdateRange()` function to add ranges to this array.
  277. *
  278. * @type {Array<Object>}
  279. */
  280. this.updateRanges = [];
  281. /**
  282. * This starts at `0` and counts how many times {@link Texture#needsUpdate} is set to `true`.
  283. *
  284. * @type {number}
  285. * @readonly
  286. * @default 0
  287. */
  288. this.version = 0;
  289. /**
  290. * A callback function, called when the texture is updated (e.g., when
  291. * {@link Texture#needsUpdate} has been set to true and then the texture is used).
  292. *
  293. * @type {?Function}
  294. * @default null
  295. */
  296. this.onUpdate = null;
  297. /**
  298. * An optional back reference to the textures render target.
  299. *
  300. * @type {?(RenderTarget|WebGLRenderTarget)}
  301. * @default null
  302. */
  303. this.renderTarget = null;
  304. /**
  305. * Indicates whether a texture belongs to a render target or not.
  306. *
  307. * @type {boolean}
  308. * @readonly
  309. * @default false
  310. */
  311. this.isRenderTargetTexture = false;
  312. /**
  313. * Indicates if a texture should be handled like a texture array.
  314. *
  315. * @type {boolean}
  316. * @readonly
  317. * @default false
  318. */
  319. this.isArrayTexture = image && image.depth && image.depth > 1 ? true : false;
  320. /**
  321. * Indicates whether this texture should be processed by `PMREMGenerator` or not
  322. * (only relevant for render target textures).
  323. *
  324. * @type {number}
  325. * @readonly
  326. * @default 0
  327. */
  328. this.pmremVersion = 0;
  329. }
  330. /**
  331. * The width of the texture in pixels.
  332. */
  333. get width() {
  334. return this.source.getSize( _tempVec3 ).x;
  335. }
  336. /**
  337. * The height of the texture in pixels.
  338. */
  339. get height() {
  340. return this.source.getSize( _tempVec3 ).y;
  341. }
  342. /**
  343. * The depth of the texture in pixels.
  344. */
  345. get depth() {
  346. return this.source.getSize( _tempVec3 ).z;
  347. }
  348. /**
  349. * The image object holding the texture data.
  350. *
  351. * @type {?Object}
  352. */
  353. get image() {
  354. return this.source.data;
  355. }
  356. set image( value = null ) {
  357. this.source.data = value;
  358. }
  359. /**
  360. * Updates the texture transformation matrix from the from the properties {@link Texture#offset},
  361. * {@link Texture#repeat}, {@link Texture#rotation}, and {@link Texture#center}.
  362. */
  363. updateMatrix() {
  364. this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y );
  365. }
  366. /**
  367. * Adds a range of data in the data texture to be updated on the GPU.
  368. *
  369. * @param {number} start - Position at which to start update.
  370. * @param {number} count - The number of components to update.
  371. */
  372. addUpdateRange( start, count ) {
  373. this.updateRanges.push( { start, count } );
  374. }
  375. /**
  376. * Clears the update ranges.
  377. */
  378. clearUpdateRanges() {
  379. this.updateRanges.length = 0;
  380. }
  381. /**
  382. * Returns a new texture with copied values from this instance.
  383. *
  384. * @return {Texture} A clone of this instance.
  385. */
  386. clone() {
  387. return new this.constructor().copy( this );
  388. }
  389. /**
  390. * Copies the values of the given texture to this instance.
  391. *
  392. * @param {Texture} source - The texture to copy.
  393. * @return {Texture} A reference to this instance.
  394. */
  395. copy( source ) {
  396. this.name = source.name;
  397. this.source = source.source;
  398. this.mipmaps = source.mipmaps.slice( 0 );
  399. this.mapping = source.mapping;
  400. this.channel = source.channel;
  401. this.wrapS = source.wrapS;
  402. this.wrapT = source.wrapT;
  403. this.magFilter = source.magFilter;
  404. this.minFilter = source.minFilter;
  405. this.anisotropy = source.anisotropy;
  406. this.format = source.format;
  407. this.internalFormat = source.internalFormat;
  408. this.type = source.type;
  409. this.offset.copy( source.offset );
  410. this.repeat.copy( source.repeat );
  411. this.center.copy( source.center );
  412. this.rotation = source.rotation;
  413. this.matrixAutoUpdate = source.matrixAutoUpdate;
  414. this.matrix.copy( source.matrix );
  415. this.generateMipmaps = source.generateMipmaps;
  416. this.premultiplyAlpha = source.premultiplyAlpha;
  417. this.flipY = source.flipY;
  418. this.unpackAlignment = source.unpackAlignment;
  419. this.colorSpace = source.colorSpace;
  420. this.renderTarget = source.renderTarget;
  421. this.isRenderTargetTexture = source.isRenderTargetTexture;
  422. this.isArrayTexture = source.isArrayTexture;
  423. this.userData = JSON.parse( JSON.stringify( source.userData ) );
  424. this.needsUpdate = true;
  425. return this;
  426. }
  427. /**
  428. * Sets this texture's properties based on `values`.
  429. * @param {Object} values - A container with texture parameters.
  430. */
  431. setValues( values ) {
  432. for ( const key in values ) {
  433. const newValue = values[ key ];
  434. if ( newValue === undefined ) {
  435. console.warn( `THREE.Texture.setValues(): parameter '${ key }' has value of undefined.` );
  436. continue;
  437. }
  438. const currentValue = this[ key ];
  439. if ( currentValue === undefined ) {
  440. console.warn( `THREE.Texture.setValues(): property '${ key }' does not exist.` );
  441. continue;
  442. }
  443. if ( ( currentValue && newValue ) && ( currentValue.isVector2 && newValue.isVector2 ) ) {
  444. currentValue.copy( newValue );
  445. } else if ( ( currentValue && newValue ) && ( currentValue.isVector3 && newValue.isVector3 ) ) {
  446. currentValue.copy( newValue );
  447. } else if ( ( currentValue && newValue ) && ( currentValue.isMatrix3 && newValue.isMatrix3 ) ) {
  448. currentValue.copy( newValue );
  449. } else {
  450. this[ key ] = newValue;
  451. }
  452. }
  453. }
  454. /**
  455. * Serializes the texture into JSON.
  456. *
  457. * @param {?(Object|string)} meta - An optional value holding meta information about the serialization.
  458. * @return {Object} A JSON object representing the serialized texture.
  459. * @see {@link ObjectLoader#parse}
  460. */
  461. toJSON( meta ) {
  462. const isRootObject = ( meta === undefined || typeof meta === 'string' );
  463. if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {
  464. return meta.textures[ this.uuid ];
  465. }
  466. const output = {
  467. metadata: {
  468. version: 4.7,
  469. type: 'Texture',
  470. generator: 'Texture.toJSON'
  471. },
  472. uuid: this.uuid,
  473. name: this.name,
  474. image: this.source.toJSON( meta ).uuid,
  475. mapping: this.mapping,
  476. channel: this.channel,
  477. repeat: [ this.repeat.x, this.repeat.y ],
  478. offset: [ this.offset.x, this.offset.y ],
  479. center: [ this.center.x, this.center.y ],
  480. rotation: this.rotation,
  481. wrap: [ this.wrapS, this.wrapT ],
  482. format: this.format,
  483. internalFormat: this.internalFormat,
  484. type: this.type,
  485. colorSpace: this.colorSpace,
  486. minFilter: this.minFilter,
  487. magFilter: this.magFilter,
  488. anisotropy: this.anisotropy,
  489. flipY: this.flipY,
  490. generateMipmaps: this.generateMipmaps,
  491. premultiplyAlpha: this.premultiplyAlpha,
  492. unpackAlignment: this.unpackAlignment
  493. };
  494. if ( Object.keys( this.userData ).length > 0 ) output.userData = this.userData;
  495. if ( ! isRootObject ) {
  496. meta.textures[ this.uuid ] = output;
  497. }
  498. return output;
  499. }
  500. /**
  501. * Frees the GPU-related resources allocated by this instance. Call this
  502. * method whenever this instance is no longer used in your app.
  503. *
  504. * @fires Texture#dispose
  505. */
  506. dispose() {
  507. /**
  508. * Fires when the texture has been disposed of.
  509. *
  510. * @event Texture#dispose
  511. * @type {Object}
  512. */
  513. this.dispatchEvent( { type: 'dispose' } );
  514. }
  515. /**
  516. * Transforms the given uv vector with the textures uv transformation matrix.
  517. *
  518. * @param {Vector2} uv - The uv vector.
  519. * @return {Vector2} The transformed uv vector.
  520. */
  521. transformUv( uv ) {
  522. if ( this.mapping !== UVMapping ) return uv;
  523. uv.applyMatrix3( this.matrix );
  524. if ( uv.x < 0 || uv.x > 1 ) {
  525. switch ( this.wrapS ) {
  526. case RepeatWrapping:
  527. uv.x = uv.x - Math.floor( uv.x );
  528. break;
  529. case ClampToEdgeWrapping:
  530. uv.x = uv.x < 0 ? 0 : 1;
  531. break;
  532. case MirroredRepeatWrapping:
  533. if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
  534. uv.x = Math.ceil( uv.x ) - uv.x;
  535. } else {
  536. uv.x = uv.x - Math.floor( uv.x );
  537. }
  538. break;
  539. }
  540. }
  541. if ( uv.y < 0 || uv.y > 1 ) {
  542. switch ( this.wrapT ) {
  543. case RepeatWrapping:
  544. uv.y = uv.y - Math.floor( uv.y );
  545. break;
  546. case ClampToEdgeWrapping:
  547. uv.y = uv.y < 0 ? 0 : 1;
  548. break;
  549. case MirroredRepeatWrapping:
  550. if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
  551. uv.y = Math.ceil( uv.y ) - uv.y;
  552. } else {
  553. uv.y = uv.y - Math.floor( uv.y );
  554. }
  555. break;
  556. }
  557. }
  558. if ( this.flipY ) {
  559. uv.y = 1 - uv.y;
  560. }
  561. return uv;
  562. }
  563. /**
  564. * Setting this property to `true` indicates the engine the texture
  565. * must be updated in the next render. This triggers a texture upload
  566. * to the GPU and ensures correct texture parameter configuration.
  567. *
  568. * @type {boolean}
  569. * @default false
  570. * @param {boolean} value
  571. */
  572. set needsUpdate( value ) {
  573. if ( value === true ) {
  574. this.version ++;
  575. this.source.needsUpdate = true;
  576. }
  577. }
  578. /**
  579. * Setting this property to `true` indicates the engine the PMREM
  580. * must be regenerated.
  581. *
  582. * @type {boolean}
  583. * @default false
  584. * @param {boolean} value
  585. */
  586. set needsPMREMUpdate( value ) {
  587. if ( value === true ) {
  588. this.pmremVersion ++;
  589. }
  590. }
  591. }
  592. /**
  593. * The default image for all textures.
  594. *
  595. * @static
  596. * @type {?Image}
  597. * @default null
  598. */
  599. Texture.DEFAULT_IMAGE = null;
  600. /**
  601. * The default mapping for all textures.
  602. *
  603. * @static
  604. * @type {number}
  605. * @default UVMapping
  606. */
  607. Texture.DEFAULT_MAPPING = UVMapping;
  608. /**
  609. * The default anisotropy value for all textures.
  610. *
  611. * @static
  612. * @type {number}
  613. * @default 1
  614. */
  615. Texture.DEFAULT_ANISOTROPY = 1;
  616. export { Texture };
粤ICP备19079148号