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