WebGLUniforms.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. /**
  2. * @author tschw
  3. * @author Mugen87 / https://github.com/Mugen87
  4. * @author mrdoob / http://mrdoob.com/
  5. *
  6. * Uniforms of a program.
  7. * Those form a tree structure with a special top-level container for the root,
  8. * which you get by calling 'new WebGLUniforms( gl, program, renderer )'.
  9. *
  10. *
  11. * Properties of inner nodes including the top-level container:
  12. *
  13. * .seq - array of nested uniforms
  14. * .map - nested uniforms by name
  15. *
  16. *
  17. * Methods of all nodes except the top-level container:
  18. *
  19. * .setValue( gl, value, [renderer] )
  20. *
  21. * uploads a uniform value(s)
  22. * the 'renderer' parameter is needed for sampler uniforms
  23. *
  24. *
  25. * Static methods of the top-level container (renderer factorizations):
  26. *
  27. * .upload( gl, seq, values, renderer )
  28. *
  29. * sets uniforms in 'seq' to 'values[id].value'
  30. *
  31. * .seqWithValue( seq, values ) : filteredSeq
  32. *
  33. * filters 'seq' entries with corresponding entry in values
  34. *
  35. *
  36. * Methods of the top-level container (renderer factorizations):
  37. *
  38. * .setValue( gl, name, value )
  39. *
  40. * sets uniform with name 'name' to 'value'
  41. *
  42. * .set( gl, obj, prop )
  43. *
  44. * sets uniform from object and property with same name than uniform
  45. *
  46. * .setOptional( gl, obj, prop )
  47. *
  48. * like .set for an optional property of the object
  49. *
  50. */
  51. import { CubeTexture } from '../../textures/CubeTexture.js';
  52. import { Texture } from '../../textures/Texture.js';
  53. var emptyTexture = new Texture();
  54. var emptyTexture3d = new Texture();
  55. var emptyCubeTexture = new CubeTexture();
  56. // --- Base for inner nodes (including the root) ---
  57. function UniformContainer() {
  58. this.seq = [];
  59. this.map = {};
  60. }
  61. // --- Utilities ---
  62. // Array Caches (provide typed arrays for temporary by size)
  63. var arrayCacheF32 = [];
  64. var arrayCacheI32 = [];
  65. // Float32Array caches used for uploading Matrix uniforms
  66. var mat4array = new Float32Array( 16 );
  67. var mat3array = new Float32Array( 9 );
  68. var mat2array = new Float32Array( 4 );
  69. // Flattening for arrays of vectors and matrices
  70. function flatten( array, nBlocks, blockSize ) {
  71. var firstElem = array[ 0 ];
  72. if ( firstElem <= 0 || firstElem > 0 ) return array;
  73. // unoptimized: ! isNaN( firstElem )
  74. // see http://jacksondunstan.com/articles/983
  75. var n = nBlocks * blockSize,
  76. r = arrayCacheF32[ n ];
  77. if ( r === undefined ) {
  78. r = new Float32Array( n );
  79. arrayCacheF32[ n ] = r;
  80. }
  81. if ( nBlocks !== 0 ) {
  82. firstElem.toArray( r, 0 );
  83. for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) {
  84. offset += blockSize;
  85. array[ i ].toArray( r, offset );
  86. }
  87. }
  88. return r;
  89. }
  90. function arraysEqual( a, b ) {
  91. if ( a.length !== b.length ) return false;
  92. for ( var i = 0, l = a.length; i < l; i ++ ) {
  93. if ( a[ i ] !== b[ i ] ) return false;
  94. }
  95. return true;
  96. }
  97. function copyArray( a, b ) {
  98. for ( var i = 0, l = b.length; i < l; i ++ ) {
  99. a[ i ] = b[ i ];
  100. }
  101. }
  102. // Texture unit allocation
  103. function allocTexUnits( renderer, n ) {
  104. var r = arrayCacheI32[ n ];
  105. if ( r === undefined ) {
  106. r = new Int32Array( n );
  107. arrayCacheI32[ n ] = r;
  108. }
  109. for ( var i = 0; i !== n; ++ i )
  110. r[ i ] = renderer.allocTextureUnit();
  111. return r;
  112. }
  113. // --- Setters ---
  114. // Note: Defining these methods externally, because they come in a bunch
  115. // and this way their names minify.
  116. // Single scalar
  117. function setValue1f( gl, v ) {
  118. var cache = this.cache;
  119. if ( cache[ 0 ] === v ) return;
  120. gl.uniform1f( this.addr, v );
  121. cache[ 0 ] = v;
  122. }
  123. function setValue1i( gl, v ) {
  124. var cache = this.cache;
  125. if ( cache[ 0 ] === v ) return;
  126. gl.uniform1i( this.addr, v );
  127. cache[ 0 ] = v;
  128. }
  129. // Single float vector (from flat array or THREE.VectorN)
  130. function setValue2fv( gl, v ) {
  131. var cache = this.cache;
  132. if ( v.x !== undefined ) {
  133. if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {
  134. gl.uniform2f( this.addr, v.x, v.y );
  135. cache[ 0 ] = v.x;
  136. cache[ 1 ] = v.y;
  137. }
  138. } else {
  139. if ( arraysEqual( cache, v ) ) return;
  140. gl.uniform2fv( this.addr, v );
  141. copyArray( cache, v );
  142. }
  143. }
  144. function setValue3fv( gl, v ) {
  145. var cache = this.cache;
  146. if ( v.x !== undefined ) {
  147. if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {
  148. gl.uniform3f( this.addr, v.x, v.y, v.z );
  149. cache[ 0 ] = v.x;
  150. cache[ 1 ] = v.y;
  151. cache[ 2 ] = v.z;
  152. }
  153. } else if ( v.r !== undefined ) {
  154. if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) {
  155. gl.uniform3f( this.addr, v.r, v.g, v.b );
  156. cache[ 0 ] = v.r;
  157. cache[ 1 ] = v.g;
  158. cache[ 2 ] = v.b;
  159. }
  160. } else {
  161. if ( arraysEqual( cache, v ) ) return;
  162. gl.uniform3fv( this.addr, v );
  163. copyArray( cache, v );
  164. }
  165. }
  166. function setValue4fv( gl, v ) {
  167. var cache = this.cache;
  168. if ( v.x !== undefined ) {
  169. if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {
  170. gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
  171. cache[ 0 ] = v.x;
  172. cache[ 1 ] = v.y;
  173. cache[ 2 ] = v.z;
  174. cache[ 3 ] = v.w;
  175. }
  176. } else {
  177. if ( arraysEqual( cache, v ) ) return;
  178. gl.uniform4fv( this.addr, v );
  179. copyArray( cache, v );
  180. }
  181. }
  182. // Single matrix (from flat array or MatrixN)
  183. function setValue2fm( gl, v ) {
  184. var cache = this.cache;
  185. var elements = v.elements;
  186. if ( elements === undefined ) {
  187. if ( arraysEqual( cache, v ) ) return;
  188. gl.uniformMatrix2fv( this.addr, false, v );
  189. copyArray( cache, v );
  190. } else {
  191. if ( arraysEqual( cache, elements ) ) return;
  192. mat2array.set( elements );
  193. gl.uniformMatrix2fv( this.addr, false, mat2array );
  194. copyArray( cache, elements );
  195. }
  196. }
  197. function setValue3fm( gl, v ) {
  198. var cache = this.cache;
  199. var elements = v.elements;
  200. if ( elements === undefined ) {
  201. if ( arraysEqual( cache, v ) ) return;
  202. gl.uniformMatrix3fv( this.addr, false, v );
  203. copyArray( cache, v );
  204. } else {
  205. if ( arraysEqual( cache, elements ) ) return;
  206. mat3array.set( elements );
  207. gl.uniformMatrix3fv( this.addr, false, mat3array );
  208. copyArray( cache, elements );
  209. }
  210. }
  211. function setValue4fm( gl, v ) {
  212. var cache = this.cache;
  213. var elements = v.elements;
  214. if ( elements === undefined ) {
  215. if ( arraysEqual( cache, v ) ) return;
  216. gl.uniformMatrix4fv( this.addr, false, v );
  217. copyArray( cache, v );
  218. } else {
  219. if ( arraysEqual( cache, elements ) ) return;
  220. mat4array.set( elements );
  221. gl.uniformMatrix4fv( this.addr, false, mat4array );
  222. copyArray( cache, elements );
  223. }
  224. }
  225. // Single texture (2D / Cube)
  226. function setValueT1( gl, v, renderer ) {
  227. var cache = this.cache;
  228. var unit = renderer.allocTextureUnit();
  229. if ( cache[ 0 ] !== unit ) {
  230. gl.uniform1i( this.addr, unit );
  231. cache[ 0 ] = unit;
  232. }
  233. renderer.setTexture2D( v || emptyTexture, unit );
  234. }
  235. function setValueT3D1(gl, v, renderer) {
  236. var cache = this.cache;
  237. var unit = renderer.allocTextureUnit();
  238. if ( cache[ 0 ] !== unit ) {
  239. gl.uniform1i( this.addr, unit );
  240. cache[ 0 ] = unit;
  241. }
  242. renderer.setTexture3D( v || emptyTexture3d, unit );
  243. }
  244. function setValueT6( gl, v, renderer ) {
  245. var cache = this.cache;
  246. var unit = renderer.allocTextureUnit();
  247. if ( cache[ 0 ] !== unit ) {
  248. gl.uniform1i( this.addr, unit );
  249. cache[ 0 ] = unit;
  250. }
  251. renderer.setTextureCube( v || emptyCubeTexture, unit );
  252. }
  253. // Integer / Boolean vectors or arrays thereof (always flat arrays)
  254. function setValue2iv( gl, v ) {
  255. var cache = this.cache;
  256. if ( arraysEqual( cache, v ) ) return;
  257. gl.uniform2iv( this.addr, v );
  258. copyArray( cache, v );
  259. }
  260. function setValue3iv( gl, v ) {
  261. var cache = this.cache;
  262. if ( arraysEqual( cache, v ) ) return;
  263. gl.uniform3iv( this.addr, v );
  264. copyArray( cache, v );
  265. }
  266. function setValue4iv( gl, v ) {
  267. var cache = this.cache;
  268. if ( arraysEqual( cache, v ) ) return;
  269. gl.uniform4iv( this.addr, v );
  270. copyArray( cache, v );
  271. }
  272. // Helper to pick the right setter for the singular case
  273. function getSingularSetter( type ) {
  274. switch ( type ) {
  275. case 0x1406: return setValue1f; // FLOAT
  276. case 0x8b50: return setValue2fv; // _VEC2
  277. case 0x8b51: return setValue3fv; // _VEC3
  278. case 0x8b52: return setValue4fv; // _VEC4
  279. case 0x8b5a: return setValue2fm; // _MAT2
  280. case 0x8b5b: return setValue3fm; // _MAT3
  281. case 0x8b5c: return setValue4fm; // _MAT4
  282. case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES
  283. case 0x8B5F: return setValueT3D1; // SAMPLER_3D
  284. case 0x8b60: return setValueT6; // SAMPLER_CUBE
  285. case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL
  286. case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2
  287. case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3
  288. case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4
  289. }
  290. }
  291. // Array of scalars
  292. function setValue1fv( gl, v ) {
  293. var cache = this.cache;
  294. if ( arraysEqual( cache, v ) ) return;
  295. gl.uniform1fv( this.addr, v );
  296. copyArray( cache, v );
  297. }
  298. function setValue1iv( gl, v ) {
  299. var cache = this.cache;
  300. if ( arraysEqual( cache, v ) ) return;
  301. gl.uniform1iv( this.addr, v );
  302. copyArray( cache, v );
  303. }
  304. // Array of vectors (flat or from THREE classes)
  305. function setValueV2a( gl, v ) {
  306. var cache = this.cache;
  307. var data = flatten( v, this.size, 2 );
  308. if ( arraysEqual( cache, data ) ) return;
  309. gl.uniform2fv( this.addr, data );
  310. this.updateCache( data );
  311. }
  312. function setValueV3a( gl, v ) {
  313. var cache = this.cache;
  314. var data = flatten( v, this.size, 3 );
  315. if ( arraysEqual( cache, data ) ) return;
  316. gl.uniform3fv( this.addr, data );
  317. this.updateCache( data );
  318. }
  319. function setValueV4a( gl, v ) {
  320. var cache = this.cache;
  321. var data = flatten( v, this.size, 4 );
  322. if ( arraysEqual( cache, data ) ) return;
  323. gl.uniform4fv( this.addr, data );
  324. this.updateCache( data );
  325. }
  326. // Array of matrices (flat or from THREE clases)
  327. function setValueM2a( gl, v ) {
  328. var cache = this.cache;
  329. var data = flatten( v, this.size, 4 );
  330. if ( arraysEqual( cache, data ) ) return;
  331. gl.uniformMatrix2fv( this.addr, false, data );
  332. this.updateCache( data );
  333. }
  334. function setValueM3a( gl, v ) {
  335. var cache = this.cache;
  336. var data = flatten( v, this.size, 9 );
  337. if ( arraysEqual( cache, data ) ) return;
  338. gl.uniformMatrix3fv( this.addr, false, data );
  339. this.updateCache( data );
  340. }
  341. function setValueM4a( gl, v ) {
  342. var cache = this.cache;
  343. var data = flatten( v, this.size, 16 );
  344. if ( arraysEqual( cache, data ) ) return;
  345. gl.uniformMatrix4fv( this.addr, false, data );
  346. this.updateCache( data );
  347. }
  348. // Array of textures (2D / Cube)
  349. function setValueT1a( gl, v, renderer ) {
  350. var cache = this.cache;
  351. var n = v.length;
  352. var units = allocTexUnits( renderer, n );
  353. if ( arraysEqual( cache, units ) === false ) {
  354. gl.uniform1iv( this.addr, units );
  355. copyArray( cache, units );
  356. }
  357. for ( var i = 0; i !== n; ++ i ) {
  358. renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] );
  359. }
  360. }
  361. function setValueT6a( gl, v, renderer ) {
  362. var cache = this.cache;
  363. var n = v.length;
  364. var units = allocTexUnits( renderer, n );
  365. if ( arraysEqual( cache, units ) === false ) {
  366. gl.uniform1iv( this.addr, units );
  367. copyArray( cache, units );
  368. }
  369. for ( var i = 0; i !== n; ++ i ) {
  370. renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );
  371. }
  372. }
  373. // Helper to pick the right setter for a pure (bottom-level) array
  374. function getPureArraySetter( type ) {
  375. switch ( type ) {
  376. case 0x1406: return setValue1fv; // FLOAT
  377. case 0x8b50: return setValueV2a; // _VEC2
  378. case 0x8b51: return setValueV3a; // _VEC3
  379. case 0x8b52: return setValueV4a; // _VEC4
  380. case 0x8b5a: return setValueM2a; // _MAT2
  381. case 0x8b5b: return setValueM3a; // _MAT3
  382. case 0x8b5c: return setValueM4a; // _MAT4
  383. case 0x8b5e: return setValueT1a; // SAMPLER_2D
  384. case 0x8b60: return setValueT6a; // SAMPLER_CUBE
  385. case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL
  386. case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2
  387. case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3
  388. case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4
  389. }
  390. }
  391. // --- Uniform Classes ---
  392. function SingleUniform( id, activeInfo, addr ) {
  393. this.id = id;
  394. this.addr = addr;
  395. this.cache = [];
  396. this.setValue = getSingularSetter( activeInfo.type );
  397. // this.path = activeInfo.name; // DEBUG
  398. }
  399. function PureArrayUniform( id, activeInfo, addr ) {
  400. this.id = id;
  401. this.addr = addr;
  402. this.cache = [];
  403. this.size = activeInfo.size;
  404. this.setValue = getPureArraySetter( activeInfo.type );
  405. // this.path = activeInfo.name; // DEBUG
  406. }
  407. PureArrayUniform.prototype.updateCache = function ( data ) {
  408. var cache = this.cache;
  409. if ( data instanceof Float32Array && cache.length !== data.length ) {
  410. this.cache = new Float32Array( data.length );
  411. }
  412. copyArray( cache, data );
  413. };
  414. function StructuredUniform( id ) {
  415. this.id = id;
  416. UniformContainer.call( this ); // mix-in
  417. }
  418. StructuredUniform.prototype.setValue = function ( gl, value ) {
  419. // Note: Don't need an extra 'renderer' parameter, since samplers
  420. // are not allowed in structured uniforms.
  421. var seq = this.seq;
  422. for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  423. var u = seq[ i ];
  424. u.setValue( gl, value[ u.id ] );
  425. }
  426. };
  427. // --- Top-level ---
  428. // Parser - builds up the property tree from the path strings
  429. var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g;
  430. // extracts
  431. // - the identifier (member name or array index)
  432. // - followed by an optional right bracket (found when array index)
  433. // - followed by an optional left bracket or dot (type of subscript)
  434. //
  435. // Note: These portions can be read in a non-overlapping fashion and
  436. // allow straightforward parsing of the hierarchy that WebGL encodes
  437. // in the uniform names.
  438. function addUniform( container, uniformObject ) {
  439. container.seq.push( uniformObject );
  440. container.map[ uniformObject.id ] = uniformObject;
  441. }
  442. function parseUniform( activeInfo, addr, container ) {
  443. var path = activeInfo.name,
  444. pathLength = path.length;
  445. // reset RegExp object, because of the early exit of a previous run
  446. RePathPart.lastIndex = 0;
  447. while ( true ) {
  448. var match = RePathPart.exec( path ),
  449. matchEnd = RePathPart.lastIndex,
  450. id = match[ 1 ],
  451. idIsIndex = match[ 2 ] === ']',
  452. subscript = match[ 3 ];
  453. if ( idIsIndex ) id = id | 0; // convert to integer
  454. if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {
  455. // bare name or "pure" bottom-level array "[0]" suffix
  456. addUniform( container, subscript === undefined ?
  457. new SingleUniform( id, activeInfo, addr ) :
  458. new PureArrayUniform( id, activeInfo, addr ) );
  459. break;
  460. } else {
  461. // step into inner node / create it in case it doesn't exist
  462. var map = container.map, next = map[ id ];
  463. if ( next === undefined ) {
  464. next = new StructuredUniform( id );
  465. addUniform( container, next );
  466. }
  467. container = next;
  468. }
  469. }
  470. }
  471. // Root Container
  472. function WebGLUniforms( gl, program, renderer ) {
  473. UniformContainer.call( this );
  474. this.renderer = renderer;
  475. var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );
  476. for ( var i = 0; i < n; ++ i ) {
  477. var info = gl.getActiveUniform( program, i ),
  478. addr = gl.getUniformLocation( program, info.name );
  479. parseUniform( info, addr, this );
  480. }
  481. }
  482. WebGLUniforms.prototype.setValue = function ( gl, name, value ) {
  483. var u = this.map[ name ];
  484. if ( u !== undefined ) u.setValue( gl, value, this.renderer );
  485. };
  486. WebGLUniforms.prototype.setOptional = function ( gl, object, name ) {
  487. var v = object[ name ];
  488. if ( v !== undefined ) this.setValue( gl, name, v );
  489. };
  490. // Static interface
  491. WebGLUniforms.upload = function ( gl, seq, values, renderer ) {
  492. for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  493. var u = seq[ i ],
  494. v = values[ u.id ];
  495. if ( v.needsUpdate !== false ) {
  496. // note: always updating when .needsUpdate is undefined
  497. u.setValue( gl, v.value, renderer );
  498. }
  499. }
  500. };
  501. WebGLUniforms.seqWithValue = function ( seq, values ) {
  502. var r = [];
  503. for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  504. var u = seq[ i ];
  505. if ( u.id in values ) r.push( u );
  506. }
  507. return r;
  508. };
  509. export { WebGLUniforms };
粤ICP备19079148号