WebGLUniforms.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. /**
  2. * @author tschw
  3. *
  4. * Uniforms of a program.
  5. * Those form a tree structure with a special top-level container for the root,
  6. * which you get by calling 'new WebGLUniforms( gl, program, renderer )'.
  7. *
  8. *
  9. * Properties of inner nodes including the top-level container:
  10. *
  11. * .seq - array of nested uniforms
  12. * .map - nested uniforms by name
  13. *
  14. *
  15. * Methods of all nodes except the top-level container:
  16. *
  17. * .setValue( gl, value, [renderer] )
  18. *
  19. * uploads a uniform value(s)
  20. * the 'renderer' parameter is needed for sampler uniforms
  21. *
  22. *
  23. * Static methods of the top-level container (renderer factorizations):
  24. *
  25. * .upload( gl, seq, values, renderer )
  26. *
  27. * sets uniforms in 'seq' to 'values[id].value'
  28. *
  29. * .seqWithValue( seq, values ) : filteredSeq
  30. *
  31. * filters 'seq' entries with corresponding entry in values
  32. *
  33. *
  34. * Methods of the top-level container (renderer factorizations):
  35. *
  36. * .setValue( gl, name, value )
  37. *
  38. * sets uniform with name 'name' to 'value'
  39. *
  40. * .set( gl, obj, prop )
  41. *
  42. * sets uniform from object and property with same name than uniform
  43. *
  44. * .setOptional( gl, obj, prop )
  45. *
  46. * like .set for an optional property of the object
  47. *
  48. */
  49. import { CubeTexture } from '../../textures/CubeTexture';
  50. import { Texture } from '../../textures/Texture';
  51. var emptyTexture = new Texture();
  52. var emptyCubeTexture = new CubeTexture();
  53. // --- Base for inner nodes (including the root) ---
  54. function UniformContainer() {
  55. this.seq = [];
  56. this.map = {};
  57. }
  58. // --- Utilities ---
  59. // Array Caches (provide typed arrays for temporary by size)
  60. var arrayCacheF32 = [];
  61. var arrayCacheI32 = [];
  62. // Float32Array caches used for uploading Matrix uniforms
  63. var mat4array = new Float32Array( 16 );
  64. var mat3array = new Float32Array( 9 );
  65. // Flattening for arrays of vectors and matrices
  66. function flatten( array, nBlocks, blockSize ) {
  67. var firstElem = array[ 0 ];
  68. if ( firstElem <= 0 || firstElem > 0 ) return array;
  69. // unoptimized: ! isNaN( firstElem )
  70. // see http://jacksondunstan.com/articles/983
  71. var n = nBlocks * blockSize,
  72. r = arrayCacheF32[ n ];
  73. if ( r === undefined ) {
  74. r = new Float32Array( n );
  75. arrayCacheF32[ n ] = r;
  76. }
  77. if ( nBlocks !== 0 ) {
  78. firstElem.toArray( r, 0 );
  79. for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) {
  80. offset += blockSize;
  81. array[ i ].toArray( r, offset );
  82. }
  83. }
  84. return r;
  85. }
  86. // Texture unit allocation
  87. function allocTexUnits( renderer, n ) {
  88. var r = arrayCacheI32[ n ];
  89. if ( r === undefined ) {
  90. r = new Int32Array( n );
  91. arrayCacheI32[ n ] = r;
  92. }
  93. for ( var i = 0; i !== n; ++ i )
  94. r[ i ] = renderer.allocTextureUnit();
  95. return r;
  96. }
  97. // --- Setters ---
  98. // Note: Defining these methods externally, because they come in a bunch
  99. // and this way their names minify.
  100. // Single scalar
  101. function setValue1f( gl, v ) { gl.uniform1f( this.addr, v ) }
  102. function setValue1i( gl, v ) { gl.uniform1i( this.addr, v ) }
  103. // Single float vector (from flat array or THREE.VectorN)
  104. function setValue2fv( gl, v ) {
  105. if ( v.x === undefined ) gl.uniform2fv( this.addr, v );
  106. else gl.uniform2f( this.addr, v.x, v.y );
  107. }
  108. function setValue3fv( gl, v ) {
  109. if ( v.x !== undefined )
  110. gl.uniform3f( this.addr, v.x, v.y, v.z );
  111. else if ( v.r !== undefined )
  112. gl.uniform3f( this.addr, v.r, v.g, v.b );
  113. else
  114. gl.uniform3fv( this.addr, v );
  115. }
  116. function setValue4fv( gl, v ) {
  117. if ( v.x === undefined ) gl.uniform4fv( this.addr, v );
  118. else gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
  119. }
  120. // Single matrix (from flat array or MatrixN)
  121. function setValue2fm( gl, v ) {
  122. gl.uniformMatrix2fv( this.addr, false, v.elements || v );
  123. }
  124. function setValue3fm( gl, v ) {
  125. if ( v.elements === undefined ) {
  126. gl.uniformMatrix3fv( this.addr, false, v );
  127. } else {
  128. mat3array.set( v.elements );
  129. gl.uniformMatrix3fv( this.addr, false, mat3array );
  130. }
  131. }
  132. function setValue4fm( gl, v ) {
  133. if ( v.elements === undefined ) {
  134. gl.uniformMatrix4fv( this.addr, false, v );
  135. } else {
  136. mat4array.set( v.elements );
  137. gl.uniformMatrix4fv( this.addr, false, mat4array );
  138. }
  139. }
  140. // Single texture (2D / Cube)
  141. function setValueT1( gl, v, renderer ) {
  142. var unit = renderer.allocTextureUnit();
  143. gl.uniform1i( this.addr, unit );
  144. renderer.setTexture2D( v || emptyTexture, unit );
  145. }
  146. function setValueT6( gl, v, renderer ) {
  147. var unit = renderer.allocTextureUnit();
  148. gl.uniform1i( this.addr, unit );
  149. renderer.setTextureCube( v || emptyCubeTexture, unit );
  150. }
  151. // Integer / Boolean vectors or arrays thereof (always flat arrays)
  152. function setValue2iv( gl, v ) { gl.uniform2iv( this.addr, v ) }
  153. function setValue3iv( gl, v ) { gl.uniform3iv( this.addr, v ) }
  154. function setValue4iv( gl, v ) { gl.uniform4iv( this.addr, v ) }
  155. // Helper to pick the right setter for the singular case
  156. function getSingularSetter( type ) {
  157. switch ( type ) {
  158. case 0x1406: return setValue1f; // FLOAT
  159. case 0x8b50: return setValue2fv; // _VEC2
  160. case 0x8b51: return setValue3fv; // _VEC3
  161. case 0x8b52: return setValue4fv; // _VEC4
  162. case 0x8b5a: return setValue2fm; // _MAT2
  163. case 0x8b5b: return setValue3fm; // _MAT3
  164. case 0x8b5c: return setValue4fm; // _MAT4
  165. case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES
  166. case 0x8b60: return setValueT6; // SAMPLER_CUBE
  167. case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL
  168. case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2
  169. case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3
  170. case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4
  171. }
  172. }
  173. // Array of scalars
  174. function setValue1fv( gl, v ) { gl.uniform1fv( this.addr, v ) }
  175. function setValue1iv( gl, v ) { gl.uniform1iv( this.addr, v ) }
  176. // Array of vectors (flat or from THREE classes)
  177. function setValueV2a( gl, v ) {
  178. gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) );
  179. }
  180. function setValueV3a( gl, v ) {
  181. gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) );
  182. }
  183. function setValueV4a( gl, v ) {
  184. gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) );
  185. }
  186. // Array of matrices (flat or from THREE clases)
  187. function setValueM2a( gl, v ) {
  188. gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) );
  189. }
  190. function setValueM3a( gl, v ) {
  191. gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) );
  192. }
  193. function setValueM4a( gl, v ) {
  194. gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) );
  195. }
  196. // Array of textures (2D / Cube)
  197. function setValueT1a( gl, v, renderer ) {
  198. var n = v.length,
  199. units = allocTexUnits( renderer, n );
  200. gl.uniform1iv( this.addr, units );
  201. for ( var i = 0; i !== n; ++ i ) {
  202. renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] );
  203. }
  204. }
  205. function setValueT6a( gl, v, renderer ) {
  206. var n = v.length,
  207. units = allocTexUnits( renderer, n );
  208. gl.uniform1iv( this.addr, units );
  209. for ( var i = 0; i !== n; ++ i ) {
  210. renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );
  211. }
  212. }
  213. // Helper to pick the right setter for a pure (bottom-level) array
  214. function getPureArraySetter( type ) {
  215. switch ( type ) {
  216. case 0x1406: return setValue1fv; // FLOAT
  217. case 0x8b50: return setValueV2a; // _VEC2
  218. case 0x8b51: return setValueV3a; // _VEC3
  219. case 0x8b52: return setValueV4a; // _VEC4
  220. case 0x8b5a: return setValueM2a; // _MAT2
  221. case 0x8b5b: return setValueM3a; // _MAT3
  222. case 0x8b5c: return setValueM4a; // _MAT4
  223. case 0x8b5e: return setValueT1a; // SAMPLER_2D
  224. case 0x8b60: return setValueT6a; // SAMPLER_CUBE
  225. case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL
  226. case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2
  227. case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3
  228. case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4
  229. }
  230. }
  231. // --- Uniform Classes ---
  232. function SingleUniform( id, activeInfo, addr ) {
  233. this.id = id;
  234. this.addr = addr;
  235. this.setValue = getSingularSetter( activeInfo.type );
  236. // this.path = activeInfo.name; // DEBUG
  237. }
  238. function PureArrayUniform( id, activeInfo, addr ) {
  239. this.id = id;
  240. this.addr = addr;
  241. this.size = activeInfo.size;
  242. this.setValue = getPureArraySetter( activeInfo.type );
  243. // this.path = activeInfo.name; // DEBUG
  244. }
  245. function StructuredUniform( id ) {
  246. this.id = id;
  247. UniformContainer.call( this ); // mix-in
  248. }
  249. StructuredUniform.prototype.setValue = function ( gl, value ) {
  250. // Note: Don't need an extra 'renderer' parameter, since samplers
  251. // are not allowed in structured uniforms.
  252. var seq = this.seq;
  253. for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  254. var u = seq[ i ];
  255. u.setValue( gl, value[ u.id ] );
  256. }
  257. };
  258. // --- Top-level ---
  259. // Parser - builds up the property tree from the path strings
  260. var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g;
  261. // extracts
  262. // - the identifier (member name or array index)
  263. // - followed by an optional right bracket (found when array index)
  264. // - followed by an optional left bracket or dot (type of subscript)
  265. //
  266. // Note: These portions can be read in a non-overlapping fashion and
  267. // allow straightforward parsing of the hierarchy that WebGL encodes
  268. // in the uniform names.
  269. function addUniform( container, uniformObject ) {
  270. container.seq.push( uniformObject );
  271. container.map[ uniformObject.id ] = uniformObject;
  272. }
  273. function parseUniform( activeInfo, addr, container ) {
  274. var path = activeInfo.name,
  275. pathLength = path.length;
  276. // reset RegExp object, because of the early exit of a previous run
  277. RePathPart.lastIndex = 0;
  278. for ( ; ; ) {
  279. var match = RePathPart.exec( path ),
  280. matchEnd = RePathPart.lastIndex,
  281. id = match[ 1 ],
  282. idIsIndex = match[ 2 ] === ']',
  283. subscript = match[ 3 ];
  284. if ( idIsIndex ) id = id | 0; // convert to integer
  285. if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {
  286. // bare name or "pure" bottom-level array "[0]" suffix
  287. addUniform( container, subscript === undefined ?
  288. new SingleUniform( id, activeInfo, addr ) :
  289. new PureArrayUniform( id, activeInfo, addr ) );
  290. break;
  291. } else {
  292. // step into inner node / create it in case it doesn't exist
  293. var map = container.map, next = map[ id ];
  294. if ( next === undefined ) {
  295. next = new StructuredUniform( id );
  296. addUniform( container, next );
  297. }
  298. container = next;
  299. }
  300. }
  301. }
  302. // Root Container
  303. function WebGLUniforms( gl, program, renderer ) {
  304. UniformContainer.call( this );
  305. this.renderer = renderer;
  306. var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );
  307. for ( var i = 0; i < n; ++ i ) {
  308. var info = gl.getActiveUniform( program, i ),
  309. path = info.name,
  310. addr = gl.getUniformLocation( program, path );
  311. parseUniform( info, addr, this );
  312. }
  313. }
  314. WebGLUniforms.prototype.setValue = function ( gl, name, value ) {
  315. var u = this.map[ name ];
  316. if ( u !== undefined ) u.setValue( gl, value, this.renderer );
  317. };
  318. WebGLUniforms.prototype.setOptional = function ( gl, object, name ) {
  319. var v = object[ name ];
  320. if ( v !== undefined ) this.setValue( gl, name, v );
  321. };
  322. // Static interface
  323. WebGLUniforms.upload = function ( gl, seq, values, renderer ) {
  324. for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  325. var u = seq[ i ],
  326. v = values[ u.id ];
  327. if ( v.needsUpdate !== false ) {
  328. // note: always updating when .needsUpdate is undefined
  329. u.setValue( gl, v.value, renderer );
  330. }
  331. }
  332. };
  333. WebGLUniforms.seqWithValue = function ( seq, values ) {
  334. var r = [];
  335. for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  336. var u = seq[ i ];
  337. if ( u.id in values ) r.push( u );
  338. }
  339. return r;
  340. };
  341. export { WebGLUniforms };
粤ICP备19079148号