Texture.js 16 KB

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